一 基本概念

所谓的灰度级膨胀和腐蚀即将而知图像的二值形态学运算推广到灰度图像上。对于一幅图像的腐蚀(膨胀)运算定义为对每个像素赋值为某个领域内输入图像灰度级的最小(或最大值)。在二值变换中的结构元素只代表一个领域,而在灰度级变化中,结构元素是一个二元函数,它规定了希望的局部灰度级性质。在求的邻域内最大值(或最小值)的同时,将结构元素的值相加(相减)。注意:这里其实主要是只非平坦结构元,对于平坦结构元,不需要做加减。

以下的灰度级运算将从数学的角度描述。这里引进两个概念。即本影和顶面函数

考虑一个n维欧式空间中的点集A(即一幅灰度图像)。并假定前(n-1)个坐标构成一个空间定义域(像素点坐标),而第n个坐标轴表示某点的函数值(对于灰度图像来说,n=3)。简单的说就是集合A为三维矩阵,其中点由坐标的三元表示,前两元表示像素的坐标,第三个元表示高度,即像素的亮度。

1.1集合A的顶面:为定义在(n-1)维底面上的函数,对于每个(n-1)元素来说,顶面就是A最后一个坐标的最高值。(个人理解:把灰度级理解为假象地貌中某个位置的海拔高度,顶面就是海拔高度函数)。

1.2顶面函数f的本影:本影的一般定义为,一个不透明物体遮挡光线而形成的完全阴影区域,在数学形态学中,函数f的本影定义为一个由f的顶面及其下所有点构成的集合。

二 灰度级腐蚀和膨胀

2.1 灰度级腐蚀

基本步骤:(1)计算它们的本影 (2)对本影采用二值腐蚀 (3)计算顶面即为结果

2.2灰度级膨胀

基本步骤:(1)计算它们的本影 (2)对本影采用二值膨胀 (3)计算顶面即为结果

以上只要理解好了本影就好了。对于灰度级的腐蚀和膨胀理解要借助一些图形,这里主要参考(MilanSonka 、Valav Hlavac、Roger Boyle 图像处理、分析与机器视觉(第三版) 十三章)

三 C语言实现(摘自百度文库)

具体的文档可在资源里下载http://download.csdn.net/detail/caiye917015406/4460983

下面的这段程序,实现了上述的腐蚀运算,针对的都是黑色点。参数中有一个BOOL变量,为真时,表示在水平方向进行腐蚀运算,即结构元素B为;否则在垂直方向上进行腐蚀运算,即结构元素B为。

BOOLErosion(HWND hWnd,BOOL Hori)
{DWORD OffBits,BufSize;
LPBITMAPINFOHEADER   lpImgData;LPSTR                  lpPtr;HLOCAL                 hTempImgData;LPBITMAPINFOHEADER    lpTempImgData;LPSTR                          lpTempPtr;HDC                     hDc;HFILE                   hf;LONG                   x,y;unsigned char             num;int                       i;
//为了处理方便,仍采用256级灰度图,不过只用调色板中0和255两项
if(NumColors!=256){ MessageBox(hWnd,"Must be a monobitmap with grayscale palette!",
"ErrorMessage",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER);
//BufSize为缓冲区大小BufSize=OffBits+bi.biHeight*LineBytes;//为新的缓冲区分配内存if((hTempImgData=LocalAlloc(LHND,BufSize))==NULL)
{MessageBox(hWnd,"Erroralloc memory!","Error Message",
MB_OK|MB_ICONEXCLAMATION);
return FALSE;}lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);   lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData);//拷贝头信息和位图数据    memcpy(lpTempImgData,lpImgData,BufSize);if(Hori){
//在水平方向进行腐蚀运算for(y=0;y<bi.biHeight;y++){//lpPtr指向原图数据,lpTempPtr指向新图数据lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes)+1;lpTempPtr=(char*)lpTempImgData+
(BufSize-LineBytes-y*LineBytes)+1;for(x=1;x<bi.biWidth-1;x++){
//注意为防止越界,x的范围从1到宽度-2num=(unsigned char)*lpPtr;if (num==0){  //因为腐蚀掉的是黑点,所以只对黑点处理*lpTempPtr=(unsigned char)0;  //先置成黑点for(i=0;i<3;i++){num=(unsigned char)*(lpPtr+i-1);if(num==255){
//自身及上下邻居中若有一个不是黑点,则将该点腐
//蚀成白点*lpTempPtr=(unsigned char)255;break;}}}
//原图中就是白点的,新图中仍是白点else *lpTempPtr=(unsigned char)255; //指向下一个象素lpPtr++;lpTempPtr++;}}}
else{
//在垂直方向进行腐蚀运算for(y=1;y<bi.biHeight-1;y++){ //注意为防止越界,y的范围从1到高度-2//lpPtr指向原图数据,lpTempPtr指向新图数据lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes);lpTempPtr=(char *)lpTempImgData+(BufSize-LineBytes-y*LineBytes);for(x=0;x<bi.biWidth;x++){num=(unsigned char)*lpPtr;if (num==0){ //因为腐蚀掉的是黑点,所以只对黑点处理*lpTempPtr=(unsigned char)0; //先置成黑点for(i=0;i<3;i++){num=(unsigned char)*(lpPtr+(i-1)*LineBytes);if(num==255){
//自身及上下邻居中若有一个不是黑点,则将该点腐
//蚀成白点*lpTempPtr=(unsigned char)255;break;}}}
//原图中就是白点的,新图中仍是白点else *lpTempPtr=(unsigned char)255;//指向下一个象素lpPtr++;lpTempPtr++;}}}if(hBitmap!=NULL)DeleteObject(hBitmap);hDc=GetDC(hWnd);    //产生新的位图hBitmap=CreateDIBitmap(hDc,(LPBITMAPINFOHEADER)lpTempImgData,
(LONG)CBM_INIT,
(LPSTR)lpTempImgData+
sizeof(BITMAPINFOHEADER)+ NumColors*sizeof(RGBQUAD),
(LPBITMAPINFO)lpTempImgData,DIB_RGB_COLORS);//起不同的结果文件名if(Hori)hf=_lcreat("c:\\herosion.bmp",0);elsehf=_lcreat("c:\\verosion.bmp",0);_lwrite(hf,(LPSTR)&bf,sizeof(BITMAPFILEHEADER));_lwrite(hf,(LPSTR)lpTempImgData,BufSize);_lclose(hf);//释放内存及资源ReleaseDC(hWnd,hDc);LocalUnlock(hTempImgData);LocalFree(hTempImgData);GlobalUnlock(hImgData);return TRUE;
}
BOOLErosion(HWND hWnd,BOOL Hori)
{DWORD OffBits,BufSize;
LPBITMAPINFOHEADER   lpImgData;LPSTR                  lpPtr;HLOCAL                 hTempImgData;LPBITMAPINFOHEADER    lpTempImgData;LPSTR                          lpTempPtr;HDC                     hDc;HFILE                   hf;LONG                   x,y;unsigned char             num;int                       i;
//为了处理方便,仍采用256级灰度图,不过只用调色板中0和255两项
if(NumColors!=256){ MessageBox(hWnd,"Must be a monobitmap with grayscale palette!",
"ErrorMessage",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER);
//BufSize为缓冲区大小BufSize=OffBits+bi.biHeight*LineBytes;//为新的缓冲区分配内存if((hTempImgData=LocalAlloc(LHND,BufSize))==NULL)
{MessageBox(hWnd,"Erroralloc memory!","Error Message",
MB_OK|MB_ICONEXCLAMATION);
return FALSE;}lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);   lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData);//拷贝头信息和位图数据    memcpy(lpTempImgData,lpImgData,BufSize);if(Hori){
//在水平方向进行腐蚀运算for(y=0;y<bi.biHeight;y++){//lpPtr指向原图数据,lpTempPtr指向新图数据lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes)+1;lpTempPtr=(char*)lpTempImgData+
(BufSize-LineBytes-y*LineBytes)+1;for(x=1;x<bi.biWidth-1;x++){
//注意为防止越界,x的范围从1到宽度-2num=(unsigned char)*lpPtr;if (num==0){  //因为腐蚀掉的是黑点,所以只对黑点处理*lpTempPtr=(unsigned char)0;  //先置成黑点for(i=0;i<3;i++){num=(unsigned char)*(lpPtr+i-1);if(num==255){
//自身及上下邻居中若有一个不是黑点,则将该点腐
//蚀成白点*lpTempPtr=(unsigned char)255;break;}}}
//原图中就是白点的,新图中仍是白点else *lpTempPtr=(unsigned char)255; //指向下一个象素lpPtr++;lpTempPtr++;}}}
else{
//在垂直方向进行腐蚀运算for(y=1;y<bi.biHeight-1;y++){ //注意为防止越界,y的范围从1到高度-2//lpPtr指向原图数据,lpTempPtr指向新图数据lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes);lpTempPtr=(char *)lpTempImgData+(BufSize-LineBytes-y*LineBytes);for(x=0;x<bi.biWidth;x++){num=(unsigned char)*lpPtr;if (num==0){ //因为腐蚀掉的是黑点,所以只对黑点处理*lpTempPtr=(unsigned char)0; //先置成黑点for(i=0;i<3;i++){num=(unsigned char)*(lpPtr+(i-1)*LineBytes);if(num==255){
//自身及上下邻居中若有一个不是黑点,则将该点腐
//蚀成白点*lpTempPtr=(unsigned char)255;break;}}}
//原图中就是白点的,新图中仍是白点else *lpTempPtr=(unsigned char)255;//指向下一个象素lpPtr++;lpTempPtr++;}}}if(hBitmap!=NULL)DeleteObject(hBitmap);hDc=GetDC(hWnd);    //产生新的位图hBitmap=CreateDIBitmap(hDc,(LPBITMAPINFOHEADER)lpTempImgData,
(LONG)CBM_INIT,
(LPSTR)lpTempImgData+
sizeof(BITMAPINFOHEADER)+ NumColors*sizeof(RGBQUAD),
(LPBITMAPINFO)lpTempImgData,DIB_RGB_COLORS);//起不同的结果文件名if(Hori)hf=_lcreat("c:\\herosion.bmp",0);elsehf=_lcreat("c:\\verosion.bmp",0);_lwrite(hf,(LPSTR)&bf,sizeof(BITMAPFILEHEADER));_lwrite(hf,(LPSTR)lpTempImgData,BufSize);_lclose(hf);//释放内存及资源ReleaseDC(hWnd,hDc);LocalUnlock(hTempImgData);LocalFree(hTempImgData);GlobalUnlock(hImgData);return TRUE;
}

下面的这段程序,实现了上述的膨胀运算,针对的都是黑色点。参数中有一个BOOL变量,为真时,表示在水平方向进行膨胀运算,即结构元素B为;否则在垂直方向上进行膨胀运算,即结构元素B为。

BOOLDilation(HWND hWnd,BOOL Hori)
{DWORD                           OffBits,BufSize;
LPBITMAPINFOHEADER   lpImgData;LPSTR                  lpPtr;HLOCAL                 hTempImgData;LPBITMAPINFOHEADER    lpTempImgData;LPSTR                   lpTempPtr;HDC                    hDc;HFILE                   hf;LONG                  x,y;unsigned char             num;int                       i;
//为了处理的方便,仍采用256级灰度图,不过只调色板中0和255两项
if(NumColors!=256){ MessageBox(hWnd,"Must bea mono bitmap with grayscale palette!",
"ErrorMessage",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER);
//BufSize为缓冲区大小BufSize=OffBits+bi.biHeight*LineBytes;
//为新的缓冲区分配内存if((hTempImgData=LocalAlloc(LHND,BufSize))==NULL){MessageBox(hWnd,"Error allocmemory!","Error Message",
MB_OK|MB_ICONEXCLAMATION);
return FALSE;}lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);   lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData);//拷贝头信息和位图数据    memcpy(lpTempImgData,lpImgData,BufSize);if(Hori){
//在水平方向进行膨胀运算for(y=0;y<bi.biHeight;y++){//lpPtr指向原图数据,lpTempPtr指向新图数据lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes)+1;lpTempPtr=(char*)lpTempImgData+
(BufSize-LineBytes-y*LineBytes)+1;for(x=1;x<bi.biWidth-1;x++){
//注意为防止越界,x的范围从1到宽度-2num=(unsigned char)*lpPtr;
//原图中是黑点的,新图中肯定也是,所以要考虑的是那些原图
//中的白点,看是否有可能膨胀成黑点if (num==255){*lpTempPtr=(unsigned char)255; //先置成白点for(i=0;i<3;i++){num=(unsigned char)*(lpPtr+i-1);
//只要左右邻居中有一个是黑点,就膨胀成黑点if(num==0){
*lpTempPtr=(unsignedchar)0;break;}}}
//原图中就是黑点的,新图中仍是黑点else *lpTempPtr=(unsigned char)0;//指向下一个象素lpPtr++;lpTempPtr++;}}}else{
//在垂直方向进行腐蚀运算for(y=1;y<bi.biHeight-1;y++){ //注意为防止越界,y的范围从1到高度-2lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes);lpTempPtr=(char *)lpTempImgData+(BufSize-LineBytes-y*LineBytes);for(x=0;x<bi.biWidth;x++){num=(unsigned char)*lpPtr;if (num==255){*lpTempPtr=(unsigned char)255;for(i=0;i<3;i++){num=(unsigned char)*(lpPtr+(i-1)*LineBytes);
//只要上下邻居中有一个是黑点,就膨胀成黑点if(num==0){*lpTempPtr=(unsigned char)0;break;}}}else *lpTempPtr=(unsigned char)0;lpPtr++;lpTempPtr++;}}}if(hBitmap!=NULL)DeleteObject(hBitmap);hDc=GetDC(hWnd);    //产生新的位图hBitmap=CreateDIBitmap(hDc,(LPBITMAPINFOHEADER)lpTempImgData,
(LONG)CBM_INIT,
(LPSTR)lpTempImgData+
sizeof(BITMAPINFOHEADER)+NumColors*sizeof(RGBQUAD),
(LPBITMAPINFO)lpTempImgData,
DIB_RGB_COLORS);//起不同的结果文件名if(Hori)hf=_lcreat("c:\\hdilation.bmp",0);elsehf=_lcreat("c:\\vdilation.bmp",0);_lwrite(hf,(LPSTR)&bf,sizeof(BITMAPFILEHEADER));_lwrite(hf,(LPSTR)lpTempImgData,BufSize);_lclose(hf);//释放内存及资源ReleaseDC(hWnd,hDc);LocalUnlock(hTempImgData);LocalFree(hTempImgData);GlobalUnlock(hImgData);return TRUE;
}
BOOLDilation(HWND hWnd,BOOL Hori)
{DWORD                           OffBits,BufSize;
LPBITMAPINFOHEADER   lpImgData;LPSTR                  lpPtr;HLOCAL                 hTempImgData;LPBITMAPINFOHEADER    lpTempImgData;LPSTR                   lpTempPtr;HDC                    hDc;HFILE                   hf;LONG                  x,y;unsigned char             num;int                       i;
//为了处理的方便,仍采用256级灰度图,不过只调色板中0和255两项
if(NumColors!=256){ MessageBox(hWnd,"Must bea mono bitmap with grayscale palette!",
"ErrorMessage",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER);
//BufSize为缓冲区大小BufSize=OffBits+bi.biHeight*LineBytes;
//为新的缓冲区分配内存if((hTempImgData=LocalAlloc(LHND,BufSize))==NULL){MessageBox(hWnd,"Error allocmemory!","Error Message",
MB_OK|MB_ICONEXCLAMATION);
return FALSE;}lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);   lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData);//拷贝头信息和位图数据    memcpy(lpTempImgData,lpImgData,BufSize);if(Hori){
//在水平方向进行膨胀运算for(y=0;y<bi.biHeight;y++){//lpPtr指向原图数据,lpTempPtr指向新图数据lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes)+1;lpTempPtr=(char*)lpTempImgData+
(BufSize-LineBytes-y*LineBytes)+1;for(x=1;x<bi.biWidth-1;x++){
//注意为防止越界,x的范围从1到宽度-2num=(unsigned char)*lpPtr;
//原图中是黑点的,新图中肯定也是,所以要考虑的是那些原图
//中的白点,看是否有可能膨胀成黑点if (num==255){*lpTempPtr=(unsigned char)255; //先置成白点for(i=0;i<3;i++){num=(unsigned char)*(lpPtr+i-1);
//只要左右邻居中有一个是黑点,就膨胀成黑点if(num==0){
*lpTempPtr=(unsignedchar)0;break;}}}
//原图中就是黑点的,新图中仍是黑点else *lpTempPtr=(unsigned char)0;//指向下一个象素lpPtr++;lpTempPtr++;}}}else{
//在垂直方向进行腐蚀运算for(y=1;y<bi.biHeight-1;y++){ //注意为防止越界,y的范围从1到高度-2lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes);lpTempPtr=(char *)lpTempImgData+(BufSize-LineBytes-y*LineBytes);for(x=0;x<bi.biWidth;x++){num=(unsigned char)*lpPtr;if (num==255){*lpTempPtr=(unsigned char)255;for(i=0;i<3;i++){num=(unsigned char)*(lpPtr+(i-1)*LineBytes);
//只要上下邻居中有一个是黑点,就膨胀成黑点if(num==0){*lpTempPtr=(unsigned char)0;break;}}}else *lpTempPtr=(unsigned char)0;lpPtr++;lpTempPtr++;}}}if(hBitmap!=NULL)DeleteObject(hBitmap);hDc=GetDC(hWnd);    //产生新的位图hBitmap=CreateDIBitmap(hDc,(LPBITMAPINFOHEADER)lpTempImgData,
(LONG)CBM_INIT,
(LPSTR)lpTempImgData+
sizeof(BITMAPINFOHEADER)+NumColors*sizeof(RGBQUAD),
(LPBITMAPINFO)lpTempImgData,
DIB_RGB_COLORS);//起不同的结果文件名if(Hori)hf=_lcreat("c:\\hdilation.bmp",0);elsehf=_lcreat("c:\\vdilation.bmp",0);_lwrite(hf,(LPSTR)&bf,sizeof(BITMAPFILEHEADER));_lwrite(hf,(LPSTR)lpTempImgData,BufSize);_lclose(hf);//释放内存及资源ReleaseDC(hWnd,hDc);LocalUnlock(hTempImgData);LocalFree(hTempImgData);GlobalUnlock(hImgData);return TRUE;
}

我们可以根据上述的判据,事先做出一张表,从0到255共有256个元素,每个元素要么是0,要么是1。我们根据某点(当然是要处理的黑色点了)的八个相邻点的情况查表,若表中的元素是1,则表示该点可删,否则保留。

查表的方法是,设白点为1,黑点为0;左上方点对应一个8位数的第一位(最低位),正上方点对应第二位,右上方点对应的第三位,左邻点对应第四位,右邻点对应第五位,左下方点对应第六位,正下方点对应第七位,右下方点对应的第八位,按这样组成的8位数去查表即可。例如上面的例子中(1)对应表中的第0项,该项应该为0;(2)对应37,该项应该为0;(3)对应173,该项应该为1;(4)对应231,该项应该为0;(5)对应237,该项应该为1;(6)对应254,该项应该为0;(7)对应255,该项应该为0。

这张表我已经替大家做好了,可花了我不少时间呢!

static interasetable[256]={0,0,1,1,0,0,1,1,         1,1,0,1,1,1,0,1,1,1,0,0,1,1,1,1,            0,0,0,0,0,0,0,1,0,0,1,1,0,0,1,1,            1,1,0,1,1,1,0,1,1,1,0,0,1,1,1,1,            0,0,0,0,0,0,0,1,1,1,0,0,1,1,0,0,            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,            0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,            1,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,            0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,            1,1,0,1,1,1,0,1,1,1,0,0,1,1,1,1,            0,0,0,0,0,0,0,1,0,0,1,1,0,0,1,1,            1,1,0,1,1,1,0,1,1,1,0,0,1,1,1,1,            0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,            0,0,0,0,0,0,0,0,1,1,0,0,1,1,1,1,            0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,            1,1,0,1,1,1,0,0,1,1,0,0,1,1,1,0,            1,1,0,0,1,0,0,0};以下是源程序,黑体字部分是值得注意的地方。
BOOLThinning(HWND hWnd)
{DWORD                            OffBits,BufSize;LPBITMAPINFOHEADER    lpImgData;LPSTR                          lpPtr;HLOCAL                 hTempImgData;LPBITMAPINFOHEADER    lpTempImgData;LPSTR                  lpTempPtr;HDC                     hDc;HFILE                   hf;LONG                   x,y;int                                      num;BOOL                    Finished;int                       nw,n,ne,w,e,sw,s,se;
//为了处理的方便,仍采用256级灰度图,不过只用调色板中0和255两项if( NumColors!=256){
MessageBox(hWnd,"Mustbe a mono bitmap with grayscale palette!",
"ErrorMessage",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER);
//BufSize为缓冲区大小BufSize=OffBits+bi.biHeight*LineBytes;
//为新的缓冲区分配内存if((hTempImgData=LocalAlloc(LHND,BufSize))==NULL)
{MessageBox(hWnd,"Erroralloc memory!","Error Message",
MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);   lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData);//拷贝头信息和位图数据    memcpy(lpTempImgData,lpImgData,BufSize);//结束标志置成假Finished=FALSE;
while(!Finished){//还没有结束//结束标志置成假Finished=TRUE;//先进行水平方向的细化for (y=1;y<bi.biHeight-1;y++){ //注意为防止越界,y的范围从1到高度-2//lpPtr指向原图数据,lpTempPtr指向新图数据lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes);lpTempPtr=(char *)lpTempImgData+(BufSize-LineBytes-y*LineBytes);x=1; //注意为防止越界,x的范围从1到宽度-2while(x<bi.biWidth-1){if(*(lpPtr+x)==0){ //是黑点才做处理w=(unsigned char)*(lpPtr+x-1);  //左邻点e=(unsigned char)*(lpPtr+x+1);  //右邻点if( (w==255)|| (e==255)){
//如果左右两个邻居中至少有一个是白点才处理nw=(unsigned char)*(lpPtr+x+LineBytes-1); //左上邻点n=(unsigned char)*(lpPtr+x+LineBytes); //上邻点ne=(unsigned char)*(lpPtr+x+LineBytes+1); //右上邻点sw=(unsigned char)*(lpPtr+x-LineBytes-1); //左下邻点s=(unsigned char)*(lpPtr+x-LineBytes); //下邻点se=(unsigned char)*(lpPtr+x-LineBytes+1); //右下邻点//计算索引num=nw/255+n/255*2+ne/255*4+w/255*8+e/255*16+
sw/255*32+s/255*64+se/255*128;if(erasetable[num]==1){ //经查表,可以删除
//在原图缓冲区中将该黑点删除*(lpPtr+x)=(BYTE)255;
//结果图中该黑点也删除*(lpTempPtr+x)=(BYTE)255;Finished=FALSE; //有改动,结束标志置成假x++; //水平方向跳过一个象素}}}x++; //扫描下一个象素}}//再进行垂直方向的细化for (x=1;x<bi.biWidth-1;x++){ //注意为防止越界,x的范围从1到宽度-2y=1; //注意为防止越界,y的范围从1到高度-2while(y<bi.biHeight-1){lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes);lpTempPtr=(char*)lpTempImgData+
(BufSize-LineBytes-y*LineBytes);if(*(lpPtr+x)==0){ //是黑点才做处理n=(unsigned char)*(lpPtr+x+LineBytes);s=(unsigned char)*(lpPtr+x-LineBytes);if( (n==255)|| (s==255)){
//如果上下两个邻居中至少有一个是白点才处理nw=(unsigned char)*(lpPtr+x+LineBytes-1);ne=(unsigned char)*(lpPtr+x+LineBytes+1);w=(unsigned char)*(lpPtr+x-1);e=(unsigned char)*(lpPtr+x+1);sw=(unsigned char)*(lpPtr+x-LineBytes-1);se=(unsigned char)*(lpPtr+x-LineBytes+1);//计算索引
num=nw/255+n/255*2+ne/255*4+w/255*8+e/255*16+
sw/255*32+s/255*64+se/255*128;if(erasetable[num]==1){ //经查表,可以删除
//在原图缓冲区中将该黑点删除*(lpPtr+x)=(BYTE)255;
//结果图中该黑点也删除*(lpTempPtr+x)=(BYTE)255;Finished=FALSE; //有改动,结束标志置成假y++;//垂直方向跳过一个象素}}}y++; //扫描下一个象素}}
}if(hBitmap!=NULL)DeleteObject(hBitmap);hDc=GetDC(hWnd);    //产生新的位图hBitmap=CreateDIBitmap(hDc,(LPBITMAPINFOHEADER)lpTempImgData,
(LONG)CBM_INIT,
(LPSTR)lpTempImgData+
sizeof(BITMAPINFOHEADER)+
NumColors*sizeof(RGBQUAD),
(LPBITMAPINFO)lpTempImgData,
DIB_RGB_COLORS);
hf=_lcreat("c:\\thinning.bmp",0);_lwrite(hf,(LPSTR)&bf,sizeof(BITMAPFILEHEADER));_lwrite(hf,(LPSTR)lpTempImgData,BufSize);_lclose(hf);//释放内存及资源ReleaseDC(hWnd,hDc);LocalUnlock(hTempImgData);LocalFree(hTempImgData);GlobalUnlock(hImgData);return TRUE;
}

系统学习数字图像处理之形态学分析补充(灰度级处理)相关推荐

  1. 系统学习数字图像处理之形态学分析

    http://blog.csdn.net/app_12062011/article/details/27351043 继膨胀.腐蚀.开运算和闭运算之后的有一个基本操作就是击中击不中变换(HMT),HM ...

  2. 系统学习数字图像处理之目标识别

    模式:描绘子组合,即特征.x = (x1,x2,...xn)T. •模式类:共享某些属性的模式族 •常见模式组合: 向量.串和树 •类型可分离的程度的高低很大程度上取决于应用的描绘子的选择 两大模式识 ...

  3. 系统学习数字图像处理之频域滤波

    最近在看模板匹配,虽然很简单,但还是想认真过下基础,因此把信号处理频域相关的内容,接着图像处理再过一遍. 理论上,对连续变量t的连续函数f(t)的傅里叶变换为F(u),利用f(t)取样后的函数重建f( ...

  4. paper 108:系统学习数字图像处理之图像复原与重建

    首先,必须注意这里所限制的处理条件. 关于图像退化/复原模型 退化的图像是由成像系统的退化加上额外的噪声形成的. 1.只考虑噪声引起的退化 噪声模型,包含于空间不相关和相关两种,除了空间周期噪声,这里 ...

  5. 系统学习数字图像处理之图像复原与重建

    首先,必须注意这里所限制的处理条件. 关于图像退化/复原模型 退化的图像是由成像系统的退化加上额外的噪声形成的. 1.只考虑噪声引起的退化 噪声模型,包含于空间不相关和相关两种,除了空间周期噪声,这里 ...

  6. 系统学习数字图像处理之图像分割

    图像分割,有区域法,直接确定边界,边缘检测法,大多数算法基于灰度的不连续性(边缘检测)和相似性(阈值处理,区域生长,区域分裂,区域聚合). 边缘分类:台阶边缘,斜坡边缘,屋顶边缘,一阶导数会产生粗边缘 ...

  7. 系统学习数字图像处理之图像压缩

    1.霍夫曼编码 霍夫曼编码的基本思想:输入一个待编码的串,首先统计串中各字符出现的次数,称之为频次,假设统计频次的数组为count[],则霍夫曼编码每次找出count数组中的值最小的两个分别作为左右孩 ...

  8. 系统学习数字图像处理之描绘子

    图像分割结果是得到了区域内像素集合,或位于区域边界上的像素集合. 把图像分割后,为了进一步的识别等处理,分割后的图像一般要进行表示和描述. 表示是直接具体地表示目标,好的表示方法应具有节省存储空间.易 ...

  9. 系统学习数字图像处理之彩色图像处理

    这章没啥要记的,因为经常用... http://blog.csdn.net/to_xidianhph_youth/article/details/12055627 

最新文章

  1. ADO.NET Entity Framework 深入分析, Part 5
  2. Redis快照(Snapshot)特点
  3. 黑科技揭秘:眼科大夫如何应用5G+8K完成远程会诊?
  4. 分治法的关键特征_经典算法思想2——分治(Divide-and-Conquer)
  5. 机器学习之降维方法(LDA、PCA)小结
  6. ArcGIS Pro 简明教程(3)数据编辑
  7. 在PDA设备上安装和部署 SQL Server Compac 3.5(官方版)
  8. 服务器ghost备份后无法进入系统还原,ghost系统备份后的恢复方法
  9. 如何学习C4D建模并达到精通?
  10. 三极管9013 9014 跟8050之间有什么区别,
  11. 将靠父id的层级关系处理成编码形式
  12. AICamera of Caffe2
  13. PDF旋转使用的转换器有哪些
  14. 女性社交电商系统:聚焦女用户 收割电商半壁江山
  15. UA MATH524 复变函数8 Cauchy定理与原函数
  16. 【轮播图】使用bootstrap轮播插件(Carousel)
  17. 计算机用户被停用,电脑教程:Windows7用户被停用解决方法
  18. 一套史诗级版vue详解!
  19. SMDK2440A 5.0BSP之eboot流程(作者:wogoyixikexie@gliet)
  20. 用AI技术防止幼儿园虐待儿童,中国研究员研发视频流分析模型

热门文章

  1. SpringBoot之集成MybatisPlus
  2. tomcat套接字接受失败_07 | What? 还有本地套接字?
  3. vue3.0 word导出
  4. Linux中如何查找占用硬盘体积最大的文件方法
  5. java 多线程为什么不建议使用onstop onsuspend_java多线程stop,suspend使用代码实际例子...
  6. php laravel 理解,程序员-说一下PHP框架Laravel,如何理解她的思想
  7. python程序字符串中字符排序_python字符串排序方法
  8. python一切皆对象的理解_Python难点解析---初级篇2.一切皆对象
  9. java插件开发_编写一个IDEA插件之:自动生成Java代码
  10. 安装redis k8s_K8S 生态周报| Docker v19.03.6-rc2 发布