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()函数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • python实现固定尺寸图像拼接

    python实现固定尺寸图像拼接

    这篇文章主要为大家详细介绍了python实现固定尺寸图像拼接,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-04-04
  • langchain中LLM模型使用详解

    langchain中LLM模型使用详解

    这篇文章主要为大家介绍了langchain中LLM模型使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • python实现名片管理器的示例代码

    python实现名片管理器的示例代码

    这篇文章主要介绍了python实现名片管理器的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • Python获取和设置代理的动态IP的方式

    Python获取和设置代理的动态IP的方式

    在网络世界中,代理和动态IP是非常常见的概念,尤其对于需要大规模访问网站或者需要隐藏真实IP地址的应用程序来说,更是必不可少的工具,本文将给大家介绍如何使用编程技术来实现动态IP的设置和管理,需要的朋友可以参考下
    2024-05-05
  • Python中安装easy_install的方法

    Python中安装easy_install的方法

    这篇文章主要介绍了Python中安装easy_install的方法,需要的朋友可以参考下
    2018-11-11
  • Python 字典的使用详解及实例代码

    Python 字典的使用详解及实例代码

    今天小编帮大家简单介绍下Python的一种数据结构: 字典,字典是 另一种可变容器模型,且可存储任意类型对象,它用于存放具有映射关系的数据,通读本篇对大家的学习或工作具有一定的价值,需要的朋友可以参考下
    2021-11-11
  • python3反转字符串的3种方法(小结)

    python3反转字符串的3种方法(小结)

    这篇文章主要介绍了python3反转字符串的3种方法(小结),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-11-11
  • Python解压可迭代对象赋值给多个变量详解

    Python解压可迭代对象赋值给多个变量详解

    这篇文章主要为大家介绍了Python赋值多个变量,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2021-12-12
  • python学习之panda数据分析核心支持库

    python学习之panda数据分析核心支持库

    这篇文章主要给大家介绍了关于python学习之panda数据分析核心支持库的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-05-05
  • Python的socket模块源码中的一些实现要点分析

    Python的socket模块源码中的一些实现要点分析

    我们平时引入Python的socket模块利用其中的方法可以轻松地写出搭建socket通信的程序,今天我们就来看一下Python的socket模块源码中的一些实现要点分析,领略Python简洁代码的一些背后功劳.
    2016-06-06

最新评论