【总结】OpenCV-Python常用API(六)—— 图像卷积滤波操作

0. 前言

图像作为二维矩阵,对其进行二维卷积/滤波操作也是非常常见的操作。美图秀秀等软件中对照片进行各种风格化操作,如油画、水雾等,本质上往往也就是对照片进行滤波操作生成的。

论坛上有关图片卷积和滤波的各种帖子也非常多,有偏重卷积概念的;有各种详细示例的;也有区分滤波和卷积的。下面这一张图也足以形象地说明图像卷积的原理了:
图像卷积

本文在此将OpenCV中提供的各种图像卷积和滤波操作记录一下。

1. 通用滤波方法

1.1 函数简述和原型

该函数是通用的滤波方法,可以自定义滤波器/卷积核对图像进行滤波操作。常用的滤波器/卷积核可以参考我之前写的一篇博客常用卷积核小结。官方文档:https://docs.opencv.org/3.4.2/d4/d86/group__imgproc__filter.html#ga27c049795ce870216ddfb366086b5a04

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

1.2 参数

  • src:要进行滤波操作的图像数据;
  • ddepth:int类型,输出图像的数据类型,往往使用-1,表示输出图像数据类型与输入图像一致;
  • kernel:ndarray类型,其中数据类型为float32,表示进行滤波操作的滤波器;
  • anchor:一个二元组,滤波器中的锚点位置,默认值为(-1, -1),表示以滤波器中心为锚点,往往不做修改;
  • delta:double类型,默认值为0,滤波后再加上该值为最终输出值;
  • borderType:边界外推类型,默认值为cv2.BORDER_DEFAULT,实质上就是镜像边界,常用的值还有:
    • cv2.BORDER_CONSTANT:使用常数填充边界;
    • cv2.BORDER_REPLICATE:复制填充边界;
    • cv2.BORDER_REFLECT:镜像边界

1.3 返回值

  • dst:滤波操作后的图像数据;

1.4 用法举例

img = cv2.imread(r'.\image.jpg', cv2.IMREAD_COLOR)

# 使用3*3的平均滤波器对图像进行滤波操作
kernel = np.zeros((3, 3), dtype=np.float32)
kernel.fill(1/9)
unifFilter = cv2.filter2D(img, -1, kernel, borderType=cv2.BORDER_DEFAULT)
cv2.imshow('unifFilter', unifFilter)

2. 低通滤波操作

低通表示仅允许图像中低频部分(即图片中变化较平缓的部分)通过。因此对图像进行低通操作,往往是为了模糊图像或消除噪点。OpenCV中提供了均值滤波、高通滤波,以及非线性的中值滤波和双边滤波。其中非线性的滤波方法,简单理解就是不方便用数学公式表示,速度相对较慢。下面依次对这些方法进行介绍。

2.1 均值滤波

2.1.1 函数简述和原型

均值滤波从名称可以看出是为了计算图像局部的平均值。其卷积核中每个元素值一致,均为卷积核元素个数的倒数,结果即为滤波器范围内像素的平均值。如 3 × 3 3\times3 3×3的均值滤波器如下:
[ 1 / 9 1 / 9 1 / 9 1 / 9 1 / 9 1 / 9 1 / 9 1 / 9 1 / 9 ] \begin{bmatrix} 1/9 & 1/9 & 1/9 \\ 1/9 & 1/9 & 1/9 \\ 1/9 & 1/9 & 1/9 \end{bmatrix} 1/91/91/91/91/91/91/91/91/9

OpenCV提供了两个函数来进行均值滤波,boxFilterblur多了ddepthnormalize参数。ddepth=-1normalize=True时,两个函数作用一致。官方文档:https://docs.opencv.org/3.4.2/d4/d86/group__imgproc__filter.html#ga8c45db9afe636703801b0b2e440fce37

dst = cv2.blur(src, ksize[, anchor[, borderType]])
dst = cv2.boxFilter(src, ddepth, ksize[, anchor[, normalize[, borderType]]])

2.1.2 参数

  • src:要进行滤波操作的图像数据;
  • ddepth:int类型,输出图像的数据类型,往往使用-1,表示输出图像数据类型与输入图像一致;
  • ksize:一个二元组,分别为均值卷积核的行数和列数,往往行列数均为奇数,以方便中心定位。核心越大,对图像的模糊/平滑效果越强;
  • anchor:一个二元组,滤波器中的锚点位置,默认值为(-1, -1),表示以滤波器中心为锚点,往往不做修改;
  • normalize:bool类型,默认值为True,是否标准化卷积核,即是否将卷积核中每个元素除以总数,False时卷积核中所有元素为1,此时也不再是均值滤波;
  • borderType:边界外推类型,默认值为cv2.BORDER_DEFAULT,实质上就是镜像边界,常用的值还有:
    • cv2.BORDER_CONSTANT:使用常数填充边界;
    • cv2.BORDER_REPLICATE:复制填充边界;
    • cv2.BORDER_REFLECT:镜像边界

2.1.3 返回值

  • dst:均值滤波后的图像数据;

2.1.4 用法举例

img = cv2.imread(r'.\image.jpg', cv2.IMREAD_COLOR)

# 使用5*5的均值滤波器对图像进行均值滤波
uni1 = cv2.boxFilter(img, -1, (5, 5))
uni2 = cv2.blur(img, (5, 5))
cv2.imshow('uni1', uni1)
cv2.imshow('uni2', uni2)

2.2 高斯滤波

2.2.1 函数简述和原型

高斯滤波与均值滤波类似,同样是计算局部区域的均值,只不过计算的是加权平均值。其卷积核中的元素就代表该区域内各个像素的权重,且权重值在x方向和y方向上均服从高斯分布。官方文档:https://docs.opencv.org/3.4.2/d4/d86/group__imgproc__filter.html#gaabe8c836e97159a9193fb0b11ac52cf1

dst = cv2.GaussianBlur(src, ksize, sigmaX[, sigmaY[, borderType]])

2.2.2 参数

  • src:要进行滤波操作的图像数据;
  • ksize:一个二元组,分别为高斯卷积核的行数和列数,行数和列数必须为奇数。核心越大,对图像的模糊/平滑效果越强;
  • sigmaX:double类型,x方向上高斯分布的标准差,标准差越大,模糊程度越大;
  • sigmaY:double类型,y方向上高斯分布的标准差,默认值为0,表示与x方向上标准差一致;
  • borderType:边界外推类型,默认值为cv2.BORDER_DEFAULT,实质上就是镜像边界,常用的值还有:
    • cv2.BORDER_CONSTANT:使用常数填充边界;
    • cv2.BORDER_REPLICATE:复制填充边界;
    • cv2.BORDER_REFLECT:镜像边界

2.2.3 返回值

  • dst:高斯滤波后的图像数据;

2.2.4 用法举例

img = cv2.imread(r'.\image.jpg', cv2.IMREAD_COLOR)

# 使用5*5, x和y方向上标准差均为1的高斯滤波器对图像进行滤波
gaussian = cv2.GaussianBlur(img, (5, 5), 1)
cv2.imshow('GaussianBlur', gaussian)

2.3 中值滤波

2.3.1 函数简述和原型

从其名称就可以看出,中值滤波计算的是局部区域内所有像素的中值。中值滤波一样起到平滑模糊图像的效果,更加适用于去除斑点状的噪声,因此往往在照片处理中实现“磨皮”功能。官方文档:https://docs.opencv.org/3.4.2/d4/d86/group__imgproc__filter.html#ga564869aa33e58769b4469101aac458f9

dst = cv2.medianBlur(src, ksize)

2.3.2 参数

  • src:要进行滤波操作的图像数据;
  • ksize:int类型,必须为大于1的奇数,表示中值滤波考虑的局部范围。如ksize=3,表示滤波器每次在 3 × 3 3\times3 3×3的范围内计算中值作为中心点的输出。同样核心越大,对图像的模糊/平滑效果越强;

2.3.3 返回值

  • dst:中值滤波后的图像数据;

2.3.4 用法举例

img = cv2.imread(r'.\image.jpg', cv2.IMREAD_COLOR)

# 在5*5的局部范围对图像进行中值滤波
med = cv2.medianBlur(img, 5)
cv2.imshow('median', med)

2.4 双边滤波

2.4.1 函数简述和原型

上面的几种低通滤波方式都会导致图像的模糊,其中包括模糊了图像中物体的边缘,也就是说物体的边缘信息会适当丢失。而双边滤波则可以在消除噪声、模糊图片的同时,尽可能地保留图像中的边缘信息(即尽可能让物体边缘保持锐利)。双边滤波是一种非线性方法,同时考虑了图像在空间上和色彩上的相似度,其数学原理较为复杂,可以参考这篇博客,在此暂不讨论。官方文档:https://docs.opencv.org/3.4.2/d4/d86/group__imgproc__filter.html#ga9d7064d478c95d60003cf839430737ed

dst = cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace[, borderType])

2.4.2 参数

  • src:要进行滤波操作的图像数据;
  • d:int类型,表示滤波器的大小。该值越大,对图像的模糊/平滑效果越强,但速度也就越慢。官方建议实时运算将该值设置在5以内,线下应用设置d=9即可。为负数时则会自动根据sigmaSpace自动计算;
  • sigmaColor:doule类型,在色彩空间内的方差;
  • sigmaSpace:doule类型,在位置空间内的方差;往往将两个方差的值设置一致,值越大(尤其在150以上),模糊的效果越强,图片越显得“卡通化”;
  • borderType:边界外推类型,默认值为cv2.BORDER_DEFAULT,实质上就是镜像边界,常用的值还有:
    • cv2.BORDER_CONSTANT:使用常数填充边界;
    • cv2.BORDER_REPLICATE:复制填充边界;
    • cv2.BORDER_REFLECT:镜像边界

2.4.3 返回值

  • dst:双边滤波后的图像数据;

2.4.4 用法举例

img = cv2.imread(r'.\image.jpg', cv2.IMREAD_COLOR)

# 在5*5的局部范围对图像进行双边滤波
# 色彩空间和位置空间上的方差均为200
bilateral = cv2.bilateralFilter(img, 5, 200, 200)
cv2.imshow('bilateral', bilateral)

2.5 实例展示

对同一张图片分别使用均值滤波、高斯滤波、中值滤波、双边滤波四种低通滤波器,进行比较:
OpenCVLPF

3. 图像梯度计算

一幅图像往往可以看做是一个二元变量函数。由微积分的知识可知,函数一阶导数的极值点、二阶导数过零点的位置往往对应着原函数变化最快的地方。这些变化最快的地方体现在图像中即为图像的高频部分,往往对应物体的边缘。因此有各种算子来近似计算图像的一阶和二阶梯度(Gradient),这些算子本质上就是各种卷积核,计算梯度的过程也都是卷积/滤波操作。

OpenCV中提供了Sobel算子、Scharr算子来计算图像的一阶梯度,Laplacian算子来计算图像的二阶梯度。这些计算后的梯度实际上往往就是对图像高频特征的提取,往往用于对边缘的检测。

3.1 Sobel算子

3.1.1 函数简述和原型

Sobel算子是最常用的计算图像差分来近似一阶导数的卷积核。其中 3 × 3 3\times3 3×3的计算x方向(水平方向)上的Sobel算子如下:
[ − 1 0 1 − 2 0 2 − 1 0 1 ] \begin{bmatrix} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \end{bmatrix} 121000121

计算水平方向上的差分,对应检测的是垂直方向上的边缘;反之垂直方向上的差分对应检测水平边缘。官方文档:https://docs.opencv.org/3.4.2/d4/d86/group__imgproc__filter.html#gacea54f142e81b6758cb6f375ce782c8d

dst = cv2.Sobel(src, ddepth, dx, dy[, ksize[, scale[, delta[, borderType]]]])

3.1.2 参数

  • src:应用Sobel算子的图像数据,可以为多通道彩色图像,但实际应用中往往使用单通道灰度图像作为输入检测物体边缘;
  • ddepth:int类型,输出图像的数据类型,往往使用-1,表示输出图像数据类型与输入图像一致;
  • dx:int类型,表示在x方向上计算差分的阶次,往往使用Sobel计算一阶差分;
  • dy:int类型,表示在y方向上计算差分的阶次,往往使用Sobel计算一阶差分;
  • ksize:int类型,必须为大于1的奇数,默认值为3,表示Sobel卷积核的大小。该值越大,对越细的边缘产生响应,3往往足够使用;
  • scale:double类型,默认值为1,对卷积计算的结果进行缩放的倍数;
  • delta:double类型,默认值为0,卷积计算结果会再加上该值作为最后的输出值;
  • borderType:边界外推类型,默认值为cv2.BORDER_DEFAULT,实质上就是镜像边界,常用的值还有:
    • cv2.BORDER_CONSTANT:使用常数填充边界;
    • cv2.BORDER_REPLICATE:复制填充边界;
    • cv2.BORDER_REFLECT:镜像边界

3.1.3 返回值

  • dst:Sobel算子运算后的图像数据,大小与通道数与输入图像一致;

3.1.4 用法举例

img = cv2.imread(r'.\image.jpg', cv2.IMREAD_GRAYSCALE)

# 使用Sobel算子计算x方向上1阶导数,即提取垂直方向边缘
sobelx = cv2.Sobel(img, ddepth=-1, dx=1, dy=0, ksize=3)
# 使用Sobel算子计算y方向上1阶导数,即提取水平方向边缘
sobely = cv2.Sobel(img, ddepth=-1, dx=0, dy=1, ksize=3)
cv2.imshow('Sobel', cv2.hconcat((img, sobelx, sobely)))

3.2 Scharr算子

3.2.1 函数简述和原型

Scharr算子是Sobel算子的改进版,同样是计算图像差分来近似一阶导数的卷积核。大小固定为 3 × 3 3\times3 3×3,计算x方向(水平方向)上的Scharr算子如下:
[ − 3 0 3 − 10 0 10 − 3 0 3 ] \begin{bmatrix} -3 & 0 & 3 \\ -10 & 0 & 10 \\ -3 & 0 & 3 \end{bmatrix} 31030003103

相比Sobel算子,Scharr算子可以检测更微弱的边缘。官方文档:https://docs.opencv.org/3.4.2/d4/d86/group__imgproc__filter.html#gaa13106761eedf14798f37aa2d60404c9

dst = cv2.Scharr(src, ddepth, dx, dy[, scale[, delta[, borderType]]])

3.2.2 参数

  • src:应用Scharr算子的图像数据,仅可使用单通道灰度图像;
  • ddepth:int类型,输出图像的数据类型,往往使用-1,表示输出图像数据类型与输入图像一致;
  • dx:int类型,表示在x方向上计算差分的阶次,往往使用Scharr计算一阶差分;
  • dy:int类型,表示在y方向上计算差分的阶次,往往使用Scharr计算一阶差分;
  • scale:double类型,默认值为1,对卷积计算的结果进行缩放的倍数;
  • delta:double类型,默认值为0,卷积计算结果会再加上该值作为最后的输出值;
  • borderType:边界外推类型,默认值为cv2.BORDER_DEFAULT,实质上就是镜像边界,常用的值还有:
    • cv2.BORDER_CONSTANT:使用常数填充边界;
    • cv2.BORDER_REPLICATE:复制填充边界;
    • cv2.BORDER_REFLECT:镜像边界

3.2.3 返回值

  • dst:Scharr算子运算后的图像数据,大小与通道数与输入图像一致;

3.2.4 用法举例

img = cv2.imread(r'.\image.jpg', cv2.IMREAD_GRAYSCALE)

# 使用Scharr算子计算x方向上1阶导数,即提取垂直方向边缘
scharrx = cv2.Scharr(img, ddepth=-1, dx=1, dy=0)
# 使用Scharr算子计算y方向上1阶导数,即提取水平方向边缘
scharry = cv2.Scharr(img, ddepth=-1, dx=0, dy=1)
cv2.imshow('Scharr', cv2.hconcat((img, scharrx, scharry)))

3.3 一阶梯度综合计算

3.3.1 函数简述和原型

该函数同时分别计算x方向和y方向上的一阶梯度,本质上就是应用Sobel算子进行的计算,只是为了方便使用。官方文档:https://docs.opencv.org/3.4.2/d4/d86/group__imgproc__filter.html#ga405d03b20c782b65a4daf54d233239a2

dx, dy = cv2.spatialGradient(src[, ksize[, borderType]])

3.3.2 参数

  • src:计算一阶梯度的图像数据,仅可使用单通道灰度图像;
  • ksize:int类型,默认值为3,只能等于3,感觉该参数多余;
  • borderType:边界外推类型,默认值为cv2.BORDER_DEFAULT,实质上就是镜像边界,常用的值还有:
    • cv2.BORDER_CONSTANT:使用常数填充边界;
    • cv2.BORDER_REPLICATE:复制填充边界;
    • cv2.BORDER_REFLECT:镜像边界

3.3.3 返回值

  • dx:使用 3 × 3 3\times3 3×3的Sobel算子计算出的x方向上的一阶梯度,注意是数据类型为int16的ndarray;
  • dy:使用 3 × 3 3\times3 3×3的Sobel算子计算出的y方向上的一阶梯度,注意是数据类型为int16的ndarray;

3.3.4 用法举例

img = cv2.imread(r'.\image.jpg', cv2.IMREAD_GRAYSCALE)

# 同时计算x方向和y方向上一阶梯度
dx, dy = cv2.spatialGradient(img, ksize=3)
# 由于返回值是int16类型,故需要转型后展示
dx = np.uint8(np.clip(dx, 0, 255))
dy = np.uint8(np.clip(dy, 0, 255))
cv2.imshow('dx', dx)
cv2.imshow('dy', dy)

3.4 Laplacian算子

3.4.1 函数简述和原型

Laplacian算子主要用于计算图像的二阶梯度。上述几种算子均计算图像的一阶梯度,往往还需要考虑方向。而Laplacian算子计算图像二阶梯度时则具有旋转不变性,即不需要考虑方向,调用时可以检测出各个方向上的边缘。其中 3 × 3 3\times3 3×3的Laplacian算子如下:
[ 0 1 0 1 − 4 1 0 1 0 ] \begin{bmatrix} 0 & 1 & 0 \\ 1 & -4 & 1 \\ 0 & 1 & 0 \end{bmatrix} 010141010

官方文档:https://docs.opencv.org/3.4.2/d4/d86/group__imgproc__filter.html#gad78703e4c8fe703d479c1860d76429e6

dst = cv2.Laplacian(src, ddepth[, ksize[, scale[, delta[, borderType]]]])

3.4.2 参数

  • src:应用Laplacian算子的图像数据,可以为多通道彩色图像,但实际应用中往往使用单通道灰度图像作为输入检测物体边缘;
  • ddepth:int类型,输出图像的数据类型,往往使用-1,表示输出图像数据类型与输入图像一致;
  • ksize:int类型,必须为正奇数,默认值为1,表示使用 3 × 3 3\times3 3×3的Laplacian算子。该值越大,对越细的边缘产生响应;
  • scale:double类型,默认值为1,对卷积计算的结果进行缩放的倍数;
  • delta:double类型,默认值为0,卷积计算结果会再加上该值作为最后的输出值;
  • borderType:边界外推类型,默认值为cv2.BORDER_DEFAULT,实质上就是镜像边界,常用的值还有:
    • cv2.BORDER_CONSTANT:使用常数填充边界;
    • cv2.BORDER_REPLICATE:复制填充边界;
    • cv2.BORDER_REFLECT:镜像边界

3.4.3 返回值

  • dst:Laplacian算子运算后的图像数据,大小与通道数与输入图像一致;

3.4.4 用法举例

img = cv2.imread(r'.\image.jpg', cv2.IMREAD_GRAYSCALE)

# 使用3*3的Laplacian算子计算图像的二阶梯度
lap = cv2.Laplacian(img, ddepth=-1, ksize=3)
cv2.imshow('Laplacian', lap)

3.5 实例展示

对同一张图片分别使用各种算子计算一阶梯度和二阶梯度,比较各自提取边缘的效果:
Gradients

  • 4
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值