【opencv 450 Image Processing】Smoothing Images图像平滑

Goal

在本教程中,您将学习如何使用 OpenCV 函数应用各种线性滤波器平滑图像,例如:

Theory

笔记

下面的解释属于 Richard Szeliski 的 Computer Vision: Algorithms and Applications 一书和 LearningOpenCV

  1. 平滑,也称为模糊,是一种简单且经常使用的图像处理操作。
  2. 平滑的原因有很多。 在本教程中,我们将专注于平滑以减少噪声其他用途将在以下教程中看到)。
  3. 为了执行平滑操作,我们将对图像应用过滤器。 最常见的滤波器类型是线性的,其中输出像素的值(即 g(i,j))被确定为输入像素值的加权和(即 f(i+k,j+l)):

h(k,l) 称为核,无非就是滤波器的系数

它有助于将滤波器可视化为在图像上滑动的系数窗口

  1. 过滤器有很多种,这里我们将提到最常用的:

Normalized Box Filter

归一化箱式过滤器

这个过滤器是最简单的! 每个输出像素是其内核邻居的平均值(它们都具有相同的权重)

内核如下:

 

Gaussian Filter

可能是最有用的过滤器(虽然不是最快的)。 高斯滤波是通过将输入数组中的每个点与高斯核进行卷积,然后将它们全部相加以产生输出数组来完成的。

只是为了让图片更清晰,还记得一维高斯核的样子吗?

 

假设图像是一维的,您会注意到位于中间的像素的权重最大。 其邻居的权重随着它们与中心像素之间的空间距离的增加而减小。

笔记

请记住,二维高斯可以表示为:

 

 

其中 μ 是平均值(峰值),

表示方差(每个变量 x 和 y)

Median Filter

中值滤波器遍历信号的每个元素(在本例中为图像),并将每个像素替换为其相邻像素的中值(位于被评估像素周围的正方形邻域中)。

Bilateral Filter

双边过滤器

到目前为止,我们已经解释了一些主要目标是平滑输入图像的过滤器。 但是,有时过滤器不仅可以消除噪声,还可以平滑边缘。 为了避免这种情况(至少在一定程度上),我们可以使用双边滤波器

与高斯滤波器类似,双边滤波器也考虑相邻像素,并为每个像素分配权重。 这些权重有两个分量第一个分量与高斯滤波器使用的权重相同第二个组件考虑了相邻像素和评估像素之间的强度差异

有关更详细的说明,您可以查看此链接Bilateral Filtering (ed.ac.uk) 灰度和彩色图像的双边过滤

 

(a)

(b)

(c)

Figure 1

Code

这个程序有什么作用?

加载图像

应用 4 种不同类型的过滤器(在理论中解释)并按顺序显示过滤后的图像

可下载代码:点击这里raw.githubusercontent.com

代码一览:

/**
 * 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;

/// 全局变量
int DELAY_CAPTION = 1500;//延迟 标题
int DELAY_BLUR = 100;//模糊延迟
int MAX_KERNEL_LENGTH = 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 );

    /// 加载源图像
    const char* filename = argc >=2 ? argv[1] : "lena.jpg";//文件名
        
    src = imread( samples::findFile( filename ), IMREAD_COLOR );//读取彩色图像
    if (src.empty())
    {
        printf(" 打开图片时出错\n");
        printf(" Usage:\n %s [image_name-- default lena.jpg] \n", argv[0]);
        return EXIT_FAILURE;
    }
       //显示文本
    if( display_caption( "Original Image" ) != 0 )//显示文本
    {
        return 0;//delay时间内按下按键 ,结束程序
    }

    dst = src.clone();
    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) );
        if( display_dst( DELAY_BLUR ) != 0 )
        {
            return 0;
        }
    }
    //![blur]

    /// Applying Gaussian blur 应用高斯模糊 高斯滤波
    if( display_caption( "Gaussian Blur" ) != 0 )
    {
        return 0;
    }

    //![gaussianblur]高斯滤波
    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]
    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]
    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) );//显示文本

    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; }//delay时间内按下按键,返回-1。
    return 0;
}

Explanation

让我们检查一下只涉及平滑过程的 OpenCV 函数,因为其余的现在已经知道了。

Normalized Block Filter: 归一化块过滤器:

OpenCV 提供了 blur() 函数来使用这个过滤器进行平滑处理。 我们指定了 4 个参数(更多详细信息,请查看参考资料):

src:源图像

dst:目标图像

Size(w, h):定义要使用的内核的大小(宽度为 w 像素,高度为 h 像素)

Point(-1, -1):指示锚点(评估的像素)相对于邻域的位置。 如果有负值,则内核的中心被认为是锚点。

    for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
    {
        blur( src, dst, Size( i, i ), Point(-1,-1) );
        if( display_dst( DELAY_BLUR ) != 0 )
        {
            return 0;
        }
    }

Gaussian Filter:

它由函数 GaussianBlur() 执行:这里我们使用 4 个参数(更多详细信息,请查看 OpenCV 参考):

src:源图像

dst:目标图像

Size(w, h):要使用的内核的大小(要考虑的邻居)。 w 和 h 必须是奇数和正数,否则将使用 σx 和 σy 参数计算大小。

σx:x 的标准差。 写入 0 意味着 σx 是使用内核大小计算的。

σy:y 的标准差。 写入 0 意味着 σy 是使用内核大小计算的。

   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;
        }
    }

Median Filter:

中值滤波器:

该过滤器由 medianBlur() 函数提供:我们使用三个参数:

src:源图像

dst:目标图片,必须和src同类型

i:内核的大小(只有一个,因为我们使用方形窗口)。 一定很奇数。

    for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
    {
        medianBlur ( src, dst, i );
        if( display_dst( DELAY_BLUR ) != 0 )
        {
            return 0;
        }
    }

Bilateral Filter

由 OpenCV 函数提供的bilateralFilter() 我们使用 5 个参数:

src:源图像

dst:目标图像

d:每个像素邻域的直径。

σColor:颜色空间的标准偏差。

σSpace:坐标空间中的标准偏差(以像素为单位)

  • Provided by OpenCV function bilateralFilter() We use 5 arguments:
    • src: Source image
    • dst: Destination image
    • d: The diameter of each pixel neighborhood.
    • σColor: Standard deviation in the color space.
    • σSpace: Standard deviation in the coordinate space (in pixel terms)
    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;
        }
    }

Results

代码打开一个图像(在本例中为 lena.jpg)并在解释的 4 个过滤器的效果下显示它。

这是使用 medianBlur (中值滤波)平滑的图像快照:

 

waitKey()函数详解

 1--waitKey()--这个函数是在一个给定的时间内(单位ms)等待用户按键触发;如果用户没有按下 键,则继续等待(循环)

 2--如下所示: while(1){ if(waitKey(100)==27)break; } 在这个程序中,我们告诉OpenCv等待用户触发事件,等待时间为100ms,如果在这个时间段内, 用户按下ESC(ASCII码为27),则跳出循环,否则,则继续循环

 3--如果设置waitKey(0),则表示程序会无限制的等待用户的按键事件

函数waitKey在delay≤0时无限等待一个按键事件,或者为正时等待delay毫秒。 由于操作系统在切换线程之间有最短时间,因此该函数不会完全等待delay毫秒,它会等待至少delay毫秒,具体取决于当时您计算机上正在运行的其他内容。 如果在指定的时间过去之前没有按下任何键,则返回被按下键的代码或 -1。

此函数是 HighGUI 中唯一可以获取和处理事件的方法,因此需要定期调用它以进行正常的事件处理,除非在处理事件处理的环境中使用 HighGUI。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值