OpenCV 3.2移除黑色背景时触发cvtColor断言错误的解决咨询
解决OpenCV背景移除时的cvtColor断言错误
这个错误我之前处理过,核心原因很明确:你调用Imgproc.cvtColor(frame, hsvImg, Imgproc.COLOR_BGR2HSV)时传入的frame是Canny边缘检测后的单通道灰度图,但COLOR_BGR2HSV要求输入必须是3通道(BGR)或4通道(BGRA)的8位无符号/32位浮点图像,这就触发了OpenCV的断言检查失败。
方案一:修正输入逻辑(最直接的修复)
背景移除的逻辑应该基于原始彩色图像,而不是Canny边缘图。你需要调整doBackgroundRemoval的调用参数,同时优化方法内部的代码:
1. 修改调用处
把传入Canny结果改为传入原始彩色图:
// 原代码 // Mat fg= this.doBackgroundRemoval(frameImg); // 修改后 Mat fg= this.doBackgroundRemoval(colorImg);
2. 优化doBackgroundRemoval方法
修正变量作用域,确保HSV转换的合法性:
private Mat doBackgroundRemoval(Mat frame) throws Exception{ // 局部化变量,避免成员变量带来的意外问题 Mat hsvImg = new Mat(); List<Mat> hsvPlanes = new ArrayList<>(); Mat thresholdImg = new Mat(); // 现在输入是3通道BGR图像,符合COLOR_BGR2HSV的要求 Imgproc.cvtColor(frame, hsvImg, Imgproc.COLOR_BGR2HSV); Core.split(hsvImg, hsvPlanes); // 计算H通道的直方图阈值 double threshValue = this.getHistAverage(hsvImg, hsvPlanes.get(0)); Imgproc.threshold(hsvPlanes.get(0), thresholdImg, threshValue, 179.0, Imgproc.THRESH_BINARY_INV); // 模糊+形态学操作填充缝隙、平滑边缘 Imgproc.blur(thresholdImg, thresholdImg, new Size(5, 5)); // 使用结构化元素替代空Mat,效果更可控 Mat structElement = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(6,6)); Imgproc.dilate(thresholdImg, thresholdImg, structElement, new Point(-1, -1), 1); Imgproc.erode(thresholdImg, thresholdImg, structElement, new Point(-1, -1), 1); Imgproc.threshold(thresholdImg, thresholdImg, threshValue, 179.0, Imgproc.THRESH_BINARY); // 生成白色背景的前景图 Mat foreground = new Mat(frame.size(), CvType.CV_8UC3, new Scalar(255, 255, 255)); frame.copyTo(foreground, thresholdImg); return foreground; }
方案二:基于Canny边缘图的背景移除(如果想保留边缘输入)
如果坚持要用Canny结果做背景移除,不需要转HSV,直接对单通道边缘图做形态学操作生成掩码即可:
private Mat doBackgroundRemovalFromEdges(Mat edgeFrame, Mat originalColorImg) throws Exception{ // 将单通道边缘图转为3通道,方便后续操作 Mat edge3Channel = new Mat(); Core.merge(Arrays.asList(edgeFrame, edgeFrame, edgeFrame), edge3Channel); // 形态学膨胀+腐蚀,填充边缘内部的空白区域 Mat structElement = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(10,10)); Imgproc.dilate(edge3Channel, edge3Channel, structElement, new Point(-1,-1), 3); Imgproc.erode(edge3Channel, edge3Channel, structElement, new Point(-1,-1), 3); // 生成掩码 Mat mask = new Mat(); Imgproc.cvtColor(edge3Channel, mask, Imgproc.COLOR_BGR2GRAY); Imgproc.threshold(mask, mask, 10, 255, Imgproc.THRESH_BINARY); // 用掩码提取原始图的前景 Mat foreground = new Mat(originalColorImg.size(), CvType.CV_8UC3, new Scalar(255,255,255)); originalColorImg.copyTo(foreground, mask); return foreground; }
额外小提示
对于支票这类对比度比较明显的文档图像,也可以尝试更简单的Otsu自动阈值分割,直接处理灰度图:
Mat mask = new Mat(); Imgproc.threshold(grayImg, mask, 0, 255, Imgproc.THRESH_BINARY_INV | Imgproc.THRESH_OTSU);
内容的提问来源于stack exchange,提问作者jackjoesmith
相关产品推荐
相关产品推荐

