OpenCV房屋屋顶边缘与轮廓检测一致性问题求助
屋顶轮廓检测一致性优化方案
1. 颜色空间掩码预处理,先过滤无关区域
屋顶通常有相对固定的颜色特征(比如红瓦、灰瓦、深色沥青),通过转换到HSV/Lab颜色空间,针对屋顶颜色范围生成掩码,直接排除树木、电线这类颜色差异大的干扰项,减少后续边缘检测的无效计算。
示例代码:
import cv2 import numpy as np img = cv2.imread("house.jpg") hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 针对红瓦屋顶的HSV范围(需根据你的数据集手动调整区间) lower_red1 = np.array([0, 120, 70]) upper_red1 = np.array([10, 255, 255]) mask1 = cv2.inRange(hsv, lower_red1, upper_red1) lower_red2 = np.array([170, 120, 70]) upper_red2 = np.array([180, 255, 255]) mask2 = cv2.inRange(hsv, lower_red2, upper_red2) # 合并掩码,保留屋顶候选区域 roof_mask = mask1 | mask2 filtered_img = cv2.bitwise_and(img, img, mask=roof_mask)
注:如果是灰瓦/沥青屋顶,需要重新调整HSV的上下限,可通过取几张样本图的屋顶像素值统计范围。
2. 基于形状特征的轮廓硬过滤
屋顶基本是多边形结构(三角形、矩形、梯形为主),通过轮廓的面积、近似多边形边数、长宽比等特征,过滤掉树叶、阴影、电线这类不符合屋顶形状的轮廓。
示例代码:
# 从过滤后的图像提取边缘 gray = cv2.cvtColor(filtered_img, cv2.COLOR_BGR2GRAY) edges = cv2.Canny(gray, 50, 150) contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) valid_roof_contours = [] for cnt in contours: # 过滤过小的轮廓(比如树叶、细电线) area = cv2.contourArea(cnt) if area < 500: continue # 多边形近似,获取轮廓边数 peri = cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, 0.02 * peri, True) # 屋顶通常为3-5边多边形,可根据样本调整 if 3 <= len(approx) <= 5: # 过滤细长型轮廓(比如电线) x, y, w, h = cv2.boundingRect(cnt) aspect_ratio = float(w) / h if 0.3 <= aspect_ratio <= 3: valid_roof_contours.append(cnt)
注:面积、边数、长宽比的阈值,建议手动统计10-15张样本图的屋顶参数后设定,匹配度会更高。
3. 自适应阈值+多尺度边缘检测,解决光照不均问题
针对背景与屋顶像素强度相近的情况,用自适应阈值代替全局阈值,结合多尺度Canny边缘检测,覆盖不同光照条件下的边缘特征,减少漏检和误检。
示例代码:
# 自适应阈值处理,应对局部光照不均 adaptive_thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2) # 多尺度Canny合并结果,覆盖不同对比度的边缘 edges1 = cv2.Canny(gray, 30, 100) edges2 = cv2.Canny(gray, 60, 150) edges3 = cv2.Canny(gray, 90, 200) combined_edges = cv2.bitwise_or(edges1, cv2.bitwise_or(edges2, edges3))
4. 针对性形态学变换,修复边缘缺口
不要用固定的膨胀/腐蚀,先通过开运算去除小噪点,再用闭运算填补屋顶边缘的缺口,强化完整轮廓。
示例代码:
# 开运算:先腐蚀再膨胀,清除小的亮噪点 kernel_open = np.ones((3, 3), np.uint8) opening = cv2.morphologyEx(combined_edges, cv2.MORPH_OPEN, kernel_open, iterations=1) # 闭运算:先膨胀再腐蚀,填补边缘的微小缺口 kernel_close = np.ones((5, 5), np.uint8) closing = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, kernel_close, iterations=1)
5. 区域生长辅助分割,应对复杂背景
如果上述方法仍有干扰,可尝试区域生长:从屋顶大概率存在的区域(比如图像中上部)选取种子点,分割出连续的屋顶区域后再提取轮廓。
示例代码:
h, w = gray.shape # 选取图像上部1/3区域作为种子候选区 seed_point = (w // 2, h // 6) mask = np.zeros_like(gray) # 区域生长,loDiff/upDiff控制生长的像素差异范围 cv2.floodFill(gray, mask, seed_point, 255, loDiff=10, upDiff=10) # 从生长后的掩码提取轮廓 contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
额外建议
因为你的数据集只有50张,建议先手动标注10-15张样本的屋顶特征(颜色范围、面积区间、形状参数),把这些统计值作为过滤规则的核心依据;同时可以将多种方法串联使用(颜色掩码→自适应阈值→形状过滤),逐步缩小候选区域,提升检测一致性。
内容的提问来源于stack exchange,提问作者Luke
相关产品推荐
相关产品推荐

