使用示例了解图像处理中的相关性

信息处理 图像处理 相关性
2022-02-16 20:08:52

我一直试图理解相关性背后的直觉。我从本指南中了解到,相关性是一种在嘈杂背景中检测已知波形的方法。我从这个SO 答案中看到,相关性可用于模板匹配。所以我决定尝试一下,这也是我困惑的原因。

考虑一个二维数组,

+---+---+---+---+
| 1 | 1 | 1 | 1 |
+---+---+---+---+
| 1 | 1 | 1 | 1 |
+---+---+---+---+
| 0 | 0 | 1 | 1 |
+---+---+---+---+
| 1 | 1 | 1 | 1 |
+---+---+---+---+

我在没有填充以下数组的情况下进行了关联,

+---+---+
| 0 | 0 |
+---+---+
| 1 | 1 |
+---+---+

我得到的输出是,

+---+---+---+
| 2 | 2 | 2 |
+---+---+---+
| 0 | 1 | 2 |
+---+---+---+
| 2 | 2 | 2 |
+---+---+---+

我期待一个最大值,其中较小的数组出现在大数组中。但我得到了多个峰值。你能帮我理解为什么会发生这种情况以及如何克服它吗?

谢谢你。

4个回答

正如它已经多次发布的那样:问题来自应用程序中相关性的不准确定义。

皮尔逊相关系数 确实要求数据

  1. 居中,即必须减去平均值
  2. 归一化,即数据必须除以标准差

对于较大矩阵的每个子矩阵,也必须对掩码进行这种居中和归一化。

在您的示例中,您最终会得到一个相关矩阵:

(0.00.00.01.00.5770.01.00.5770.0)

让我们看一下矩阵条目:

  • 0.0 它们在那里,因为其中有包含所有子矩阵的子矩阵。这样一个矩阵的平均值是 1,因此它以对应的矩阵为中心,只有零。因此相关性为零。

  • 1.0 是您希望看到的相关性。对应的子矩阵完美拟合掩码,因此完全相关,即 1

  • -1.0 是适合掩码的子矩阵的相关性,但子矩阵条目是切换的(即子矩阵为 0,掩码为 1,反之亦然)。这是一个完美的反相关,即-1

  • 0.577 情况:它们发生在子矩阵与掩码偏离一个数字的情况下(即掩码为 0 时为 1,反之亦然)。在您的示例中,它也恰好是一个对称情况,因此有一对与一个开关正相关,一个与相应开关负相关。

这是一个非常丑陋的蛮力代码来说明它。它既没有以任何方式进行优化,也没有针对您的任何其他情况进行测试。我希望它是正确的;)

import numpy as np
In [2]:

img = np.ones([4,4])
img[2,0:2] = 0.0
print(img)
[[1. 1. 1. 1.]
 [1. 1. 1. 1.]
 [0. 0. 1. 1.]
 [1. 1. 1. 1.]]
In [3]:

mask = np.ones([2,2])
mask[0,0:2] = 0.0
print(mask)
[[0. 0.]
 [1. 1.]]
In [4]:

def simple_convolve(img, mask):
    max_row    = img.shape[0] - mask.shape[0] + 1
    max_col    = img.shape[1] - mask.shape[1] + 1

    output = np.zeros([max_row, max_col])

    for curr_row in range(0, max_row):
        for curr_col in range(0, max_col):
            for curr_mask_row in range(0, mask.shape[0]):
                for curr_mask_col in range(0, mask.shape[1]):
                    output[curr_row, curr_col] += img[curr_row + curr_mask_row, curr_col + curr_mask_col] * mask[curr_mask_row, curr_mask_col]
    return output
​
def better_convolve(img, mask):
    max_row    = img.shape[0] - mask.shape[0] + 1
    max_col    = img.shape[1] - mask.shape[1] + 1

    output = np.zeros([max_row, max_col])

    mask_mean  = mask.mean()
    mask_sigma = (((mask - mask_mean)**2).sum())**0.5
    mask = mask - mask_mean
    mask = mask/mask_sigma

    curr_img_mean  = 0.0
    curr_img_sigma = 0.0

    for curr_row in range(0, max_row):
        for curr_col in range(0, max_col):

            curr_img_mean  = 0.0
            curr_img_sigma = 0.0

            # Compute the mean value of the sub-image of img
            ctr = 0.0
            for curr_mask_row in range(0, mask.shape[0]):
                for curr_mask_col in range(0, mask.shape[1]):
                    ctr = ctr + 1.0
                    curr_img_mean += img[curr_row + curr_mask_row, curr_col + curr_mask_col]
            curr_img_mean = curr_img_mean / ctr

            # Compute the std value of the sub-image of img
            ctr = 0.0
            for curr_mask_row in range(0, mask.shape[0]):
                for curr_mask_col in range(0, mask.shape[1]):
                    ctr = ctr + 1.0
                    curr_img_sigma += (img[curr_row + curr_mask_row, curr_col + curr_mask_col] - curr_img_mean)**2.0
            curr_img_sigma = curr_img_sigma**0.5

            for curr_mask_row in range(0, mask.shape[0]):
                for curr_mask_col in range(0, mask.shape[1]):         
                    x = (img[curr_row + curr_mask_row, curr_col + curr_mask_col] - curr_img_mean)
                    if curr_img_sigma == 0.0:
                        x = 0.0
                    else:
                        x = x/curr_img_sigma

                    output[curr_row, curr_col] += x * mask[curr_mask_row, curr_mask_col]
    return output
In [5]:

simple_convolve(img, mask)
Out[5]:
array([[2., 2., 2.],
       [0., 1., 2.],
       [2., 2., 2.]])
In [6]:

better_convolve(img, mask)
Out[6]:
array([[ 0.        ,  0.        ,  0.        ],
       [-1.        , -0.57735027,  0.        ],
       [ 1.        ,  0.57735027,  0.        ]])

你选择了一个艰难的例子。

简短的回答:将您的“0”更改为另一个值,例如 2,它应该会更好地工作。

真正发生的事情:您的信号不是零均值,相关性需要使信号居中(即减去均值)例如,因为在一维中更容易理解:假设您想p=[0,2,2,0]在序列中找到模式s=[2,1,1,1,0,1,1,0,1,1,1,2]您希望在中间找到它(请注意,模式的缩放在相关性中被忽略)。现在比较:

conv(s,p,'same')
>> 6   4   4   2   2   4   2   2   4   4   6   4

conv(s-mean(s),p-mean(p),'same')
>> 1  -1   1  -1  -1   2  -1  -1   1  -1   1   1

有你的高峰,就在中间:)

尝试以这种方式说明这可能很难,但我走了。获取原始图像并将滤镜放在左上角,如下所示:

+-------+-------+---+---+
| 1 [0] | 1 [0] | 1 | 1 |
+-------+-------+---+---+
| 1 [1] | 1 [1] | 1 | 1 |
+-------+-------+---+---+
| 0     | 0     | 1 | 1 |
+-------+-------+---+---+
| 1     | 1     | 1 | 1 |
+-------+-------+---+---+

括号中的值是过滤器的值。现在计算相关性:10+10+11+11=2. 所以我们赋值2到结果图像中的相应位置:

+---+---+---+
| 2 | ? | ? |
+---+---+---+
| ? | ? | ? |
+---+---+---+
| ? | ? | ? |
+---+---+---+

现在我们移动过滤器,让我们向右移动一个像素。

+---+-------+-------+---+
| 1 | 1 [0] | 1 [0] | 1 |
+---+-------+-------+---+
| 1 | 1 [1] | 1 [1] | 1 |
+---+-------+-------+---+
| 0 | 0     | 1     | 1 |
+---+-------+-------+---+
| 1 | 1     | 1     | 1 |
+---+-------+-------+---+

同样,我们计算相关性:10+10+11+11=2. 并再次分配相关图像中的像素:

+---+---+---+
| 2 | 2 | ? |
+---+---+---+
| ? | ? | ? |
+---+---+---+
| ? | ? | ? |
+---+---+---+

然后继续此操作,直到将过滤器一直移动到原始图像。我希望这有帮助!

编辑:

由于另一个答案与我的有争议,我将展开。在图像处理中,相关性定义为: Y(r,c)=r=hhc=wwI(r+r,c+c)F(r,c), 其中图像I有尺寸(2h+1)×(2w+1)和过滤器F假定使用某种填充(https://www2.cs.duke.edu/courses/fall15/compsci527/notes/convolution-filtering.pdf)。直观地说,该等式正在执行我在回答中已经概述的内容:将过滤器放在图像上,计算像素乘积的总和,将过滤器移动到图像中。事实上,MATLAB 是执行图像处理任务的流行工具,它们实现了一个名为的函数imfilter,您可以尝试检查您的答案!它们提供了一个易于理解的相关性教程(https://www.mathworks.com/help/images/what-is-image-filtering-in-the-spatial-domain.html)。

image = [1 1 1 1; 1 1 1 1; 0 0 1 1; 1 1 1 1];
filt = [0 0; 1 1];
imfilter(image, filt)

MATLAB 实际上沿边界计算附加值,如下所示:

    [0]     [0]
+-------+-------+---+---+
| 1 [1] | 1 [1] | 1 | 1 |
+-------+-------+---+---+
| 1     | 1     | 1 | 1 |
+-------+-------+---+---+
| 0     | 0     | 1 | 1 |
+-------+-------+---+---+
| 1     | 1     | 1 | 1 |
+-------+-------+---+---+

默认情况下,它使用零填充来执行与我在答案中显示的相同的计算:00+00+11+11=2. 然后它们填充相应的结果像素。结果是输出与输入图像具有相同的大小。

+-------+-------+---+---+
| 2     | ?     | ? | ? |
+-------+-------+---+---+
| ?     | ?     | ? | ? |
+-------+-------+---+---+
| ?     | ?     | ? | ? |
+-------+-------+---+---+
| ?     | ?     | ? | ? |
+-------+-------+---+---+

这是因为相关性(和卷积)并不意味着“匹配”给定的模式。它们本质上是乘法运算符,因此如果将参考信号乘以 ,它们与信号幅度密切相关N,输出会变大两倍。

例如,当遇到这条信号时,您的操作员将返回两倍的峰值:

+---+---+
| 0 | 0 |
+---+---+
| 2 | 2 |
+---+---+ 

比遇到这个时:

+---+---+
| 0 | 0 |
+---+---+
| 1 | 1 |
+---+---+

此外,与您的操作员一起遇到此信号时的结果:

+---+---+
|999|999|
+---+---+
| 1 | 1 |
+---+---+

由于零,将与上述相同。

实际上,在这种情况下,您的操作员就像一个低通水平滤波器。正如@M529 所述,Pearson 相关系数更适合您的应用。

另一个有趣的运算符是均方误差或任何其他误差函数。当信号完全匹配时其输出为 0,当误差变大时其输出为 >0。