📅  最后修改于: 2023-12-03 14:55:59.156000             🧑  作者: Mango
求和面积表(Summed Area Table)是一种预处理技术,用于有效地计算图像或矩阵中的子矩阵和。它可以在$O(n^2)$的时间复杂度内处理一个矩阵,并在$O(1)$时间复杂度内回答任意矩阵的子矩阵和。这个技术在计算机视觉和图像处理中被广泛使用。
求和面积表的思想非常简单:我们需要将每个元素的值变成它到左上角的子矩阵和。为了达到这个目的,我们可以使用一个递推式:
$$S(x, y) = A(x, y) + S(x-1, y) + S(x, y-1) - S(x-1, y-1)$$
其中,$S(x,y)$表示左上角在$(1,1)$,右下角在$(x,y)$的子矩阵的和,$A(x,y)$表示矩阵中的元素。
该式子可以递归地计算出求和面积表。然而,递归方法的时间复杂度为$O(n^4)$,因此我们需要使用迭代的方法。
以下是一个以二维数组为输入的求和面积表的实现(假设输入矩阵为$A$,求和面积表为$S$):
def summed_area_table(A):
n = len(A)
m = len(A[0])
S = [[0] * m for _ in range(n)]
S[0][0] = A[0][0]
# 预处理第一行和第一列
for i in range(1, n):
S[i][0] = S[i-1][0] + A[i][0]
for j in range(1, m):
S[0][j] = S[0][j-1] + A[0][j]
# 根据递推式计算其他元素
for i in range(1, n):
for j in range(1, m):
S[i][j] = A[i][j] + S[i-1][j] + S[i][j-1] - S[i-1][j-1]
return S
接下来,我们可以使用求和面积表计算任意子矩阵的和。假设我们想要计算一个左上角坐标为$(x_1,y_1)$,右下角坐标为$(x_2,y_2)$的子矩阵的和,可以使用以下公式:
$$\text{sum} = S(x_2,y_2) - S(x_1-1,y_2) - S(x_2,y_1-1) + S(x_1-1,y_1-1)$$
以下是对应的代码实现:
def submatrix_sum(S, x1, y1, x2, y2):
ret = S[x2][y2]
if x1 > 0:
ret -= S[x1-1][y2]
if y1 > 0:
ret -= S[x2][y1-1]
if x1 > 0 and y1 > 0:
ret += S[x1-1][y1-1]
return ret
求和面积表可以广泛应用于计算机视觉和图像处理中的各种算法,包括:
下面是一些使用求和面积表的例子:
假设我们有一个矩阵$A$和一个左上角坐标为$(x_1,y_1)$,右下角坐标为$(x_2,y_2)$的子矩阵。我们可以使用求和面积表来计算这个子矩阵的和:
S = summed_area_table(A)
sub_sum = submatrix_sum(S, x1, y1, x2, y2)
图像模糊是将一幅图像变得模糊的过程。它可以用于去除图像中的噪声、减少细节信息、降低图像的分辨率等。
假设我们有一个灰度图$A$和一个$N \times N$的卷积核$K$。对于图像中的每个像素$(x,y)$,我们可以通过以下公式来计算模糊后的像素值:
$$A'(x,y) = \frac{\sum_{i=-\lfloor N/2 \rfloor}^{\lfloor N/2 \rfloor} \sum_{j=-\lfloor N/2 \rfloor}^{\lfloor N/2 \rfloor} K(i,j)A(x+i,y+j)}{\sum_{i=-\lfloor N/2 \rfloor}^{\lfloor N/2 \rfloor} \sum_{j=-\lfloor N/2 \rfloor}^{\lfloor N/2 \rfloor} K(i,j)}$$
对于这样的操作,我们需要在每个像素上进行卷积,时间复杂度为$O(n^4)$,无法接受。但是,我们可以使用求和面积表来加速操作。
首先,我们可以将卷积核$K$变成两个求和面积表:$K_x$和$K_y$。其中,$K_x(i,j) = \sum_{k=0}^{i} K(k,j)$,$K_y(i,j) = \sum_{k=0}^{j} K(i,k)$。
之后,我们可以使用求和面积表得到每个像素周围$N \times N$的子矩阵的和:
S = summed_area_table(A)
sub_sum = submatrix_sum(S, x-N//2, y-N//2, x+N//2, y+N//2)
最后,我们只需要使用$K_x$和$K_y$来计算卷积即可:
conv_x = submatrix_sum(K_x, 0, y-N//2, N-1, y+N//2)
conv_y = submatrix_sum(K_y, x-N//2, 0, x+N//2, N-1)
A_prime[x][y] = (conv_x * conv_y) / K_sum
这样,我们可以将图像模糊的时间复杂度降低到$O(n^2)$。
Harris角点检测是一种经典的计算机视觉算法,用于检测图像中的角点。它可以用于物体识别、相机标定等应用中。
该算法的核心是一个矩阵:
$$M = \begin{pmatrix} \sum_{x,y \in W} I_x^2 & \sum_{x,y \in W} I_xI_y \ \sum_{x,y \in W} I_xI_y & \sum_{x,y \in W} I_y^2 \end{pmatrix}$$
其中,$W$表示一个窗口,$I_x$和$I_y$分别是图像中每个像素的$x$和$y$方向的梯度。这个矩阵在角点处的特征值分别较大,而在平面区域和边缘处较小。
我们可以使用求和面积表来快速计算矩阵$M$在每个像素处的值。首先,我们需要计算每个像素的梯度:
Ix = [[0] * m for _ in range(n)]
Iy = [[0] * m for _ in range(n)]
for i in range(1, n-1):
for j in range(1, m-1):
Ix[i][j] = (A[i-1][j+1] + A[i][j+1] + A[i+1][j+1]) - (A[i-1][j-1] + A[i][j-1] + A[i+1][j-1])
Iy[i][j] = (A[i+1][j-1] + A[i+1][j] + A[i+1][j+1]) - (A[i-1][j-1] + A[i-1][j] + A[i-1][j+1])
然后,我们需要使用$K_x$和$K_y$来计算每个像素的矩阵$M$:
W = N // 2
K_x = [[0] * (2*W+1) for _ in range(2*W+1)]
K_y = [[0] * (2*W+1) for _ in range(2*W+1)]
for i in range(-W, W+1):
for j in range(-W, W+1):
K_x[i+W][j+W] = i
K_y[i+W][j+W] = j
M = [[0] * m for _ in range(n)]
M11 = [[0] * m for _ in range(n)]
M12 = [[0] * m for _ in range(n)]
M22 = [[0] * m for _ in range(n)]
for i in range(W, n-W):
for j in range(W, m-W):
# 计算矩阵M
sum_ix2 = submatrix_sum(K_x, i-W, j-W, i+W, j+W, Ix, sqr=True)
sum_iy2 = submatrix_sum(K_y, i-W, j-W, i+W, j+W, Iy, sqr=True)
sum_ixiy = submatrix_sum(K_x, i-W, j-W, i+W, j+W, Iy, sqr=False)
sum_ixiy += submatrix_sum(K_y, i-W, j-W, i+W, j+W, Ix, sqr=False)
M11[i][j] = sum_ix2
M12[i][j] = sum_ixiy
M22[i][j] = sum_iy2
# 计算特征值
det = M11[i][j] * M22[i][j] - M12[i][j] * M12[i][j]
trace = M11[i][j] + M22[i][j]
M[i][j] = det - k * trace * trace
最后,我们可以通过比较一个窗口内像素的矩阵$M$的特征值来检测角点:
corners = []
for i in range(W, n-W):
for j in range(W, m-W):
# 检查相邻窗口内的特征值是否更大
is_max = True
for ii in range(-W, W+1):
for jj in range(-W, W+1):
if ii == 0 and jj == 0:
continue
if M[i][j] < M[i+ii][j+jj]:
is_max = False
break
if not is_max:
break
if is_max and M[i][j] > threshold:
corners.append((i, j))
求和面积表是一种非常基础的预处理技术,但是在计算机视觉和图像处理中被广泛应用,并且优化计算复杂度。求和面积表的基本思想是,将每个元素的值变成它到左上角的子矩阵和。利用这个预处理的结果,我们可以在$O(1)$时间复杂度内计算任何子矩阵的和。