本章主要讲图像处理中的模糊处理部分
红色部分是我想直接用at,而不用指针,但是效率低的厉害。
上面代码有两处问题:
第二个问题是本文中sigma 是个固定值,实际上它是个可变值,具体怎么计算,我没有搞清楚,可以查看opencv的源代码,下面文章有参考价值
英文叫做blur, 也叫做smootiing, 中文中叫做模糊或者平滑。
用过photoshop的人都应该知道,滤镜里面就有模糊这个选项,我们现在看看它是怎么实现的。
一含义
模糊(平滑)是一种常用的图片处理方式,它的作用可以用来降低噪声,还有其他用途
看一下opencv 里面的公式
g(i,j)是目标坐标的像素值, f(i+k,j+l)是k,l这些地方的像素值, h(k,l)是 kernel, 我不知道怎么去准确翻译它的意义,它是过滤器的系数。
简单的按照我的思路去理解,就是一个权值,模糊的含义是将所有的像素按照一定的权值进行运算,得到一个比较均衡的结果。
二 类型
类型有很多种:
均值模糊(box blur) 高斯模糊(gaussian blur) 中值模糊(media blur) 二值模糊(bilateral blur)
本文只讲均值模糊和高斯模糊
三 算法
1 均值模糊
均值模糊很简单就是周边所有的影响都是1,求平均值即可

2 高斯模糊
关于高斯模糊的算法,推荐这个文章

根据这个公式计算出系数即可。
上篇文章写得很详细,我就不班门弄斧了。
四均值模糊的代码和效果
先放上均值模糊的代码
void boxblur(Mat input ,Mat &out, int x, int y)
{
// accept only char type matrices
CV_Assert(input.depth() != sizeof(uchar));
out.create(input.size(),input.type());
int nChannels = input.channels();
int nRows = input.rows;
int nCols = input.cols;
int size = x * y;
float kernel = 1.0/size;
int i,j;
uchar* p;
uchar* q;
uchar R,G,B;
for( i = x; i < nRows - x; ++i)
{
q = out.ptr<uchar>(i);
for ( j = y; j < nCols - y; ++j)
{
float sumR = 0;
float sumG = 0;
float sumB = 0;
for (int k =0; k<x;k++)
{
p = input.ptr<uchar>(i-x+k);
for(int l = 0; l < y;l++)
{
sumB += input.at<uchar>(i - x + k,(j + l - y)*nChannels) * kernel;//p[(l + j -y)*nChannels ] * kernel;
sumG += input.at<uchar>(i - x + k,(j + l - y)*nChannels + 1) * kernel;//p[(l + j -y)*nChannels + 1] * kernel;
sumR += input.at<uchar>(i - x + k,(j + l - y)*nChannels + 2) * kernel;//p[(l + j -y)*nChannels + 2] * kernel;
}
}
q[j*nChannels] = sumB;
q[j*nChannels+1] = sumG;
q[j*nChannels+2] = sumR;
}
}
}
红色部分是我想直接用at,而不用指针,但是效率低的厉害。

下图是用指针的相差了20倍。。。可见指针虽然万恶,但是确实是个好东西。

由于size(4,4)图太小看不清, 实际用的是8
原始 | opencv | 本文 |
![]() | ![]() | ![]() |
五高斯模糊的代码和效果
代码如下:
void gaussblur(Mat input ,Mat &out, int x, int y)
{
float sigma = 1.5;
Mat kernel;
float pi = 3.1415926;
kernel.create(x ,y ,CV_32F);
float mx = x/2.0;
float my = y/2.0;
//这里有问题,后面做修正。
for (int i =0; i< x;i++)
{
for (int j =0; j<y;j++)
{
kernel.at<float>(i,j) = exp(-1 * ((i - mx) * (i - mx) +(j - my) * (j-my) )/( 2 * sigma * sigma))/(2 * pi * sigma *sigma) ;
}
}
int nChannels = input.channels();
int nRows = input.rows;
int nCols = input.cols;
out.create(input.size(),input.type());
uchar* p;
uchar* q;
float* s;
for(int i = x; i < nRows - x; ++i)
{
q = out.ptr<uchar>(i);
for (int j = y; j < nCols - y; ++j)
{
float sumR = 0;
float sumG = 0;
float sumB = 0;
for (int k =0; k<x;k++)
{
p = input.ptr<uchar>(i-x+k);
s = kernel.ptr<float>(k);
for(int l = 0; l < y;l++)
{
sumB += p[(l + j -y)*nChannels ] * s[l];//input.at<uchar>(i - x + k,(j + l - y)*nChannels) * kernel;//
sumG += p[(l + j -y)*nChannels + 1] *s[l];//input.at<uchar>(i - x + k,(j + l - y)*nChannels + 1) * kernel;//
sumR += p[(l + j -y)*nChannels + 2] * s[l];//input.at<uchar>(i - x + k,(j + l - y)*nChannels + 2) * kernel;
}
}
q[j*nChannels] = sumB;
q[j*nChannels+1] = sumG;
q[j*nChannels+2] = sumR;
}
}
}
效率如下:

效果图如下:
本文没有考虑边界的情况,所以都是灰色的,可以考虑一下如何处理边界。
原始 | opencv | 本文 |
![]() | ![]() | ![]() |
上面代码有两处问题:
第一是在size比较小的时候,这些点的概率之和不等于1,会导致图片出问题。修正如下:
float sum = 0;
for (int i =0; i< x;i++)
{
for (int j =0; j<y;j++)
{
sum+= kernel.at<float>(i,j) = exp(-1 * ((i - mx) * (i - mx) +(j - my) * (j-my) )/( 2 * sigma * sigma))/(2 * pi * sigma *sigma) ;
}
}
for (int i =0; i< x;i++)
{
for (int j =0; j<y;j++)
{
kernel.at<float>(i,j) = kernel.at<float>(i,j)/ sum ;
}
}
第二个问题是本文中sigma 是个固定值,实际上它是个可变值,具体怎么计算,我没有搞清楚,可以查看opencv的源代码,下面文章有参考价值
更新一下参考opencv里面的可以这样计算
sigma = 0.3*((ksize-1)*0.5 - 1) + 0.8
.
修改程序之后发现和原始的高斯函数基本一致,希望广大朋友们多多评论,本人水平有限,很多地方有纰漏,希望能够共同提高。