空间域 变换域_图像处理里的空间域滤波&形态学运算小结

7c4c25508f0bb6e87a593a0e1413f9de.png

图像处理里面比较基本的操作是在空间域的滤波处理。最常见的模糊啊锐化啊的都可以归于这类。其实质就是邻域间的组合运算,在shader技术上的乒乓也就差不多这个样子,而且操作纹理要更简捷。——ZwqXin.com

本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
原文地址:http://www.zwqxin.com/archives/image-processing/image-process-spatial-domain-filter.html

比较直方图类那种“单点”操作,空间域滤波中的每个像素都得多少顾及一下邻里情况,因而依其关注面大小而出现了不同大小的“邻域”的定义。至于不同的空间域滤波对应不同类型的矩阵式,就是所谓的“滤波”(FILTER),或者说模板。简单的就是3*3大小的高斯平滑,中值平滑,一阶梯度锐化/边缘,二阶拉普拉斯锐化/边缘等等,还有实现各种不同效果的FILTER,可上网找找,其实运用都是差不多的,但是要得出一个“有价值”的FILTER,可不是随随便便就能行的。

在[基于亮度的图像二值化处理] 里对8BIT,16BIT,24/32BIT的BMP分别应用“单点”的计算,一提取BMP数据区的像素信息,马上就作转换操作了,而在“邻域”计算里,把全部像素信息提取出来后,再进行转换操作比较合适:

  1. void SpatialDomainTemplate::BmpProcess24_32(OperateBMP& image, int Hdis, int Wdis, int step)
  2. {
  3. int index;
  4. int Wmax = (Wdis-1)*step;
  5. FilterColor *color;
  6. FilterColor *result_color;
  7. color = new FilterColor[Hdis*Wdis];
  8. result_color = new FilterColor[Hdis*Wdis];
  9. memset(color, 0, Hdis*Wdis*sizeof(FilterColor));
  10. memset(result_color, 0, Hdis*Wdis*sizeof(FilterColor));
  11. for(int j = 0; j < Hdis; j++)
  12. for(int i = 0; i < Wdis; i++)
  13. {
  14. index = j*Wdis + i;
  15. color[index].blue = image.GetImageData().buffer[i*step + 0 + Wmax * j];
  16. color[index].green = image.GetImageData().buffer[i*step + 1 + Wmax * j];
  17. color[index].red = image.GetImageData().buffer[i*step + 2 + Wmax * j];
  18. }
  19. ApplyTemplate(color, result_color, Hdis, Wdis);
  20. if(GetReadBack())
  21. {
  22. for(int k = 0; k < Hdis; k++)
  23. for(int l = 0; l < Wdis; l++)
  24. {
  25. index = k*Wdis + l;
  26. image.GetImageData().buffer[l*step + 0 + Wmax * k] = result_color[index].blue;
  27. image.GetImageData().buffer[l*step + 1 + Wmax * k] = result_color[index].green;
  28. image.GetImageData().buffer[l*step + 2 + Wmax * k] = result_color[index].red;
  29. }
  30. }
  31. delete []color;
  32. delete []result_color;
  33. }

这是24/32BIT版本,其他BIT的可参照上篇文章,类似的。color,resultcolor数组分别作为操作函数ApplyTemplate的输入和输出,模板操作就在此函数里完成——它无关BMP是多少BIT的,进来的是一个代表原图像的RGB数组,出来的是代表结果图像的RGB数组,就这样。当然可以看出为了结构我还是牺牲了不少“效率”的。

  1. void SpatialDomainTemplate::ApplyTemplate(FilterColor* oColor, FilterColor* rColor, int Hdis, int Wdis)
  2. {
  3. if(FilterType == AverageBlur)
  4. ApplyAverageBlur(oColor, rColor, Hdis, Wdis);
  5. if(FilterType == MedianBlur)
  6. ApplyMedianBlur(oColor, rColor, Hdis, Wdis);
  7. if(FilterType == FirstOrderSharp)
  8. ApplyFirstOrderSharp(oColor, rColor, Hdis, Wdis);
  9. if(FilterType == SecondOrderSharp)
  10. ApplySecondOrderSharp(oColor, rColor, Hdis, Wdis);
  11. if(FilterType == SelfDefine)
  12. ApplySelfDefine(oColor, rColor, Hdis, Wdis);
  13. }

根据客户端的要求,调用不同的FILTER。举最后一个(自定义)为例:

  1. void SpatialDomainTemplate::ApplySelfDefine(FilterColor* oColor, FilterColor* rColor, int Hdis, int Wdis)
  2. {
  3. for(int j = 0; j < Hdis; j++)
  4. for(int i = 0; i < Wdis; i++)
  5. {
  6. AdjustImageEdge(i, j, Wdis, Hdis);
  7. rColor[Index[1][1]]
  8. = oColor[ Index[0][0] ] * templateParam[2]
  9. + oColor[ Index[0][1] ] * templateParam[3]
  10. + oColor[ Index[0][2] ] * templateParam[4]
  11. + oColor[ Index[1][0] ] * templateParam[5]
  12. + oColor[ Index[1][1] ] * templateParam[6]
  13. + oColor[ Index[1][2] ] * templateParam[7]
  14. + oColor[ Index[2][0] ] * templateParam[8]
  15. + oColor[ Index[2][1] ] * templateParam[9]
  16. + oColor[ Index[2][2] ] * templateParam[10];
  17. rColor[Index[1][1]] = rColor[Index[1][1]] * templateParam[0] / templateParam[1];
  18. if(rColor[Index[1][1]].blue < 0) rColor[Index[1][1]].blue = 0;
  19. if(rColor[Index[1][1]].green < 0)rColor[Index[1][1]].green = 0;
  20. if(rColor[Index[1][1]].red < 0) rColor[Index[1][1]].red = 0;
  21. }
  22. }

对每个像素,以它为中心的3邻域每个邻接像素都乘以一个系数,相加后结果乘以一个主调节量,判断边界后得到该元素的结果。加上类型转换的截断,可以保证结果在0~255之间——为免去计算过程越界的危险,FilerColor间是整型的运算:

  1. class FilterColor {
  2. public:
  3. int red;
  4. int green;
  5. int blue;
  6. ............
  7. }

只要最后结果的类型是UCHAR(或BYTE),VC6就能自动类型转换从INT转换到UCHAR。AdjustImageEdge函数是调节边界的,这里对于边界元素,它的那些“不存在的邻居”都被虚拟成一个与该像素等值的像素。边界问题总是那样需要掂量啊。

高斯平滑:

原图片:

14b51e86120537e04efa69604356c7ae.png


处理后:

878eb543c665568e08941415c92bedb8.png

数学形态学(Mathematical Morphology),是分析几何形状和结构的数学方法,包括膨胀、腐蚀、开、闭等运算。在图像处理里面,用于“保持基本形状,去除不相关特征”——ZwqXin.com
什么意思呢?图像中有一个貌似正方形的区域,然后你通过处理,可以把“貌似”去掉;图像是一个不清晰的指纹,然后你通过处理,也把“不”字去掉;图像中有很多大小不一的圆形区域,然后你通过处理,找出大者祛除小者(去噪)......应用是很多的,但是没有一个完整的范畴,就是很多时候要做某种图像处理实际应用的时候,会想起它吧。
本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
原文地址:http://www.zwqxin.com/archives/image-processing/morphologic-process.html

因为处理的都是二值图像(用来分清目标和背景),因此有必要预先把图片转化为8BIT的二值图像,便于处理操作。我是把黑色像素作为目标区域,白色像素作为背景的(当然可以反相)。

结构元素:

其实就是自己定义的一块像素区域,形态学处理的时候用这个元素与原图像作与或运算,生成结果。

膨胀:

c334fd38e66a37e4cd5aa97606904883.png

设A为原图像。B为结构元素(C为B的映像,没用),那么D的整块“灰+黑”区域就是新图像了(反映在图像里都是黑像素),可以看到图中用黑色标识的是“膨胀”出来的。它就是把结构元素B的中心(+)分别与原图像A“重合”,然后考虑结构元素里的“中心以外”的像素将在原图像中出现的位置,并与原图像该位置的像素进行0-1值的“或运算”(设黑色为0,白色为1,下同)

腐蚀:

6ffabe6895209243056bf9e04f1e206a.png

腐蚀的解释与膨胀相同,不过执行的是“与运算”。如图,最终图像是C中的纯黑色部分。由此可见膨胀与腐蚀的关系与不同点。

至于“开”与“闭”,无非是结合膨胀腐蚀的二次操作。前者是先腐蚀后膨胀,后者是先膨胀后腐蚀。要注意的是第一次是用结构元素,第二次则是用结构元素的倒置(相当于矩阵的转置咯,这是为了最后图像的“平衡”,如果两次用同一个的话,结果只会是等同双重“膨胀”或“腐蚀”。注意,概念跟“映像”是不同 di,见下图,元素中心也换位置了):

a7e0ce33b867b6620ff495d4a0f68ddb.png

开运算效果:
- 删除小物体;
–将物体拆分为小物体;
–平滑大物体边界而不明显改变它们的面积;

闭运算效果:
- 填充物体的小洞;
–连接相近的物体;
–平滑物体的边界而不明显改变它们的面积。

84955a0c119817d0b70c755fec840b02.png


(前者为“开”,后者为“闭”)
[注:以上图片多转自冈萨雷斯那本图像处理的书,老师的教学用PPT中截取]
总的操作代码见下。

  1. void MorphologicProcess::BmpProcess8(OperateBMP& image, int Hdis, int Wdis)
  2. {
  3. GetMinimumStructureElement(MorphElement, LineGrid, StructLineGridX, StructLineGridY);
  4. if(MorphType == MorphErosion)
  5. MorphDilaEroProcess(image, Hdis, Wdis, MorphErosion);
  6. else if(MorphType == MorphDilation)
  7. MorphDilaEroProcess(image, Hdis, Wdis, MorphDilation);
  8. else if(MorphType == MorphOpen)
  9. {
  10. MorphDilaEroProcess(image, Hdis, Wdis, MorphErosion);
  11. GetRotInverseStructureElement();
  12. GenBinaryMorph(image);
  13. MorphDilaEroProcess(image, Hdis, Wdis, MorphDilation);
  14. GetRotInverseStructureElement();
  15. }
  16. else if(MorphType == MorphClose)
  17. {
  18. MorphDilaEroProcess(image, Hdis, Wdis, MorphDilation);
  19. GetRotInverseStructureElement();
  20. GenBinaryMorph(image);
  21. MorphDilaEroProcess(image, Hdis, Wdis, MorphErosion);
  22. GetRotInverseStructureElement();
  23. }
  24. }

在细节上不想多说了。难处不在形态学算法本身,而在“自定义结构元素”(MorphElement)这块,包括它与原图像进行形态学运算时必须要考虑的边界条件处理。有需要的同学可以留言给我[http://www.zwqxin.com]。(当然如果自己连算法怎么样也不懂,只是为了交作业交任务而索要的话,恕不理会- -)

b6630ca0d7be3d2102d780a4f11ace8b.png


(原始测试图片,处理对话框)

e0bbcb908f9cf7d6d53c8f74498dc66c.png


(自定义任意的结构元素,约定中间红点为结构元素中心)

8b6df683216854649dc744fcd9803b0a.png


(膨胀,处理结果图片)

4530978509f6f4208e3b0a5c05ed2701.png


(任意定义新的结构元素,作为腐蚀例子)

736792a5a0821a563b2094b0e9aa91d0.png


(腐蚀,处理结果图片)

开和闭运算就不截图了,免得一篇日志里图太多。

2009-4-27 / 2009-5-16

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值