Java中OpenCV基于ROI的图像二值化算法报错求助
问题重现
在Java中使用OpenCV实现分块ROI图像二值化时,在Mat roi = new Mat(gray, roi_rect)行抛出断言异常,异常信息如下:
OpenCV(4.7.0-dev) Error: Assertion failed (0 <= _colRange.start && _colRange.start <= _colRange.end && _colRange.end <= m.cols) in cv::Mat::Mat, file C:\GHA-OCV-2_work\ci-gha-workflow\ci-gha-workflow\opencv\modules\core\src\matrix.cpp, line 776
Exception in thread "main" CvException [org.opencv.core.CvException: cv::Exception: OpenCV(4.7.0-dev) C:\GHA-OCV-2_work\ci-gha-workflow\ci-gha-workflow\opencv\modules\core\src\matrix.cpp:776: error: (-215:Assertion failed) 0 <= _colRange.start && _colRange.start <= _colRange.end && _colRange.end <= m.cols in function 'cv::Mat::Mat']
原代码片段:
System.loadLibrary(org.opencv.core.Core.NATIVE_LIBRARY_NAME); String filename = "path/to/image"; Mat image = Imgcodecs.imread(filename, Imgcodecs.IMREAD_COLOR); int ch = 255, cw = 255; //---small block int h = image.rows(), w = image.cols(); //---height vs width Mat gray = new Mat(); Imgproc.cvtColor(image, gray, Imgproc.COLOR_BGR2GRAY); //--to gray //-----handle every small block for (int row = 0; row < h; row += ch) { for (int col = 0; col < w; col += cw) { Rect roi_rect = new Rect(col, row, cw, ch); //------small block:rectangle Mat roi = new Mat(gray, roi_rect); Imgproc.adaptiveThreshold(roi, roi, 255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY, 127, 10); //----binarization roi.copyTo(gray.submat(roi_rect));//----replace original block } } HighGui.imshow("binary", gray);
问题原因
循环中固定使用ch=255和cw=255作为块的宽高,但图像的实际宽/高不一定是255的整数倍。当循环到最后一块时,row + ch会超过图像实际高度h,col + cw会超过图像实际宽度w,导致创建的Rect范围超出了gray矩阵的边界,触发OpenCV的断言检查(要求ROI的起始和结束坐标必须在矩阵合法范围内)。
解决方案
在每次创建ROI矩形前,计算当前块的实际宽高,确保不超出图像边界:
System.loadLibrary(org.opencv.core.Core.NATIVE_LIBRARY_NAME); String filename = "path/to/image"; Mat image = Imgcodecs.imread(filename, Imgcodecs.IMREAD_COLOR); int ch = 255, cw = 255; //---small block int h = image.rows(), w = image.cols(); //---height vs width Mat gray = new Mat(); Imgproc.cvtColor(image, gray, Imgproc.COLOR_BGR2GRAY); //--to gray //-----handle every small block for (int row = 0; row < h; row += ch) { // 计算当前块的实际高度,避免超出图像底部 int currentBlockHeight = Math.min(ch, h - row); for (int col = 0; col < w; col += cw) { // 计算当前块的实际宽度,避免超出图像右边界 int currentBlockWidth = Math.min(cw, w - col); Rect roi_rect = new Rect(col, row, currentBlockWidth, currentBlockHeight); Mat roi = new Mat(gray, roi_rect); // adaptiveThreshold直接修改roi(gray的子矩阵),无需额外copyTo Imgproc.adaptiveThreshold(roi, roi, 255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY, 127, 10); } } HighGui.imshow("binary", gray); HighGui.waitKey(0); // 补充waitKey确保窗口正常显示
额外说明:原代码中的roi.copyTo(gray.submat(roi_rect))是多余的,因为roi本身是gray的子矩阵(共享内存),调用adaptiveThreshold修改roi时,已经直接修改了gray中对应的区域。
内容的提问来源于stack exchange,提问作者biotech7

