• 图像处理基础(1):噪声的添加和过滤


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

    这是第一篇,简单的介绍下使用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 imes 5)的矩形。

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

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

    总结

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

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

  • 相关阅读:
    第六节:流上下文
    第五节:控制序列化和反序列化的数据
    第四节:格式化器如何序列化类型实例
    第三节:控制序列化和反序列化
    第二节:使类型可序列化
    第一节:序列化和反序列化快速入门
    第五节:使用反射发现类型成员
    golang 一些坑 rang
    golang json格式字符串反序列化映射到切片结构体
    golang 结构体内嵌结构体序列化填充
  • 原文地址:https://www.cnblogs.com/wangguchangqing/p/6372025.html
Copyright © 2020-2023  润新知