OpenCV2学习笔记(五):图像滤波基础

一:基本概念

滤波是数字图像处理中的一个基本操作,在信号处理领域可以说无处不在。图像滤波,即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,通常是数字图像处理中不可缺少的操作,其处理效果的好坏将直接影响到后续运算和分析的效果。简单来说,图像滤波的根本目的是在图像中提取出人类感兴趣的特征。

当我们观察一幅图像时,有两种处理方法:

1. 观察不同的灰度(或彩色值)在图像中的分布情况,即空间分布。

2. 观察图像中的灰度(或彩色值)的变化情况,这涉及到频率方面的问题。

因此,图像滤波分为频域和空域滤波,简单来说,空域指用图像的灰度值来描述一幅图像;而频域指用图像灰度值的变化来描述一幅图像。而低通滤波器和高通滤波器的概念就是在频域中产生的。低通滤波器旨在去除图像中的高频成分,而高通滤波器则是去除了图像中的低频成分。

这里简单记录以下低通滤波器中的均值和高斯滤波器(线性滤波器)、中值滤波器(非线性滤波器);高通滤波器中的sobel算子(方向滤波器)和拉普拉斯变换(二阶导数),其中,sobel算子和拉普拉斯变换均可以对图像的边缘进行检测。

二:低通滤波器

消除图像中的噪声成分叫作图像的平滑化或低通滤波。信号或图像的能量大部分集中在幅度谱的低频和中频段是很常见的,而在较高频段,感兴趣的信息经常被噪声淹没。因此一个能降低高频成分幅度的滤波器就能够减弱噪声的影响。

图像滤波的目的有两个:一是抽出对象的特征作为图像识别的特征模式;另一个是为适应图像处理的要求,消除图像数字化时所混入的噪声。

当然,在设计低通滤波器时,要考虑到滤波对图像造成的细节丢失等问题。

平滑滤波是低频增强的空间域滤波技术。它的目的有两类:一类是图像模糊;另一类是滤除图像噪声。

空间域的平滑滤波一般采用简单平均法进行,就是求邻近像元点的平均灰度值或亮度值。邻域的大小与平滑的效果直接相关,邻域越大平滑的效果越好,但邻域过大,平滑会使边缘信息损失的越大,从而使输出的图像变得模糊,因此需合理选择邻域的大小。

关于滤波器,一种形象的比喻法是:我们可以把滤波器想象成一个包含加权系数的窗口,当使用这个滤波器平滑处理图像时,就把这个窗口放到图像之上,透过这个窗口来看我们得到的图像。

滤波器的种类有很多, 在OpenCV中,提供了如下几种常用的图像平滑处理操作方法及函数:

1.领域均值滤波:blur函数,将图像的每个像素替换为相邻矩形内像素的平均值(均值滤波)

2.高斯低通滤波:GaussianBlur函数

3.方框滤波:boxblur函数

4.中值滤波:medianBlur函数

5.双边滤波:bilateralFilter函数

以下是均值滤波和高斯低通滤波的简单代码(未修改):

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

int main()
{
    // Read input image
    cv::Mat image= cv::imread("../boldt.jpg",0);
    if (!image.data)
        return 0; 

    // Display the image
    cv::namedWindow("Original Image");
    cv::imshow("Original Image",image);

    // Blur the image
    cv::Mat result;
    cv::GaussianBlur(image,result,cv::Size(5,5),1.5);

    // Display the blurred image
    cv::namedWindow("Gaussian filtered Image");
    cv::imshow("Gaussian filtered Image",result);

    // Get the gaussian kernel (1.5)
    cv::Mat gauss= cv::getGaussianKernel(9,1.5,CV_32F);

    // Display kernel values
    cv::Mat_<float>::const_iterator it= gauss.begin<float>();
    cv::Mat_<float>::const_iterator itend= gauss.end<float>();
    std::cout << "[";
    for ( ; it!= itend; ++it) {
        std::cout << *it << " ";
    }
    std::cout << "]" << std::endl;

    // Get the gaussian kernel (0.5)
    gauss= cv::getGaussianKernel(9,0.5,CV_32F);

    // Display kernel values
    it= gauss.begin<float>();
    itend= gauss.end<float>();
    std::cout << "[";
    for ( ; it!= itend; ++it) {
        std::cout << *it << " ";
    }
    std::cout << "]" << std::endl;

    // Get the gaussian kernel (2.5)
    gauss= cv::getGaussianKernel(9,2.5,CV_32F);

    // Display kernel values
    it= gauss.begin<float>();
    itend= gauss.end<float>();
    std::cout << "[";
    for ( ; it!= itend; ++it) {
        std::cout << *it << " ";
    }
    std::cout << "]" << std::endl;

    // Get the Deriv kernel (2.5)
    cv::Mat kx, ky;
    cv::getDerivKernels(kx,ky,2,2,7,true);

    // Display kernel values
    cv::Mat_<float>::const_iterator kit= kx.begin<float>();
    cv::Mat_<float>::const_iterator kitend= kx.end<float>();
    std::cout << "[";
    for ( ; kit!= kitend; ++kit) {
        std::cout << *kit << " ";
    }
    std::cout << "]" << std::endl;

    // Blur the image with a mean filter
    cv::blur(image,result,cv::Size(5,5));

    // Display the blurred image
    cv::namedWindow("Mean filtered Image");
    cv::imshow("Mean filtered Image",result);

    // Read input image with salt&pepper noise
    image= cv::imread("../salted.bmp",0);
    if (!image.data)
        return 0; 

    // Display the S&P image
    cv::namedWindow("S&P Image");
    cv::imshow("S&P Image",image);

    // Blur the image with a mean filter
    cv::blur(image,result,cv::Size(5,5));

    // Display the blurred image
    cv::namedWindow("Mean filtered S&P Image");
    cv::imshow("Mean filtered S&P Image",result);

    // Applying a median filter
    cv::medianBlur(image,result,5);

    // Display the blurred image
    cv::namedWindow("Median filtered S&P Image");
    cv::imshow("Median filtered S&P Image",result);

    // Reduce by 4 the size of the image (the wrong way)
    image= cv::imread("../boldt.jpg",0);
    cv::Mat reduced(image.rows/2,image.cols/2,CV_8U);

    for (int i=0; i<reduced.rows; i++)
        for (int j=0; j<reduced.cols; j++)
            reduced.at<uchar>(i,j)= image.at<uchar>(i*2,j*2);

    // Display the reduced image
    cv::namedWindow("Badly reduced Image");
    cv::imshow("Badly reduced Image",reduced);

    cv::waitKey();
    return 0;
}

未完待续……

时间: 2024-08-05 09:18:01

OpenCV2学习笔记(五):图像滤波基础的相关文章

OpenCV2学习笔记:图像的读取与显示

1. 图像读取:imread() Mat imread(const string& ?lename, int ?ags=1 ) 参数介绍: filename: 待加载的文件名称. flags: 此标志用来指定被加载图像的颜色类型(color type).这个标志的取值可以有: -- CV_LOAD_IMAGE_ANYDEPTH : 如果设置这个标志的话,如果图像为16位或32位深度的图像,则返回对应深度的图像:否则,将图像转换为8位深度图像再返回. -- CV_LOAD_IMAGE_COLOR

NLTK学习笔记(五):分类和标注词汇

[TOC] 词性标注器 之后的很多工作都需要标注完的词汇.nltk自带英文标注器pos_tag import nltk text = nltk.word_tokenize("And now for something compleyely difference") print(text) print(nltk.pos_tag(text)) 标注语料库 表示已经标注的标识符:nltk.tag.str2tuple('word/类型') text = "The/AT grand/J

Linux System Programming 学习笔记(五) 进程管理

1. 进程是unix系统中两个最重要的基础抽象之一(另一个是文件) A process is a running program A thread is the unit of activity inside of a process the virtualization of memory is associated with the process, the threads all share the same memory address space 2. pid The idle pro

Linux程序设计学习笔记----Socket网络编程基础之TCP/IP协议簇

转载请注明出处: ,谢谢! 内容提要 本节主要学习网络通信基础,主要涉及的内容是: TCP/IP协议簇基础:两个模型 IPv4协议基础:IP地址分类与表示,子网掩码等 IP地址转换:点分十进制\二进制 TCP/IP协议簇基础 OSI模型 我们知道计算机网络之中,有各种各样的设备,那么如何实现这些设备的通信呢? 显然是通过标准的通讯协议,但是,整个网络连接的过程相当复杂,包括硬件.软件数据封包与应用程序的互相链接等等,如果想要写一支将联网全部功能都串连在一块的程序,那么当某个小环节出现问题时,整只

图像编程学习笔记8——图像的平滑(去噪)

第一种方法:高斯模版 以下文字内容copy于<<数字图像处理编程入门>>,code为自己实现,是win32控制台程序. 先举个例子说明一下什么是平滑(smoothing),如下面两幅图所示:可以看到,图3.2比图3.1柔和一些(也模糊一些).是不是觉得很神奇?其实实现起来很简单.我们将原图中的每一点的灰度和它周围八个点的灰度相加,然后除以9,作为新图中对应点的灰度,就能实现上面的效果. 这么做并非瞎蒙,而是有其道理的.大概想一想,也很容易明白.举个例子,就象和面一样,先在中间加点水

Caliburn.Micro学习笔记(五)----协同IResult

Caliburn.Micro学习笔记(五)----协同IResult 今天说一下协同IResult 看一下IResult接口 /// <summary> /// Allows custom code to execute after the return of a action. /// </summary> public interface IResult { /// <summary> /// Executes the result using the specif

angular学习笔记(五)-阶乘计算实例(1)

<!DOCTYPE html> <html ng-app> <head> <title>2.3.2计算阶乘实例1</title> <meta charset="utf-8"> <script src="../angular.js"></script> <script src="script.js"></script> </

java之jvm学习笔记五(实践写自己的类装载器)

java之jvm学习笔记五(实践写自己的类装载器) 课程源码:http://download.csdn.net/detail/yfqnihao/4866501 前面第三和第四节我们一直在强调一句话,类装载器和安全管理器是可以被动态扩展的,或者说,他们是可以由用户自己定制的,今天我们就是动手试试,怎么做这部分的实践,当然,在阅读本篇之前,至少要阅读过笔记三. 下面我们先来动态扩展一个类装载器,当然这只是一个比较小的demo,旨在让大家有个比较形象的概念. 第一步,首先定义自己的类装载器,从Clas

WEB前端学习笔记 五

接web前端学习笔记第四篇,此篇为web学习笔记 五,在此感谢您的采集和转发,但请注明文章出自网知博学. 2.0.3  html标签的属性格式 现在我们知道了两个双标签分别是,标题标签:<h1> - <h6>.和段落标签:<p></p>还知道了一个换行的单标签:<br />,现在我们给<p></p>标签添加一个属性,来改变段落是右对齐,还是左对齐,还是居中. 如上图,<p>标签中的 align(中文就是排列的意

小猪的数据结构学习笔记(五)

小猪的数据结构学习笔记(五) 线性表之--循环链表                           --转载请注明出处:coder-pig 循环链表知识点归纳: 相关代码实现: ①判断是否为空表: ②单循环链表的存储结构 其实和单链表的结构是一样的! /*定义循环链表的存储结构*/ typedef struct Cir_List { int data; struct Cir_List *next; }Lnode; ③初始化循环单链表 代码如下: //1.循环链表的初始化 //表示一个元素,如