从头开始opencv(十二)——image:Smoothing


Goal

  In this tutorial you will learn how to apply diverse linear filters to smooth images using OpenCV functions

  在本章节中,我们将学习如何使用opencv函数将各种线性滤波器应用于平滑图像。

  • blur()
  • GaussianBlur()
  • medianBlur()
  • bilaterlFilter()

Theory

  Smoothing, also called blurring, is a simple and frequently used image processing operation.

  smoothing,也被称为blurring,是一种简单且经常使用的图像处理操作。

  为了执行smooth操作,我们需要使用filter。最常用的filter是线性滤波器。输出 g ( i , j ) g(i,j) g(i,j)由输入 f f f和滤波器参数 h h h决定。
g ( i , j ) = ∑ k , l f ( i + k , j + l ) h ( k , l ) g(i,j)=\sum_{k,l}f(i+k,j+l)h(k,l) g(i,j)=k,lf(i+k,j+l)h(k,l)
h ( i , j ) h(i,j) h(i,j)被称为kernel。

  有很多种滤波器:

Normalized Box Filter

  这是最简单的一种滤波器了。内核矩阵(kernel)如下:
K = 1 K w i d t h ⋅ K h e i g h t [ 1 1 1 ⋯ 1 1 1 1 ⋯ 1 ⋅ ⋅ ⋅ ⋯ 1 ⋅ ⋅ ⋅ ⋯ 1 1 1 1 ⋯ 1 ] K=\frac{1}{K_{width}\cdot K_{height}} \begin{bmatrix} 1&1&1&\cdots&1\\ 1&1&1&\cdots&1\\ \cdot&\cdot&\cdot&\cdots&1\\ \cdot&\cdot&\cdot&\cdots&1\\ 1&1&1&\cdots&1\\ \end{bmatrix} K=KwidthKheight111111111111111

Gaussian Filter

  Probably the most useful filter (although not the fastest). Gaussian filtering is done by convolving each point in the input array with a Gaussian kernel and then summing them all to produce the output array.

  Gaussian滤波器可能是最有用的滤波器(尽管它的运行速度不是最快的)。

  高斯滤波是通过将输入数组与每个高斯核卷积,然后将它们全部求和以生成输出数组来完成的。

  

  Assuming that an image is 1D, you can notice that the pixel located in the middle would have the biggest weight. The weight of its neighbors decreases as the spatial distance between them and the center pixel increases.

  假设图像是1D的,则可以注意到位于中间的像素的权重最大。随着邻居像素和中心像素之间空间距离的增加,邻居像素的权重减小。

  2维高斯的公式如下所示:
G 0 ( x , y ) = A e − ( x − μ x ) 2 2 σ x 2 + − ( y − μ y ) 2 2 σ y 2 G_0(x,y)=Ae^{\frac{-(x-\mu_x)^2}{2\sigma_x^2}+\frac{-(y-\mu_y)^2}{2\sigma_y^2}} G0(x,y)=Ae2σx2(xμx)2+2σy2(yμy)2
   μ \mu μ是均值, σ \sigma σ是方差。

Median Filter

  The median filter run through each element of the signal (in this case the image) and replace each pixel with the median of its neighboring pixels (located in a square neighborhood around the evaluated pixel).

  中值滤波器贯穿图像的每个元素,并用相邻像素的中值替换每个像素。

Bilateral Filter

  So far, we have explained some filters which main goal is to smooth an input image. However, sometimes the filters do not only dissolve the noise, but also smooth away the edges. To avoid this (at certain extent at least), we can use a bilateral filter.

  我们已经讲解过一些滤波器,他们的主要目标是让输入图像变得平滑,从而达到消除噪声的目的。但是有时候,滤波器不仅可以消除噪声,还会使边缘变得模糊不清,为了避免这种情况(起码在一定程度上),我们可以使用双边滤波器。
  
  In an analogous way as the Gaussian filter, the bilateral filter also considers the neighboring pixels with weights assigned to each of them. These weights have two components, the first of which is the same weighting used by the Gaussian filter. The second component takes into account the difference in intensity between the neighboring pixels and the evaluated one.

  和高斯滤波器类似,双边滤波器也考虑了给不同的像素点分配不同的权重。这些权重包含两个分量,第一个分量与高斯滤波器相同,第二个分量考虑了相邻像素和预估像素之间的差异。

Code

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

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] : "D:/0PKU/opencv/test.jpg";

    src = imread(filename, IMREAD_COLOR);
    if (src.empty())
    {
        printf(" Error opening image\n");
        printf(" Usage:\n %s [image_name-- default lena.jpg] \n", argv[0]);
        return EXIT_FAILURE;
    }

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

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

  结果类似视频,显示了四种滤波器各种参数变化对图像的影响,还挺直观的。

Explanation

Blur()

void cv::blur	(	
InputArray 	src,//input image,it can have any number of channels, which are processed independently, but the depth should be CV_8U, CV_16U, CV_16S, CV_32F or CV_64F.
OutputArray 	dst,//output image of the same size and type as src.
Size 	ksize,//blurring kernel size.
Point 	anchor = Point(-1,-1),//anchor point; default value Point(-1,-1) means that the anchor is at the kernel center.(锚点,默认值(-1,-1)表示锚点位于内核中心)
int 	borderType = BORDER_DEFAULT //border mode used to extrapolate pixels outside of the image
)	

GaussianBlur()

void cv::GaussianBlur	(	
InputArray 	src,//input image; the image can have any number of channels, which are processed independently, but the depth should be CV_8U, CV_16U, CV_16S, CV_32F or CV_64F.
OutputArray 	dst,//output image of the same size and type as src.
Size 	ksize,//Gaussian kernel size. ksize.width and ksize.height can differ but they both must be positive and odd. Or, they can be zero's and then they are computed from sigma.(必须为正数和奇数)
double 	sigmaX,//Gaussian kernel standard deviation in X direction.(X方向上的标准差)
double 	sigmaY = 0,//Gaussian kernel standard deviation in Y direction; if sigmaY is zero, it is set to be equal to sigmaX, if both sigmas are zeros, they are computed from ksize.width and ksize.height, respectively (see getGaussianKernel for details); to fully control the result regardless of possible future modifications of all this semantics, it is recommended to specify all of ksize, sigmaX, and sigmaY.
int 	borderType = BORDER_DEFAULT 
)		

MedianBlur()

void cv::medianBlur	(	
InputArray 	src,//input 1-, 3-, or 4-channel image; when ksize is 3 or 5, the image depth should be CV_8U, CV_16U, or CV_32F, for larger aperture sizes, it can only be CV_8U.
OutputArray 	dst,//destination array of the same size and type as src.
int 	ksize //aperture linear size; it must be odd and greater than 1, for example: 3, 5, 7 ...
)	

  The function smoothes an image using the median filter with the ksize×ksize aperture. Each channel of a multi-channel image is processed independently. In-place operation is supported.

  该函数使用具有 k s i z e × k s i z e ksize\times ksize ksize×ksize大小的中值滤波器对图像进行平滑处理。多通道的每个通道都是独立处理的.

  该函数支持就地操作(不需要拷贝,在原函数的基础上直接进行操作)。

bilateralFilter()

void cv::bilateralFilter	(	
InputArray 	src,//Source 8-bit or floating-point, 1-channel or 3-channel image.
OutputArray 	dst,//Destination image of the same size and type as src .
int 	d,//Diameter of each pixel neighborhood that is used during filtering. If it is non-positive, it is computed from sigmaSpace.
double 	sigmaColor,//Filter sigma in the color space. A larger value of the parameter means that farther colors within the pixel neighborhood (see sigmaSpace) will be mixed together, resulting in larger areas of semi-equal color.
double 	sigmaSpace,//Filter sigma in the coordinate space. A larger value of the parameter means that farther pixels will influence each other as long as their colors are close enough (see sigmaColor ). When d>0, it specifies the neighborhood size regardless of sigmaSpace. Otherwise, d is proportional to sigmaSpace.
int 	borderType = BORDER_DEFAULT 
)	

  双边滤波器可以很好地减小不必要的噪声,同时保持边缘的清晰。但是有收获就要有付出,双边滤波器的运行效率格外的低,可以说,同大多数滤波器相比,他都是慢的那一个。

  为了简单起见,我们可以将两个sigma的值设置为相同的值。如果它们很小(<10),那么效果不大,如果它们很大(>150),那么效果会很强,使图像看起来卡通化(cartoonish)。

  大型的滤波器(d>5)的速度就更慢了,所以对于实时应用程序,建议d=5,如果使需要过滤很多噪声的离线应用程序建议d=9。

  双边滤波器不支持就地操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值