OpenCV中contourArea函数不符合预期的问题排查
轮廓排序异常与cv2.contourArea计算逻辑解析
操作流程
我按照OpenCV相关指南对乐高图片的轮廓按面积大小排序,步骤如下:
1. 计算边缘图
先对图片各通道分别做模糊、边缘检测,再合并得到最终边缘图:
# 加载图片并初始化累积边缘图 image = cv2.imread("legos.png") accumEdged = np.zeros(image.shape[:2], dtype="uint8") # 遍历蓝、绿、红三个通道 for chan in cv2.split(image): # 通道模糊、边缘检测,累积边缘结果 chan = cv2.medianBlur(chan, 11) edged = cv2.Canny(chan, 50, 200) accumEdged = cv2.bitwise_or(accumEdged, edged) # 保存边缘图 cv2.imwrite("images/Edge_Map.png", accumEdged)
2. 提取并绘制轮廓
定义绘制函数,提取轮廓后按面积排序并绘制:
def draw_contour(image, c, color): # 计算轮廓中心 M = cv2.moments(c) cX = int(M["m10"] / M["m00"]) cY = int(M["m01"] / M["m00"]) # 绘制轮廓及面积文本 cv2.drawContours(image, [c], -1, color, 2) cv2.putText(image, str(cv2.contourArea(c)), (cX - 10, cY - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.65, color,2) # 在累积边缘图中查找轮廓 cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) # 按轮廓面积降序排序 sorted_cnts = sorted(cnts, key=cv2.contourArea, reverse=True) # 绘制指定轮廓 draw_contour(image, sorted_cnts[0], (0, 0, 0)) # 第四个积木 draw_contour(image, sorted_cnts[4], (80, 0, 80)) # 小圆圈 draw_contour(image, sorted_cnts[6], (255, 255, 255)) # 第三个积木
问题现象
排序结果不符合预期:第5个轮廓的面积为229,实际比第7个面积为130的轮廓大,但排序后位置更靠后。
cv2.contourArea计算逻辑
cv2.contourArea基于**鞋带算法(Shoelace Formula,由格林公式推导而来)**计算,核心是通过轮廓点的坐标计算多边形包围区域的面积,需注意两个关键细节:
- 符号与轮廓方向:返回的面积值符号由轮廓的缠绕方向决定——逆时针轮廓返回正数,顺时针轮廓返回负数,面积的绝对值才是实际区域大小。
- 闭合性处理:算法默认输入轮廓是闭合的,即使输入的轮廓点未重复第一个点,也会自动闭合计算。
排序异常的原因与解决
你的问题大概率是部分轮廓返回了负的面积值,直接用原始值排序时,负数会被判定为小于正数,导致实际面积更大的负数值排在正数后面(比如-229 < 130,排序后130会在-229前面)。
解决方法是排序时使用面积的绝对值作为判断依据:
sorted_cnts = sorted(cnts, key=lambda x: abs(cv2.contourArea(x)), reverse=True)
相关图片
- 初始图片:

- 边缘图:

- 最终图片:

内容的提问来源于stack exchange,提问作者benjamin
相关产品推荐
相关产品推荐

