【 imgproc 模块. 图像处理】图像平滑处理


一、图像平滑

   本教程中,图像平滑处理主要有归一化块滤波器(Normalized Box Filter)、高斯滤波器(Gaussian Filter)、中值滤波器(Median Filter)、双边滤波器(Bilatera Filter)。

二、归一化快滤波器(Normalized Box Filter)

  • 最简单的滤波器, 输出像素值是核窗口内像素值的 均值 ( 所有像素加权系数相等)

 

核如下:

K = \dfrac{1}{K_{width} \cdot K_{height}} \begin{bmatrix}
    1 & 1 & 1 & ... & 1 \\
    1 & 1 & 1 & ... & 1 \\
    . & . & . & ... & 1 \\
    . & . & . & ... & 1 \\
    1 & 1 & 1 & ... & 1
   \end{bmatrix}

 void blur(InputArray src, OutputArray dst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )

         . InputArray src: 输入图像,可以是Mat类型,图像深度是CV_8U、CV_16U、CV_16S、CV_32F以及CV_64F其中的某一个。 

         . OutputArray dst: 输出图像,深度和类型与输入图像一致 

    . Size ksize: 滤波模板kernel的尺寸,一般使用Size(w, h)来指定,如Size(3,3) 

    . Point anchor=Point(-1, -1): 字面意思是锚点,也就是处理的像素位于kernel的什么位置,默认值为(-1, -1)即位于kernel中心点,如果没有特殊需要则不需要更改 

    . int borderType=BORDER_DEFAULT: 用于推断图像外部像素的某种边界模式,有默认值BORDER_DEFAULT

三、高斯滤波器(Gaussian Filter)

 

  • 最有用的滤波器 (尽管不是最快的)。 高斯滤波是将输入数组的每一个像素点与 高斯内核 卷积将卷积和当作输出像素值。

 

    1维高斯函数:

../../../../_images/Smoothing_Tutorial_theory_gaussian_0.jpg

      假设图像是1维的,那么观察上图,不难发现中间像素的加权系数是最大的, 周边像素的加权系数随着它们远离中间像素的距离增大而逐渐减小。

   2维高斯函数可以表达为 :

G_{0}(x, y) = A  e^{ \dfrac{ -(x - \mu_{x})^{2} }{ 2\sigma^{2}_{x} } +  \dfrac{ -(y - \mu_{y})^{2} }{ 2\sigma^{2}_{y} } }

其中 \mu 为均值 (峰值对应位置), \sigma 代表标准差 (变量 x 和 变量 y 各有一个均值,也各有一个标准差)

  API:

void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
                                double sigmaX, double sigmaY = 0,
                                int borderType = BORDER_DEFAULT );

四、中值滤波器(Median Filter)

     实现:中值滤波将图像的每个像素用邻域 (以当前像素为中心的正方形区域)像素的 中值 代替 。

     API:

void medianBlur( InputArray src, OutputArray dst, int ksize );

 

五、双边滤波器

  • 目前我们了解的滤波器都是为了平滑 图像,有些时候这些滤波器不仅仅削弱了噪声,连带着把边缘也给磨掉了。 为避免这样的情形 (至少在一定程度上 ), 可以使用双边滤波。
  • 类似于高斯滤波器,双边滤波器也给每一个邻域像素分配一个加权系数。 这些加权系数包含两个部分, 第一部分加权方式与高斯滤波一样,第二部分的权重则取决于该邻域像素与当前像素的灰度差值。

          详细的解释可以查看 链接

     API:

void bilateralFilter( InputArray src, OutputArray dst, int d,
                                   double sigmaColor, double sigmaSpace,
                                   int borderType = BORDER_DEFAULT );

六、opencv例程

#include "stdafx.h"

/**
* file Smoothing.cpp
* brief Sample code for simple filters
* author OpenCV team
*/

#include <iostream>
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"

using namespace std;
using namespace cv;

/// Global Variables  全局变量
int DELAY_CAPTION = 1500;//窗口延时,各个平滑模式切换的时间间隔
int DELAY_BLUR = 100;//同一个滤波模式下 改变核大小时的时间间隔
int MAX_KERNEL_LENGTH = 31;//核大小不超过31   正奇数

Mat src; Mat dst;
char window_name[] = "Smoothing Demo";

/// Function headers
int display_caption(const char* caption);
int display_dst(int delay);


/**
* function main
*/
int main(int argc, char ** argv)
{
    namedWindow(window_name, WINDOW_AUTOSIZE); 

    /// Load the source image
    const char* filename = argc >= 2 ? argv[1] : "lena512color.tiff";
    /*argc,argv 用命令行编译程序时有用。
主函数main中变量(int argc,char *argv[ ])的含义
有些编译器允许将main()的返回类型声明为void,这已不再是合法的C++
main(int argc, char *argv[ ], char **env)才是UNIX和Linux中的标准写法。
argc: 整数,用来统计你运行程序时送给main函数的命令行参数的个数
* argv[ ]: 指针数组,用来存放指向你的字符串参数的指针,每一个元素指向一个参数
argv[0] 指向程序运行的全路径名
argv[1] 指向在DOS命令行中执行程序名后的第一个字符串
argv[2] 指向执行程序名后的第二个字符串
...
argv[argc]为NULL。*/

    src = imread(filename, IMREAD_COLOR);
    if (src.empty())
    {
        /* if(img.empty())return -1; //是否加载成功
           if(!img.data)return -1;   //判断是否有数据*/
        printf(" Error opening image\n");
        return -1;
    }

    if (display_caption("Original Image") != 0)
    {
        return 0;
    }

    dst = src.clone();
    /*clone 跟copyto的区别
     roi=src.clone();    //1
     src.copyTo(roi);    //2
语句1:不管roi在之前有没有分配内存,clone都会为其分配新内存。如果roi指向某图像img的某个rect,此语句并不能实现对img(rect)的操作,clone分配新内存后,roi不再指向img(rect).
语句2:如果roi在之前未分配内存,copyTo会为其分配新内存,若roi已分配内存,copyTo不再为其分配。
     结束:copyTo才是实现图像roi操作的途径啊。。。
*/
    if (display_dst(DELAY_CAPTION) != 0)
    {
        return 0;
    }

    /// Applying Homogeneous blur
    if (display_caption("Homogeneous Blur") != 0)
    {
        return 0;
    }

    //![blur]
    for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2)
    {
        blur(src, dst, Size(i, i), Point(-1, -1));
        /*参数:

src:原图像。dst:目标图像。ksize:定义滤波器的大小。如Size(3,3)。
anchor:指定锚点位置(被平滑点), 如果是负值,取核的中心为锚点。可省略
borderType:推断边缘像素,一般取默认值BORDER_DEFAULT。可省略
例:
blur(src,dst,Size(3,3));*/
        if (display_dst(DELAY_BLUR) != 0)
        {
            return 0;
        }
    }
    //![blur]

    /// Applying Gaussian blur
    if (display_caption("Gaussian Blur") != 0)
    {
        return 0;
    }

    //![gaussianblur]
    /*参数:
    OpenCV2函数 GaussianBlur 执行高斯平滑,高斯滤波是将输入数组的每一个像素点与 高斯内核 卷积,将卷积和当作输出像素值。
    void GaussianBlur( const Mat& src, Mat& dst, Size ksize,double sigmaX, double sigmaY=0,int borderType=BORDER_DEFAULT );
    参数:
    sigmaX:x方向的标准方差。可设置为0让系统自动计算。
    sigmaY:y方向的标准方差。可设置为0让系统自动计算。
    例:
    GaussianBlur(src,dst,Size(9,9),0,0);*/
    for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2)
    {
        GaussianBlur(src, dst, Size(i, i), 0, 0);
        if (display_dst(DELAY_BLUR) != 0)
        {
            return 0;
        }
    }
    //![gaussianblur]

    /// Applying Median blur
    if (display_caption("Median Blur") != 0)
    {
        return 0;
    }

    //![medianblur]
    /*OpenCV2函数 medianBlur 执行中值滤波操作,中值滤波将图像的每个像素用邻域 
    (以当前像素为中心的正方形区域)像素的 中值 代替 。*/
    for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2)
    {
        medianBlur(src, dst, i);
        if (display_dst(DELAY_BLUR) != 0)
        {
            return 0;
        }
    }
    //![medianblur]

    /// Applying Bilateral Filter
    if (display_caption("Bilateral Blur") != 0)
    {
        return 0;
    }

    //![bilateralfilter]
    /*OpenCV2函数 bilateralFilter 执行双边滤波操作,类似于高斯滤波器,双边滤波器也给每一个邻域像素分配一个加权系数。 这些加权系数包含两个部分, 第一部分加权方式与高斯滤波一样,第二部分的权重则取决于该邻域像素与当前像素的灰度差值。
void bilateralFilter( const Mat& src, Mat& dst, int d,double sigmaColor, double sigmaSpace,int borderType=BORDER_DEFAULT );
参数:
d:像素的邻域直径。
sigmaColor:颜色空间的标准方差.
sigmaSpace:坐标空间的标准方差(像素单位).
例:
bilateralFilter ( src, dst, i, i*2, i/2 );*/
    for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2)
    {
        bilateralFilter(src, dst, i, i * 2, i / 2);
        if (display_dst(DELAY_BLUR) != 0)
        {
            return 0;
        }
    }
    //![bilateralfilter]

    /// Done
    display_caption("Done!");

    return 0;
}

/**
* @function display_caption
*/
int display_caption(const char* caption)
{
    dst = Mat::zeros(src.size(), src.type());
    putText(dst, caption,
        Point(src.cols / 4, src.rows / 2),
        FONT_HERSHEY_COMPLEX, 1, Scalar(255, 255, 255));
/*第一个参数是:需要写字的原图像,
第二个:需要写的内容,string类型的;
第三个:需要写的内容的左下角的坐标
第五个:字体大小
第六个:颜色
第七个:字体的厚度
第八个:默认8*/

    return display_dst(DELAY_CAPTION);
}

/**
* @function display_dst
*/
int display_dst(int delay)
{
    imshow(window_name, dst);
    int c = waitKey(delay);
    if (c >= 0) { return -1; }
    return 0;
}

 

 

归ww一化块滤波器 (Normalized Box Filter)

posted @ 2018-12-05 14:12  鸡鸣昧旦  阅读(545)  评论(0编辑  收藏  举报