opencv canny+findcontours 检测边缘

检测的步骤相信大家很熟悉了,我今天在这里是要说一个大坑。
首先,常见的步骤如下:

edge = cv2.Canny(img, low, high)
contours, hier = cv2.findContours(edge, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

但是,这样会丢边缘!
这是因为cv2.RETR_EXTERNAL只会检测最外层的边缘。也就是说,一旦你用cv2.Canny检测到的边缘有嵌套,那么内层的边缘都会丢失!

解决方案是使用cv2.RETR_CCOMP然后手动筛选。
当使用cv2.RETR_CCOMP时,hier中包含每个边缘的相对关系,以“后前子父”的索引值表示(-1表示无),且只会保留一层相对关系。因此,我们只要筛选出“父”索引值为-1的项即可。
实现如下:

def outer_contours(img):
    contours, hier = cv2.findContours(img, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
    return [contours[i] for i in range(len(hier[0])) if hier[0][i][3]==-1]
edge = cv2.Canny(img, low, high)
contours = outer_contours(edge)

详细的说,cv2.Canny得到的只是“反映边缘位置的二值图”。对cv2.Canny的结果使用cv2.findContours,一个Canny边缘会检测出内外两个contour。
举个栗子,假设cv2.Canny检测出来的边缘包含甲、乙、丙三条线,那么cv2.findContours会找到甲内、甲外、乙内、乙外、丙内、丙外6个contour。如果甲、乙、丙无嵌套关系,使用cv2.RETR_EXTERNAL时,筛选出的contour就是甲外、乙外、丙外这3个,正常工作。
但是,如果甲、乙有着甲>乙的嵌套关系,那么它们检测出的contours就有甲外>甲内>乙外>乙内的关系,此时使用cv2.RETR_EXTERNAL,就只会返回甲外乙外就丢失了。

彩蛋

这个方法9年前在StackOverflow上就有了:
https://stackoverflow.com/a/15867297/16599029
可惜很多教程还是在用会丢边缘的方法。

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值