实验原理
图像的平滑处理就是为了消除图像中存在的噪声而对图像施加的一种处理。也称为图像的去噪声处理。
施行平滑化处理的方法大致分为两类:
频域法:相应处理在频域进行
其中主要的两种方法是空间邻域平均法和中值滤波法。
空域法:相应处理在空域进行
主要有理想低通滤波器和巴特沃思低通滤波器。
还有一种多图像平均法。
首先为了是图像上的噪声更加明显,我们手动的给图像加入噪声,即椒盐化处理:
void salt(Mat &image, int num) {
if (!image.data) return;//判断图像是否存在,防止传入空图
int i, j;
srand(time(NULL));
for (int x = 0; x < num; ++x) {//通过c++z自带的随机数生成器生成随机数,将图片中的num个像素点颜色变成白色,RGB三种颜色均赋值255
i = rand() % image.rows;
j = rand() % image.cols;
image.at<Vec3b>(i, j)[0] = 255;
image.at<Vec3b>(i, j)[1] = 255;
image.at<Vec3b>(i, j)[2] = 255;
}
}
效果如下:
一、空间邻域平均法(均值滤波)
//均值滤波
void AverFiltering(const Mat &src,Mat &dst) {
if (!src.data) return;
//at访问像素点
for (int i = 1; i<src.rows; ++i)
for (int j = 1; j < src.cols; ++j) {
if ((i - 1 >= 0) && (j - 1) >= 0 && (i + 1)<src.rows && (j + 1)<src.cols) {//边缘不进行处理
//由于是彩色图像,需要对RGB三个通道的数据值分别进行处理,以当前像素 f(i,j)为中心切出一个 3*3 像素组成的图像块,然后求取像素块的均值作为当前位置点的像素值
dst.at<Vec3b>(i, j)[0] = (src.at<Vec3b>(i, j)[0] + src.at<Vec3b>(i - 1, j - 1)[0] + src.at<Vec3b>(i - 1, j)[0] + src.at<Vec3b>(i, j - 1)[0] +
src.at<Vec3b>(i - 1, j + 1)[0] + src.at<Vec3b>(i + 1, j - 1)[0] + src.at<Vec3b>(i + 1, j + 1)[0] + src.at<Vec3b>(i, j + 1)[0] +
src.at<Vec3b>(i + 1, j)[0]) / 9;
dst.at<Vec3b>(i, j)[1] = (src.at<Vec3b>(i, j)[1] + src.at<Vec3b>(i - 1, j - 1)[1] + src.at<Vec3b>(i - 1, j)[1] + src.at<Vec3b>(i, j - 1)[1] +
src.at<Vec3b>(i - 1, j + 1)[1] + src.at<Vec3b>(i + 1, j - 1)[1] + src.at<Vec3b>(i + 1, j + 1)[1] + src.at<Vec3b>(i, j + 1)[1] +
src.at<Vec3b>(i + 1, j)[1]) / 9;
dst.at<Vec3b>(i, j)[2] = (src.at<Vec3b>(i, j)[2] + src.at<Vec3b>(i - 1, j - 1)[2] + src.at<Vec3b>(i - 1, j)[2] + src.at<Vec3b>(i, j - 1)[2] +
src.at<Vec3b>(i - 1, j + 1)[2] + src.at<Vec3b>(i + 1, j - 1)[2] + src.at<Vec3b>(i + 1, j + 1)[2] + src.at<Vec3b>(i, j + 1)[2] +
src.at<Vec3b>(i + 1, j)[2]) / 9;
}
else {//边缘赋值
dst.at<Vec3b>(i, j)[0] = src.at<Vec3b>(i, j)[0];
dst.at<Vec3b>(i, j)[1] = src.at<Vec3b>(i, j)[1];
dst.at<Vec3b>(i, j)[2] = src.at<Vec3b>(i, j)[2];
}
}
}
和OpenCV自带的均值滤波函数比较,效果如下图所示:
我们发现OpenCV自带的均值滤波函数和自定义均值滤波函数效果类似,但是滤波效果不是很好,并且图像变得更加模糊了。
二、中值滤波
//中值滤波函数
void MedianFlitering(const Mat &src, Mat &dst) {
if (!src.data)return;
Mat _dst(src.size(), src.type());
for(int i=0;i<src.rows;++i)
for (int j=0; j < src.cols; ++j) {
if ((i - 1) > 0 && (i + 1) < src.rows && (j - 1) > 0 && (j + 1) < src.cols) {
_dst.at<Vec3b>(i, j)[0] = Median(src.at<Vec3b>(i, j)[0], src.at<Vec3b>(i + 1, j + 1)[0],
src.at<Vec3b>(i + 1, j)[0], src.at<Vec3b>(i, j + 1)[0], src.at<Vec3b>(i + 1, j - 1)[0],
src.at<Vec3b>(i - 1, j + 1)[0], src.at<Vec3b>(i - 1, j)[0], src.at<Vec3b>(i, j - 1)[0],
src.at<Vec3b>(i - 1, j - 1)[0]);
_dst.at<Vec3b>(i, j)[1] = Median(src.at<Vec3b>(i, j)[1], src.at<Vec3b>(i + 1, j + 1)[1],
src.at<Vec3b>(i + 1, j)[1], src.at<Vec3b>(i, j + 1)[1], src.at<Vec3b>(i + 1, j - 1)[1],
src.at<Vec3b>(i - 1, j + 1)[1], src.at<Vec3b>(i - 1, j)[1], src.at<Vec3b>(i, j - 1)[1],
src.at<Vec3b>(i - 1, j - 1)[1]);
_dst.at<Vec3b>(i, j)[2] = Median(src.at<Vec3b>(i, j)[2], src.at<Vec3b>(i + 1, j + 1)[2],
src.at<Vec3b>(i + 1, j)[2], src.at<Vec3b>(i, j + 1)[2], src.at<Vec3b>(i + 1, j - 1)[2],
src.at<Vec3b>(i - 1, j + 1)[2], src.at<Vec3b>(i - 1, j)[2], src.at<Vec3b>(i, j - 1)[2],
src.at<Vec3b>(i - 1, j - 1)[2]);
}
else
_dst.at<Vec3b>(i, j) = src.at<Vec3b>(i, j);
}
_dst.copyTo(dst);//拷贝
}
其中,Median()函数如下,主要目的为寻找九个数中的中值:
//求九个数的中值
uchar Median(uchar n1, uchar n2, uchar n3, uchar n4, uchar n5,
uchar n6, uchar n7, uchar n8, uchar n9) {
uchar arr[9];
arr[0] = n1;
arr[1] = n2;
arr[2] = n3;
arr[3] = n4;
arr[4] = n5;
arr[5] = n6;
arr[6] = n7;
arr[7] = n8;
arr[8] = n9;
for (int gap = 9 / 2; gap > 0; gap /= 2)//希尔排序
for (int i = gap; i < 9; ++i)
for (int j = i - gap; j >= 0 && arr[j] > arr[j + gap]; j -= gap)
swap(arr[j], arr[j + gap]);
return arr[4];//返回中值
}
和OpenCV自带的中值滤波函数比较,效果如下图所示:
我们发现OpenCV自带的中值滤波函数和自定义中值滤波函数效果类似,滤波效果较好,将噪声点基本上全部滤除干净。
三、最大值滤波
//最大值滤波函数
void MaxFlitering(const Mat &src, Mat &dst) {
if (!src.data)return;
Mat _dst(src.size(), src.type());
for(int i=0;i<src.rows;++i)
for (int j=0; j < src.cols; ++j) {
if ((i - 1) > 0 && (i + 1) < src.rows && (j - 1) > 0 && (j + 1) < src.cols) {
_dst.at<Vec3b>(i, j)[0] = Max(src.at<Vec3b>(i, j)[0], src.at<Vec3b>(i + 1, j + 1)[0],
src.at<Vec3b>(i + 1, j)[0], src.at<Vec3b>(i, j + 1)[0], src.at<Vec3b>(i + 1, j - 1)[0],
src.at<Vec3b>(i - 1, j + 1)[0], src.at<Vec3b>(i - 1, j)[0], src.at<Vec3b>(i, j - 1)[0],
src.at<Vec3b>(i - 1, j - 1)[0]);
_dst.at<Vec3b>(i, j)[1] = Max(src.at<Vec3b>(i, j)[1], src.at<Vec3b>(i + 1, j + 1)[1],
src.at<Vec3b>(i + 1, j)[1], src.at<Vec3b>(i, j + 1)[1], src.at<Vec3b>(i + 1, j - 1)[1],
src.at<Vec3b>(i - 1, j + 1)[1], src.at<Vec3b>(i - 1, j)[1], src.at<Vec3b>(i, j - 1)[1],
src.at<Vec3b>(i - 1, j - 1)[1]);
_dst.at<Vec3b>(i, j)[2] = Max(src.at<Vec3b>(i, j)[2], src.at<Vec3b>(i + 1, j + 1)[2],
src.at<Vec3b>(i + 1, j)[2], src.at<Vec3b>(i, j + 1)[2], src.at<Vec3b>(i + 1, j - 1)[2],
src.at<Vec3b>(i - 1, j + 1)[2], src.at<Vec3b>(i - 1, j)[2], src.at<Vec3b>(i, j - 1)[2],
src.at<Vec3b>(i - 1, j - 1)[2]);
}
else
_dst.at<Vec3b>(i, j) = src.at<Vec3b>(i, j);
}
_dst.copyTo(dst);//拷贝
}
其中,Max()函数如下,主要目的是寻找九个数中的最大值:
//求九个数的最大值
uchar Max(uchar n1, uchar n2, uchar n3, uchar n4, uchar n5,
uchar n6, uchar n7, uchar n8, uchar n9) {
uchar arr[9];
arr[0] = n1;
arr[1] = n2;
arr[2] = n3;
arr[3] = n4;
arr[4] = n5;
arr[5] = n6;
arr[6] = n7;
arr[7] = n8;
arr[8] = n9;
int maxPosition = max_element(arr,arr+6) - arr;
return arr[maxPosition];//返回中值
}
最大值滤波效果如下:
由于椒盐化使得图像中的噪声点较多,并且噪声点为白色点(RGB数值均为255),非常容易成为最大值点,所以最大值滤波使得原来的噪声点被进一步放大,图像质量变差。
四、最小值滤波
//最小值滤波函数
void MinFlitering(const Mat &src, Mat &dst) {
if (!src.data)return;
Mat _dst(src.size(), src.type());
for(int i=0;i<src.rows;++i)
for (int j=0; j < src.cols; ++j) {
if ((i - 1) > 0 && (i + 1) < src.rows && (j - 1) > 0 && (j + 1) < src.cols) {
_dst.at<Vec3b>(i, j)[0] = Min(src.at<Vec3b>(i, j)[0], src.at<Vec3b>(i + 1, j + 1)[0],
src.at<Vec3b>(i + 1, j)[0], src.at<Vec3b>(i, j + 1)[0], src.at<Vec3b>(i + 1, j - 1)[0],
src.at<Vec3b>(i - 1, j + 1)[0], src.at<Vec3b>(i - 1, j)[0], src.at<Vec3b>(i, j - 1)[0],
src.at<Vec3b>(i - 1, j - 1)[0]);
_dst.at<Vec3b>(i, j)[1] = Min(src.at<Vec3b>(i, j)[1], src.at<Vec3b>(i + 1, j + 1)[1],
src.at<Vec3b>(i + 1, j)[1], src.at<Vec3b>(i, j + 1)[1], src.at<Vec3b>(i + 1, j - 1)[1],
src.at<Vec3b>(i - 1, j + 1)[1], src.at<Vec3b>(i - 1, j)[1], src.at<Vec3b>(i, j - 1)[1],
src.at<Vec3b>(i - 1, j - 1)[1]);
_dst.at<Vec3b>(i, j)[2] = Min(src.at<Vec3b>(i, j)[2], src.at<Vec3b>(i + 1, j + 1)[2],
src.at<Vec3b>(i + 1, j)[2], src.at<Vec3b>(i, j + 1)[2], src.at<Vec3b>(i + 1, j - 1)[2],
src.at<Vec3b>(i - 1, j + 1)[2], src.at<Vec3b>(i - 1, j)[2], src.at<Vec3b>(i, j - 1)[2],
src.at<Vec3b>(i - 1, j - 1)[2]);
}
else
_dst.at<Vec3b>(i, j) = src.at<Vec3b>(i, j);
}
_dst.copyTo(dst);//拷贝
}
其中,Median()函数如下,主要目的为寻找九个数中的最小值:
//求九个数的最小值
uchar Min(uchar n1, uchar n2, uchar n3, uchar n4, uchar n5,
uchar n6, uchar n7, uchar n8, uchar n9) {
uchar arr[9];
arr[0] = n1;
arr[1] = n2;
arr[2] = n3;
arr[3] = n4;
arr[4] = n5;
arr[5] = n6;
arr[6] = n7;
arr[7] = n8;
arr[8] = n9;
int minPosition = min_element(arr,arr+6) - arr;
return arr[minPosition];//返回中值
}
最小值滤波效果如下:
由于椒盐化使得图像中的噪声点较多,并且噪声点为白色点(RGB数值均为255),很难成为最小值点,所以最小值滤波使得原来的噪声点基本被去除,图像质量变好,甚至优于中值滤波效果。
五、图像锐化
图像锐化的主要原理为:
void Sharpening(const Mat &src,Mat &dst) {
if (!src.data) return;
//at访问像素点
for (int i = 1; i<src.rows; ++i)
for (int j = 1; j < src.cols; ++j) {
if ((i - 1 >= 0) && (j - 1) >= 0 && (i + 1)<src.rows && (j + 1)<src.cols) {//边缘不进行处理
dst.at<Vec3b>(i, j)[0] = src.at<Vec3b>(i, j)[0] + N * (4 * src.at<Vec3b>(i, j)[0] - ( src.at<Vec3b>(i - 1, j)[0] + src.at<Vec3b>(i + 1, j)[0] + src.at<Vec3b>(i, j - 1)[0] + src.at<Vec3b>(i, j + 1)[0])) ;
dst.at<Vec3b>(i, j)[1] = src.at<Vec3b>(i, j)[1] + N * (4 * src.at<Vec3b>(i, j)[1] - ( src.at<Vec3b>(i - 1, j)[1] + src.at<Vec3b>(i + 1, j)[1] + src.at<Vec3b>(i, j - 1)[1] + src.at<Vec3b>(i, j + 1)[1])) ;
dst.at<Vec3b>(i, j)[2] = src.at<Vec3b>(i, j)[2] + N * (4 * src.at<Vec3b>(i, j)[2] - ( src.at<Vec3b>(i - 1, j)[2] + src.at<Vec3b>(i + 1, j)[2] + src.at<Vec3b>(i, j - 1)[2] + src.at<Vec3b>(i, j + 1)[2])) ;
}
else {//边缘赋值
dst.at<Vec3b>(i, j)[0] = src.at<Vec3b>(i, j)[0];
dst.at<Vec3b>(i, j)[1] = src.at<Vec3b>(i, j)[1];
dst.at<Vec3b>(i, j)[2] = src.at<Vec3b>(i, j)[2];
}
}
}
选择锐化因子为0.001,效果如下图所示:
选择锐化因子为0.005,效果如下图所示:
选择锐化因子为0.01,效果如下图所示:
选择锐化因子为0.05,效果如下图所示:
选择锐化因子为0.1,效果如下图所示:
原图像经过锐化处理后,灰度变化较平坦的区域,变化不大,而包括轮廓点在内的灰度变化较剧烈的区域,灰度差别加大,即图像细节得到增强。同时锐化因子不宜过大,取0.005时效果较优。