Python 中使用 OpenCV 的 matchTemplate 方法进行二维互相关的奇怪结果
Strange result of 2D cross-correlation using OpenCV's matchTemplate method in Python
在下面的示例中,A、B 数组的互相关是使用 cv2.matchTemplate 方法计算的。结果存入C数组:
import cv2
import numpy as np
A=np.ones((3,3), dtype=np.uint8)
B=np.array([[1,2,3],[4,5,6],[7,8,9]], dtype=np.uint8)
C=cv2.matchTemplate( A, B, cv2.TM_CCORR )
>>> A
array([[1, 1, 1],
[1, 1, 1],
[1, 1, 1]], dtype=uint8)
>>> B
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]], dtype=uint8)
>>> C
array([[ 45.]], dtype=float32)
让我们使用 scipy:
实现相同的示例
import cv2
import numpy as np
import scipy
import scipy.signal
A = np.ones((3,3), dtype=np.uint8)
B = np.array([[1,2,3],[4,5,6],[7,8,9]], dtype=np.uint8)
C = scipy.signal.correlate2d(A,B)
>>> C
array([[ 9, 17, 24, 15, 7],
[15, 28, 39, 24, 11],
[18, 33, 45, 27, 12],
[ 9, 16, 21, 12, 5],
[ 3, 5, 6, 3, 1]], dtype=uint8)
现在让我们使用 Octave 实现相同的示例:
octave:4> A=ones(3,3)
A =
1 1 1
1 1 1
1 1 1
octave:5> B=[1 2 3; 4 5 6; 7 8 9]
B =
1 2 3
4 5 6
7 8 9
octave:6> C=xco
xcorr xcorr2 xcov
octave:6> C=xcorr2(A,B)
C =
9 17 24 15 7
15 28 39 24 11
18 33 45 27 12
9 16 21 12 5
3 5 6 3 1
通过比较结果我们可以看出opencv的方法产生了明显不同的结果。
有人可以解释一下 2D 互相关的各种实现之间的区别吗?
我应该如何更改我的 opencv 代码才能正确计算 2D 互相关?
谢谢大家,
放克
嗯,首先我们需要参考 OpenCV 文档:
Matlab/OpenCV
cv2.matchTemplate(image, templ, method[, result]) → result
result
– 比较结果图。必须是单通道32位浮点数。如果图像是 W x H
并且 templ
是 w x h
,那么结果是 (W-w+1) x (H-h+1)
.
使用 3x3 图像和 3x3 模板,您的结果将是一个 (3-3+1)x(3-3+1) = (1x1)
矩阵,这就是该方法实际所做的 return。
TM_CCORR
方法使用的公式如下:
现在让我们看看这个和其他实现之间的区别。
SciPy
scipy.signal.correlate2d(in1, in2, mode='full', boundary='fill', fillvalue=0)[source]
结果大小由mode
参数决定。使用默认参数 full
意味着结果大小将为 (W+w-1) x (H+h-1)
。但是,将模式更改为 valid
将导致 (W-w+1) x (H-h+1)
结果,这与 OpenCV 实现的结果相同。
八度
C = xcorr2(A,B)
结果矩阵的大小为:
C_rows = A_rows + B_rows - 1
C_cols = A_cols + B_cols - 1
使用 3x3 图像和 3x3 模板,您的结果将是一个 (3+3-1)x(3+3-1)=(5x5)
矩阵。
此方法使用的公式看起来与OpenCV使用的公式不同,但实际上只是同一方程的不同形式。
结论
所有三个实现中使用的公式似乎是相同的。这些方法之间存在差异的原因在于处理边界条件的方式。互相关是通过 "sliding" 图像矩阵上的模板矩阵并将给定单元格的结果总和设置为图像和模板中重叠单元格的乘积之和来实现的。但是,对于图像中的边缘情况,除非模板是 1x1 矩阵,否则它将与图像的边缘重叠(示例见下图)。这种情况可以通过填充或环绕图像来处理。在第一种情况下,图像被放大并用零填充以确保模板不会突出图像。
在SciPy和Octave中,默认的方法是填充图像,这将生成一个比输入图像更大的图像(事实上,在两个3x3矩阵的情况下,结果是5x5,因为当模板以图像的边缘单元格为中心时,模板总共悬垂图像 2 行和 2 列)。在 OpenCV 中,默认方法是删除模板悬在图像上方的边缘情况,在这种情况下,这意味着模板的唯一有效位置恰好位于图像的中心。这解释了值为45的单个结果单元格:模板所有元素的总和乘以1。
要回答有关如何使用 OpenCV 的 Matlab 实现获得相同结果的问题:只需扩大输入矩阵,使其大小为
(W+w-1) x (H+h-1)
,将图像置于新矩阵的中心,并用 0 填充图像之外的区域:
A=padarray(np.ones((3,3), dtype=np.uint8), [1, 1])
在下面的示例中,A、B 数组的互相关是使用 cv2.matchTemplate 方法计算的。结果存入C数组:
import cv2
import numpy as np
A=np.ones((3,3), dtype=np.uint8)
B=np.array([[1,2,3],[4,5,6],[7,8,9]], dtype=np.uint8)
C=cv2.matchTemplate( A, B, cv2.TM_CCORR )
>>> A
array([[1, 1, 1],
[1, 1, 1],
[1, 1, 1]], dtype=uint8)
>>> B
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]], dtype=uint8)
>>> C
array([[ 45.]], dtype=float32)
让我们使用 scipy:
实现相同的示例import cv2
import numpy as np
import scipy
import scipy.signal
A = np.ones((3,3), dtype=np.uint8)
B = np.array([[1,2,3],[4,5,6],[7,8,9]], dtype=np.uint8)
C = scipy.signal.correlate2d(A,B)
>>> C
array([[ 9, 17, 24, 15, 7],
[15, 28, 39, 24, 11],
[18, 33, 45, 27, 12],
[ 9, 16, 21, 12, 5],
[ 3, 5, 6, 3, 1]], dtype=uint8)
现在让我们使用 Octave 实现相同的示例:
octave:4> A=ones(3,3)
A =
1 1 1
1 1 1
1 1 1
octave:5> B=[1 2 3; 4 5 6; 7 8 9]
B =
1 2 3
4 5 6
7 8 9
octave:6> C=xco
xcorr xcorr2 xcov
octave:6> C=xcorr2(A,B)
C =
9 17 24 15 7
15 28 39 24 11
18 33 45 27 12
9 16 21 12 5
3 5 6 3 1
通过比较结果我们可以看出opencv的方法产生了明显不同的结果。
有人可以解释一下 2D 互相关的各种实现之间的区别吗?
我应该如何更改我的 opencv 代码才能正确计算 2D 互相关?
谢谢大家,
放克
嗯,首先我们需要参考 OpenCV 文档:
Matlab/OpenCV
cv2.matchTemplate(image, templ, method[, result]) → result
result
– 比较结果图。必须是单通道32位浮点数。如果图像是W x H
并且templ
是w x h
,那么结果是(W-w+1) x (H-h+1)
.
使用 3x3 图像和 3x3 模板,您的结果将是一个 (3-3+1)x(3-3+1) = (1x1)
矩阵,这就是该方法实际所做的 return。
TM_CCORR
方法使用的公式如下:
现在让我们看看这个和其他实现之间的区别。
SciPy
scipy.signal.correlate2d(in1, in2, mode='full', boundary='fill', fillvalue=0)[source]
结果大小由mode
参数决定。使用默认参数 full
意味着结果大小将为 (W+w-1) x (H+h-1)
。但是,将模式更改为 valid
将导致 (W-w+1) x (H-h+1)
结果,这与 OpenCV 实现的结果相同。
八度
C = xcorr2(A,B)
结果矩阵的大小为:
C_rows = A_rows + B_rows - 1
C_cols = A_cols + B_cols - 1
使用 3x3 图像和 3x3 模板,您的结果将是一个 (3+3-1)x(3+3-1)=(5x5)
矩阵。
此方法使用的公式看起来与OpenCV使用的公式不同,但实际上只是同一方程的不同形式。
结论
所有三个实现中使用的公式似乎是相同的。这些方法之间存在差异的原因在于处理边界条件的方式。互相关是通过 "sliding" 图像矩阵上的模板矩阵并将给定单元格的结果总和设置为图像和模板中重叠单元格的乘积之和来实现的。但是,对于图像中的边缘情况,除非模板是 1x1 矩阵,否则它将与图像的边缘重叠(示例见下图)。这种情况可以通过填充或环绕图像来处理。在第一种情况下,图像被放大并用零填充以确保模板不会突出图像。
在SciPy和Octave中,默认的方法是填充图像,这将生成一个比输入图像更大的图像(事实上,在两个3x3矩阵的情况下,结果是5x5,因为当模板以图像的边缘单元格为中心时,模板总共悬垂图像 2 行和 2 列)。在 OpenCV 中,默认方法是删除模板悬在图像上方的边缘情况,在这种情况下,这意味着模板的唯一有效位置恰好位于图像的中心。这解释了值为45的单个结果单元格:模板所有元素的总和乘以1。
要回答有关如何使用 OpenCV 的 Matlab 实现获得相同结果的问题:只需扩大输入矩阵,使其大小为
(W+w-1) x (H+h-1)
,将图像置于新矩阵的中心,并用 0 填充图像之外的区域:
A=padarray(np.ones((3,3), dtype=np.uint8), [1, 1])