图像卷积

这几天在进行其它运算时突然发觉自己对于卷积的概念和运算有一些陌生,重新复习一下。


目录

  1. 图像卷积概念
  2. 图像卷积数学公式
  3. 图像卷积注意事项
  4. 图像卷积算法实现

图像卷积概念

参考:

Convolution

Kernel (image processing)

图像卷积操作(convolution),或称为核操作(kernel),是进行图像处理的一种常用手段,

图像卷积操作的目的是利用像素点和其邻域像素之前的空间关系,通过加权求和的操作,实现模糊(blurring),锐化(sharpening),边缘检测(edge detection)等功能。

图像卷积的计算过程就是卷积核按步长对图像局部像素块进行加权求和的过程。

卷积核实质上是一个固定大小的权重数组,该数组中的锚点通常位于中心。

卷积核大小

通常情况下,选取卷积核大小为1x1,3x3,5x5,7x7

取奇数大小的目的是为了设置卷积核中心为锚点,方便卷积核和图像的对齐处理

设置卷积核大小对称的目的是为了在空间域中充分利用像素点和其领域像素间的关系。当然这不是必须的,如果需要针对某一轴进行处理,可以设置1x33x1大小。


图像卷积数学公式

二维离散卷积公式如下:

h [ x , y ] = f [ x , y ] ∗ g [ x , y ] = ∑ n 1 = − ∞ ∞ ∑ n 2 = − ∞ ∞ f ( n 1 , n 2 ) ⋅ g ( x − n 1 , y − n 2 ) h[x,y]=f[x,y]\ast g[x,y]=\sum_{n_{1}=-\infty}^{\infty}\sum_{n_{2}=-\infty}^{\infty}f(n_1, n_2)\cdot g(x-n_{1},y-n_{2}) h[x,y]=f[x,y]g[x,y]=n1=n2=f(n1,n2)g(xn1,yn2)

图像卷积通常使用这个公式,其中 g [ x , y ] g[x,y] g[x,y]为卷积核,符号 ∗ \ast 表示卷积操作

图形化表示

以一维离散卷积公式为例:

y ( t ) = ( f ∗ g ) ( t ) = ∑ τ = − ∞ ∞ f ( τ ) g ( t − τ ) d τ y(t)=(f\ast g)(t)=\sum_{\tau =-\infty}^{\infty}f(\tau )g(t-\tau )d\tau y(t)=(fg)(t)=τ=f(τ)g(tτ)dτ

其图形化公式如下:

图像卷积_第1张图片

  • 首先将函数 g ( τ ) g(\tau) g(τ)反射为 g ( − τ ) g(-\tau) g(τ),相当于 g ( τ ) g(\tau) g(τ)沿 y y y轴翻转
  • 再对函数 g ( − τ ) g(-\tau) g(τ)添加一个时间偏移量 t t t,它允许函数 g ( t − τ ) g(t-\tau) g(tτ)沿着 τ \tau τ轴移动
  • 变量 t t t每增加1,表示函数 g ( t − τ ) g(t-\tau) g(tτ)向左移动一步
  • 计算t遍历 − ∞ -\infty ∞ \infty 的过程中,函数 f ( τ ) f(\tau) f(τ) g ( t − τ ) g(t-\tau) g(tτ)的重叠面积

举例

函数 f = [ 1 , 2 , 3 , 4 ] f=[1, 2, 3, 4] f=[1,2,3,4],函数 g = [ 1 , 3 , 2 ] g=[1, 3, 2] g=[1,3,2]

将函数 g g g逆转: g ( τ ) ⇒ g ( − τ ) g(\tau)\Rightarrow g(-\tau) g(τ)g(τ),值变为 [ 2 , 3 , 1 ] [2, 3, 1] [2,3,1]

计算过程如下:

h ( 0 ) = [ 1 ] ⋅ [ 1 ] = 1 ⋅ 1 = 1 h(0)=[1]\cdot [1]=1\cdot 1=1 h(0)=[1][1]=11=1
h ( 1 ) = [ 1 , 2 ] ⋅ [ 3 , 1 ] = 1 ⋅ 3 + 2 ⋅ 1 = 3 + 2 = 5 h(1)=[1,2]\cdot [3,1]=1\cdot 3+2\cdot 1=3+2=5 h(1)=[1,2][3,1]=13+21=3+2=5
h ( 2 ) = [ 1 , 2 , 3 ] ⋅ [ 2 , 3 , 1 ] = 1 ⋅ 2 + 2 ⋅ 3 + 3 ⋅ 1 = 2 + 6 + 3 = 11 h(2)=[1,2,3]\cdot [2,3,1]=1\cdot 2 +2\cdot 3+3\cdot 1=2+6+3=11 h(2)=[1,2,3][2,3,1]=12+23+31=2+6+3=11
h ( 3 ) = [ 2 , 3 , 4 ] ⋅ [ 2 , 3 , 1 ] = 2 ⋅ 2 + 3 ⋅ 3 + 4 ⋅ 1 = 4 + 9 + 4 = 17 h(3)=[2,3,4]\cdot [2,3,1]=2\cdot 2+3\cdot 3+4\cdot 1=4+9+4=17 h(3)=[2,3,4][2,3,1]=22+33+41=4+9+4=17
h ( 4 ) = [ 3 , 4 ] ⋅ [ 2 , 3 ] = 3 ⋅ 2 + 4 ⋅ 3 = 6 + 12 = 18 h(4)=[3,4]\cdot [2,3]=3\cdot 2+4\cdot 3=6+12=18 h(4)=[3,4][2,3]=32+43=6+12=18
h ( 5 ) = [ 4 ] ⋅ [ 2 ] = 4 ⋅ 2 = 8 h(5)=[4]\cdot [2]=4\cdot 2=8 h(5)=[4][2]=42=8
h ( x ) = [ 1 , 5 , 11 , 17 , 18 , 8 ] h(x)=[1, 5, 11, 17, 18, 8] h(x)=[1,5,11,17,18,8]

以此类推可知二维离散卷积的计算过程,先对角翻转卷积核,在逐步向两个正方向移动,计算重叠面积

  • flip the mask (horizontally and vertically) only once(水平和垂直翻转掩模一次)
  • slide the mask onto the image(在图像上滑动掩模)
  • multiply the corresponding elements and then add them(将相应的元素相乘,然后求和)
  • repeat this procedure until all values of the image has been calculated(重复这一过程,直到所有图像值均已被计算)

多说一句,关于信号与系统中的LTIlinear time-invariant systems,线性时不变系统)和LSIlinear shift invariant system,线性位移不变系统)的不变性一直没太理解,图形化理解就是信号(函数)可以随着时间/空间移动而不改变它的原先的形状,就像卷积核一样。

卷积核为啥要翻转?

参考:

在定义卷积时为什么要对其中一个函数进行翻转?

如何通俗易懂地解释卷积?

LTILSI中,信号在时间和空间中移动不改变其特性,不断有信号随时间移动和系统产生响应,某一时刻的输出(即卷积输出)不仅包括当前信号的响应,还有之前信号的残留,所以是累加的,转换卷积核是为了计算这一过程。

如何翻转?

参考:卷积核翻转方法

在计算之前需要对卷积核进行 18 0 ∘ 180^{^{\circ}} 180翻转

[ 1 2 3 4 5 6 7 8 9 ] 18 0 ∘ ⇒ [ 9 8 7 6 5 4 3 3 1 ] \begin{bmatrix} 1& 2& 3\\ 4& 5& 6\\ 7& 8& 9 \end{bmatrix}\frac{180^{^{\circ}}}{\Rightarrow} \begin{bmatrix} 9& 8& 7\\ 6& 5& 4\\ 3& 3& 1 \end{bmatrix} 147258369180963853741

可以先通过水平翻转,再进行垂直翻转,就能实现 18 0 ∘ 180^{^{\circ}} 180翻转

[ 1 2 3 4 5 6 7 8 9 ] ⇒ [ 3 2 1 6 5 4 9 8 7 ] ⇒ [ 9 8 7 6 5 4 3 2 1 ] \begin{bmatrix} 1& 2& 3\\ 4& 5& 6\\ 7& 8& 9 \end{bmatrix}\Rightarrow \begin{bmatrix} 3& 2& 1\\ 6& 5& 4\\ 9& 8& 7 \end{bmatrix}\Rightarrow \begin{bmatrix} 9& 8& 7\\ 6& 5& 4\\ 3& 2& 1 \end{bmatrix} 147258369369258147963852741

卷积结果大小

由一维离散卷积公式可知,设函数 f f f大小为 A A A,函数 g g g大小为 B B B,那么卷积结果大小为 A + B − 1 A+B-1 A+B1

对于二维离散卷积公式,设函数 f f f大小为 [ A , B ] [A,B] [A,B],函数 g g g大小为 [ C , D ] [C,D] [C,D],那么结果卷积大小为 [ ( A + C − 1 ) , ( B + D − 1 ) ] [(A+C-1), (B+D-1)] [(A+C1),(B+D1)]

对于图像卷积而言,输出图像与输入图像大小一致,因此需要舍弃卷积核锚点(图像中心)不与图像像素点重叠时的计算

边界填充方式

对图像卷积而言,当卷积核在图像边界计算时,会发生超出图像边界的情况,有几种方式来填充:

  1. 扩展(extend):扩展最近邻的像素点,即扩展图像边界
  2. 环绕(wrap):假设图像是首尾相接的,即使用图像相反方向的像素值
  3. 镜像(mirror):超出边界多少个像素,就向图像内部读取对应位置的像素
  4. 固定值:超出图像边界的像素取固定值,默认为0

卷积操作和傅里叶变换

参考:Convolution Theorem

  • 在空间域执行卷积操作,相当于在频率域执行滤波操作(乘法操作)
  • 在频率域执行卷积操作,相当于在空间域执行滤波操作(乘法操作)

公式如下:

f ∗ g = D F T ( f ) ⋅ D F T ( g ) f\ast g=DFT(f)\cdot DFT(g) fg=DFT(f)DFT(g)
D F T ( f ) ∗ D F T ( g ) = f ⋅ g DFT(f)\ast DFT(g)=f\cdot g DFT(f)DFT(g)=fg


图像卷积注意事项

在进行卷积操作时,需要注意两点

卷积核归一化

卷积核的大小和值可以根据要求定义,但通常会将整个卷积核进行归一化操作,其目的是为了保证修改后结果图像的平均元素值和原始图像平均元素值一样。

因为卷积操作满足齐次性,所以可以卷积计算完成后再除以整个卷积核的值。

数值精度

图像数值类型通常为uint8,在进行卷积操作时很容易造成数值溢出,所以在进行操作之前可以先转换成更高精度的数值类型


图像卷积算法实现

参考:

Signal processing (scipy.signal) Convolution

Multi-dimensional image processing (scipy.ndimage)

Python有多个包(Numpy/Scipy/OpenCV)提供了卷积操作

Numpy

numpy.convolve

Numpy包提供了方法convolve,用于计算一维线性卷积

它提供了3种模式:

  • full:默认方式,计算所有重叠的面积
  • same:返回和最大数组一样长度的数组。从卷积核锚点位置开始计算
  • valid:仅计算完全重叠的面积

实现结果如下:

import numpy as np

a = np.array([1, 2, 3, 4])
v = np.array([1, 3, 2])
full = np.convolve(a, v)
same = np.convolve(a, v, 'same')
valid = np.convolve(a, v, 'valid')

[ 1  5 11 17 18  8]
[ 5 11 17 18]
[11 17]

Scipy.signal

scipy.signal包提供了好几种方法来计算卷积:

convolve(in1, in2[, mode, method]) 
correlate(in1, in2[, mode, method]) 	
fftconvolve(in1, in2[, mode]) 
convolve2d(in1, in2[, mode, boundary, fillvalue]) 	
correlate2d(in1, in2[, mode, boundary, …]) 	
sepfir2d(input, hrow, hcol) 
choose_conv_method(in1, in2[, mode, measure])

signal.convolve2d()

参考:

scipy.signal.convolve2d

convolve2d(in1, in2, mode='full', boundary='fill', fillvalue=0)

in1in2的维数大小相同

参数mode决定了返回卷积结果的大小:

  • full:输出全离散线性卷积(默认)
  • valid:输出不依赖于零填充的卷积结果
  • same:输出大小和in1一样,卷积核锚点对齐图像处理的结果

它额外提供了边界填充模式boundary和填充值fillvalue

3种填充模式:

  1. fill:默认方式,使用fillvalue的值进行填充
  2. wrap:环形填充
  3. symm:镜像模式

signal.convolve()

参考:scipy.signal.convolve

convolve(in1, in2, mode='full', method='auto')

convolve用于计算N维数组的卷积,除了可以设定模式外,还可以选择计算方法:

  1. direct:直接按照卷积的定义进行加权求和计算
  2. fft:使用快速傅里叶变换(fft)进行卷积操作
  3. auto:默认方式,估计计算时间,选择直接计算还是快速傅里叶方法

Signal.ndimage

signal.ndiamge包提供了多个卷积函数:

convolve(input, weights[, output, mode, …]) 
convolve1d(input, weights[, axis, output, …])
correlate(input, weights[, output, mode, …]) 
correlate1d(input, weights[, axis, output, …])

OpenCV

参考:

Making your own linear filters!

filter2D()

BorderTypes

Depth combinations

OpenCV提供了一个2维滤波器filter2D

def filter2D(src, ddepth, kernel, dst=None, anchor=None, delta=None, borderType=None)

src:输入图像
dst:相同大小和通道的结果图像
ddepth:结果图像深度,看Depth combinations
kernel:卷积核,一个单通道浮点矩阵
anchor:卷积核锚点位置,默认值(-1,-1),表示在核中心
delta:额外值
borderType:边界填充像素方式,参考BorderTypes

比如实现一个Sobel算子,计算x轴梯度

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt

def realize_sobel():
    gray = cv.imread("lena.jpg", 0)
    ddepth = cv.CV_16S

    kernel = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]], dtype=np.float)
    res_x = cv.filter2D(gray, ddepth, kernel)
    abs_res_x = cv.convertScaleAbs(res_x)

    sobel_x = cv.Sobel(gray, ddepth, 1, 0, ksize=3)
    abs_sobel_x = cv.convertScaleAbs(sobel_x)

    plt.figure(figsize=(10, 5))  # 设置窗口大小
    plt.suptitle('sobel')  # 图片名称
    plt.subplot(1, 2, 1)
    plt.title('filter2D_x')
    plt.imshow(abs_res_x, cmap='gray'), plt.axis('off')
    plt.subplot(1, 2, 2)
    plt.title('sobel_x')
    plt.imshow(abs_sobel_x, cmap='gray'), plt.axis('off')
    plt.savefig('./sobel_x.png')  # 保存图像
    plt.show()

图像卷积_第2张图片

你可能感兴趣的:(opencv,python,图像,图像,卷积)