说明:关于击中击不中问题还有很多疑点,代码实现存在问题,日后遇到在详细解决。
击中击不中变换
击中击不中变换是形状检测的一个基本工具。
设X为目标图像,B为结构元素,且B由两个不相交的部分
B
1
B_1
B1和
B
2
B_2
B2组成,即
B
=
B
1
∪
B
2
B=B_1∪B_2
B=B1∪B2,且
B
1
∩
B
2
=
ϕ
B_1∩B_2=\phi
B1∩B2=ϕ。则目标图像X被结构元素B击中的数学表达式:
X
⊗
B
=
{
x
∣
(
B
1
)
x
⊆
X
,
(
B
2
)
x
⊆
X
c
}
X \otimes B= \{x|(B_1)_x \subseteq X , (B_2)_x \subseteq X^c \}
X⊗B={x∣(B1)x⊆X,(B2)x⊆Xc}其中,
⊗
\otimes
⊗表示击中击不中变换的运算符。
HMT变换的含义是在目标图像 X被结构元素B击中的输出结果中,每点x必须同时满足两个条件: B 1 B_1 B1被x平移后包含在X内; B 2 B_2 B2被x平移后不包含在X内。
连通子图的匹配和定位
假设A的前景由若干个互不连通子图构成,而且相隔不是太近。另有图像G,需要检测G的前景是否匹配A的某个子图,也就是G的前景集与A的某个子图是否完全一样。若匹配则称为“击中”,否则称为“击不中”。当击中时,还要得到匹配子图在A中的位置,位置的含义是:在A中用两条水平线,两条垂直线恰好框住匹配子图,该矩形的左上角、右下角在A中的坐标。
以下图为例说明击中不击中的原理。图A背景为灰色,前景为白色,前景是三个不连续的子图:圆盘、三角形和正方形。其中正方形的高宽是30*30,
A
c
A^{c}
Ac是A的反转。现在设图G的前景色也是30×30的白正方形,那么问题是如何确认G的前景与A的正方形匹配,并得到正方块的位置。
下图示意了目标图像X被结构元素B击中的过程与结果。
Hit-miss原理
基于腐蚀运算的一个特性:腐蚀的过程相当于对可以填入结构元素的位置作标记的过程。
腐蚀中,虽然标记点取决于原点在结构元素中的相对位置,但输出图像的形状与此无关,改变原点的位置,只会导致输出结果发生平移。腐蚀的过程相当于对可以填入结构元素的位置作标记的过程,可以利用腐蚀确定目标的位置。
进行目标检测,既要检测到目标的内部,也要检测到目标的外部,即在一次运算中可以同时捕获内外标记。
由于以上两点。采用两个结构基元H、M,作为一个结构元素对 B = ( H , M ) B=(H,M) B=(H,M),一个探测目标内部,一个探测目标外部。当且仅当H平移到某一点可填入X的内部,M平移到该点可填入X的外部是,该点才在击中不击中变换中输出。
Hit-miss算法步骤
击中击不中变换基本原理是使用腐蚀;如果要在一幅图像A上找到B形状的目标,我们需要做的是:
- 建立一个比B大的模板W,使用此模板对图像A进行腐蚀,得到图像假设为Process1;
- 用B减去W,从而得到V模板(W-B);使用V模板对图像A的补集进行腐蚀,得到图像假设为Process2;
- Process1和Process2进行取交集;得到结果就是B的位置。这里的位置可能不是B的中心位置,要视W-B时对其的位置而异。
void hitmiss(Mat &src,Mat &kernel) {
//原图像src,待检测目标kernel
//1. 灰度化处理
Mat gray;
cvtColor(kernel, kernel, COLOR_BGR2GRAY);
cvtColor(src, gray, COLOR_BGR2GRAY);
//2. 二值化处理
Mat binary_src,binary_kernel;
int thresholdValue = 180;
threshold(gray, binary_src, thresholdValue, 255, THRESH_BINARY);
threshold(kernel, binary_kernel, thresholdValue, 255, THRESH_BINARY);
//3. 击中不击中检测
Mat hit_result, hit_result1, hit_result2;
//3.1 Process1: 使用待检测图像腐蚀原图
erode(binary_src, hit_result1, binary_kernel);
imshow("hit_result1", hit_result1);
//3.2 Process2: 图像A的补集,即src的补集
//原图像的补集
Mat bigBlackImg = Mat::ones(src.rows, src.cols, CV_8UC1);
Mat src1 = bigBlackImg * 255 - binary_src;
imshow("反置", src1);
//W-B --> 得到模板V,即kernel2
Mat blackImg = Mat::ones(binary_kernel.rows, binary_kernel.cols, CV_8UC1);
Mat kernel2 = blackImg * 255 - binary_kernel;
//3.3 腐蚀:使用V模板对图像A的补集进行腐蚀:
erode(src1, hit_result2, kernel2);
imshow("hit_result2", hit_result2);
//3.4 Process1与Process2取交集;得到的结果就是B的位置。
hit_result = hit_result1 & hit_result2;
imshow("击中击不中", hit_result);
}
int main() {
src= imread("D:/test/src.png");
Mat kernel = imread("D:/test/kernel.png");
if (src.empty()||kernel.empty()) {
cout << " input the image error!" << endl;
}
imshow("src", src);
imshow("kernel", kernel);
hitmiss(src, kernel);
waitKey(0);
return 0;
}
MorphologyEx中的击中参数:
void hitmiss2(Mat &src) {
//1. 灰度化处理
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
//2. 二值化处理
Mat binary;
int thresholdValue = 180;
threshold(gray, binary, thresholdValue, 255, THRESH_BINARY);
Mat kernel1 = (Mat_<int>(3, 3) <<
0, 1, 0,
1, -1, 1,
0, 1, 0);
Mat kernel2 = (Mat_<int>(3, 3) <<
0, 1, 0,
1, 0, 1,
0, 1, 0);
Mat kernel3 = (Mat_<int>(3, 3) <<
0, 0, 0,
0, 1, 0,
0, 0, 0);
Mat outImg, output_image1, output_image2, output_image3, output_image4;
Mat element = getStructuringElement(0, Size(3, 3), Point(-1, -1));
erode(binary, outImg, element);
imshow("腐蚀图", outImg);
morphologyEx(binary, output_image1, MORPH_HITMISS, kernel1);
morphologyEx(binary, output_image2, MORPH_HITMISS, kernel2);
morphologyEx(binary, output_image3, MORPH_HITMISS, kernel3);
imshow("Hit or Miss1", output_image1);
imshow("Hit or Miss2", output_image2);
imshow("Hit or Miss3", output_image3);
absdiff(output_image1, output_image2, output_image4);
imshow("Hit or Miss", output_image4);
}
参考:
数字图像处理(七)形态学处理之击中击不中变换、骨架提取