第6章腐蚀,膨胀,细化算法 这一章的内容我认为是最有趣的。还记得前言中那个抽取骨架的例子吗?现在我们就来看看 它是如何实现的 今天所讲的内容属于一门新兴的学科:数学形态学( Mathematical Morphology)。说起来很有 意思,它是法国和德国的科学家在研究岩石结构时建立的一门学科。形态学的用途主要是获 取物体拓扑和结构信息,它通过物体和结构元素相互作用的某些运算,得到物体更本质的形 态。在图象处理中的应用主要是:(1)利用形态学的基本运算,对图象进行观察和处理,从 而达到改善图象质量的目的;(2)描述和定义图象的各种几何参数和特征,如面积、周长、 连通度、颗粒度、骨架和方向性等。 限于篇幅,我们只介绍二值图象的形态学运算,对于灰度图象的形态学运算,有兴趣的读者 可以阅读有关的参考书。在程序中,为了处理的方便,还是采用256级灰度图,不过只用到 了调色板中的0和255两项 先来定义一些基本符号和关系。 1.元素 设有一幅图象X,若点a在X的区域以内,则称a为X的元素,记作a∈X,如图61所示 2.B包含于X 设有两幅图象B,Ⅹ。对于B中所有的元素ai,都有ai∈X,则称B包含于( included in)X 记作BCX,如图62所示 3.B击中X 设有两幅图象B,Ⅹ。若存在这样一个点,它即是B的元素,又是X的元素,则称B击中ht)X, 记作B↑X,如图6.3所示。 4.B不击中X 设有两幅图象B,X。若不存在任何一个点,它即是B的元素,又是X的元素,即B和X 的交集是空,则称B不击中(misx,记作BnX=Φ:其中∩是集合运算相交的符号,Φ表 示空集。如图64所示。 X X 图61元素 图62包含 X 图6.3击中 图6.4不击中 5.补集
第 6 章 腐蚀,膨胀,细化算法 这一章的内容我认为是最有趣的。还记得前言中那个抽取骨架的例子吗?现在我们就来看看 它是如何实现的。 今天所讲的内容属于一门新兴的学科:数学形态学(Mathematical Morphology)。说起来很有 意思,它是法国和德国的科学家在研究岩石结构时建立的一门学科。形态学的用途主要是获 取物体拓扑和结构信息,它通过物体和结构元素相互作用的某些运算,得到物体更本质的形 态。在图象处理中的应用主要是:(1)利用形态学的基本运算,对图象进行观察和处理,从 而达到改善图象质量的目的;(2)描述和定义图象的各种几何参数和特征,如面积、周长、 连通度、颗粒度、骨架和方向性等。 限于篇幅,我们只介绍二值图象的形态学运算,对于灰度图象的形态学运算,有兴趣的读者 可以阅读有关的参考书。在程序中,为了处理的方便,还是采用 256 级灰度图,不过只用到 了调色板中的 0 和 255 两项。 先来定义一些基本符号和关系。 1. 元素 设有一幅图象 X,若点 a 在 X 的区域以内,则称 a 为 X 的元素,记作 a∈X,如图 6.1 所示。 2. B 包含于 X 设有两幅图象 B,X。对于 B 中所有的元素 ai,都有 ai∈X,则称 B 包含于(included in)X, 记作 B X,如图 6.2 所示。 3. B 击中 X 设有两幅图象 B,X。若存在这样一个点,它即是 B 的元素,又是 X 的元素,则称 B 击中(hit)X, 记作 B↑X,如图 6.3 所示。 4. B 不击中 X 设有两幅图象 B,X。若不存在任何一个点,它即是 B 的元素,又是 X 的元素,即 B 和 X 的交集是空,则称 B 不击中(miss)X,记作 B∩X=Ф;其中∩是集合运算相交的符号,Ф表 示空集。如图 6.4 所示。 图 6.1 元素 图 6.2 包含 图 6.3 击中 图 6.4 不击中 5. 补集
设有一幅图象X,所有X区域以外的点构成的集合称为X的补集,记作X,如图65所示 显然,如果B∩X=Φ,则B在Ⅹ的补集内,即BCX 图6.5补集的示意图 6.结构元紊 设有两幅图象B,X。若X是被处理的对象,而B是用来处理X的,则称B为结构元素( structure element),又被形象地称做刷子。结构元素通常都是一些比较小的图象。 7.对称集 设有一幅图象B,将B中所有元素的坐标取反,即令(x,y)变成(x,-y),所有这些点构成 的新的集合称为B的对称集,记作B,如图66所示。 8.平移 设有一幅图象B,有一个点ax0,yo),将B平移a后的结果是,把B中所有元素的横坐标加 X0,纵坐标加y,即令(x,y)变成(x+x0,y+y0),所有这些点构成的新的集合称为B的平移, 记作Ba,如图6.7所示 ,a(x0,yo)/B B 图6.6对称集的示意图 图6.7平移的示意图 好了,介绍了这么多基本符号和关系,现在让我们应用这些符号和关系,看一下形态学的基 本运算。 61腐蚀 把结构元素B平移a后得到Ba,若B包含于X,我们记下这个a点,所有满足上述条件的 a点组成的集合称做x被B腐蚀 Erosion)的结果。用公式表示为:E(x)={aB3cx}=XB, 如图68所示
设有一幅图象 X,所有 X 区域以外的点构成的集合称为 X 的补集,记作 Xc,如图 6.5 所示。 显然,如果 B∩X=Ф,则 B 在 X 的补集内,即 B Xc。 图 6.5 补集的示意图 6. 结构元素 设有两幅图象B,X。若 X是被处理的对象,而 B是用来处理X的,则称B为结构元素(structure element),又被形象地称做刷子。结构元素通常都是一些比较小的图象。 7. 对称集 设有一幅图象 B,将 B 中所有元素的坐标取反,即令(x,y)变成(-x,-y),所有这些点构成 的新的集合称为 B 的对称集,记作 B v,如图 6.6 所示。 8. 平移 设有一幅图象 B,有一个点 a(x0,y0),将 B 平移 a 后的结果是,把 B 中所有元素的横坐标加 x0,纵坐标加 y0,即令(x,y)变成(x+x0,y+y0),所有这些点构成的新的集合称为 B 的平移, 记作 Ba,如图 6.7 所示。 图 6.6 对称集的示意图 图 6.7 平移的示意图 好了,介绍了这么多基本符号和关系,现在让我们应用这些符号和关系,看一下形态学的基 本运算。 6.1 腐蚀 把结构元素 B 平移 a 后得到 Ba,若 Ba包含于 X,我们记下这个 a 点,所有满足上述条件的 a 点组成的集合称做 X 被 B 腐蚀(Erosion)的结果。用公式表示为:E(X)={a| Ba X}=X B, 如图 6.8 所示
多 图68腐蚀的示意图 图68中X是被处理的对象,B是结构元素。不难知道,对于任意一个在阴影部分的点 Ba包含于X,所以X被B腐蚀的结果就是那个阴影部分。阴影部分在X的范围之内,且比 Ⅹ小,就象Ⅹ被剥掉了一层似的,这就是为什么叫腐蚀的原因。 值得注意的是,上面的B是对称的,即B的对称集B=B,所以X被B腐蚀的结果和Ⅹ被B 腐蚀的结果是一样的。如果B不是对称的,让我们看看图6.9,就会发现X被B腐蚀的结 果和被Bv腐蚀的结果不同。 X BˇB X B 图6.9结构元素非对称时,腐蚀的结果不同 图68和图69都是示意图,让我们来看看实际上是怎样进行腐蚀运算的。 在图6.10中,左边是被处理的图象ⅹ(二值图象,我们针对的是黑点),中间是结构元素B 那个标有 origin的点是中心点,即当前处理元素的位置,我们在介绍模板操作时也有过类似 的概念。腐蚀的方法是,拿B的中心点和X上的点一个一个地对比,如果B上的所有点都 在X的范围内,则该点保留,否则将该点去掉;右边是腐蚀后的结果。可以看出,它仍在 原来ⅹ的范围内,且比ⅹ包含的点要少,就象Ⅹ被腐蚀掉了一层
图 6.8 腐蚀的示意图 图 6.8 中 X 是被处理的对象,B 是结构元素。不难知道,对于任意一个在阴影部分的点 a, Ba 包含于 X,所以 X 被 B 腐蚀的结果就是那个阴影部分。阴影部分在 X 的范围之内,且比 X 小,就象 X 被剥掉了一层似的,这就是为什么叫腐蚀的原因。 值得注意的是,上面的 B 是对称的,即 B 的对称集 B v=B,所以 X 被 B 腐蚀的结果和 X 被 B v 腐蚀的结果是一样的。如果 B 不是对称的,让我们看看图 6.9,就会发现 X 被 B 腐蚀的结 果和 X 被 B v 腐蚀的结果不同。 图 6.9 结构元素非对称时,腐蚀的结果不同 图 6.8 和图 6.9 都是示意图,让我们来看看实际上是怎样进行腐蚀运算的。 在图 6.10 中,左边是被处理的图象 X(二值图象,我们针对的是黑点),中间是结构元素 B, 那个标有 origin 的点是中心点,即当前处理元素的位置,我们在介绍模板操作时也有过类似 的概念。腐蚀的方法是,拿 B 的中心点和 X 上的点一个一个地对比,如果 B 上的所有点都 在 X 的范围内,则该点保留,否则将该点去掉;右边是腐蚀后的结果。可以看出,它仍在 原来 X 的范围内,且比 X 包含的点要少,就象 X 被腐蚀掉了一层
●oo0ooo0●●●●000 ●●00◎ 。+ ●o E B 图6.10腐蚀运算 图6.11为原图,图6.12为腐蚀后的结果图,能够很明显地看出腐蚀的效果。 Hi, I'm phoenix Glad to meet u 图6.11原图 Hi, I'm phoenix Glad to meet u 图6.12腐蚀后的结果图 下面的这段程序,实现了上述的腐蚀运算,针对的都是黑色点。参数中有一个BOOL变量 为真时,表示在水平方向进行腐蚀运算,即结构元素B为11.1,否则在垂直方向上进 行腐蚀运算,即结构元素B为[1」 BOOL Erosion(HWND hWnd, BOOL Hori) DWORD OffBits. Bufsize LPBITMAPINFOHEADER Iplmg Data; LPSTR HLOCAL hTemplmg Data; LPBITMAPINFOHEADER Ip TemplmgData' LPSTR HDC HFILE
图 6.10 腐蚀运算 图 6.11 为原图,图 6.12 为腐蚀后的结果图,能够很明显地看出腐蚀的效果。 图 6.11 原图 图 6.12 腐蚀后的结果图 下面的这段程序,实现了上述的腐蚀运算,针对的都是黑色点。参数中有一个 BOOL 变量, 为真时,表示在水平方向进行腐蚀运算,即结构元素 B 为 ;否则在垂直方向上进 行腐蚀运算,即结构元素 B 为 。 BOOL Erosion(HWND hWnd,BOOL Hori) { DWORD OffBits,BufSize; LPBITMAPINFOHEADER lpImgData; LPSTR lpPtr; HLOCAL hTempImgData; LPBITMAPINFOHEADER lpTempImgData; LPSTR lpTempPtr; HDC hDc; HFILE hf;
LONG unsigned char num ∥.了处理方便,仍采用256级灰度图,不过只用调色板中0和255两项 if( Num Colors!=256)1 Message Box(hWnd, Must be a mono bitmap with grayscale palette Error Message", MB OK MB ICONEXCLAMATION) return false OffBits=bf. bfOffBits-sizeof( BITMAPFILEHEADER) BufSize为缓冲区大小 BufSize=OffBitsbi. biHeight"Line Bytes; ∥新的缓冲区分配内存 if((hTemplmg Data=LocalAlloc(LHND, BufSize))==NULL) Message Box(hWnd, "Error alloc memory! ","Error Message MB OK MB ICONEXCLAMATION) return False } lplmg Data=(LPBITMAPINFOHEADER)Global Lock(hlmg Data) lpTemplmg Data=(LPBITMAPINFOHEADER)LocalLock(h Data ∥拷贝头信息和位图数据 memcpy(lp Templmg Data, lplmg Data, BufSize); if(Hori) ∥在水平方向进行腐蚀运算 r(y=0; y<bi. biHeight; y++)i ∥pPr指向原图数据, IpTempPtr指向新图数据 lpPtr=(char *)lplmg Data+( BufSize-LineBytes-y*Line Bytes)+1 IpTempPtr=(char*)IpTemplmg Data+ (BufSize-LineBytes-y"Line Bytes)+I or(x=l; x<bi bi Width-1; x++)1 ∥注意为防止越界,x的范围从1到宽度-2 if(num=0){∥因为腐蚀掉的是黑点,所以只对黑点处理 r)0,∥先置成黑点 for(i=0;i<3;i++){ d char)°(lpPr+i-1)
LONG x,y; unsigned char num; int i; //为了处理方便,仍采用 256 级灰度图,不过只用调色板中 0 和 255 两项 if( NumColors!=256){ MessageBox(hWnd,"Must be a mono bitmap with grayscale palette!", "Error Message",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 alloc 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 到宽度-2 num=(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)1 ∥身及上下邻居中若有一个不是黑点,则将该点腐 ∥成白点 ∥原图中就是白点的,新图中仍是白点 else *lpTempPtr=(unsigned char)25 ∥指向下一个象素 IpTempPtr++ ∥在垂直方向进行腐蚀运算 for(y=ly< bi biWeight-ly++){∥/注意为防止越界,y的范围从1到高度2 pPtr指向原图数据, IpTempPtr指向新图数据 lpPtr=(char *)lplmg Data+( BufSize-LineBytes-y*Line Bytes); for(x=0; x<bi bi Width; x++)i if(num=0){/因为腐蚀掉的是黑点,所以只对黑点处理 * IpTempPtra=( unsigned char)0,∥先置成黑点 num=(unsigned char )*(IpPtr+(i-1 )*LineBytes); if(num==255)( 身及上下邻居中若有一个不是黑点,则将该点腐 ∥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;
∥指向下一个象素 IpTempPtr if(hBitmap!=NULL) DeleteObject(hBitmap) hDc=GetDC(hWnd) ∥产生新的位图 hBitmap=CreateDIBitmap(hDc, (LPBITMAPINFOHEADER)lpTemplmg Data (LONGCBM INT (LPSTR)Ip Templmg Data+ sizeof( BI TMAPINFOHEADER (LPBITMAPINFO)IpTemplmg Data, DIB RGB COLORS) ∥起不同的结果文件名 if(Hori) hf- Creat("c: I erosion. bmp",0); hf- Creat("c: llverosion bmp",0) Iwrite(hf, (LPSTR)&bf, sizeof( BITMAPFILEHEADER)) Iwrite(hf, (LPSTR)IpTemplmg Data, BufSize) ∥释放内存及资源 ReleaseDC(hWnd, hDc) Local Unlock (h Templmg Data) Local Free(hTemplmg Data) Global Unlock(hmg Data) return trUe 62膨胀 膨胀( dilation)可以看做是腐蚀的对偶运算,其定义是:把结构元素B平移a后得到B,若 B击中X,我们记下这个a点。所有满足上述条件的a点组成的集合称做X被B膨胀的结 果。用公式表示为:Dx={a|Batx}=XB,如图613所示。图613中X是被处理的对 象,B是结构元素,不难知道,对于任意一个在阴影部分的点a,Ba击中X,所以Ⅹ被B
//指向下一个象素 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); else hf=_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; } 6.2 膨胀 膨胀(dilation)可以看做是腐蚀的对偶运算,其定义是:把结构元素 B 平移 a 后得到 Ba,若 Ba击中 X,我们记下这个 a 点。所有满足上述条件的 a 点组成的集合称做 X 被 B 膨胀的结 果。用公式表示为:D(X)={a | Ba↑X}=X B,如图 6.13 所示。图 6.13 中 X 是被处理的对 象,B 是结构元素,不难知道,对于任意一个在阴影部分的点 a,Ba 击中 X,所以 X 被 B
膨胀的结果就是那个阴影部分。阴影部分包括Ⅹ的所有范围,就象X膨胀了一圈似的,这 就是为什么叫膨胀的原因。 同样,如果B不是对称的,ⅹ被B膨胀的结果和X被B膨胀的结果不同 让我们来看看实际上是怎样进行膨胀运算的。在图6.14中,左边是被处理的图象X(二值图 象,我们针对的是黑点),中间是结构元素B。膨胀的方法是,拿B的中心点和X上的点及 X周围的点一个一个地对,如果B上有一个点落在Ⅹ的范围内,则该点就为黑:右边是膨 胀后的结果。可以看出,它包括X的所有范围,就象Ⅹ膨胀了一圈似的。 X 图6.13膨胀的示意图 ●● ●● 0●●●●0000⊙●●O 0●● ●●●●0o0 origin X 图6.14膨胀运算 图6.15为图6.11膨胀后的结果图,能够很明显的看出膨胀的效果 Hir'm phoenix Glad to meet u
膨胀的结果就是那个阴影部分。阴影部分包括 X 的所有范围,就象 X 膨胀了一圈似的,这 就是为什么叫膨胀的原因。 同样,如果 B 不是对称的,X 被 B 膨胀的结果和 X 被 B v 膨胀的结果不同。 让我们来看看实际上是怎样进行膨胀运算的。在图 6.14 中,左边是被处理的图象 X(二值图 象,我们针对的是黑点),中间是结构元素 B。膨胀的方法是,拿 B 的中心点和 X 上的点及 X 周围的点一个一个地对,如果 B 上有一个点落在 X 的范围内,则该点就为黑;右边是膨 胀后的结果。可以看出,它包括 X 的所有范围,就象 X 膨胀了一圈似的。 图 6.13 膨胀的示意图 图 6.14 膨胀运算 图 6.15 为图 6.11 膨胀后的结果图,能够很明显的看出膨胀的效果
图615图611膨胀后的结果图 下面的这段程序,实现了上述的膨胀运算,针对的都是黑色点。参数中有一个BOOL变量, 为真时,表示在水平方向进行膨胀运算,即结构元素B为[11.1]:否则在垂直方向上进 行膨胀运算,即结构元素B为 BOOL Dilation(HWND hWnd, BOOL Hori) DWORD OffBits. Bufsize LPBITMAPINFOHEADER Iplmg Data; LPSTR IpPt HLOCAL hTemplmg Data LPBITMAPINFOHEADER IpTemplmg Data; LPSTR HDC HFILE pcf LONG num ∥.了处理的方便,仍采用256级灰度图,不过只调色板中0和255两项 if( Num Colors!=256)( Message Box(hWnd, Must be a mono bitmap with grayscale palette Error Message", MB OK MB ICONEXCLAMATION) return false OffBits=bf. bfoffBits-sizeof(BITMAPFILEHEADER) BufSize为缓冲区大小 BufSize=OffBits+bi. biHeight*LineBytes ∥.新的缓冲区分配内存 if(hTemplmg Data=LocalAlloc(LHND, BufSize))==NULL) Message Box(hwnd, Error alloc memory! ", "Error Message MB OK MB ICONEXCLAMATION) return False lplmg Data=(LPBITMAPINFOHE ADER)GlobalLock(hImg Data); IpTemplmg Data=(LPBITMAPINFOHEADER)LocalLock(hTemplmg Data
图 6.15 图 6.11 膨胀后的结果图 下面的这段程序,实现了上述的膨胀运算,针对的都是黑色点。参数中有一个 BOOL 变量, 为真时,表示在水平方向进行膨胀运算,即结构元素 B 为 ;否则在垂直方向上进 行膨胀运算,即结构元素 B 为 。 BOOL Dilation(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 mono bitmap with grayscale palette!", "Error Message",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 alloc memory!","Error Message", MB_OK|MB_ICONEXCLAMATION); return FALSE; } lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData); lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData);
∥拷贝头信息和位图数据 emcp(lp Templmg Data, lplmg Data, BufSize); if(Hori) ∥在水平方向进行膨胀运算 for(y=0; y<bi. biHeight; y++)1 ∥ IpPt指向原图数据, IpTempPtr指向新图数据 lpPtr=(char *)lplmg Data+( BufSize-LineBytes-y*Line Bytes)+1 IpTempPtr=(char*)IpTemplmg Data+ BufSize-Line Bytes- for(x=l; x<bibi Width-1; x++)1 ∥注意为防止越界,x的范围从1到宽度-2 ∥)图中是黑点的,新图中肯定也是,所以要考虑的是那些原图 ∥中的白点,看是否有可能膨胀成黑点 if(num==255){ p Temp( unsigned char)25;/先置成白点 for(i=0,i<3:i++){ 要左右邻居中有一个是黑点,就膨胀成黑点 if(num===) IpTempPtr(unsigned char )0 ∥)图中就是黑点的,新图中仍是黑点 else "lpTempPtr=(unsigned char)o ∥指向下一个象素 lpTe ∥在垂直方向进行腐蚀运算 for(y=ly< bi biweight-ly++){∥/注意为防止越界,y的范围从1到高度-2 lpPtr=(char *)lplmg Data+( BufSize-LineBytes-y*Line Bytes);
//拷贝头信息和位图数据 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 到宽度-2 num=(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=(unsigned char)0; break; } } } //原图中就是黑点的,新图中仍是黑点 else *lpTempPtr=(unsigned char)0; //指向下一个象素 lpPtr++; lpTempPtr++; } } } else{ //在垂直方向进行腐蚀运算 for(y=1;y<bi.biHeight-1;y++){ //注意为防止越界,y 的范围从 1 到高度-2 lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes);