📜  求和面积表–子矩阵求和(1)

📅  最后修改于: 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
应用

求和面积表可以广泛应用于计算机视觉和图像处理中的各种算法,包括:

  • 二维积分图像(Integral Image)
  • 快速计算矩形区域和
  • 图像模糊和平均值滤波
  • 直方图均衡化
  • 特征点检测(例如Harris角点检测)

下面是一些使用求和面积表的例子:

矩阵子矩阵和

假设我们有一个矩阵$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角点检测

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)$时间复杂度内计算任何子矩阵的和。