图像去噪
图像噪声介绍
1.什么是图像噪声
图像中各种妨碍人们对其信息接受的因素即可称为图像噪声(即图像数据中的干扰信息)
2.图像噪声的来源
- 图像获取过程
- 数字图像传输的过程
3.特点
- 噪声的分布和大小不规则
- 噪声和原图像有相关性
- 噪声具有叠加性
4.分类
1.高斯噪声(它的幅度分布服从高斯分布,而它的功率谱密度又是均匀分布)
根据Box-Muller变换原理,建设随机变量U1、U2来自独立的处于(0,1)之间的均匀分布,则经过下面两个式子产生的随机变量Z0,Z1服从标准高斯分布
![](https://i-blog.csdnimg.cn/blog_migrate/6997a650bfc094f6f7521ea2d2a03446.jpeg)
上式中Z0,Z1满足正态分布,其中均值为0,方差为1,变量U1和U2可以修改为下式:
![](https://i-blog.csdnimg.cn/blog_migrate/86702a1ae05cf8733d1283c97fd4ce96.jpeg)
2.椒盐噪声(强烈变化引起的突然产生不规则的白点和黑点)
通过随机获取像素点并设置为高亮度点和低灰度点来实现
代码如下:
# include<limits.h>
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;
void salt_noise(Mat& image, Mat& salt_image, const int n);//椒盐噪声
double addgassian_noise(double mu,double sigma);//高斯噪声的实现
void gassian_noise(Mat& image, Mat& gassian_image);//高斯噪声
int main()
{
Mat image = imread("d:/texting.jpg", 0);
if (image.empty())
{
cout << "未找到文件" << endl;
return -1;
}
Mat salt_image;
salt_noise(image, salt_image, 5000);
Mat gassian_image;
gassian_noise(image, gassian_image);
imshow("image", image);
imshow("salt_image", salt_image);
imshow("gassian_image", gassian_image);
waitKey(0);
return 0;
}
void salt_noise(Mat& image, Mat& salt_image, const int n)
{
salt_image = image.clone();
int i, j;
for (int x=0; x < n; ++x)
{
i = rand() % image.rows;
j = rand() % image.cols;
if (salt_image.channels() == 1)
{
salt_image.at<uchar>(i, j) = 255;
}
else
{
salt_image.at<Vec3b>(i, j)[0] = 255;
salt_image.at<Vec3b>(i, j)[1] = 255;
salt_image.at<Vec3b>(i, j)[2] = 255;
}
}
for (int x=0; x < n; ++x)
{
i = rand() % image.rows;
j = rand() % image.cols;
if (salt_image.channels() == 1)
{
salt_image.at<uchar>(i, j) = 0;
}
else
{
salt_image.at<Vec3b>(i, j)[0] = 0;
salt_image.at<Vec3b>(i, j)[1] = 0;
salt_image.at<Vec3b>(i, j)[2] = 0;
}
}
}
double addgassian_noise(double mu, double sigma)
{
//定义小值
const double min_number = numeric_limits<double>::min();
static double z0, z1;
static bool flag = false;
flag = !flag;
if (!flag)
return z1 * sigma + mu;
double u1, u2;
do
{
u1 = rand() * (1.0 / RAND_MAX);
u2 = rand() * (1.0 / RAND_MAX);
} while (u1 <= min_number);
z0 = sqrt(-2.0 * log(u1)) * cos(2 * CV_PI * u2);
z1 = sqrt(-2.0 * log(u1)) * sin(2 * CV_PI * u2);
return z0 * sigma + mu;
}
void gassian_noise(Mat& image, Mat& gassian_image)
{
gassian_image = image.clone();
for (int row=0; row < image.rows; ++row)
{
for (int col=0; col < image.cols; ++col)
{
int x = int(gassian_image.at<uchar>(row, col) + addgassian_noise(2, 0.8) * 32);
if (x < 0)
x = 0;
if (x > 255)
x = 255;
gassian_image.at<uchar>(row,col) = (uchar)x;
}
}
}
效果如下:
![](https://i-blog.csdnimg.cn/blog_migrate/31442c81b7c6e7375bae0faf113a0d22.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5143752c4600d571dcf30f4f0fd12f48.png)
![](https://i-blog.csdnimg.cn/blog_migrate/cd2fd927d5bb77603f5b2582f64c4e23.png)
局部运算
为了减少噪声到来的影响,因此很多时候我们采用很多的方法进行处理,而其中最有效的方法的使用局部运算,即使用像素和像素周围的像素进行运算,从而综合单个像素对于图像过于明显的影响。
1.邻域平均法(box模板)
使用以一个点为中心f(x,y)和其周围的n*n的像素进行取平均,然后再赋值给f(x,y),这样该位置就变成g(x,y)。即其就是一个平均的概念
代码如下:
/**********************************************************
*项目名称:使用均值滤波进行图像去噪
*缺点: 1.图像模糊,2.计算量大
*
*函数:void average_filter(Mat& src, Mat& dst,const int& Border);
* 描述:无返回值,src是输入的图像,dst是改变后输出的图像,Border表示模板大小 Border*Border
*
*作者:***
*日期:2020.04.06
*修改:使用积分图技术进行减少计算量(未完成)
***********************************************************/
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
void average_filter(Mat& src, Mat& dst,const int& Border);
int main(void)
{
Mat image = imread("d:/模糊的图片.PNG", 0);
if (image.empty())
{
cout << "未找到指定的文件" << endl;
return -1;
}
// 生成一个大小一样的全黑的图像
Mat average_image = Mat::zeros(image.size(), CV_8UC1);
//模板的大小
const int border = 7;
//自定义均值滤波函数
average_filter(image,average_image, border);
Mat output_blur;
//库的均值滤波函数
blur(image, output_blur, Size(7, 7));
imshow("image", image);
imshow("average_image", average_image);
imshow("output_blur", output_blur);
waitKey(0);
return 0;
}
void average_filter(Mat& src, Mat& dst, const int& Border)
{
int width = src.rows;
int height = src.cols;
cout << width << endl;
cout << height << endl;
int row,col;
int sum=0;//表示模板内的像素的总和
for (row = 0; row < width ; ++row)
{
for (col =0; col < height; ++col)
{
if (row >= (Border - 1) / 2 && row < width - (Border - 1) / 2 && col >= (Border - 1) / 2 && col < height - (Border - 1) / 2)
{
for (int i = -(Border - 1) / 2; i <= (Border - 1) / 2; ++i)
for (int j = -(Border - 1) / 2; j <= (Border - 1) / 2; ++j)
{
sum = sum+src.at<uchar>(row + i, col + j);
}
dst.at<uchar>(row, col) = sum / (Border * Border);
sum = 0;
}
else
{
dst.at<uchar>(row, col) = src.at<uchar>(row, col);
}
}
}
}
2.中值滤波法
其步骤为:
- 将窗口中心与图中某个象素位置重合(和均值一样取n*n)
- 提取出该模板的个个像素灰度值
- 进行排序
- 取出中间的值
- 进行赋值给窗口的f(x,y),得到新的值g(x,y)
代码如下:
/**********************************************************
*项目名称:使用中值滤波进行图像去噪
*缺点: 图像中点、线、尖角细节较多,则不宜采用中值滤波
*
*函数:void median_filter(Mat& src, Mat& dst,const int& Border);
* 描述:无返回值,src是输入的图像,dst是改变后输出的图像,Border表示模板大小 Border*Border
*
*作者:***
*日期:2020.04.06
*修改:使用多种排列的算法进行处理(未完成)
***********************************************************/
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
void median_filter(Mat& src, Mat& dst, const int& Border);
int main(void)
{
Mat image = imread("d:/模糊的图片.PNG", 0);
if (image.empty())
{
cout << "未找到指定的文件" << endl;
return -1;
}
// 生成一个大小一样的全黑的图像
Mat median_image = Mat::zeros(image.size(), CV_8UC1);
//模板的大小
const int border = 5;
//自定义均值滤波函数
median_filter(image, median_image, border);
Mat output_blur;
//库的均值滤波函数
//blur(image, output_blur, Size(7, 7));
imshow("image", image);
imshow("median_image", median_image);
//imshow("output_blur", output_blur);
waitKey(0);
return 0;
}
void median_filter(Mat& src, Mat& dst, const int& Border)
{
int width = src.rows;
int height = src.cols;
cout << width << endl;
cout << height << endl;
int row, col;
int* border = new int[Border * Border];
for (int i = 0; i < Border * Border; ++i)
{
border[i] = i;
}
for (int i = 0; i < Border * Border; ++i)
{
//cout<<border[i]<<endl;
}
//cout << sizeof(border);//8
int median = 0;
for (row = 0; row < width; ++row)
{
for (col = 0; col < height; ++col)
{
if (row >= (Border - 1) / 2 && row < width - (Border - 1) / 2 && col >= (Border - 1) / 2 && col < height - (Border - 1) / 2)
{
for (int i = -(Border - 1) / 2; i <= (Border - 1) / 2; ++i)
{
for (int j = -(Border - 1) / 2; j <= (Border - 1) / 2; ++j)
{
//进行赋值到中间数组里面进行排序
border[(i+ (Border - 1) / 2)+(j+ (Border - 1) / 2) *Border]= src.at<uchar>(row + i, col + j);
}
}
//进行排序
for (int i = 1; i < Border * Border; ++i)
{
for (int j = 1; j < Border * Border-i; ++j)
{
if (border[j - 1] > border[j])
{
median = border[j - 1];
border[j - 1] = border[j];
border[j] = median;
}
median = 0;
}
}
//排序完进行赋值
dst.at<uchar>(row, col) = border[(Border * Border - 1) / 2];
}
else
{
dst.at<uchar>(row, col) = src.at<uchar>(row, col);
}
}
}
}
3.高斯滤波
模板运算的数学含义是卷积运算,使用高斯滤波来解决图像去噪的问题,只有基本掌握模板运算和高斯滤波的公式就可以。
代码如下:
//*************************************************
//第三周作业:编写代码,实现高斯滤波,模板大小为3*3
//
//Version: 0.1
//author : ***
//date : 2020/03/29
//***********************************************
#include <iostream>
#include<opencv2/opencv.hpp>
#include <math.h>
using namespace std;
using namespace cv;
double** getGuassionArray(int size, double sigma) {
int i, j;
double sum = 0.0;
int center = size; //以第一个点的坐标为原点,求出中心点的坐标
double** arr = new double* [size];//建立一个size*size大小的二维数组
for (i = 0; i < size; ++i)
arr[i] = new double[size];
for (i = 0; i < size; ++i)
for (j = 0; j < size; ++j) {
arr[i][j] = exp(-((i - center) * (i - center) + (j - center) * (j - center)) / (sigma * sigma * 2));
sum += arr[i][j];
}
for (i = 0; i < size; ++i)
for (j = 0; j < size; ++j)
arr[i][j] /= sum;
return arr;
}
//**********************************
//第一个参数是_src表示输入
//第二个参数是_dst表示输出
void myGaussian(const Mat _src, Mat& _dst) {
if (_src.empty())
{
cout << "未找到有效文件" << endl;
return;
}
double** arr;
Mat tmp(_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) {
arr = getGuassionArray(3, 1);//自定义得到的权值数组
tmp.at<Vec3b>(i, j)[0] = 0;
tmp.at<Vec3b>(i, j)[1] = 0;
tmp.at<Vec3b>(i, j)[2] = 0;
for (int x = 0; x < 3; ++x) {
for (int y = 0; y < 3; ++y) {
tmp.at<Vec3b>(i, j)[0] += arr[x][y] * _src.at<Vec3b>(i + 1 - x, j + 1 - y)[0];
tmp.at<Vec3b>(i, j)[1] += arr[x][y] * _src.at<Vec3b>(i + 1 - x, j + 1 - y)[1];
tmp.at<Vec3b>(i, j)[2] += arr[x][y] * _src.at<Vec3b>(i + 1 - x, j + 1 - y)[2];
}
}
}
}
tmp.copyTo(_dst);
}
void main() {
Mat image = imread("D:/图片/壁纸/动漫/1.jpg",1);
Mat Gaussian;
Mat image1;
GaussianBlur(image, image1, Size(3, 3), 1); //自带的高斯滤波
myGaussian(image, Gaussian);
imshow("原图", image);
imshow("opencv自带的高斯滤波", image1);
imshow("自定义高斯滤波", Gaussian);
waitKey(0);
}