本节使用击中击不中变换,根据给定的结构元素(模式),来寻找二值图像中特定的结构。击中击不中变换是更高级形态学变换的基础,例如图像细化、剪枝等。本节使用的函数跟上一节一样,仍然是morphologyEx()。
击中击不中变换
形态学算子都是基于形状来处理图像的。这些算子用一个或多个结构元素来处理图像。前面讲过,最基础的两个形态学操作是腐蚀与膨胀。通过两者的结合有了更高级的形态学变换,开运算、闭运算、形态学梯度、顶帽变换、黑帽变换等(参考前两节的例子)。
击中击不中变换用于寻找二值图像中存在的某些结构(模式)。寻找邻域匹配第一个结构元素B1,同时不匹配第二个结构元素B2的像素。用数学公式表达上述操作如下:
因此,击中击不中变换由下面三步构成:
- 用结构元素B1来腐蚀图像A
- 用结构元素B2来腐蚀图像A的补集(Ac)
- 前两步结果的与运算
结构元素B1和B2可以结合为一个元素B。例如:
本例中,我们寻找这样一种结构模式,中间像素属于背景,其上下左右属于前景,其余领域像素忽略不计(背景为黑色,前景为白色)。然后用上面的核在输入图像中找这种结构。从下面的输出图像中可以看到,输入图像中只有一个位置满足要求。
注:大多数翻译都将hit or miss翻译为击中击不中变换,实际上是先在图像中,寻找满足第一个结构元素模式的结构,找到之后相当于“击中”。然后用第二个结构元素,直接在原图击中的位置进行匹配,如果不匹配,也就是“击不中”。如果满足以上两点,就是我们要找的结构,把中心像素置为255,作为输出。(这里结构元素中表示结构的值都为1)
代码及注释
// @tutorials imgproc module 6
// @文件 HitMiss.cpp
// @主题 击中击不中变换
// @修改 CVer
// @日期 2019年12月11日
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;
int main()
{//创建输入图像和核
Mat input_image = (Mat_<uchar>(8, 8) <<
0, 0, 0, 0, 0, 0, 0, 0,
0, 255, 255, 255, 0, 0, 0, 255,
0, 255, 255, 255, 0, 0, 0, 0,
0, 255, 255, 255, 0, 255, 0, 0,
0, 0, 255, 0, 0, 0, 0, 0,
0, 0, 255, 0, 0, 255, 255, 0,
0, 255, 0, 255, 0, 0, 255, 0,
0, 255, 255, 255, 0, 0, 0, 0);
Mat kernel = (Mat_<int>(3, 3) <<
0, 1, 0,
1, -1, 1,
0, 1, 0);
//创建输出图像,并进行变换
Mat output_image;
morphologyEx(input_image, output_image, MORPH_HITMISS, kernel);
//为便于观察,将输入图像、输出图像、核放大五十倍,显示
//一个小方块表示一个像素
const int rate = 50;
kernel = (kernel + 1) * 127;
kernel.convertTo(kernel, CV_8U);
resize(kernel, kernel, Size(), rate, rate, INTER_NEAREST);
imshow("kernel", kernel);
moveWindow("kernel", 0, 0);
resize(input_image, input_image, Size(), rate, rate, INTER_NEAREST);
imshow("Original", input_image);
moveWindow("Original", 0, 200);
resize(output_image, output_image, Size(), rate, rate, INTER_NEAREST);
imshow("Hit or Miss", output_image);
moveWindow("Hit or Miss", 500, 200);
waitKey(0);
return 0;
}
结果
程序中处理结果如下:
下面是不同的核,处理同一副图像得到的结果,核的值1表示图像中的白色结构,-1表示黑色结构,0表示忽略位置。