大家好,我们是大黄蜂队,编号是 CICC1948。上一篇分享贴(使用Otsu阈值算法将灰度图像二值化)讲解了将一个灰度图像作二值化的方法,那我们为什么要进行二值化这个操作呢?其一部分原因,就是我们要进行接下来的膨胀(Dilation)与腐蚀(Erosion)操作。
膨胀是将与物体接触的全部背景点合并到该物体中,使边界向外部扩张的过程,能够用来填补物体中的空洞。物体膨胀前后的效果,就相当于物体在边界上向外扩张了一圈,因此膨胀操作也可以用于外扩物体的轮廓,以起到一些特殊的作用。而腐蚀则可以当作是膨胀的逆过程,相当于物体在边界上向内收缩了一圈。下图展示了对一个图像进行膨胀和腐蚀操作后的效果,中间的图像是操作之前的样子:
除了对图象中物体产生的影响外,膨胀和腐蚀的操作也会对图像背景产生影响:当图像背景中存在白色噪点时,如果对图像进行腐蚀操作,则这些白色噪点就会被周围的黑色所“吞噬”掉,同时图片前景的物体轮廓也会被腐蚀一圈。如果这个时候再对图像作膨胀操作,此时由于白色的噪点已经不复存在了,所以只会表现为前景物体的轮廓扩大回了原来的尺寸。这样的操作我们称为“开操作”(Opening),这是一种消除图像背景白色噪点的有效方式。下图演示了对一副背景中存在白色噪点的图像进行开操作的效果,可以看到操作之后前景(白色字母)的轮廓并没有发生变化,但是图像背景中的白色噪点已经全部被消除了。
当然,除了“开操作”之外,自然也存在“闭操作”(Closing)。闭操作是开操作的逆过程,他会先对图像进行膨胀操作,然后再进行腐蚀。当图像的前景中存在白色噪点时,闭操作是一种有效的去噪方式。下图演示了对一幅前景中存在黑色噪点的图像进行闭操作的效果:
根据上面所演示的例子,其实我们对于形态学膨胀和腐蚀就可以有一个更加概括的理解:膨胀就是用图像中的白色来“侵蚀”黑色,腐蚀就是用图像中的黑色来“侵蚀”白色。当他们交替使用时,就可以在保证大块像素区域不变的前提下,消除图象中小的像素噪点。除此之外,如果只是单纯想要扩大或者收缩前景图像的边界,膨胀和腐蚀也可以成为一种有效的手段。
最后,这里贴出一段用 C 语言实现膨胀的代码,供大家参考:
/**
* @desc 图像指定区域取最大值(不判断是否超出边界)
* @param ppImg 输入图像(灰阶),
* @param x 当前像素坐标,
* @param y 当前像素坐标,
* @param pKernal 内核模版
* @param halfKernalW 内核模版宽度一半
* @return 返回模版内最大值
* **/
unsigned char getMaxPixelCenter(unsigned char **ppImg,int x,int y,
int *pKernal,int kernalW,int halfKernalW){
int i=0,j=0,x0=x,y0=y,x1,y1;
unsigned char maxValue = 0x00;
int *p = pKernal;
x0 -= halfKernalW;
y0 -= halfKernalW;
for (i=0;i>1);
int y,x;
unsigned char *pImgOut = (unsigned char *)malloc(w*h); //输出图像
//方便处理,转成二维矩阵
unsigned char** ppImgIn = create2DImg(pImgIn,w,h);
unsigned char** ppImgOut = create2DImg(pImgOut,w,h);
//Mode 2 先中间,后四个边处理
for(y=halfKernalW;y ppImg[y1][x1])
minValue = ppImg[y1][x1];
}
}
}
return minValue;
}
void imgDilate(unsigned char *pImgIn,int w,int h){ //内核大小为3 int kernal[9] = {0,1,0,1,1,1,0,1,0}; int kernalW = 3,halfKernalW = (3>>1); int y,x; unsigned char *pImgOut = (unsigned char *)malloc(w*h); //输出图像 //方便处理,转成二维矩阵 unsigned char** ppImgIn = create2DImg(pImgIn,w,h); unsigned char** ppImgOut = create2DImg(pImgOut,w,h); for(y=0;y
for(x=0;x
ppImgOut[y][x] = getMaxPixelWhole(ppImgIn,x,y,w,h,kernal,kernalW,halfKernalW);
} } }