Java OpenCV无法正确检测卡片轮廓问题求助
OpenCV白色背景卡片轮廓检测解决方案
问题核心原因
当前代码存在两个关键问题:
- 阈值方向错误:用
THRESH_BINARY后,卡片是黑色、背景是白色,而findContours默认检测白色轮廓,导致把整个背景识别成一个大轮廓; - 未做降噪处理:阈值图像有大量微小噪点,切换
RETR_LIST模式时会把所有噪点都当成轮廓,直接导致轮廓数量爆炸。
修正步骤及代码调整
- 反转阈值方向
把阈值操作改为THRESH_BINARY_INV,让卡片区域变成白色、背景变成黑色,这样外部轮廓检测就能精准定位卡片:
Imgproc.threshold(grayImage, thresholdImage, 242, 255, Imgproc.THRESH_BINARY_INV);
- 添加形态学降噪
在阈值处理后,用闭操作填充卡片内部的小空隙,同时消除背景的微小噪点:
// 构造结构元素,执行闭操作降噪 Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(3, 3)); Imgproc.morphologyEx(thresholdImage, thresholdImage, Imgproc.MORPH_CLOSE, kernel);
- 过滤小面积轮廓
遍历轮廓时,通过面积筛选掉噪点对应的小轮廓(面积阈值根据实际卡片大小调整,示例用10000):
// 跳过面积过小的噪点轮廓 double area = Imgproc.contourArea(contour); if (area < 10000) { continue; }
完整修正后代码
public void convert() throws FileNotFoundException { // 加载图像 File file = ResourceUtils.getFile("classpath:input_image.png"); Mat image = Imgcodecs.imread(file.getAbsolutePath()); // 转灰度图 Mat grayImage = new Mat(); Imgproc.cvtColor(image, grayImage, Imgproc.COLOR_BGR2GRAY); // 反转阈值:卡片变白,背景变黑 Mat thresholdImage = new Mat(); Imgproc.threshold(grayImage, thresholdImage, 242, 255, Imgproc.THRESH_BINARY_INV); // 形态学闭操作降噪 Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(3, 3)); Imgproc.morphologyEx(thresholdImage, thresholdImage, Imgproc.MORPH_CLOSE, kernel); Imgcodecs.imwrite("thresh.png", thresholdImage); // 检测外部轮廓 List<MatOfPoint> contours = new ArrayList<>(); Mat hierarchy = new Mat(); Imgproc.findContours(thresholdImage, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); int emptySpace = 20; // 遍历并过滤轮廓 for (int i = 0; i < contours.size(); i++) { MatOfPoint contour = contours.get(i); // 过滤小噪点轮廓 double area = Imgproc.contourArea(contour); if (area < 10000) { continue; } // 裁剪卡片区域 Rect boundingRect = Imgproc.boundingRect(contour); Mat rect = new Mat(image, boundingRect); // 扶正卡片 RotatedRect rotatedRect = Imgproc.minAreaRect(new MatOfPoint2f(contour.toArray())); double angle = rotatedRect.angle; if (rotatedRect.size.width > rotatedRect.size.height) { angle += 90; } Mat rotationMatrix = Imgproc.getRotationMatrix2D(rotatedRect.center, angle, 1.0); Mat rotatedRectMat = new Mat(); Imgproc.warpAffine(rect, rotatedRectMat, rotationMatrix, rect.size()); // 添加白色边框 Mat rectWithBorder = new Mat(); Core.copyMakeBorder(rotatedRectMat, rectWithBorder, emptySpace, emptySpace, emptySpace, emptySpace, Core.BORDER_CONSTANT, new Scalar(255, 255, 255)); // 保存结果 Imgcodecs.imwrite(String.format("output_image_%d.png", i), rectWithBorder); } }
效果说明
- 修正后的阈值图像中,卡片为实心白色区域,背景无明显噪点;
RETR_EXTERNAL模式下会精准检测出3张卡片的轮廓,不会出现全图轮廓或大量噪点轮廓的问题;- 最终会输出3张单独裁剪并扶正的卡片图片,每张都带有指定宽度的白色边框。
内容的提问来源于stack exchange,提问作者andresmonc
相关产品推荐
相关产品推荐

