OpenCV图像卷积之cv.filter2D()函数详解

 更新时间:2022年09月14日 14:47:17   作者:零度蛋花粥  
在其官方文档中,filter2D()函数在掩模板介绍中一笔带过,我认为该函数应该进行详细介绍,下面这篇文章主要给大家介绍了关于OpenCV图像卷积之cv.filter2D()函数的相关资料,需要的朋友可以参考下

API

照例,我们搬一下官网的 API:

C++

void cv::filter2D(InputArray   src,
                  OutputArray  dst,
                  int          ddepth,
                  InputArray   kernel,
                  Point        anchor=Point(-1, -1),
                  double       delta=0,
                  int          borderType=BORDER_DEFAULT
                  )

Python

dst=cv.filter2D(src, ddepth, kernel[, dst[, anchor[, delta[, borderType]]]])

函数详解

这个函数一般是用于图像的卷积,但 OpenCV 文档里说这个函数不完全等于图像卷积。这一点说实话我看到的时候也震惊到了,我一直是拿它当卷积来用的。但是仔细考虑后,我认为这一点完全是定义上的差异,日常用的图像卷积定义和这个函数的功能实际上是一致的。

HelloWorld

直接上手做往往能给人最直观的感受。因此,最开始这里我要放一个使用这个函数的最小化程序,可以称之为该函数的 hello world 程序。这个程序跑通了,就可以很方便地尝试其他参数的作用了。

# HelloWrold Program of cv.filter2D
# by Aling on 2021/1/18
import numpy as np
import cv2 as cv

def main():
    img = cv.imread("你想读的图片")
    
    # 定义卷积核
    kernel = np.ones((10, 10)) / 100
    
    # 执行滤波
    avg_filtered = cv.filter2D(img, -1, kernel)

    # 显示图片
    cv.imshow("Average filtered", avg_filtered)
    cv.waitKey(0)
    cv.destroyAllWindows()

if __name__ == "__main__":
    main()

这个程序读取一张图片,并将其通过10 x 10 的卷积核作均值滤波并显示结果。

滤波效果

参数详解

同样的,这里把各个参数打一张表:

参数类型是否必须指定(默认值)具体含义
srcnumpy.ndarray原图像
ddepthint目标图像深度(指数据类型)
kernelnumpy.ndarray卷积核
anchortuple否(卷积核中心)卷积锚点
delta是数据类就行否(0)偏移量,卷积结果要加上这个数字
borderTypeint(实际上是 enum 类)否(cv.BORDER_DEFALUT)边缘类型

src

这个参数没什么好说的,就是原图像。它可以是任何色彩模式,这就意味着如果你把原本送到这个函数里的图片从黑白变成了彩色(单通道变成了 3 通道),你并不需要更改其他参数。本身,对多通道的图像,卷积就是以通道为单位进行的。

ddepth

这个参数有点费解了。大部分情况下不需要管它是干嘛的,直接把它设成 -1 就没有任何问题。

参数名 ddepth ,英文是 desired depth,即期望深度。什么意思呢?我们来看它的可能取值表(来自这里):

输入深度(src.depth())期望深度(ddepth
CV_8U-1/CV_16S/CV_32F/CV_64F
CV_16U/CV_16S-1/CV_32F/CV_64F
CV_32F-1/CV_32F/CV_64F
CV_64F-1/CV_64F

这个表里的一大串 CV 打头的符号到底是什么意思呢?实际上这些符号的末尾字母对应了数据类型:

U == Unsigned int  # 无符号整型
S == Signed int    # 有符号整型
F == Float         # 浮点型

中间的数字很显然代表了数据类型所占用的空间(bit)。所以,所谓深度,其实指的是数据类型

那就好说了,你会发现这里其实是规定了输出数据的类型,包括每个通道的每个像素占用多少空间。输出数据的类型必须根据上面的表格中输入对应的类型指定。这里 -1 表示输出类型和输入相同

不过,值得注意的是,似乎有些数据类型无法通过 cv2.imshow 正常显示,可以用 matplotlib.pyplot.imshow 来代替。

但是,还是注意,没有关于数据类型的特别要求时,这个功能是不需要的,取 -1 即可。

kernel & anchor

这两个参数都是卷积相关的,因此放在一节里面讲述。接下来的内容假设你已经了解了图像卷积

这里,kernel 很显然表示的是卷积核,这是一个 numpy.ndarray 类型的矩阵。这个矩阵的生成可以用 numpy 自带的函数,但是对于复杂一些的卷积核,OpenCV 内部的一些函数显然更合适。如 getStructuringElementgetGaussianKernel,前者用于获取特定形状的核,后者则是高斯核生生成器(不过要注意生成的是一个向量)。

# 方法一示例
kernel = cv.getStructuringElement(cv.MORPH_RECT, (11, 11))

# 方法二示例
vector = cv.getGaussianKernel(11, -1)
kernel = vector @ vector.T

anchor 则表示锚点。什么叫锚点呢?看下面这张图:

convolution

可以说,锚点 anchor 决定了卷积核相对于生成目标点的位置。虽然锚点是相对于卷积核来定义的,但是卷积的过程更像是通过锚点去寻找卷积核。遍历图像中的每一个像素,以每一个像素为锚点,按照相对位置生成卷积范围,和卷积核对应元素相乘再求和得到目标图像中对应像素的值。可以用公式表示成:

这实际上就是一般的图像卷积计算方法。OpenCV 文档里面叙述的卷积定义则是需要将卷积核围绕锚点对称变换,再用上面的公式计算。这种方法更接近卷积原始的定义,但是对图像的卷积一般的应用而言(滤波、深度学习)这两种定义实际上没有什么区别。、

锚点用一个元组指定,是相对于卷积核左上角的坐标,从 0 开始

# 替换掉 HelloWorld 程序的对应行。
avg_filtered = cv.filter2D(img, -1, kernel, (1, 2))

delta

这个参数的存在其实有些费解,它的效果很简单,就是把卷积的结果加上一个固定的数字。直观上将,它将整个图像变亮或者变暗了。从应用上来说,它实际上将卷积过程扩展成了一般的线性运算( ∗ * ∗ 表示卷积):

这个线性运算可以将结果限定在某一符合要求的范围内(比如大于 0),而且不会阻断梯度的传递。

borderType

这个参数更改的是 border 的生成方式。这个 border,也就是边缘,是在靠近边缘的部分卷积时用到的,参考上面那张图。无论 anchor 是什么,总有些边缘上的点对应的卷积范围无法完全落在原图内,这就需要对原图进行扩展。扩展的方法就是我们这里参数的含义。

这个参数取值是 OpenCV 里的 cv::BorderTypes enum 类定义的值,其可能取值及其对于边缘的作用结果如下图所示(图片来自 OpenCV Python 教程):

border from opencv document

要注意几点:

  • cv.BORDER_WARP 在这个函数里面是不支持的;
  • cv.BORDER_CONSTANT 会将边缘取为 0(黑色),而且没法改,因为原函数并没有留出相关的接口。

扩展——滤波相关函数

图像滤波是一个很常用的功能,对此,OpenCV 也定义了很多函数。这里介绍的 cv.filter2D 是这些函数中可控性最高的,因为你可以用自定义的核进行卷积。但是一些常用的滤波,我们可以不必自己定义相应的核,直接用定义好的函数就可以了。

均值滤波

dst = cv.blur(img, (11, 11))

# 等效于:
dst = cv.filter2D(img, -1, np.ones((11, 11))/11**2)

高斯滤波

dst = cv.GaussianBlur(img, (11, 11), -1)

# 等效于
vector = cv.getGaussianKernel(11, -1)
kernel = vector @ vector.T
dst = cv.filter2D(img, -1, kernel)

中值滤波

dst = cv.medianBlur(img, 11)

注意中值滤波是取中位数作为目标值,是一个非线性滤波子,因此无法通过线性滤波的 cv.filter2D 来等效实现。

参考链接

总结

到此这篇关于OpenCV图像卷积之cv.filter2D()函数详解的文章就介绍到这了,更多相关OpenCV图像卷积cv.filter2D()函数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Django如何重置migration的几种情景

    Django如何重置migration的几种情景

    这篇文章主要介绍了Django如何重置migration的几种情景,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02
  • pandas中的ExcelWriter和ExcelFile的实现方法

    pandas中的ExcelWriter和ExcelFile的实现方法

    这篇文章主要介绍了pandas中的ExcelWriter和ExcelFile的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-04-04
  • 深入理解Python的jieba模块

    深入理解Python的jieba模块

    这篇文章主要介绍了深入理解Python的jieba模块,英语单词之间是通过空格分隔的,但是中文却不存在空格的概念,因此需要一个模块来解决中文的分词问题,jieba模块是一个python第三方中文分词模块,可以用于将语句中的中文词语分离出来,需要的朋友可以参考下
    2023-11-11
  • python实现一个简单RPC框架的示例

    python实现一个简单RPC框架的示例

    本文将会使用Python实现一个最简单的RPC框架,不具有实用意义,但可以让你清醒地理解RPC框架的几个组成部分,只是比看Python自带的xmlrpc清晰。
    2020-10-10
  • 使用python接受tgam的脑波数据实例

    使用python接受tgam的脑波数据实例

    这篇文章主要介绍了使用python接受tgam的脑波数据实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-04-04
  • 用Python做一个久坐提醒小助手的示例代码

    用Python做一个久坐提醒小助手的示例代码

    这篇文章主要介绍了用Python做一个久坐提醒小助手的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-02-02
  • python神经网络Batch Normalization底层原理详解

    python神经网络Batch Normalization底层原理详解

    这篇文章主要为大家介绍了python神经网络Batch Normalization底层原理详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • 多版本Python共存的配置方法

    多版本Python共存的配置方法

    本文给大家分享的是由于工作需求,要求PC中安装python2和python3的开发环境。那么我们应该如何操作呢,下面我们就来探讨下。
    2017-05-05
  • 15个高级Python技巧提高代码效率更加Pythonic

    15个高级Python技巧提高代码效率更加Pythonic

    Python 是一种多用途、功能强大的编程语言,具有广泛的特性和能力,在本文中,我们将探讨 15 个高级 Python 技巧,它们有助于改善您的开发工作流程,并使您的代码更加高效,更加 Pythonic
    2023-12-12
  • Python入门基础之变量及字符串

    Python入门基础之变量及字符串

    Python是一个不错的语言,Python语言简单易懂还具有强大的编程能力,同时对于大数据分析有很明显的帮助,这篇文章主要给大家介绍了关于Python入门基础之变量及字符串的相关资料,需要的朋友可以参考下
    2022-01-01

最新评论