发布时间:2024-04-13 14:01
这次介绍的是和图像区域操作的相关问题和解决办法。
rect = cv2.boundingRect(contours[c])
在c++中,是返回的一个Rect类,可以使用rect.tl()和rect.br()返回左上角和右下角的坐标,而python中是返回一个tuple,只能直接使用:
而这个tuple返回了四个元素,分别的含义是:
把这个矩形在图像上画出来可以用下面的代码:
cv2.rectangle(ori_img, (rect[0], rect[1]), (rect[0] + rect[2], rect[1] + rect[3]), (0, 0, 255), 1)
我使用这两个操作是要用于区域的合并和分离。一般来说,合并是用膨胀操作,分离使用腐蚀操作。
这两个操作还可以用于去除图像周边的毛刺(一个是填充,一个是消除)
这两个过程很类似,只不过效果完全想法,类似一个做加法,一个做减法。
膨胀与腐蚀的基本过程
如果是三通道图,这两个过程也是分成3份,独立来处理的,假设有这么一副灰度图:
我们要在这个图上做膨胀操作:
这个框就被成为核(kernel),被处理的这个像素的值就依赖这个框的数据来进行计算(下面会提到这个计算过程),如果是边或者顶点的像素,就对应的去匹配一下,我懒的配图了。
核是可以被设计的,也就是说这个3*3的矩阵是可以被填充数据的,每个元素可以被填充0或者1。比如长这样:
0和1分别表示设计者是否要处理这个位置的数据,1是要处理,0是不处理。
核的处理过程
这个核覆盖上去之后,可以看到,为1的位置如下:
两种操作的区别就在如何对上述提到的这个像素点的计算过程:
找到核中为1覆盖的区域里的最大值,并把这个值赋值给这个像素点,相当于把图像本身的数据给扩充了一下,也就称之为膨胀。
如果是上图中标记的这个蓝色框中的数字,如果经过膨胀后就会变成255。
找到核中为1覆盖的区域里的最小值,并把这个值赋值给这个像素点,相当于把背景的数据给扩充了一下,也就称之为腐蚀
如果是上图中标记的这个蓝色框中的数字,如果经过腐蚀后就会变成0。
迭代过程,遍历原图中所有的像素点,都做这样一个计算,就得到了膨胀或者腐蚀之后的图。
代码实现
代码实现很简单:
膨胀:
kernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
bin_clo = cv2.dilate(img, kernel2, iterations=1)
其中的kernel2就是上面提到的核,通过opencv内置的枚举类型来初始化一个核,上面代码中的cv2.MORPH_RECT, (3, 3)就是初始化了一个3*3的矩形,这个矩形中的9个元素都是1。opencv还提供了其他的类型:
实现的效果就是,明显的胖了一圈:
上面dilate中的第二个函数iterations=1,表示做几次膨胀,就是上面描述的基本过程来几遍。在我设计的场景中,两个迭代,这两个矩形就接一起了,达到了我想要的合并区域的目的。
如果使用findContouers来输出上面的图像,就只有一个轮廓了。
腐蚀的代码类似:
kernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
bin_clo = cv2.erode(img, kernel2, iterations=2)
明显瘦了一圈。
在我碰到的问题里,还需要计算两个通道之间的重叠区域,我想到的办法是使用opencv中提供的bitwise_and方法
这个方法的逻辑是,将两个二值化图按对应位置匹配求逻辑与,作为新图像的位置的值:
当然这个方法还可以接受第三个参数,也就是mask,mask矩阵中元素为true的位置才进行处理(mask的使用可参见第一篇:
dst_green = cv2.bitwise_and(output_blue, dst_green)
两个参数都是二值化后的图层,后续我就可以使用连通域的函数来针对dst_green做处理,并计算面积了。