从头开始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,l∑f(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=Kwidth⋅Kheight1⎣⎢⎢⎢⎢⎡11⋅⋅111⋅⋅111⋅⋅1⋯⋯⋯⋯⋯11111⎦⎥⎥⎥⎥⎤
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。
双边滤波器不支持就地操作。