12、OpenCV平滑(模糊)图像

一、学习目标

  • 了解什么是图像的平滑(模糊)
  • 学会使用均值模糊、高斯模糊、双边模糊、中值模糊等处理图像
  • 动手练习平滑实例

二、平滑理论介绍

平滑,也叫模糊,是一种简单而常用的图像处理操作。平滑通常可以用来减少噪声(其他用途将在下面的教程中看到)。

为了执行平滑操作,我们将对我们的图像应用一个滤波器。最常见的滤波器类型是线性的,输出像素的值(即g(i,j))为输入像素值(即 f(i+k,j+l))的加权和:

在这里插入图片描述
h(k,l) 称为核,有的地方也称为算子,它是滤波的系数。可以将滤波器看作是一个固定尺寸的系数窗口,在图像上滑动,图像处理的很多操作都是基于滑动窗口来完成的。

三、学习四种不同的滤波器

1、均值滤波器
这个滤波器是所有过滤器中最简单的。每个输出像素都是其中心像素及其相邻像素的均值(它们的权重都相等)。内核如下:
在这里插入图片描述
Kwidth 和 Kheight 分别表示内核的宽和高。
OpenCV使用 cv::blur函数实现均值滤波,其函数原型为:

void cv::blur(	InputArray 		src,
				OutputArray 	dst,
				Size 			ksize,
				Point 			anchor = Point(-1,-1),
				int 			borderType = BORDER_DEFAULT )	
  • 参数 src: 待处理的图像,它可以有任意数量的通道,这些通道是独立处理的,但是图像深度应该是CV_8UCV_16UCV_16SCV_32FCV_64F
  • 参数 dst: 与src相同大小和类型的输出图像
  • 参数 ksize: 均值模糊的内核大小
  • 参数 anchor: 锚点,默认值 Point(-1,-1) 表示锚位于内核中心
  • 参数 borderType: 用于指定图像边界处理时的填充像素方式,枚举自BorderTypes。不支持BORDER_WRAP

边界类型的取值见下表:

参数取值取值说明
BORDER_CONSTANTiiiiiii(abcdefgh)iiiiiii,使用指定的常量 i 填充
BORDER_REPLICATEaaaaaa(abcdefgh)hhhhhhh,填充的所有像素都使用对应的边界像素
BORDER_REFLECTfedcba(abcdefgh)hgfedcb,填充几个边界像素就从对应边界处取几个像素
BORDER_WRAPcdefgh(abcdefgh)abcdefg
BORDER_REFLECT_101gfedcb(abcdefgh)gfedcba
BORDER_TRANSPARENTuvwxyz(abcdefgh)ijklmno
BORDER_REFLECT101与BORDER_REFLECT_101一样
BORDER_DEFAULT与BORDER_REFLECT_101一样
BORDER_ISOLATED不管ROI之外的像素

还可以使用函数cv::boxFilter完成均值滤波,其函数原型为:

void cv::boxFilter(	InputArray 		src,
					OutputArray 	dst,
					int 			ddepth,
					Size 			ksize,
					Point 			anchor = Point(-1,-1),
					bool 			normalize = true,
					int 			borderType = BORDER_DEFAULT )
  • 参数 src: 输入图像
  • 参数 dst: 与src相同大小和类型的输出图像
  • 参数 ddepth: 输出图像的深度(-1使用src.depth())
  • 参数 ksize: 均值模糊的内核大小
  • 参数 anchor: 锚点,默认值 Point(-1,-1) 表示锚位于内核中心
  • 参数 normalize: 标志,指定内核是否按其面积进行归一化,即为true时表示求均值
  • 参数 borderType: 用于指定图像边界处理时的填充像素方式,枚举自BorderTypes。不支持BORDER_WRAP

非归一化盒滤波器用于计算每个像素邻域上的各种积分特征,如图像导数的协方差矩阵(用于密集光流算法等)。其遵循如下公式:
在这里插入图片描述
其中:
在这里插入图片描述
如下两种调用效果相同:

blur(src, dst, ksize, anchor, borderType)

boxFilter(src, dst, src.type(), ksize, anchor, true, borderType).

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

一维高斯的公式为:
在这里插入图片描述
其分布图如下:
在这里插入图片描述
假设图像是一维的,可以注意到位于中间的像素拥有最大的权值。相邻像素的权重随着它们与中心像素空间距离的增大而减小。

二维高斯的公式为:
在这里插入图片描述
其分布图如下:
在这里插入图片描述
其中μ是平均值(峰值),σ2 代表方差(变量x和y各自的方差)
OpenCV使用cv::GaussianBlur函数完成高斯滤波,其函数原型为:

void cv::GaussianBlur(	InputArray 		src,
						OutputArray 	dst,
						Size 			ksize,
						double 			sigmaX,
						double 			sigmaY = 0,
						int 			borderType = BORDER_DEFAULT )	
  • 参数 src: 输入图像
  • 参数 dst: 与src相同大小和类型的输出图像
  • 参数 ksize: 高斯核的大小。ksize.widthksize.height可以不同,但它们都必须是正奇数。它们可以是0,这时就需要根据sigma来计算
  • 参数 sigmaX: X方向的高斯核标准差
  • 参数 sigmaY: Y方向的高斯核标准差。如果sigmaY为零,则设置为sigmaX,如果两个sigma都为零,则根据ksize计算。(详见getGaussianKernel函数)。建议指定所有的ksizesigmaXsigmaY
  • 参数 borderType: 用于指定图像边界处理时的填充像素方式,枚举自BorderTypes。不支持BORDER_WRAP

3、中值滤波器
中值滤波器遍历内核窗口中的每一个像素,并用这些相邻像素的中值替换每个中心像素(位于评估像素周围的正方形区域)。

OpenCV使用cv::medianBlur函数完成中值滤波,其函数原型为:

void cv::medianBlur(	InputArray 		src,
						OutputArray	 	dst,
						int 			ksize )	
  • 参数 src: 输入1,3,或4通道图像。当ksize为3或5时,图像深度应为CV_8U, CV_16U,或CV_32F。对于较大的内核尺寸,只能为CV_8U
  • 参数 dst: 与输入图像尺寸和类型一致的输出图像
  • 参数 ksize: 内核尺寸;必须为奇数且大于1,例如:3,5,7…

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

与高斯滤波器类似,双边滤波器也考虑给每个相邻像素赋不同的权值。这些权重有两个分量,第一个是与高斯滤波器使用的相同的权重。第二个分量考虑相邻像素和核心像素之间的强度差异。

双边滤波的详细理论介绍请移步:
飞机票一
CSDN飞机票

OpenCV使用cv::bilateralFilter函数完成双边滤波,其函数原型为:

void cv::bilateralFilter(	InputArray 		src,
							OutputArray 	dst,
							int 			d,
							double 			sigmaColor,
							double 			sigmaSpace,
							int 			borderType = BORDER_DEFAULT )	
  • 参数 src: 8位或浮点,1通道或3通道输入图像
  • 参数 dst: 与src相同大小和类型的目标图像
  • 参数 d: 滤波过程中使用的每个像素邻域的直径。如果它是负数,则根据sigmaSpace计算
  • 参数 sigmaColor: 在颜色空间中过滤的sigma。该参数的值越大,意味着像素邻域内更远的颜色(参见sigmaSpace)将混合在一起,从而产生更大的相等颜色区域
  • 参数 sigmaSpace: 在坐标空间中过滤的sigma。参数值越大,越远的像素将相互影响,只要它们的颜色足够接近(请参阅sigmaColor)。当d>0时,它指定邻域大小,而不考虑sigmspace。否则,d与sigmaSpace成比例
  • 参数 borderType: 用于指定图像边界处理时的填充像素方式,枚举自BorderTypes

四、完整使用实例

#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;

int DELAY_CAPTION = 1500;
int DELAY_BLUR = 500;
int MAX_KERNEL_LENGTH = 31;
Mat src; Mat dst;
char window_name[] = "Smoothing Demo";
int display_caption(const char* caption);
int display_dst(int delay);

int main(int argc, char** argv)
{
	namedWindow(window_name, WINDOW_AUTOSIZE);
	string fileName = samples::findFile("lena.jpg");

	src = imread(fileName, IMREAD_COLOR);
	if (src.empty())
	{
		fprintf(stderr, "Failed to load image: %s.\n", fileName);
		system("pause");
		return EXIT_FAILURE;
	}

    if (display_caption("Original Image") != 0)
    {
        return 0;
    }
    dst = src.clone();
    if (display_dst(DELAY_CAPTION) != 0)
    {
        return 0;
    }
    if (display_caption("Homogeneous Blur") != 0)
    {
        return 0;
    }
    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;
        }
    }
    if (display_caption("Gaussian Blur") != 0)
    {
        return 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;
        }
    }
    if (display_caption("Median Blur") != 0)
    {
        return 0;
    }
    for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2)
    {
        medianBlur(src, dst, i);
        if (display_dst(DELAY_BLUR) != 0)
        {
            return 0;
        }
    }
    if (display_caption("Bilateral Blur") != 0)
    {
        return 0;
    }
    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;
        }
    }
    display_caption("Done!");
    return EXIT_SUCCESS;
}

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

int display_dst(int delay)
{
    imshow(window_name, dst);
    int c = waitKey(delay);
    if (c >= 0) { return -1; }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值