跳转到内容

理解多点触控/卷积和反卷积

50% developed
来自维基教科书,开放世界开放书籍

卷积概述

[编辑 | 编辑源代码]

数学上,卷积被描述为一个运算符,它接收两个函数 f 和 g,并生成第三个函数,表示 f 和 g 之间的重叠量。卷积在信号分析中被广泛使用,因为卷积运算通常非常便宜,可以并行执行,并且易于调整以适应情况。

在我们的案例中,我们将讨论二维意义上的卷积,其中图像和已知卷积函数(称为 **滤波器**)将被组合以生成一个更好地表示我们正在寻找的数据的图像。由于您应该已经熟悉这些概念,因此这将是对卷积工作原理的简要概述,以及我们感兴趣的特定卷积滤波器。

如果您回忆一下,我们的理论传感器模型将数据作为 8x8 的 8 位数据矩阵输入系统,类似于以下内容

            Horizontal Address 
          0  1  2  3  4  5  6  7   
 V A   0 00 00 00 00 00 00 00 00   
 e d   1 00 00 00 00 00 00 00 00   
 r d   2 00 00 00 00 00 00 00 00   
 t r   3 00 00 00 00 00 00 00 00   
 i e   4 00 00 00 00 00 00 00 00   
 c s   5 00 00 00 00 00 00 00 00   
 a s   6 00 00 00 00 00 00 00 00   
 l     7 00 00 00 00 00 00 00 00 

然而,我们的卷积核尺寸要小得多。对于我们将用于理论模型的算法,3x3 卷积核就足够了。

在实现卷积核时,重要的是要记住,我们正在接受两个函数并生成第三个函数。原始函数可以被丢弃,或者可以返回到系统中用于其他目的,例如降噪。因此,当我们在软件中实现我们的滤波器时,我们将希望生成一个函数,该函数将返回一个与输入缓冲区大小相同的填充缓冲区给用户。

卷积操作应该看起来非常类似于此伪代码

function Convolution ( in_buffer[8][8], out_buffer[8][8], divisor )
{
   define kernel[3][3];

   for (x from 0 to 7)
      for (y from 0 to 7)
         out_buffer[x][y]  = 0; //make sure the buffer is zeroed

         if (x-1 >= 0 && y-1 >= 0)
            out_buffer[x][y] += in_buffer[x-1][y-1] * kernel[0][0];
         if (x-1 >= 0)
            out_buffer[x][y] += in_buffer[x-1][y] * kernel[0][1];
         if (x-1 >= 0 && y+1 <= 7)
            out_buffer[x][y] += in_buffer[x-1][y+1] * kernel[0][2];

         if (y-1 >= 0)
            out_buffer[x][y] += in_buffer[x][y-1] * kernel[1][0];
         //this is our "free" operation, the only one that will always work.
            out_buffer[x][y] += in_buffer[x][y] * kernel[1][1];
         if (y+1 <= 7)
            out_buffer[x][y] += in_buffer[x][y+1] * kernel[1][2];

         if (x+1 <= 7 && y-1 >= 0)
            out_buffer[x][y] += in_buffer[x+1][y-1] * kernel[2][0];
         if (x+1 <= 7)
            out_buffer[x][y] += in_buffer[x+1][y] * kernel[2][1];
         if (x+1 <= 7 && y+1 <= 7)
            out_buffer[x][y] += in_buffer[x+1][y+1] * kernel[2][2];

         if (divisor && divisor > 0)
            out_buffer[x][y] /= divisor;
         else
            out_buffer[x][y] /= 9;

      end for
   end for
}

上述内容可能在您的硬件上进行了优化,因为您通常可以通过确保您的缓冲区足够大来避免分支语句,以捕获可能发生的溢出,而无需进行边界测试,以内存换分支操作。您的算法通常不需要除了常数内核之外的任何临时数据,如果您过去处理过信号处理,您可能已经在工具包中拥有类似的功能。

人们经常问在滤波器边缘(称为 **边缘条件**)应该怎么做。有许多选择,例如选择已知的“背景强度”并将其应用于该元素(在本例中,数据为零,因此不会影响内核操作;这与在本例中截断滤波器完全相同),但是还有许多其他方法来处理这种情况,包括扩展图像边缘的数据,截断内核并且不运行操作,或者根本不将内核应用于超出边缘的点(通常称为“就地复制”)。

您还会注意到,我们在像素操作期间没有指定任何 else 情况;在所有不进行操作的情况下,我们假设该操作的输出将为零。例如,如果卷积核的中心项为零,就像通常情况下那样,没有必要进行乘法并得到零。只需跳过该计算并继续下一个。

在某些情况下,例如如果我们的数据是整数格式,我们需要应用一个除数来满足数字格式的约束(在本例中,我们的缓冲区由 8 位数据组成)。除数通常是内核宽度乘以内核高度(3x3 为 9,5x5 为 25 等等),但也有一些情况下可能需要其他除数。

索贝尔算子

[编辑 | 编辑源代码]

我们将要看的第一个内核是 **索贝尔算子**,也称为 **边缘检测** 内核。索贝尔算子通过降低低频像素的强度,同时提高高频像素的强度,来强调图像函数中的高频变化。本质上,索贝尔算子正在对图像的每个坐标处的强度求二阶导数,然而,对于那些对数学不太了解的人,以及我们中的一些人来说,将其理解为卷积滤波器更容易。

 -1 0 1
 -2 0 2
 -1 0 1

我们可以通过查看常数函数 f(x) 的一维二阶导数来思考这一点:. 通过取常数并将它们存储在一个矩阵中,我们构建了一维卷积核:. 我们通过将该矩阵与垂直矩阵 相乘,将其转换为二维卷积核,该矩阵只是我们正在进行卷积的点的绝对差值。

这个相对简单的滤波器有几个变体。例如,一个常见的调整是强调的方向

 1 0 -1
 2 0 -2
 1 0 -1

这种调整将滤波器的前沿改为右侧,而不是像前一个例子中那样强调左侧。它与上面相同的滤波器“翻转”了,方法是将函数乘以 -1。

  1  2  1        -1 -2 -1 
  0  0  0   or    0  0  0
 -1 -2 -1         1  2  1

这些是原始滤波器的旋转,分别强调向上和向下。

反卷积

[编辑 | 编辑源代码]
华夏公益教科书