基于C#的OpenCV形状检测:忽略边缘标记干扰的实现方案
解决儿童涂鸦干扰下的页面Bounding Box检测问题
看起来你已经搭建了基础的检测流程,但儿童的涂鸦确实会给轮廓检测带来不小的麻烦——要么把矩形轮廓的点数搞成非4个,要么拉伸Bounding Box的范围。结合你的代码,我给你几个针对性的优化方向,既能抗涂鸦干扰,又能实现全自动检测:
1. 预处理阶段:用形态学操作过滤小涂鸦噪点
你的现有流程用了高斯模糊和阈值,但对于边缘的小涂鸦,单纯模糊不够。可以加一步形态学开运算(先腐蚀再膨胀),能有效去掉零散的小噪点,保留大的矩形结构:
// 创建一个3x3的矩形结构元素 Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3)); Mat processedThreshold = new Mat(); // 开运算过滤小噪点 Cv2.MorphologyEx(thresholdImage, processedThreshold, MorphTypes.Open, kernel); // 后续用处理后的图找轮廓,替代原来的thresholdImage Cv2.FindContours(processedThreshold, out contours, out hierachy, RetrievalModes.External, ContourApproximationModes.ApproxNone);
2. 轮廓筛选:多层校验排除异常轮廓
涂鸦会让轮廓变形,所以不能只靠approx.Length == 4来判断,要加多层校验:
- 凸包预处理:先对轮廓取凸包,消除涂鸦导致的凹点,再做多边形近似:
var hull = Cv2.ConvexHull(c); // 先获取凸包,过滤局部凹点 var peri = Cv2.ArcLength(hull, true); var approx = Cv2.ApproxPolyDP(hull, 0.02 * peri, true); - 长宽比校验:题目框一般是固定比例(比如接近正方形),过滤掉畸形的轮廓:
OpenCvSharp.Rect r = Cv2.BoundingRect(approx); float aspectRatio = (float)r.Width / r.Height; // 假设题目框长宽比在0.8-1.2之间,可根据实际调整 if (aspectRatio < 0.8 || aspectRatio > 1.2) continue; - 旋转矩形校验:用
MinAreaRect获取最小外接矩形,判断它的长宽比是否合理,避免被涂鸦拉伸的异常框:RotatedRect rr = Cv2.MinAreaRect(approx); float rectAspectRatio = Math.Max(rr.Size.Width, rr.Size.Height) / Math.Min(rr.Size.Width, rr.Size.Height); if (rectAspectRatio > 1.5) continue; // 过滤极端比例的矩形
3. 区域验证:优化像素占比和气泡关联逻辑
你原来用固定的非零像素数范围(4000-4700)不够灵活,换成像素占比更可靠——涂鸦会增加额外的非零像素,导致占比过高,而错误的框会占比过低:
Mat temp = new Mat(); var xCenter = (r.TopLeft.X + r.BottomRight.X) / 2; var yCenter = (r.TopLeft.Y + r.BottomRight.Y) / 2; Cv2.GetRectSubPix(thresholdImage, new OpenCvSharp.Size(r.Width, r.Height), new Point2f(xCenter, yCenter), temp); int total = Cv2.CountNonZero(temp); float pixelRatio = (float)total / (r.Width * r.Height); // 假设框内填充率在85%-95%之间,根据实际打印的框调整 if (pixelRatio < 0.85 || pixelRatio > 0.95) continue;
另外,气泡关联时可以加一个边缘距离校验,确保圆圈在框的合理区域内,而不是涂鸦附近的错误关联:
foreach (var cir in circles) { OpenCvSharp.Point circleCenter = new OpenCvSharp.Point(cir.Center.X, cir.Center.Y); // 不仅判断在矩形内,还要距离边缘至少10像素 if (r.Contains(circleCenter) && circleCenter.X - r.Left > 10 && r.Right - circleCenter.X > 10 && circleCenter.Y - r.Top > 10 && r.Bottom - circleCenter.Y > 10) { containsQuestionBubble = true; break; } }
4. 全自动ROI:自动定位页面边界
要避免用户手动定义ROI,你可以先找页面的最大轮廓作为全局检测范围:
// 遍历所有轮廓,找到面积最大的那个(就是页面边框) double maxArea = 0; Rect pageRoi = new Rect(); foreach (var cnt in contours) { double area = Cv2.ContourArea(cnt); if (area > maxArea) { maxArea = area; pageRoi = Cv2.BoundingRect(cnt); } } // 把后续的检测都限定在pageRoi内,比如裁剪图像 Mat roiImage = new Mat(thresholdImage, pageRoi); // 然后在roiImage上重新执行轮廓检测、圆圈检测等操作
如果页面有轻微倾斜,也可以用霍夫直线检测页面的四条边,拟合出页面的矩形ROI,效果会更精准。
把这些优化点整合到你的现有代码里,应该能大幅降低儿童涂鸦对Bounding Box检测的干扰,同时实现全自动的检测流程。
内容的提问来源于stack exchange,提问作者CJP
相关产品推荐
相关产品推荐

