欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

图像卷积-空间卷积

程序员文章站 2022-07-06 11:07:16
...

空间卷积-Spatial convolution

译自Spatial convolution

卷积是对f和g两个函数的运算,产生了第三个函数,可以理解为f的一个修改(“过滤”)版本。 在这个解释中,我们称之为过滤器。 如果f定义在像x这样的空间变量而不是像t这样的时间变量上,我们称之为空间卷积运算。 卷积是执行平滑或锐化的任何物理设备或计算过程的核心。 应用于像图像这样的二维函数,对于边缘寻找,特征检测,运动检测,图像匹配以及无数其他任务也是有用的。 形式上,对于连续变量x的函数f(x)和g(x),卷积定义为:

图像卷积-空间卷积

其中*表示卷积,并且意味着普通的乘法。 对于离散变量x的函数,即数组数组,其定义如下:

图像卷积-空间卷积

最后,对于两个变量x和y(例如图像)的函数,这些定义变成:

图像卷积-空间卷积

以及

图像卷积-空间卷积

在数码摄影中,镜头产生的图像是一个连续的函数f(x,y)。在传感器前放置一个抗混叠滤波器,用平滑滤波器g(x,y)来对图像进行卷积。就是上面的第三个方程式。一旦图像被传感器记录并存储在文件中,将文件加载到Photoshop中并使用滤波器g [x,y]对其进行锐化就是第四个方程式。

尽管定义很简单,但卷积是一个难以获得直觉的概念,并且通过对特定函数应用特定的过滤器而获得的效果并不总是显而易见的。在这个小程序中,我们探讨连续一维函数(第一个方程)和离散二维函数(第四个方程)的卷积。

图像卷积-空间卷积

Applet参见Applet链接

一维函数的卷积

小程序的左侧是一维函数(“信号”)。这是f。可以借助该功能进行更改,但暂时保留原有功能。在此之下是一维滤镜菜单。这是g。你可以选择“custom/自定义”,也可以使用过滤功能,选择后离开。底部是由g卷积f的结果。点击几个过滤器。请注意,“big rect”比“rect”模糊不清,但是很多地方会产生kinks(尖锐的转折点)。还要注意,“gaussian”模糊不及“big rect”,但不会产生kinks(尖锐的转折点)。

这两个函数(f和g)都被描绘成好像它们是连续变量x的函数,所以看起来这个可视化显示了连续函数的卷积(上面的第一个方程)。在实践中,这两个函数被精细地采样并用一维数组表示。这些数字在绘制时使用线连接,呈现出连续的功能。实际上在小程序脚本中执行的卷积是两个离散函数(上面的第二个方程)。

无论我们将卷积视为连续的还是离散的,其解释都是相同的:对于输出函数中的每个位置x,我们将滤波函数g向左或向右移动直到它在该位置居中,我们从左到右翻转,我们将f上的每一点乘以移位的g上的相应点,并且将这些乘积相加(或集成)在一起。从左到右的翻转是因为不明原因,卷积方程定义为g [x-k],而不是g [x + k](以第二个方程为例)。

另一种思考卷积的方法

如果这个过程有点困难,可以用下面的方法来描述它,这个过程可能更容易看出来:在输出函数的每个位置x,我们放置一个过滤器g的副本,在该位置左右,从左到右翻转,并根据该位置处的信号f的值放大或缩小。放下这些副本之后,如果我们把它们全部加在一起,我们就可以得到正确的答案!

要看到这种理解卷积的另一种方法,点击“animate”,然后点击“big rect”。动画从原始信号f开始,然后将过滤器g的副本放置在沿着f的位置,根据该位置处的f的高度将它们垂直拉伸,然后将这些副本相加在一起以制作厚输出曲线。尽管动画只显示了几十个过滤器的副本,但实际上每个位置x需要一个副本。另外,对于这个过程来说,复制的总和必须除以过滤函数下的区域,称为标准化。否则,输出将比输入更高或更低,而不是简单地被平滑或锐化。对于除“custom/自定义”之外的所有过滤器,在绘制厚输出曲线之前,会为您执行标准化。对于“custom/自定义”过滤器,请参阅下文。

一旦你明白这是如何工作,请尝试“sharpen”或“shift”过滤器。锐化滤波器用它的直接邻居的加权和代替f的每个值,但是将邻居的值减去一点。这些减法的作用是增强原始信号中的特征。在Photoshop中的“Sharpen”过滤器做到这一点;就像视网膜中的某些神经元层。移位滤波器将f的每个值替换为距离右边一定距离的邻居的f值。 (是的,在右边,尽管滤波器的尖峰在左边,请记住,卷积滤波器函数在应用之前从左向右翻转)。

最后,点击“custom/自定义”,然后尝试绘制自己的过滤器。如果过滤器的面积大于或小于1.0,则输出功能将分别向上或向下跳跃。为了避免这种情况,请点击“normalize”,这会使过滤器向上或向下缩放,直到其面积恰好为1.0。顺便说一下,如果你为自定义过滤器的应用设置了动画效果,那么如果你的自定义过滤器在其x = 0位置达到y = 1.0,缩放的副本将仅触碰原始函数上的相应点。无论如何,如果你的过滤器是标准化的,输出功能将是正确的高度。

二维函数卷积

在applet的右侧,我们将这些思想扩展到二维离散函数,特别是普通的摄影图像。最初的2D信号在顶部,2d滤波器位于中间,被描绘成一组数字,输出在底部。单击不同的过滤函数并观察结果。“sharpen”和“edges”之间的唯一区别是中间过滤值从9到8之间的变化。然而,这一变化是至关重要的,正如你所看到的。特别是,“edges”中所有非零滤波值之和为零。因此,原始信号中平滑的位置(如背景),卷积的输出为零(即黑色)。这种“hand shake”类似于长曝光照片中发生的情况,在这种情况下,相机的目标在曝光时从左上方移动到右下方。

最后,单击“identity”,将中间筛选器抽头(有时称为筛选器中的位置)设置为1,其余的设置为0。毫不奇怪,这仅仅是对原始信号的复制。现在点击“自定义”,然后点击单独的抽头,为他们输入新的值。当你进入每一个值,重新计算的卷积。尝试创建平滑滤波或锐化滤波。或从“identity”开始,将中间抽头改为0.5或2。图像缩小或放大了吗?Applet是裁剪输出0(黑色)和255(白色),所以若增加强度,则图像将饱和-就像一个相机曝光的时间太长。试着在左上角和右下角放置1个,把其他的设置为0。会有双像吗?与自定义1D过滤器一样,如果过滤值不等于1,则可能需要按“normalize”。除非你正在寻找边缘,在这种情况下,它们应该等于0。


卷积示意:

图像卷积-空间卷积

演示:

# #!/usr/bin/python
# # coding:utf-8

import cv2
import numpy as np

img = cv2.imread('daibola.jpg', 1)
# # 卷积核
rect = np.array([[0, 0, 0, 0, 0],
                 [0, 0.11, 0.11, 0.11, 0],
                 [0, 0.11, 0.11, 0.11, 0],
                 [0, 0.11, 0.11, 0.11, 0],
                 [0, 0, 0, 0, 0]], np.float32)

bigrect = np.array([[0.04, 0.04, 0.04, 0.04, 0.04],
                    [0.04, 0.04, 0.04, 0.04, 0.04],
                    [0.04, 0.04, 0.04, 0.04, 0.04],
                    [0.04, 0.04, 0.04, 0.04, 0.04],
                    [0.04, 0.04, 0.04, 0.04, 0.04]], np.float32)

gaussian = np.array([[0.01, 0.02, 0.03, 0.02, 0.01],
                     [0.02, 0.06, 0.08, 0.06, 0.02],
                     [0.03, 0.08, 0.11, 0.08, 0.03],
                     [0.02, 0.06, 0.08, 0.06, 0.02],
                     [0.01, 0.02, 0.03, 0.02, 0.01]], np.float32)

sharpen = np.array([[0, 0, 0, 0, 0],
                    [0.02, 0.06, 0.08, 0.06, 0.02],
                    [0.03, 0.08, 0.11, 0.08, 0.03],
                    [0.02, 0.06, 0.08, 0.06, 0.02],
                    [0.01, 0.02, 0.03, 0.02, 0.01]], np.float32)

edges = np.array([[0, 0, 0, 0, 0],
                  [0, 0, -2, 0, 0],
                  [0, -2, 8, -2, 0],
                  [0, 0, -2, 0, 0],
                  [0, 0, 0, 0, 0]], np.float32)

shift = np.array([[0, 0, 0, 0, 0],
                  [0, 0, 0, 0, 0],
                  [1, 0, 0, 0, 0],
                  [0, 0, 0, 0, 0],
                  [0, 0, 0, 0, 0]], np.float32)

handshake = np.array([[0.2, 0, 0, 0, 0],
                      [0, 0.2, 0, 0, 0],
                      [0, 0, 0.2, 0, 0],
                      [0, 0, 0, 0.2, 0],
                      [0, 0, 0, 0, 0.2]], np.float32)


# 该函数实际上计算相关性,而不是卷积.  ddepth:目标图像的所需深度
rectimage = cv2.filter2D(img, -1, rect)
bigrectimage = cv2.filter2D(img, -1, bigrect)
gaussianimage = cv2.filter2D(img, -1, gaussian)
sharpenimage = cv2.filter2D(img, -1, sharpen)
edgesimage = cv2.filter2D(img, -1, edges)
shiftimage = cv2.filter2D(img, -1, shift)
handshakeimage = cv2.filter2D(img, -1, handshake)


cv2.imshow('original', img)
cv2.imshow('rect', rectimage)
cv2.imshow('bigrect', bigrectimage)
cv2.imshow('gaussian', gaussianimage)
cv2.imshow('sharpen', sharpenimage)
cv2.imshow('edges', edgesimage)
cv2.imshow('shift', shiftimage)
cv2.imshow('handshake', handshakeimage)

cv2.imwrite('map/rect.jpg',rectimage)
cv2.imwrite('map/bigrect.jpg',bigrectimage)
cv2.imwrite('map/gaussian.jpg',gaussianimage)
cv2.imwrite('map/sharpen.jpg',sharpenimage)
cv2.imwrite('map/edges.jpg',edgesimage)
cv2.imwrite('map/shift.jpg',shiftimage)
cv2.imwrite('map/handshake.jpg',handshakeimage)

cv2.waitKey(0)

输出:

图像卷积-空间卷积图像卷积-空间卷积

图像卷积-空间卷积图像卷积-空间卷积

图像卷积-空间卷积图像卷积-空间卷积

图像卷积-空间卷积图像卷积-空间卷积

相关标签: 卷积