OpenCV基础(8)OpenCV中的卷积图像滤波

你有没有试过在Photoshop或移动应用程序的帮助下模糊或锐化图像?如果是,那么您已经使用过卷积核。在这里,我们将解释如何在OpenCV中使用卷积进行图像滤波。

您将使用2d卷积核和OpenCV计算机视觉库应用不同的模糊和锐化技术的图像。我们将向您展示如何在Python和c++中实现这些技术。

我们将使用下面的图像为我们所有的编码操作。
在这里插入图片描述
让我们先看一看将用于过滤图像的代码。我们将详细讨论每一行代码,以便您完全理解它。

1.滤波代码

(1)Python

import cv2
import numpy as np

image = cv2.imread('test.jpg')

# 如果图像为空,则打印错误信息
if image is None:
    print('Could not read image')

# 应用恒等核
kernel1 = np.array([[0, 0, 0],
                    [0, 1, 0],
                    [0, 0, 0]])

identity = cv2.filter2D(src=image, ddepth=-1, kernel=kernel1)

cv2.imshow('Original', image)
cv2.imshow('Identity', identity)
    
cv2.waitKey()
cv2.imwrite('identity.jpg', identity)
cv2.destroyAllWindows()

# 应用均值滤波器
kernel2 = np.ones((5, 5), np.float32) / 25
img = cv2.filter2D(src=image, ddepth=-1, kernel=kernel2)

cv2.imshow('Original', image)
cv2.imshow('Kernel Blur', img)
    
cv2.waitKey()
cv2.imwrite('blur_kernel.jpg', img)
cv2.destroyAllWindows()

(2)C++

// 导入依赖
#include <opencv2/opencv.hpp>
#include <iostream>

// 命名空间
using namespace std;
using namespace cv;

int main()
{
    // 读取图像
    Mat image = imread("test.jpg");

    // 如果图像为空,则打印错误信息
    if (image.empty()) 
        {
            cout << "Could not read image" << endl;
        }
    
    // 应用恒等核
    Mat kernel1 = (Mat_<double>(3,3) << 0, 0, 0, 0, 1, 0, 0, 0, 0);
    Mat identity; 
    filter2D(image, identity, -1 , kernel1, Point(-1, -1), 0, 4);
    imshow("Original", image);
    imshow("Identity", identity);
    waitKey();
    imwrite("identity.jpg", identity);
    destroyAllWindows();

    // 使用均值滤波器
    // 创建全一矩阵
    Mat kernel2 = Mat::ones(5,5, CV_64F);
    // 规范化元素
    kernel2 = kernel2 / 25;
    Mat img;
    filter2D(image, img, -1 , kernel2, Point(-1, -1), 0, 4);
    imshow("Original", image);
    imshow("Kernel blur", img);
    imwrite("blur_kernel.jpg", img);
    waitKey();
    destroyAllWindows();
}

2.图像处理中卷积核的介绍

在图像处理中,卷积核是一个用于过滤图像的二维矩阵。卷积核也被称为卷积矩阵,它通常是一个正方形的MxN矩阵,其中M和N都是奇整数(例如3×3, 5×5, 7×7等)。

这种核可以用于对图像的每个像素执行数学运算,以达到预期的效果(如模糊或锐化图像)。但为什么要模糊图像呢?这里有两个重要的原因:

  • 因为它减少了图像中某些类型的噪声。由于这个原因,模糊通常被称为平滑。
  • 为了去除分散注意力的背景,你可能会故意模糊图像的部分,就像在移动设备相机的“肖像”模式中所做的那样。

3.如何使用卷积核锐化或模糊图像

图像的滤波是通过核与图像的卷积来实现的。简单地说,图像与核的卷积代表了核与图像中相应元素之间的简单数学运算。

  • 假设核的中心位于图像中的一个特定像素§上。
  • 然后将核中每个元素的值(本例为1)与源图像中对应的像素元素(即像素强度)相乘。
  • 现在,将这些乘法的结果相加并计算平均值。
  • 最后,用刚刚计算的平均值替换像素§的值。

一旦使用上面的3×3内核对源图像中的每个像素执行此操作,得到的过滤图像将显得模糊。这是因为与这个核的卷积操作有一个平均效果,这往往是平滑或模糊的图像。您很快就会看到内核中单个元素的值如何决定过滤的性质。例如,通过更改核元素的值,还可以实现锐化效果。这个概念简单却非常强大,因此被用于许多图像处理管道中。

现在您已经学会了使用卷积核,让我们来探索如何在OpenCV中实现卷积核。

4. 在OpenCV中对图像应用恒等卷积

下面的代码执行了以下步骤

  • 读取测试图像
  • 使用3x3 NumPy数组定义恒等卷积核
  • 使用OpenCV中的filter2D()函数来执行线性滤波操作
  • 使用imshow()显示原始和过滤后的图像
  • 使用imwrite()将过滤后的图像保存到磁盘

filter2D(src, ddepth, kernel)需要三个输入参数:

  • 第一个参数是源图像
  • 第二个参数是ddepth,它表示得到的图像的深度。值为-1表示最终图像也将具有与源图像相同的深度
  • 最后一个输入参数是kernel,我们将它应用到源图像
    (1)Python
import cv2
import numpy as np
image = cv2.imread('test.jpg')
"""
恒等卷积
"""
kernel1 = np.array([[0, 0, 0],
                    [0, 1, 0],
                    [0, 0, 0]])
# filter2D() 函数可用于将核应用于图像
# ddepth表示得到的图像的深度。值为-1表示最终图像也将具有与源图像相同的深度
identity = cv2.filter2D(src=image, ddepth=-1, kernel=kernel1)

# 我们应该得到相同的图像
cv2.imshow('Original', image)
cv2.imshow('Identity', identity)

cv2.waitKey()
cv2.imwrite('identity.jpg', identity)
cv2.destroyAllWindows()

(2)C++

// 导入依赖
#include <opencv2/opencv.hpp>
#include <iostream>

//命名空间
using namespace std;
using namespace cv;
int main()
{
    // 读取图像
    Mat image = imread("test.jpg");

    // 如果图像为空,则打印错误信息
    if (image.empty()) 
        {
            cout << "Could not read image" << endl;
        }
 
	// 使用恒等卷积
	Mat kernel1 = (Mat_<double>(3,3) << 0, 0, 0, 0, 1, 0, 0, 0, 0);
	Mat identity;
	filter2D(image, identity, -1 , kernel1, Point(-1, -1), 0, 4);
	imshow("Original", image);
	imshow("Identity", identity);
	waitKey();
	imwrite("identity.jpg", identity);
	destroyAllWindows();
}

在这里插入图片描述

5.使用自定义2D卷积核模糊图像

接下来,我们将演示如何模糊图像。在这里,我们也将定义一个自定义核,并使用OpenCV中的filter2D()函数对源图像应用过滤操作。

首先定义一个5×5核,它只包含一个核。注意,我们还将核除以25。这是为什么呢?在你使用2D卷积矩阵对图像进行卷积之前,你需要确保所有的值都是标准化的。这是通过将核中的每个元素除以内核中的元素数来完成的,在这种情况下,内核中的元素数是25。这确保了所有值都在[0,1]范围内。

现在使用filter2D()函数来过滤图像。如您所见,可以使用filter2D()对任意用户定义的核进行卷积。
(1)Python

import cv2
import numpy as np
image = cv2.imread('test.jpg')
"""
图像模糊
"""
kernel2 = np.ones((5, 5), np.float32) / 25
img = cv2.filter2D(src=image, ddepth=-1, kernel=kernel2)

cv2.imshow('Original', image)
cv2.imshow('Kernel Blur', img)

cv2.waitKey()
cv2.imwrite('blur_kernel.jpg', img)
cv2.destroyAllWindows()

(2)C++

// 导入依赖
#include <opencv2/opencv.hpp>
#include <iostream>

//命名空间
using namespace std;
using namespace cv;
int main()
{
    // 读取图像
    Mat image = imread("test.jpg");

    // 如果图像为空,则打印错误信息
    if (image.empty()) 
        {
            cout << "Could not read image" << endl;
        }
	// 图像模糊
	// 使用全一初始化矩阵
	Mat kernel2 = Mat::ones(5,5, CV_64F);
	
	// 标准化元素
	kernel2 = kernel2 / 25;
	Mat img;
	filter2D(image, img, -1 , kernel2, Point(-1, -1), 0, 4);
	imshow("Original", image);
	imshow("Kernel blur", img);
	imwrite("blur_kernel.jpg", img);
	waitKey();
	destroyAllWindows();
}

在这里插入图片描述

6.使用OpenCV的内置函数模糊图像

你也可以使用OpenCV内置的blur()函数来模糊图像。使用它来模糊图像,在这里您不需要明确定义内核。只需使用ksize输入参数指定内核大小,如下面的代码所示。模糊函数将在内部创建5×5模糊核,并将其应用到源图像。

下面的示例使用了 blur() 函数,将生成与上面使用 filter2d() 函数的示例完全相同的输出。
(1)Python

import cv2
import numpy as np
image = cv2.imread('test.jpg')
"""
图像模糊
"""
img_blur = cv2.blur(src=image, ksize=(5,5)) # 使用模糊函数模糊一个图像,其中ksize是内核大小

# 显示使用cv2.imshow ()
cv2.imshow('Original', image)
cv2.imshow('Blurred', img_blur)

cv2.waitKey()
cv2.imwrite('blur.jpg', img_blur)
cv2.destroyAllWindows()

(2)C++

// 导入依赖
#include <opencv2/opencv.hpp>
#include <iostream>

//命名空间
using namespace std;
using namespace cv;
int main()
{
    // 读取图像
    Mat image = imread("test.jpg");

    // 如果图像为空,则打印错误信息
    if (image.empty()) 
        {
            cout << "Could not read image" << endl;
        }
	// 使用OpenCV c++ blur()函数进行模糊
	Mat img_blur;
	blur(image, img_blur, Size(5,5));
	imshow("Original", image);
	imshow("Blurred", img_blur);
	imwrite("blur.jpg", img_blur);
	waitKey();
	destroyAllWindows();
}

在这里插入图片描述

7.在OpenCV中对图像应用高斯模糊

现在我们将使用OpenCV对图像应用高斯模糊。该技术使用高斯滤波器,它执行加权平均,而不是第一个示例中描述的均匀平均。在这种情况下,高斯模糊权重像素值,基于它们到核中心的距离。离中心越远的像素对加权平均的影响越小。下面的代码使用OpenCV中的GaussianBlur()函数卷积图像。GaussianBlur(src, ksize, sigmaX[, dst[, sigmaY[, borderType]]])
函数GaussianBlur()需要四个输入参数:

  • 第一个参数src指定要过滤的源图像。
  • 第二个参数是ksize,它定义了高斯核的大小。这里,我们使用的是5×5核。
  • 最后两个参数是sigmaXsigmaY,它们都被设置为0。这些是高斯核在X(水平)和Y(垂直)方向上的标准差。sigmaY的默认设置是0。如果您简单地将sigmaX设置为0,那么将从核大小(分别为宽度和高度)计算标准偏差。您还可以显式地将每个参数的大小设置为大于零的正值。

(1)Python

import cv2
import numpy as np
image = cv2.imread('test2.png')
"""
Gaussian模糊
"""
# sigmaX是高斯核标准差
# Ksize是核大小
gaussian_blur = cv2.GaussianBlur(src=image, ksize=(5,5), sigmaX=0, sigmaY=0)

cv2.imshow('Original', image)
cv2.imshow('Gaussian Blurred', gaussian_blur)
    
cv2.waitKey()
cv2.imwrite('gaussian_blur.jpg', gaussian_blur)
cv2.destroyAllWindows()

(2)C++

// 导入依赖
#include <opencv2/opencv.hpp>
#include <iostream>

//命名空间
using namespace std;
using namespace cv;
int main()
{
    // 读取图像
    Mat image = imread("test.jpg");

    // 如果图像为空,则打印错误信息
    if (image.empty()) 
        {
            cout << "Could not read image" << endl;
        }
	// 实现高斯模糊
	Mat gaussian_blur;
	GaussianBlur(image, gaussian_blur, Size(5,5), SigmaX=0, SigmaY=0);
	imshow("Original", image);
	imshow("Gaussian Blurred", gaussian_blur);
	imwrite("gaussian_blur.jpg", gaussian_blur);
	waitKey();
	destroyAllWindows();
}

在这里插入图片描述

8.在OpenCV中对图像应用中值滤波

我们也可以使用中值模糊,使用OpenCV中的medianBlur()函数。中值滤波是将源图像中的每个像素替换为图像在核区域中像素的中值。
medianBlur(src, ksize)这个函数只有两个必需参数:

  • 第一个是源图像。
  • 第二个是核大小,它必须是一个奇数,正整数。
    (1)Python
import cv2
import numpy as np
image = cv2.imread('test2.png')
"""
中值滤波
"""
median = cv2.medianBlur(src=image, ksize=5)

cv2.imshow('Original', image)
cv2.imshow('Median Blurred', median)
    
cv2.waitKey()
cv2.imwrite('median_blur.jpg', median)
cv2.destroyAllWindows()

(2)C++

// 导入依赖
#include <opencv2/opencv.hpp>
#include <iostream>

//命名空间
using namespace std;
using namespace cv;
int main()
{
    // 读取图像
    Mat image = imread("test.jpg");

    // 如果图像为空,则打印错误信息
    if (image.empty()) 
        {
            cout << "Could not read image" << endl;
        }
	// 中值滤波
	Mat median_blurred;
	medianBlur(image, median_blurred, (5,5));
	imshow("Original", image);
	imshow("Median Blurred", median_blurred);
	imwrite("median_blur.jpg", median_blurred);
	waitKey();
	destroyAllWindows();
}

见下图中值滤波的结果。注意,对于相同的核大小,中值滤波的效果比高斯模糊更突出。中值滤波通常用于减少图像中的椒盐噪声,如图所示。
在这里插入图片描述

9.使用自定义2D卷积核锐化图像

你也可以用2D卷积核锐化图像。首先定义一个自定义的2D核,然后使用filter2D()函数对图像应用卷积操作。

在下面的代码中,定义了一个3×3锐化核。
(1)Python

import cv2
import numpy as np
image = cv2.imread('test2.png')
"""
锐化
"""
kernel3 = np.array([[0, -1,  0],
                   [-1,  5, -1],
                    [0, -1,  0]])
sharp_img = cv2.filter2D(src=image, ddepth=-1, kernel=kernel3)

cv2.imshow('Original', image)
cv2.imshow('Sharpened', sharp_img)
    
cv2.waitKey()
cv2.imwrite('sharp_image.jpg', sharp_img)
cv2.destroyAllWindows()

(2)C++

// 导入依赖
#include <opencv2/opencv.hpp>
#include <iostream>

//命名空间
using namespace std;
using namespace cv;
int main()
{
    // 读取图像
    Mat image = imread("test.jpg");

    // 如果图像为空,则打印错误信息
    if (image.empty()) 
        {
            cout << "Could not read image" << endl;
        }
	// 锐化
	Mat sharp_img;
	Mat kernel3 = (Mat_<double>(3,3) << 0, -1,  0, 
	                                   -1,  5, -1, 
	                                    0, -1, 0);
	filter2D(image, sharp_img, -1 , kernel3, Point(-1, -1), 0, BORDER_DEFAULT);
	imshow("Original", image);
	imshow("Sharpenned", sharp_img);
	imwrite("sharp_image.jpg", sharp_img);
	waitKey();
	destroyAllWindows();
}

那么,我们会得到什么样的结果呢?请看下面的图表。锐化效果非常令人印象深刻。右图的锐化图像显示了之前看不见的木头裂缝。在这里插入图片描述

10.在OpenCV中对图像进行双边滤波

虽然模糊是降低图像噪声的一种有效方法,但通常不希望对整个图像进行模糊,因为重要的细节和锐利的边缘可能会丢失。在这种情况下,可以使用双边滤波器。

  • 这种技术应用滤波器有选择地模糊邻域内相似强度的像素,而锐利的边缘都会被保留下来。
  • 它让您不仅可以控制过滤器的空间大小,还可以控制在滤波输出中包含邻近像素的程度。这是根据它们的颜色强度的变化,以及与过滤后的像素的距离来完成的。

双边滤波实质上是对图像应用二维高斯(加权)模糊,同时也考虑相邻像素强度的变化,以最小化边缘附近的模糊(这是我们希望保持的)。这意味着核的形状实际上取决于每个像素位置的局部图像内容。

这是一个具体的例子。假设,您正在过滤图像中靠近边缘的一个区域。一个简单的高斯模糊过滤器会模糊边缘,因为它位于过滤区域附近(接近高斯过滤器的中心)。但双边滤波器可以感知边缘,因为它也考虑像素强度的差异。因此,它将为横跨边缘的像素计算一个更低的权重,从而减少它们对过滤区域的影响。强度较均匀的区域模糊较重,因为它们与强边缘无关。

幸运的是,OpenCV提供了bilateralFilter()函数来过滤图像。bilateralFilter(src, d, sigmaColor, sigmaSpace)

  • 函数的第一个参数是源图像。
  • 下一个参数d,定义用于过滤的像素邻域的直径。
  • 接下来的两个参数,sigmaColorsigmspace分别定义了(1D)颜色强度分布和(2D)空间分布的标准偏差。sigmaSpace参数定义了核在x和y方向上的空间范围(就像前面描述的高斯模糊过滤器)。sigmaColor参数定义一维高斯分布,它指定像素强度差异可以容忍的程度。

过滤后的图像中像素的最终(加权)值是其空间权重和强度权重的乘积。因此,

  • 与过滤像素相似和接近的像素将产生影响
  • 远离过滤像素的像素影响很小(由于空间高斯分布)
  • 具有不同强度的像素影响很小(由于颜色强度高斯分布),即使它们接近核的中心。

(1)Python

import cv2
import numpy as np
image = cv2.imread('test2.png')
"""
双边滤波器
"""
bilateral_filter = cv2.bilateralFilter(src=image, d=9, sigmaColor=75, sigmaSpace=75)

cv2.imshow('Original', image)
cv2.imshow('Bilateral Filtering', bilateral_filter)

cv2.waitKey(0)
cv2.imwrite('bilateral_filtering.jpg', bilateral_filter)
cv2.destroyAllWindows()

(2)C++

// 导入依赖
#include <opencv2/opencv.hpp>
#include <iostream>

//命名空间
using namespace std;
using namespace cv;
int main()
{
    // 读取图像
    Mat image = imread("test.jpg");

    // 如果图像为空,则打印错误信息
    if (image.empty()) 
        {
            cout << "Could not read image" << endl;
        }
	// 双边滤波器
	Mat bilateral_filter;
	bilateralFilter(image, bilateral_filter, 9, 75, 75);
	imshow("Original", image);
	imshow("Bilateral filtering", bilateral_filter);
	imwrite("bilateral_filtering.jpg", bilateral_filter);
	waitKey(0);
	destroyAllWindows();
	return 0;
}

下图是双边滤波的结果。看看如何使像素强度更均匀的区域变得平滑(模糊),同时保留木材中的细微裂缝(边缘)。双边滤波是一种非常有效的技术,但它的计算代价很高(特别是对于大的核大小)。因此,根据您的特定应用程序明智地进行选择。
在这里插入图片描述

总结

我们从卷积核的概念开始,以及如何使用它们来对图像滤波。然后学习如何使用它们对图像的每个像素执行数学运算,以达到预期的效果,如模糊或锐化。了解了如何使用OpenCV实现2D滤波。在理解了identity核之后,我们继续创建更多的自定义核,这些核可以与OpenCV中的filter2D()函数一起使用。探索了OpenCV中一些重要的内置滤波函数,如MedianBlur()GaussianBlur()。最后,演示了OpenCV中的bilateralFilter(),看看它是如何在保持清晰边缘的同时平滑图像的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值