如何用OpenCV沿图像质心拆分计算两半像素比检测对称性
沿质心拆分计算图像对称度实现方案
核心思路
不需要对轮廓做复杂几何拆分,直接基于填充后的二值形状掩码做数组切片、像素统计即可,实现简单效率高,可直接达成目标效果。
已完成的阈值化、轮廓提取、质心计算逻辑可直接复用,仅需补充掩码生成、拆分线绘制、像素统计三个模块即可解决两个待解决问题:
- 沿质心拆分:通过质心坐标对二值掩码做行列切片,即可得到拆分后的两个独立区域
- 像素比值计算:统计两个切片区域内的形状像素总数,做比值计算即可量化对称度
具体实现步骤
1. 生成独立的形状填充掩码
不要直接在原图上做填充后再统计,单独生成和灰度图同尺寸的掩码,将轮廓区域填充为255(白色)、背景保留0(黑色),避免原图颜色干扰统计结果:
import numpy as np # 补充导入numpy依赖 filled_mask = np.zeros(gray.shape, dtype="uint8") cv2.drawContours(filled_mask, [c], -1, 255, thickness=cv2.FILLED)
同时给质心计算加异常判断,避免轮廓面积为0时触发除零报错:
for c in cnts: M = cv2.moments(c) if M["m00"] != 0: cX = int(M["m10"] / M["m00"]) cY = int(M["m01"] / M["m00"])
2. 沿质心拆分区域+绘制拆分线
质心坐标cX是质心在水平轴的位置,对应垂直拆分的竖线;cY是质心在垂直轴的位置,对应水平拆分的横线。
- 垂直拆分(左右分区):直接按列切片
- 左区掩码:
filled_mask[:, 0:cX] - 右区掩码:
filled_mask[:, cX:]
- 左区掩码:
- 水平拆分(上下分区):直接按行切片
- 上区掩码:
filled_mask[0:cY, :] - 下区掩码:
filled_mask[cY:, :]
在展示用的原图上绘制拆分线,即可复现目标效果:
- 上区掩码:
# 红色垂直拆分线 cv2.line(image, (cX, 0), (cX, image.shape[0]), (0,0,255), 2) # 绿色水平拆分线 cv2.line(image, (0, cY), (image.shape[1], cY), (0,255,0), 2)
3. 统计像素数计算对称比值
用cv2.countNonZero()统计每个切片内的白色像素(即形状区域)数量,计算比值时统一用较小像素数/较大像素数,结果范围在0~1之间,越接近1代表对应方向对称性越好:
# 垂直方向对称度 left_p = cv2.countNonZero(filled_mask[:, :cX]) right_p = cv2.countNonZero(filled_mask[:, cX:]) v_sym = min(left_p, right_p) / max(left_p, right_p) if max(left_p,right_p)!=0 else 0 # 水平方向对称度 top_p = cv2.countNonZero(filled_mask[:cY, :]) bottom_p = cv2.countNonZero(filled_mask[cY:, :]) h_sym = min(top_p, bottom_p) / max(top_p, bottom_p) if max(top_p,bottom_p)!=0 else 0
可直接将比值标注在图上方便查看:
cv2.putText(image, f"垂直对称度: {v_sym:.2f}", (10,30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0,0,255),2) cv2.putText(image, f"水平对称度: {h_sym:.2f}", (10,60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0,255,0),2)
参考素材
- 原始测试图像:

- 目标实现效果:

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

