形态学的击中和击不中是形状检测的基本工具。
其基本原理为:(集合X为原二值化图像的像素集合,对X取反求得~X(非X, Y表示), 选择的结构元为s1, 对结构元s1取反的结构元为s2)
首先对用s1对X进行腐蚀得到A1,, 用s2对Y(即~X)进行腐蚀得到A2。最终结果C = A1 & A2。
对本次的样例图片,我们选取以下的结构元s1:
0 0 1 0 0
0 0 1 0 0
1 1 1 1 1
0 0 1 0 0
0 0 1 0 0
对其取反后的结构元s2为:
1 1 0 1 1
1 1 0 1 1
0 0 0 0 0
1 1 0 1 1
1 1 0 1 1
我们要做就是用s1腐蚀原图像X, 用s2去腐蚀对X取反的图像Y,然后对2个最终的结果相与即可。
公式为:
代码如下:其中m_dib为打开的bmp图像,m_dib.GetBits()为获得图像的像素
void CImgDoc::OnHit()
{
if( m_dib.IsValid() )
{
int width = m_dib.GetWidth();
int height = m_dib.GetHeight();
int pitch = ( width*8+31)/32*4; //步长
unsigned char* bufNotImg = new unsigned char[height*pitch]; //原图像的反
unsigned char* bufErodeImg = new unsigned char[height*pitch]; //定义好的结构元腐蚀原图像
unsigned char* bufErodeNotImg = new unsigned char[height*pitch]; //取反的结构元腐蚀取反的原图像
unsigned char* bufRes = new unsigned char[height*pitch]; //击中击不中目标图像
//初始化
memset(bufNotImg, 0x00, height*pitch);
memset(bufErodeImg, 0x00, height*pitch);
memset(bufErodeNotImg, 0x00, height*pitch);
memset(bufRes, 0x00, height*pitch);
//定义的腐蚀结构元
// 0 0 1 0 0
// 0 0 1 0 0
// 1 1 1 1 1
// 0 0 1 0 0
// 0 0 1 0 0
Pt s1[9];
s1[0].x = 0; s1[0].y = -2;
s1[1].x = 0; s1[1].y = -1;
s1[2].x = 0; s1[2].y = 0;
s1[3].x = 0; s1[3].y = 1;
s1[4].x = 0; s1[4].y = 2;
s1[5].x = -2; s1[5].y = 0;
s1[6].x = -1; s1[6].y = 0;
s1[7].x = 1; s1[7].y = 0;
s1[8].x = 2; s1[8].y = 0;
//将上面的结构元取反,共16个,比较戳的写法,将最中间的点设为原点,其余点即为相对于原点的坐标
Pt s2[16];
s2[0].x = -2; s2[0].y = -2;
s2[1].x = -1; s2[1].y = -2;
s2[2].x = 1; s2[2].y = -2;
s2[3].x = 2; s2[3].y = -2;
s2[4].x = -2; s2[4].y = -1;
s2[5].x = -1; s2[5].y = -1;
s2[6].x = 1; s2[6].y = -1;
s2[7].x = 2; s2[7].y = -1;
s2[8].x = -2; s2[8].y = 1;
s2[9].x = -1; s2[9].y = 1;
s2[10].x = 1; s2[10].y = 1;
s2[11].x = 2; s2[11].y = 1;
s2[12].x = -2; s2[12].y = 2;
s2[13].x = -1; s2[13].y = 2;
s2[14].x = 1; s2[14].y = 2;
s2[15].x = 2; s2[15].y = 2;
//1、求取反的图像
for(int y = 0; y < height; y ++)
{
for(int x = 0; x < width; x ++)
{
if(m_dib.GetBits()[y*pitch+x])
bufNotImg[y*pitch+x] = 0x0;
else bufNotImg[y*pitch+x] = 0xFF;
}
}
//2、经过结构元s1的源图像的腐蚀
//参数:原图像的缓冲区像素,宽度,高度,步长,目标腐蚀图像缓冲区,目标图像步长,结构元,结构元点数
Erode(m_dib.GetBits()+2*pitch+4, width-4, height-4, pitch,
bufErodeImg, pitch, s1, 9);
//3、经过结构元的反s2的源图像的反图像的腐蚀
Erode(bufNotImg+2*pitch+4, width-4, height-4, pitch,
bufErodeNotImg, pitch, s2, 16);
//4、经过求第3步的bufErodeImg和第4步的bufErodeNotImg求与
for(int y = 0; y < height; y ++)
for(int x = 0; x <width; x ++)
bufRes[y*pitch+x] = bufErodeImg[y*pitch+x] & bufErodeNotImg[y*pitch+x];
CDib::SaveImg8("G:\\bufNotImg.bmp", bufNotImg, width, height, pitch);
CDib::SaveImg8("G:\\bufErodeImg.bmp", bufErodeImg, width, height, pitch);
CDib::SaveImg8("G:\\bufErodeNotImg.bmp", bufErodeNotImg, width, height, pitch);
CDib::SaveImg8("G:\\bufRes.bmp", bufRes, width, height, pitch);
delete[] bufRes;
delete[] bufErodeNotImg;
delete[] bufErodeImg;
delete[] bufNotImg;
}
}
void Erode( const unsigned char* srcBuf, int w, int h, int pitch, unsigned char* dstBuf, int dstPitch, Pt pt[], int nCount ) { for( int y = 0; y < h; y++ ) { for( int x = 0; x < w; x++) { int n = 0; for( int i = 0; i < nCount; i++ ) { if( srcBuf[(y+pt[i].y)*pitch+(x+pt[i].x)] ) n++; } if( n == nCount ) dstBuf[y*dstPitch+x] = 0xFF; } } }