图像处理 高斯噪声生成

2012-11-01 20:40:33 guoyk1990 阅读数 17357
  • 图像噪声

    掌握OpenCV核心模块,熟练使用相关API 理解各个API背后的相关算法原理,每个参数意义 有能力解决使用应用场景问题,大量工程代码经验分享 掌握图像处理与视频分析,图像分析与测量编码与开发技巧

    88人学习 贾志刚
    免费试看

    图像中的噪声就是图像中的杂点或者干扰成分,噪声主要产生于图像的获取和传输过程中。噪声一般分为分为加性噪声和乘性噪声。

                                   

其中f(x,y)表示图像,g(x,y)表示没有噪声的图像部分,q表示噪声。这两种噪声可以通过取对数和指数相互转换:

                                   

常见的几种噪声有,高斯噪声、瑞利噪声、指数噪声和椒盐噪声。除椒盐噪声以外其他三种均属于加性噪声。椒盐噪声既不属于加性噪声也不属于乘性噪声。

噪声是有噪声分量灰度值的统计特性来描述的,它们可以被认为是由概率密度函数(PDF)表示的随机变量,下面是几种噪声的概率密度函数。

高斯随机变量z的概率密度函数为:

                                    

μ表示z的期望值,σ表示z的方差。

瑞利噪声的概率密度函数为:

                                    

它的均值和方差分别为a+√(πb)/4和b(4-π)/4。

指数分布噪声的概率密度函数为:

                                    

它的均值和方差分别为1/a和1/a2,其中a>0。

椒盐噪声又称双击脉冲噪声,它类似于随机分布在图像上的胡椒(黑色)和盐粒(白色)。它的概率密度函数为:

                                   

对于一个8位深度图像,a=0,b=255.

下面仅将添加高斯噪声和椒盐噪声的代码贴出:

/// <summary>
    /// 给图像添加噪声
    /// </summary>
    /// <param name="srcBmp">原始图像</param>
    /// <param name="noiseType">噪声类型</param>
    /// <param name="para">噪声类型参数(2个)</param>
    /// <param name="dstBmp">目标图像</param>
    /// <returns>处理成功 true 失败 false</returns>
    public static bool AddNoise(Bitmap srcBmp, NoiseType noiseType, double[] para, out Bitmap dstBmp) {//NoiseType 为自定义噪声枚举类型
        dstBmp = null;
        if (srcBmp == null) { return false; }
        dstBmp = new Bitmap(srcBmp);
        BitmapData bmpData = dstBmp.LockBits(new Rectangle(0, 0, dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
        Random r1 = new Random(unchecked((int)DateTime.Now.Ticks));
        Random r2 = new Random(~unchecked((int)DateTime.Now.Ticks));
        double temp, tempR, tempG, tempB, v1, v2;

        unsafe {
            byte* ptr = (byte*)bmpData.Scan0;
            switch (noiseType) {
                case NoiseType.GAUSSIAN://高斯噪声para[0]为μ  para[1]为σ
                    for (int i = 0; i < bmpData.Height; i++) {
                        for (int j = 0; j < bmpData.Width; j++) {
                            do {
                                v1 = r1.NextDouble();
                            } while (v1 <= 0.00000000001);
                            v2 = r2.NextDouble();
                            //应用雅克比变换直接生成正态分布
                            temp = Math.Sqrt(-2 * Math.Log(v1)) * Math.Cos(2 * Math.PI * v2) * para[1] + para[0];
                            tempB = temp + ptr[i * bmpData.Stride + j * 3];
                            tempG = temp + ptr[i * bmpData.Stride + j * 3 + 1];
                            tempR = temp + ptr[i * bmpData.Stride + j * 3 + 2];
                            tempB = tempB > 255 ? 255 : tempB; tempB = tempB < 0 ? 0 : tempB;
                            tempG = tempG > 255 ? 255 : tempG; tempG = tempG < 0 ? 0 : tempG;
                            tempR = tempR > 255 ? 255 : tempR; tempR = tempR < 0 ? 0 : tempR;
                            ptr[i * bmpData.Stride + j * 3] = (byte)tempB;
                            ptr[i * bmpData.Stride + j * 3 + 1] = (byte)tempG;
                            ptr[i * bmpData.Stride + j * 3 + 2] = (byte)tempR;
                        }
                    }
                    break;
                case NoiseType.SALTPEPPER://椒盐噪声para[0]为椒含量 para[1]为盐含量
                    for (int i = 0; i < bmpData.Height; i++) {
                        for (int j = 0; j < bmpData.Width; j++) {
                            v1 = r1.NextDouble();
                            if (v1 <= para[0]) {
                                temp = -500;
                            }
                            else {
                                if (v1 >= 1 - para[1]) {
                                    temp = 500;
                                }
                                else {
                                    temp = 0;
                                }
                            }
                            tempB = temp + ptr[i * bmpData.Stride + j * 3];
                            tempG = temp + ptr[i * bmpData.Stride + j * 3 + 1];
                            tempR = temp + ptr[i * bmpData.Stride + j * 3 + 2];
                            tempB = tempB > 255 ? 255 : tempB; tempB = tempB < 0 ? 0 : tempB;
                            tempG = tempG > 255 ? 255 : tempG; tempG = tempG < 0 ? 0 : tempG;
                            tempR = tempR > 255 ? 255 : tempR; tempR = tempR < 0 ? 0 : tempR;
                            ptr[i * bmpData.Stride + j * 3] = (byte)tempB;
                            ptr[i * bmpData.Stride + j * 3 + 1] = (byte)tempG;
                            ptr[i * bmpData.Stride + j * 3 + 2] = (byte)tempR;
                        }
                    }
                    break;
                default:
                    break;
            }
        }
        dstBmp.UnlockBits(bmpData);
        return true;
    }
效果图:


2019-09-14 17:23:52 weixin_44225182 阅读数 15111
  • 图像噪声

    掌握OpenCV核心模块,熟练使用相关API 理解各个API背后的相关算法原理,每个参数意义 有能力解决使用应用场景问题,大量工程代码经验分享 掌握图像处理与视频分析,图像分析与测量编码与开发技巧

    88人学习 贾志刚
    免费试看

添加高斯噪声

概念
高斯噪声是指它的概率密度函数服从高斯分布(即正态分布)的一类噪声。如果一个噪声,它的幅度分布服从高斯分布,而它的功率谱密度又是均匀分布的,则称它为高斯白噪声。高斯白噪声的二阶矩不相关,一阶矩为常数,是指先后信号在时间上的相关性。高斯白噪声包括热噪声和散粒噪声。在通信信道测试和建模中,高斯噪声被用作加性白噪声以产生加性白高斯噪声。

我的理解:高斯噪声就是符合高斯分布的噪声,在数字图像处理这块就是二维的高斯分布(正态分布)。那么怎么添加高斯噪声呢?首先我们肯定要得到一个二维的、符合高斯分布的噪声矩阵,最后将其添加到原图像上。这样高斯噪声就添加成功了。

方法一:利用randn()函数生成高斯噪声
这里需要用到randn()函数,该函数就专门用来生成正态分布数据的一个函数。
比如 randn(10,10),然后这个矩阵和原图像矩阵相加即可(注意缩放灰度范围)

randn(10,10)

ans =

   -0.3587    0.6694    0.2922    0.4127   -0.4399   -0.2027    1.2917   -1.2807    0.0522   -0.2097
    1.4851   -0.2349   -0.0809   -1.0475    0.4734   -0.8012   -1.3658   -2.4489    1.2525   -0.0725
    0.1214    0.2553    0.2774   -0.9508   -0.3378   -1.1350    0.7951   -0.6749   -0.1797   -0.5646
   -0.7056    1.5769   -0.8234    1.5896    1.5743    0.1304   -0.3575   -1.0971   -0.2286   -1.5035
    1.4605    0.6227   -0.6059    1.5939    0.0120   -0.1012   -0.9397   -1.6988   -2.3375    1.0503
   -0.2038    0.7016    0.7310   -0.4053   -0.0419   -0.3276   -0.7675    0.6319    0.3662   -1.0595
   -1.3164    0.1579    0.3200   -0.2055    0.5508   -0.6350    0.7769    1.1788    1.1854   -1.0070
    0.1054    1.0071    0.4267    0.6462   -1.1893   -1.8829   -1.2166   -0.2838    1.0378    0.8280
    0.9141   -1.5962   -0.0540    1.5489    1.0449    0.6155   -0.6227    0.2447    0.2955   -0.7770
   -0.7474    0.6233    1.3306   -0.2167   -0.2423    1.3778   -1.0203   -1.1946    0.4893    0.0451

代码:

t=imread('a1.jpg');
[m,n,z]=size(t);
y=0+0.1*randn(m,n);%二维高斯分布矩阵 0是均值 0.1是标准差

%先将其double化,再除以255 便于后面计算
t1=double(t)/255;

%加上噪声
t1=t1+y;

%将像素范围扩大至0--255
t1=t1*255;

%转换为uint8类型
t1=uint8(t1);

subplot(1,2,1),imshow(t),title('原图');
subplot(1,2,2),imshow(t1),title('加入均值为0,标准差为0.1的高斯噪声后');

效果图:
在这里插入图片描述
方法二:随机生成高斯噪声(这个我还没有懂那个随机生成高斯噪声函数怎么来的!)
代码:

 image=imread('a1.jpg');
[width,height,z]=size(image);
subplot(1,2,1);
imshow(image);
title('原图');
av=0;
std=0.1;
u1=rand(width,height);
u2=rand(width,height);
x=std*sqrt(-2*log(u1)).*cos(2*pi*u2)+av;
result1=double(image)/255+x;
result1=uint8(255*result1);
subplot(1,2,2);
imshow(result1);
title('加入均值为0,标准差为0.1的高斯噪声后');

效果图:
在这里插入图片描述

方法三:利用imnoise()函数
代码

t=imread('a1.jpg');
imshow(t),title('原图');
t1=imnoise(t,'gaussian',0,0.01);
figure,imshow(t1),title('添加均值为0,方差为0.01的高斯噪声');
t2=imnoise(t,'gaussian',0,0.02);
figure,imshow(t2),title('添加均值为0,方差为0.02的高斯噪声');
t3=imnoise(t,'gaussian',0,0.03);
figure,imshow(t3),title('添加均值为0,方差为0.03的高斯噪声');
t4=imnoise(t,'gaussian',0.2,0.01);
figure,imshow(t4),title('添加均值为0.2,方差为0.01的高斯噪声');
t5=imnoise(t,'gaussian',0.4,0.01);
figure,imshow(t5),title('添加均值为0.4,方差为0.01的高斯噪声');

效果图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

注意:imnoise()中参数写的是方差,而方法一、二中是标准差。

更多

获取更多资料、代码,微信公众号:海轰Pro
回复 海轰 即可

2017-08-03 11:49:49 u010368556 阅读数 18880
  • 图像噪声

    掌握OpenCV核心模块,熟练使用相关API 理解各个API背后的相关算法原理,每个参数意义 有能力解决使用应用场景问题,大量工程代码经验分享 掌握图像处理与视频分析,图像分析与测量编码与开发技巧

    88人学习 贾志刚
    免费试看

 http://blog.csdn.net/qq_34784753/article/details/69379135

 

下面简单介绍两种图像噪声,即椒盐噪声和高斯噪声。

1.椒盐噪声

       椒盐噪声也称为脉冲噪声,是图像中经常见到的一种噪声,它是一种随机出现的白点或者黑点,可能是亮的区域有黑色像素或是在暗的区域有白色像素(或是两者皆有)。盐和胡椒噪声的成因可能是影像讯号受到突如其来的强烈干扰而产生、类比数位转换器或位元传输错误等。例如失效的感应器导致像素值为最小值,饱和的感应器导致像素值为最大值。图像模拟添加椒盐噪声是通过随机获取像素点并设置为高亮度点和低灰度点来实现的

2.高斯噪声

       高斯噪声是指高绿密度函数服从高斯分布的一类噪声。特别的,如果一个噪声,它的幅度分布服从高斯分布,而它的功率谱密度有事均匀分布的,则称这个噪声为高斯白噪声。高斯白噪声二阶矩不相关,一阶矩为常数,是指先后信号在时间上的相关性。高斯噪声包括热噪声和三里噪声。高斯噪声万有由它的事变平均值和两瞬时的协方差函数来确定,若噪声是平稳的,则平均值与时间无关,而协方差函数则变成仅和所考虑的两瞬时之差有关的相关函数,在意义上它等同于功率谱密度。高斯早生可以用大量独立的脉冲产生,从而在任何有限时间间隔内,这些脉冲中的每一个买充值与所有脉冲值得总和相比都可忽略不计。

        根据Box-Muller变换原理,建设随机变量U1、U2来自独立的处于(0,1)之间的均匀分布,则经过下面两个式子产生的随机变量Z0,Z1服从标准高斯分布。


上式中Z0,Z1满足正态分布,其中均值为0,方差为1,变量U1和U2可以修改为下式:


给图像添加两种噪声的程序如下:

#include <cstdlib>  
#include <iostream>  
#include <opencv2\core\core.hpp>  
#include <opencv2\highgui\highgui.hpp>  
#include <opencv2\imgproc\imgproc.hpp>  

using namespace cv;
using namespace std;

double generateGaussianNoise(double m, double sigma);
Mat addSaltNoise(const Mat srcImage, int n);
Mat addGaussianNoise(Mat &srcImag);

int main()
{
	Mat srcImage = imread("imL.png");
	if (!srcImage.data)
	{
		cout << "读入图像有误!" << endl;
		system("pause");
		return -1;
	}
	imshow("原图像", srcImage);
	Mat dstImage1 = addSaltNoise(srcImage, 3000);
	Mat dstImage2 = addGaussianNoise(srcImage);
	imshow("添加椒盐噪声的图像", dstImage1);
	imshow("添加高斯噪声的图像", dstImage2);
	//存储图像  
	imwrite("salt_pepper_Image.jpg", dstImage1);
	imwrite("GaussianNoise_Image.jpg", dstImage2);
	waitKey();
	return 0;
}

Mat addSaltNoise(const Mat srcImage, int n)
{
	Mat dstImage = srcImage.clone();
	for (int k = 0; k < n; k++)
	{
		//随机取值行列  
		int i = rand() % dstImage.rows;
		int j = rand() % dstImage.cols;
		//图像通道判定  
		if (dstImage.channels() == 1)
		{
			dstImage.at<uchar>(i, j) = 255;       //盐噪声  
		}
		else
		{
			dstImage.at<Vec3b>(i, j)[0] = 255;
			dstImage.at<Vec3b>(i, j)[1] = 255;
			dstImage.at<Vec3b>(i, j)[2] = 255;
		}
	}
	for (int k = 0; k < n; k++)
	{
		//随机取值行列  
		int i = rand() % dstImage.rows;
		int j = rand() % dstImage.cols;
		//图像通道判定  
		if (dstImage.channels() == 1)
		{
			dstImage.at<uchar>(i, j) = 0;     //椒噪声  
		}
		else
		{
			dstImage.at<Vec3b>(i, j)[0] = 0;
			dstImage.at<Vec3b>(i, j)[1] = 0;
			dstImage.at<Vec3b>(i, j)[2] = 0;
		}
	}
	return dstImage;
}
//生成高斯噪声  
double generateGaussianNoise(double mu, double sigma)
{
	//定义小值  
	const double epsilon = numeric_limits<double>::min();
	static double z0, z1;
	static bool flag = false;
	flag = !flag;
	//flag为假构造高斯随机变量X  
	if (!flag)
		return z1 * sigma + mu;
	double u1, u2;
	//构造随机变量  
	do
	{
		u1 = rand() * (1.0 / RAND_MAX);
		u2 = rand() * (1.0 / RAND_MAX);
	} while (u1 <= epsilon);
	//flag为真构造高斯随机变量  
	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;
}

//为图像添加高斯噪声  
Mat addGaussianNoise(Mat &srcImag)
{
	Mat dstImage = srcImag.clone();
	int channels = dstImage.channels();
	int rowsNumber = dstImage.rows;
	int colsNumber = dstImage.cols*channels;
	//判断图像的连续性  
	if (dstImage.isContinuous())
	{
		colsNumber *= rowsNumber;
		rowsNumber = 1;
	}
	for (int i = 0; i < rowsNumber; i++)
	{
		for (int j = 0; j < colsNumber; j++)
		{
			//添加高斯噪声  
			int val = dstImage.ptr<uchar>(i)[j] +
				generateGaussianNoise(0, 2.235) * 32;
			if (val < 0)
				val = 0;
			if (val>255)
				val = 255;
			dstImage.ptr<uchar>(i)[j] = (uchar)val;
		}
	}
	return dstImage;
}


2018-11-25 09:52:46 sw3300255 阅读数 1398
  • 图像噪声

    掌握OpenCV核心模块,熟练使用相关API 理解各个API背后的相关算法原理,每个参数意义 有能力解决使用应用场景问题,大量工程代码经验分享 掌握图像处理与视频分析,图像分析与测量编码与开发技巧

    88人学习 贾志刚
    免费试看

目录

Sigma对高斯噪声的影响

应用高斯噪声 

图像处理需注意的地方


好的。 我们说噪音的大小是由sigma决定的。

实际上,我们可以只看噪声函数本身,所以不要添加原始图像,只需看看噪声函数。

Sigma对高斯噪声的影响

噪音是什么意思? 是 0。

非常好!

这意味着一些值是什么? 有些值是正的,有些值是负的。

我们如何看待其中有"+"和"-"的图片?

如果我们说0是黑色的,另1是白色的,或 0是黑色的,255是白色的,我们怎么做呢?

错就错在说0是黑的。

我们要说:看,我们会将一些最小值映射到黑色,将一些最大值映射到白色,然后我们将它们分布在两者之间。

这样的话,0应该是什么颜色的?

你认为零应该是什么颜色,在黑色和白色之间?

宇宙中的黑与白之间是什么?

灰色。 

假设我们有值,从负20到正20,在图像中,我们可以使-20变成黑色,+20变成白色,零变成灰色。

如果我们这样做,它看起来就像这样:

所以这里我们向您展示高斯噪声的图像。

只是噪音,所以如果有一个非常小的sigma,你几乎看不到这只是一个不变的灰色。

随着我们让Sigma变得越来越大,你开始看到越来越多的斑点。

这就是效果,它只是一个加到图像上的噪声函数。

应用高斯噪声 

给定一个图像,你知道它的大小是图像的大小,你可以生成一个同样大小的噪声图像通过把它传递给randn函数。

该噪声图像中的值通常分布在零附近。 标准偏差为1。

将这些生成的值乘以2时会发生什么?这对分配结果有何影响? 

>> noise = randn(size(img)) .*2;

正确答案:它只会在x轴延伸:

请注意,我们只是将这些值相乘。

将一组正态分布数乘以一个值,有效地改变了它们所得到的分布的标准差。

现在为什么知道这一点很重要?

请记住,randn函数生成的标准偏差值为1,而我们使用的图像的类型为uint8,范围从0到255。

randn函数的结果直接添加到图像时,您认为会发生什么?让我们来看看,是时候使用新图像了。

>> img = imread('saturn.png');
>> imshow(img);

运行代码,我们得到:

如果你仔细观察,你将能够看到三个卫星和一个阴影。

现在我们生成噪声图像并将其添加到原始图像中。

>> img = imread('saturn.png');
>> imshow(img);
>>
>> noise = randn(size(img));
>> output = img + noise;
>> imshow(output);

运行结果:

没什么不同,是吧?

这是因为与图像相比,randn函数生成的值非常小。

让我们把这些值放大。

>> img = imread('saturn.png');
>> imshow(img);
>>
>> noise = randn(size(noise)) .* 25;
>> output = img + noise;
>> imshow(output);

运行代码,得到:

我们再增加一点怎么样? .* 50

更多。.* 100

现在很难看到卫星,不是吗? 

图像处理需注意的地方

我并没有在上一张图片中说我们的图片会是什么样的范围。

记得我告诉过你一个图像可能会从0到1,从最暗的黑色到最亮的白色。

如果我图像值的范围控制在0到1,我有个Sigma的值是2的话, 你会发现在整个图像得到白色的。

然而,当我回到这里时,Sigma的值为2只是一个小变化。

 而Sigma的值为64是一个很大的变化。为什么?

在这个图像中,我们有这样的概念,减去127为黑色,加上128为白色。

当我们根据强度来讨论图像中的噪声量时,它必须与图像整体范围有关。

所以在图像中使用双精度的另一个原因:

把它们看成是从0到1,然后我们可以讨论Sigma,你知道0.1。嗯,这是从黑色到白色的十分之一。

如果你想任意使用0到255这样的图像。你可以这么做:首先,使用0.0到255.0,使用浮点数。

在使用0到1的图像情况下的是0.1,在使用0到255图像是25的。

对,因为我已经把整个都拉长了,所以你必须担心Sigma相对于你的图像的整个范围的大小。

当你去显示图像时,尽力去了解你的图像的数字。因为现在你必须告诉机器,我有这个图像。你想要如何显示它?

Matlab有很多显示图像的方法。如果你有imshow函数,我认为它实际上来自图像处理工具箱。

你可以用这种方式显示它你会告诉它低的值和高的值。它会显示,任何低于黑色的值,任何高于白色的值。

你也可以用imshow给它一个空数组。Matlab会自动为您缩放图像。

 还有另一个名为imagesc的功能,用于图像比例。这是一个更老的功能。

 它不在图像处理工具箱中,它也会显示它。

 不要在如何显示图像和如何使用图像之间纠结。

 您只需将其标准化以显示它,而不是为了使用它进行计算。

1 到 9 的文章介绍机器视觉和图像处理相关方法,继续前进,下面还会有更酷的技术值得你学习。


——学会编写自己的代码,才能练出真功夫。

2017-02-08 18:23:36 vnddtt3b 阅读数 933
  • 图像噪声

    掌握OpenCV核心模块,熟练使用相关API 理解各个API背后的相关算法原理,每个参数意义 有能力解决使用应用场景问题,大量工程代码经验分享 掌握图像处理与视频分析,图像分析与测量编码与开发技巧

    88人学习 贾志刚
    免费试看

总结学习下图像处理方面基础知识。

这是第一篇,简单的介绍下使用OpenCV的三个基本功能:

  • 图像的读取
  • 图像的显示
  • 访问图像的像素值

然后概述下图像噪声的类型,并为图像添加两种常见的噪声:高斯噪声和椒盐噪声。
最后,使用中值滤波和均值滤波来处理带有噪声的图像。

OpenCV基础

在OpenCV中,完成图像的输入输出以及显示,只需要以下几个函数:

namedWindow
创建一个可以通过其名字引用的窗口。第一个参数,设置窗口的name,可以通过name引用该窗口;第二个参数,设置窗口的大小。有以下几个选择:

  • WINDOW_NORMAL or WINDOW_AUTOSIZE 调整窗口的大小以适应图像,不同的是,使用WINDOW_NORMAL可以手动调整窗口的大小;WINDOW_AUTOSIZE不能调整窗口的大小。
  • WINDOW_FREERATIO or WINDOW_KEEPRATIO 改变窗口时是否会保持图像的ratio不变,没发现这俩有什么区别。

imshow显示图像

imread 读取图像数据到Mat中,第一个参数是图像的文件名;第二个参数是标志,标识怎么处理图像的色彩。常用的几个选项:

  • IMREAD_UNCHANGED 和原图像保持一直不变
  • IMREAD_GRAYSCALE 将图像转换为单通道的灰度图
  • IMREAD_COLOR 将图像转换为3通道的BGR,默认选项
  • IMREAD_REDUCED_GRAYSCALE_2 IMREAD_REDUCED_GRAYSCALE_4 IMREAD_REDUCED_GRAYSCALE_8 单通道灰度图读入图像,并减小图像的大小。减小的值为1/2,1/4,1/8
  • IMREAD_REDUCED_COLOR_2 IMREAD_REDUCED_COLOR_4 IMREAD_REDUCED_COLOR_2 3通道BGR读入图像,并减小图像的大小。减小的值为1/2,1/4,1/8

Mat是OpenCV中最重要的数据结构,在做图像处理时基本都是对该结构体的操作。Mat由两部分构成:矩阵头矩阵数据,矩阵头较小,创建的每个Mat实例都拥有一个矩阵头,而矩阵数据通常占有较大的空间,OpenCV中通过引用计数来管理这部分内存空间,当调用赋值运算符和拷贝构造函数时,并不会只复制矩阵头,并不会复制矩阵数据,只是将其的引用计数加1.例如:

Mat m = imread("img.jpg");
Mat a = m; // 赋值运算符
Mat b(m); // 拷贝构造函数

上面代码中的a,bm各自拥有自己的矩阵头,其引用的数据却指向同一份。也就是说,修改了其中任意一个,都会影响到其余的两个。

要想复制矩阵数据,可以调用clonecopyTo这两个函数

Mat m = imread("img.jpg");
Mat f = m.clone();
Mat g ;
m.copyTo(g);

将图像读入到Mat后,有三种方式访问Mat中的数据:

  • 通过指针
  • 使用迭代器
  • 调用at

图像噪声

图像噪声是图像在获取或传输的过程中受到随机信号的干扰,在图像上出现的一些随机的、离散的、孤立的像素点,这些点会干扰人眼对图像信息的分析。图像的噪声通常是比较复杂的,很多时候将其看成是多维随机过程,因而可以借助于随即过程描述噪声,即使用概率分布函数和概率密度函数。

图像的噪声很多,性质也千差万别, 可以通过不同的方法给噪声分类。
按照产生的原因:

  • 外部噪声
  • 内部噪声
    这种分类方法,有助于理解噪声产生的源头,但对于降噪算法只能起到原理上的帮组。

噪声和图像信号的关系,可以分为:

  • 加性噪声,加性噪声和图像信号强度不相关,这类噪声可以看着理想无噪声图像f和噪声的和。
  • 乘性噪声,乘性噪声和图像信号是相关的,往往随图像信号的变化而变化。
    而为了分析处理的方便,常常将乘性噪声近似认为是加性噪声,而且总是假定信号和噪声是互相独立的。

最重要的来了,按照概率密度函数(PDF)分类:

  • 高斯噪声,高斯噪声模型经常被用于实践中。
  • 脉冲噪声(椒盐噪声),图像上一个个点,也可称为散粒和尖峰噪声。
  • 伽马噪声
  • 瑞利噪声
  • 指数分布噪声
  • 均匀分布噪声
    这种分类方法,引入了数学模型,对设计过滤算法比较有帮助。

给图像添加噪声

按照指定的噪声类型,生成一个随机数,然后将这个随机数加到源像素值上,并将得到的值所放到[0,255]区间即可。

C++11 随机数发生器

新的随机数生成器被抽象成了两个部分:随机数生成引擎和要生成的随机数符合的分布。
随机数引擎有三种:

  • linear_congruential_engine 线性同余算法
  • mersenne_twister_engine 梅森旋转算法
  • subtract_with_carry_engine 带进位的线性同余算法

第一种最常用,而且速度比较快;第二种号称最好的伪随机数生成器

#include <random>

std::random_device rd; // 随机数种子
std::mt19937 mt(rd()); // 随机数引擎
std::normal_distribution<> d(5,20); // 高斯分布

std::map<int,int> hist;
for(int n = 0; n < 10000; n ++)
    ++hist[std::round(d(mt))]; // 生成符合高斯分布的随机数

添加图像噪声

使用C++的随机数发生器为图像添加两种噪声:椒盐噪声和高斯噪声。
椒盐噪声是图像中离散分布的白点或者黑点,其代码如下:

// 添加椒盐噪声
void addSaltNoise(Mat &m, int num)
{
    // 随机数产生器
    std::random_device rd; //种子
    std::mt19937 gen(rd()); // 随机数引擎

    auto cols = m.cols * m.channels();

    for (int i = 0; i < num; i++)
    {
        auto row = static_cast<int>(gen() % m.rows);
        auto col = static_cast<int>(gen() % cols);

        auto p = m.ptr<uchar>(row);
        p[col++] = 255;
        p[col++] = 255;
        p[col] = 255;
    }
}

上述代码中使用ptr<uchar>()获取图像某一行的行首指针,得到行首指针后就可以任意的访问改行的像素值。

高斯噪声是一种加性噪声,为图像添加高斯噪声的代码如下:

// 添加Gussia噪声
// 使用指针访问
void addGaussianNoise(Mat &m, int mu, int sigma)
{
    // 产生高斯分布随机数发生器
    std::random_device rd;
    std::mt19937 gen(rd());

    std::normal_distribution<> d(mu, sigma);

    auto rows = m.rows; // 行数
    auto cols = m.cols * m.channels(); // 列数

    for (int i = 0; i < rows; i++)
    {
        auto p = m.ptr<uchar>(i); // 取得行首指针
        for (int j = 0; j < cols; j++)
        {
            auto tmp = p[j] + d(gen);
            tmp = tmp > 255 ? 255 : tmp;
            tmp = tmp < 0 ? 0 : tmp;
            p[j] = tmp;
        }
    }
}

随机产生符合高斯分布的随机数,然后将该值和图像原有的像素值相加,并将得到的和压缩到[0,255]区间内。

左边是原图,中间的是添加高斯噪声后的图像,最右边的是添加椒盐噪声后的图像。

使用滤波器去除噪声

根据噪声类型的不同,选择不同的滤波器过滤掉噪声。通常,对于椒盐噪声,选择中值滤波器(Median Filter),在去掉噪声的同时,不会模糊图像;对于高斯噪声,选择均值滤波器(Mean Filter),能够去掉噪声,但会对图像造成一定的模糊。
在OpenCV中,对应于均值滤波器的函数是blur,该函数需要5个参数,通常只设置前3个后两个使用默认值即可。
blur(m, m2, Size(5, 5));第一个参数是输入的图像,第二个参数是输出的图像,第三个参数是滤波器的大小,这里使用的是\(5 \times 5\)的矩形。

对应于中值滤波器的函数是medianBlur(m1, m3, 5);前两个参数是输入输出的图像,第三个参数是滤波器的大小,由于是选取的是中值,滤波器的大小通常是一个奇数。

下图是对有噪声图像使用滤波器后的结果,中间的是原始图像,左边的是使用均值滤波器过滤高斯噪声后的结果;右边的是使用中值滤波器过滤椒盐噪声后的结果。可以明显的看出,这两种滤波器都能够很好的去掉图像的噪声,但会对图像造成一定的模糊,尤其是均值滤波器造成的模糊比较明显。

总结

本文算是第一篇文章,简单的介绍下OpenCV的基本使用;接着访问图像中的像素,并借助于C++11的随机数库,为图像添加高斯噪声和椒盐噪声;最后使用中值滤波器和均值滤波器除去图像,并对结果进行了对比。

以后坚持每日对图像处理的一些知识进行整理。


模拟生成高斯噪声

阅读数 6585