OpenCV:如何获取孔洞的外部轮廓以实现图像精准重绘?
我刚好遇到过类似的问题,给你两个靠谱的解决方案,帮你精准重建带孔洞的图像~
问题根源
你用RETR_CCOMP获取的内部轮廓,其实是物体区域的内边界(也就是孔洞的内侧边缘),而非孔洞的外边界。直接用这个轮廓做fillPoly时,会因为轮廓本身的像素级偏移,导致重绘的孔洞尺寸偏差1像素。
解决方案1:反转图像提取孔洞外轮廓(最直观)
思路很简单:把原图像二值化后反转,此时原来的孔洞会变成白色前景,再用RETR_EXTERNAL提取反转图像的外部轮廓,这些轮廓就是原孔洞的精准外边界。
示例代码:
import cv2 import numpy as np # 1. 读取并二值化图像 img = cv2.imread("your_image.png", 0) _, binary_img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) # 2. 提取原物体的外部轮廓 obj_contours, _ = cv2.findContours(binary_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 3. 反转图像,提取孔洞的外轮廓(此时孔洞是前景) inv_binary = cv2.bitwise_not(binary_img) hole_contours, _ = cv2.findContours(inv_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 4. 重建图像 reconstructed = np.zeros_like(binary_img) # 填充物体区域 cv2.fillPoly(reconstructed, obj_contours, 255) # 挖去孔洞区域 cv2.fillPoly(reconstructed, hole_contours, 0) # 5. 保存轮廓(方便后续复用) np.save("object_contours.npy", obj_contours) np.save("hole_contours.npy", hole_contours)
解决方案2:调整内部轮廓坐标(适合已有RETR_CCOMP流程的场景)
如果不想改现有轮廓提取流程,可以直接对RETR_CCOMP得到的内部轮廓做坐标偏移,把内侧边缘转换成外侧边缘:
示例代码:
import cv2 import numpy as np img = cv2.imread("your_image.png", 0) _, binary_img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) # 用RETR_CCOMP获取轮廓和层级 contours, hierarchy = cv2.findContours(binary_img, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE) reconstructed = np.zeros_like(binary_img) for idx, cnt in enumerate(contours): # 判断是否为外部轮廓(层级的parent字段为-1) if hierarchy[0][idx][3] == -1: # 填充物体外部轮廓 cv2.fillPoly(reconstructed, [cnt], 255) else: # 对内部轮廓做坐标偏移,得到孔洞外边界 # 偏移方向如果不对,把-1改成+1即可,取决于你的图像前景/背景设置 offset_cnt = cnt - 1 # 挖去孔洞区域 cv2.fillPoly(reconstructed, [offset_cnt], 0)
注意事项
- 如果图像有噪声,建议先做形态学开运算(
cv2.morphologyEx)去除细小杂点,避免提取无效轮廓。 - 保存轮廓时,用
np.save可以直接存储轮廓数组,后续重建直接加载即可。 - 两种方法都能保证重绘的图像和原图像像素级一致,你可以根据现有代码流程选更顺手的。
内容的提问来源于stack exchange,提问作者bachinblack
相关产品推荐
相关产品推荐

