图像处理里面比较基本的操作是在空间域的滤波处理。最常见的模糊啊锐化啊的都可以归于这类。其实质就是邻域间的组合运算,在shader技术上的乒乓也就差不多这个样子,而且操作纹理要更简捷。——ZwqXin.com
本文来源于 ZwqXin (http://www.zwqxin.cn/), 转载请注明
原文地址:http://www.zwqxin.cn/archives/image-processing/image-process-spatial-domain-filter.html
比较直方图类那种“单点”操作,空间域滤波中的每个像素都得多少顾及一下邻里情况,因而依其关注面大小而出现了不同大小的“邻域”的定义。至于不同的空间域滤波对应不同类型的矩阵式,就是所谓的“滤波”(FILTER),或者说模板。简单的就是3*3大小的高斯平滑,中值平滑,一阶梯度锐化/边缘,二阶拉普拉斯锐化/边缘等等,还有实现各种不同效果的FILTER,可上网找找,其实运用都是差不多的,但是要得出一个“有价值”的FILTER,可不是随随便便就能行的。
在[基于亮度的图像二值化处理] 里对8BIT,16BIT,24/32BIT的BMP分别应用“单点”的计算,一提取BMP数据区的像素信息,马上就作转换操作了,而在“邻域”计算里,把全部像素信息提取出来后,再进行转换操作比较合适:
- void SpatialDomainTemplate::BmpProcess24_32(OperateBMP& image, int Hdis, int Wdis, int step)
- {
- int index;
- int Wmax = (Wdis-1)*step;
- FilterColor *color;
- FilterColor *result_color;
- color = new FilterColor[Hdis*Wdis];
- result_color = new FilterColor[Hdis*Wdis];
- memset(color, 0, Hdis*Wdis*sizeof(FilterColor));
- memset(result_color, 0, Hdis*Wdis*sizeof(FilterColor));
- for(int j = 0; j < Hdis; j++)
- for(int i = 0; i < Wdis; i++)
- {
- index = j*Wdis + i;
- color[index].blue = image.GetImageData().buffer[i*step + 0 + Wmax * j];
- color[index].green = image.GetImageData().buffer[i*step + 1 + Wmax * j];
- color[index].red = image.GetImageData().buffer[i*step + 2 + Wmax * j];
- }
- ApplyTemplate(color, result_color, Hdis, Wdis);
- if(GetReadBack())
- {
- for(int k = 0; k < Hdis; k++)
- for(int l = 0; l < Wdis; l++)
- {
- index = k*Wdis + l;
- image.GetImageData().buffer[l*step + 0 + Wmax * k] = result_color[index].blue;
- image.GetImageData().buffer[l*step + 1 + Wmax * k] = result_color[index].green;
- image.GetImageData().buffer[l*step + 2 + Wmax * k] = result_color[index].red;
- }
- }
- delete []color;
- delete []result_color;
- }
这是24/32BIT版本,其他BIT的可参照上篇文章,类似的。color,resultcolor数组分别作为操作函数ApplyTemplate的输入和输出,模板操作就在此函数里完成——它无关BMP是多少BIT的,进来的是一个代表原图像的RGB数组,出来的是代表结果图像的RGB数组,就这样。当然可以看出为了结构我还是牺牲了不少“效率”的。
- void SpatialDomainTemplate::ApplyTemplate(FilterColor* oColor, FilterColor* rColor, int Hdis, int Wdis)
- {
- if(FilterType == AverageBlur)
- ApplyAverageBlur(oColor, rColor, Hdis, Wdis);
- if(FilterType == MedianBlur)
- ApplyMedianBlur(oColor, rColor, Hdis, Wdis);
- if(FilterType == FirstOrderSharp)
- ApplyFirstOrderSharp(oColor, rColor, Hdis, Wdis);
- if(FilterType == SecondOrderSharp)
- ApplySecondOrderSharp(oColor, rColor, Hdis, Wdis);
- if(FilterType == SelfDefine)
- ApplySelfDefine(oColor, rColor, Hdis, Wdis);
- }
根据客户端的要求,调用不同的FILTER。举最后一个(自定义)为例:
- void SpatialDomainTemplate::ApplySelfDefine(FilterColor* oColor, FilterColor* rColor, int Hdis, int Wdis)
- {
- for(int j = 0; j < Hdis; j++)
- for(int i = 0; i < Wdis; i++)
- {
- AdjustImageEdge(i, j, Wdis, Hdis);
- rColor[Index[1][1]]
- = oColor[ Index[0][0] ] * templateParam[2]
- + oColor[ Index[0][1] ] * templateParam[3]
- + oColor[ Index[0][2] ] * templateParam[4]
- + oColor[ Index[1][0] ] * templateParam[5]
- + oColor[ Index[1][1] ] * templateParam[6]
- + oColor[ Index[1][2] ] * templateParam[7]
- + oColor[ Index[2][0] ] * templateParam[8]
- + oColor[ Index[2][1] ] * templateParam[9]
- + oColor[ Index[2][2] ] * templateParam[10];
- rColor[Index[1][1]] = rColor[Index[1][1]] * templateParam[0] / templateParam[1];
- if(rColor[Index[1][1]].blue < 0) rColor[Index[1][1]].blue = 0;
- if(rColor[Index[1][1]].green < 0)rColor[Index[1][1]].green = 0;
- if(rColor[Index[1][1]].red < 0) rColor[Index[1][1]].red = 0;
- }
- }
对每个像素,以它为中心的3邻域每个邻接像素都乘以一个系数,相加后结果乘以一个主调节量,判断边界后得到该元素的结果。加上类型转换的截断,可以保证结果在0~255之间——为免去计算过程越界的危险,FilerColor间是整型的运算:
- class FilterColor {
- public:
- int red;
- int green;
- int blue;
- ............
- }
只要最后结果的类型是UCHAR(或BYTE),VC6就能自动类型转换从INT转换到UCHAR。AdjustImageEdge函数是调节边界的,这里对于边界元素,它的那些“不存在的邻居”都被虚拟成一个与该像素等值的像素。边界问题总是那样需要掂量啊。
高斯平滑:
本文来源于 ZwqXin (http://www.zwqxin.cn/), 转载请注明
原文地址:http://www.zwqxin.cn/archives/image-processing/image-process-spatial-domain-filter.html