如何用Python统计含噪网格图中的阴影与非阴影矩形?技术咨询
解决含噪网格图中阴影/非阴影矩形统计的实用思路
我太懂这种困境了——带噪声的网格图总能把常规的CV方法搞崩,轮廓检测被小噪点干扰得一团糟,霍夫线检测出一堆冗余线条后又不知道怎么落地。结合我处理类似任务的经验,给你一套更针对性的推进方案:
第一步:先把噪声干掉,预处理是关键
所有后续操作的基础都是干净的图像,之前的方法失效大概率是预处理没做足:
- 自适应阈值化:别用普通的
cv2.threshold,网格图往往存在光照不均,cv2.adaptiveThreshold能根据局部区域的灰度值调整阈值,完美区分网格线和背景。代码示例:import cv2 import numpy as np img = cv2.imread("your_grid_image.jpg", 0) # 转灰度图 # 自适应高斯阈值,INV是让网格线变成白色,背景黑色 thresh = cv2.adaptiveThreshold( img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2 ) - 形态学清理:用开运算(先腐蚀再膨胀)去掉小噪点,闭运算填补网格线的细微缺口。选矩形核适配网格的形状:
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) cleaned_img = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=1) cleaned_img = cv2.morphologyEx(cleaned_img, cv2.MORPH_CLOSE, kernel, iterations=1)
第二步:二选一提取网格结构
根据你的网格规则程度,选下面两种方案:
方案A:霍夫线聚类(适合规则网格)
如果你已经用霍夫线检测出线条,接下来的核心是去重聚类,得到真正的网格线:
- 分类线条:把检测到的线分成水平和垂直两类(通过斜率判断,水平线条y坐标变化极小,垂直线条x坐标变化极小)
- 聚类去重:把同一位置的重复线条合并,比如对水平线条的y坐标排序后,用阈值合并接近的值(阈值根据你的网格间距调整):
# 假设霍夫线检测结果是lines = cv2.HoughLinesP(cleaned_img, ...) horizontal_ys = [] vertical_xs = [] for line in lines: x1, y1, x2, y2 = line[0] if abs(y2 - y1) < 5: # 水平线条 horizontal_ys.append((y1 + y2) // 2) elif abs(x2 - x1) < 5: # 垂直线条 vertical_xs.append((x1 + x2) // 2) # 去重水平线条 horizontal_ys = sorted(list(set(horizontal_ys))) filtered_h = [] prev_y = None for y in horizontal_ys: if prev_y is None or y - prev_y > 4: # 间距阈值,按需调整 filtered_h.append(y) prev_y = y # 垂直线条同理处理得到filtered_v - 生成矩形:有了干净的水平和垂直坐标列表,两两组合就能得到所有矩形的四个顶点(比如
(filtered_v[i], filtered_h[j])作为左上角,(filtered_v[i+1], filtered_h[j+1])作为右下角)
方案B:优化轮廓检测(适合不规则网格)
如果之前用轮廓检测失败,试试加筛选条件:
- 找轮廓后,用
cv2.approxPolyDP把轮廓近似成多边形,只保留4个顶点的轮廓(矩形特征) - 额外筛选:排除面积太小的噪点轮廓,检查轮廓的宽高比是否符合网格的大致比例,甚至可以验证四个角是否为直角
contours, _ = cv2.findContours(cleaned_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) rect_contours = [] for cnt in contours: area = cv2.contourArea(cnt) if area < 50 or area > 5000: # 过滤过小/过大的轮廓 continue # 近似多边形 epsilon = 0.02 * cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, epsilon, True) if len(approx) == 4: # 矩形是4个顶点 rect_contours.append(approx)
第三步:区分阴影与非阴影矩形
不管用哪种方法得到矩形,判断阴影的核心是区域灰度统计:
- 对每个矩形区域,提取灰度均值(如果是彩色图可以转成HSV取亮度通道)
- 提前统计非阴影区域的平均灰度值,设定一个阈值(比如阴影区域的均值是非阴影的70%-80%),低于阈值的就是阴影矩形
# 假设非阴影区域的均值已经通过采样得到non_shadow_mean for rect in rect_contours: # 获取矩形的边界框 x, y, w, h = cv2.boundingRect(rect) roi = img[y:y+h, x:x+w] mean_gray = np.mean(roi) if mean_gray < non_shadow_mean * 0.8: print("这是阴影矩形") else: print("这是非阴影矩形")
最后补充几个小技巧
- 如果网格线很细,可以先做一次膨胀操作加粗线条,方便后续检测
- 对于噪声特别大的图,可以试试高斯模糊(
cv2.GaussianBlur)配合自适应阈值,效果会更好 - 统计矩形数量时,注意排除那些不完整的矩形(比如只有三条边的),可以通过检查矩形的四条边是否都在网格线中,或者区域内的像素覆盖率
内容的提问来源于stack exchange,提问作者jayant rohra
相关产品推荐
相关产品推荐

