微信公众号:OpenCV学堂 关注获取更多计算机视觉与深度学习知识
击中击不中解释
OpenCV中除了常见腐蚀、膨胀、开闭操作之外,还有个非常有用的形态学操作,就是击中击不中。击中击不中也是基础形态学操作组合,它可以实现对象的细化跟剪枝操作。形态学操作都是基于各种形状的结构元素,基于输入图像,跟结构元素操作得到输出图像,击中击不中操作是在二值图像的模式匹配跟发现上非常有用,它是基于两个结构元素分别是B1跟B2、第一个结构元素B1在图像上完成腐蚀操作、然后第二个结构元素B2在图像完成腐蚀操作,然后合并最终输出得到。详解步骤如下:
1.使用B1完成对图像腐蚀 2.使用B2完成对图像腐蚀 3.对上述两步的输出结果进行位与操作输出最终结果
上述步骤可以把结构元素B1+B2得到一个结构元素B,可以直接完成一次操作就为击中击不中(Hit And Miss)。
举例如下,假设有如下的结构元素:
上图B1、B2对图像进行腐蚀,可以合并为B1 + B2,其中中心元素-1表示该点为背景像素点,上下左右四个点值为1表示前景像素点,角上四个点为0表示任意像素值均可。这里就是Hit And Miss的结构元素定义。使用该结构元素对输入图像二值图像完成操作,得到输出图像,图示如下:
通过设计不同的结构元素会得到不同的输出图像,图示如下:
OpenCV中的Hit And Miss
OpenCV中的击中击不中的函数,是跟开闭操作一样的功能函数
void cv::morphologyEx(
InputArray src,
OutputArray dst,
int op,
InputArray kernel,
Point anchor = Point(-1,-1),
int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar & borderValue = morphologyDefaultBorderValue()
)
参数解释:
剩下的参数默认即可,一般情况都不用设置。
代码演示-二值对象边缘发现
使用击中击不中实现二值对象的边缘提取,如下:
左侧是原图、右侧是基于击中击不中提取到轮廓边缘。
代码实现如下:
image = cv.imread("D:/images/my_mask.png")
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
cv.imshow("input", binary)
hmk1 = np.zeros((3, 3), dtype=np.int32)
hmk1[0, 0] = 1
hmk1[1, 1] = -1
print(hmk1)
h1 = cv.morphologyEx(binary, cv.MORPH_HITMISS, hmk1)
hmk1 = np.zeros((3, 3), dtype=np.int32)
hmk1[2, 2] = 1
hmk1[1, 1] = -1
print(hmk1)
h2 = cv.morphologyEx(binary, cv.MORPH_HITMISS, hmk1)
dst = cv.add(h1, h2)
cv.imshow("output", dst)
cv.waitKey(0)
cv.destroyAllWindows()
使用的结构元素为:
另外一个运行测试
原图
运行结果
掉坑的记录
OpenCV中的结构元素通过Mat来表示,默认的数据类是CV_8UC1, 但是对Hit And Miss来说,数据类型必须是 CV_32SC 如果使用默认数据类型就会导致-1溢出错误。但是多数初学者都不会意识到这个问题,一般如下定义结构元素,然后去执行Hit And Miss操作:
hmk1 = np.zeros((3, 3), dtype=np.uint8)
hmk1[0, 0] = 1
hmk1[1, 1] = -1
h1 = cv.morphologyEx(binary, cv.MORPH_HITMISS, hmk1)
输出结果跟原图一样,没有任何改变,很多人都会因此发狂,还反馈说OpenCV的Hit And Miss根本无法使用,我晕!这个时候只需要把结构元素定义改为
hmk1 = np.zeros((3, 3), dtype=np.int32)
hmk1[0, 0] = 1
hmk1[1, 1] = -1
print(hmk1)
h1 = cv.morphologyEx(binary, cv.MORPH_HITMISS, hmk1)
就会运行正确了。