numpy 在PYTHON中加速矩阵搜索

vsnjm48y  于 2022-11-10  发布在  Python
关注(0)|答案(1)|浏览(213)

现在有a way可以在主矩阵中搜索子矩阵的索引,但两者都很慢,有谁能想到更快的方法

a=np.array([[5 4 5 9 3 4 6 2 5 3]
            [8 3 5 4 3 4 5 8 4 4]
            [5 7 8 5 2 3 3 6 8 8]
            [4 5 6 2 6 5 6 7 9 3]
            [3 6 8 2 8 7 3 8 8 8]])

b=np.array([[2 3 3]
            [6 5 6]])
def search_image(kernel,array):
    array_H, array_W = array.shape
    kernel_H, kernel_W = kernel.shape
    for x in range(0,array_W-kernel_W+1):
        for y in range(0,array_H-kernel_H+1):
           array_subsection = array[y:y+kernel_H, x:x+kernel_W]
           if (array_subsection== kernel).all():
               print(f"kernel was found at x={x}, y={y}.")
               return [x,y]
    print(f"kernel was not found in array.")
rta7y2nd

rta7y2nd1#

假设小矩阵的分量在大矩阵中没有多次重复,并且小矩阵远远小于大矩阵,则可以在大矩阵中搜索小矩阵的第一项,从而在目标找到的位置搜索实际的小矩阵。这应该有助于将子搜索的数量大幅减少。下面是一个例子:

def search_image_faster(kernel, array):
    h, w = kernel.shape
    assert w > 0 and h > 0

    locs = np.where(array == kernel[0,0])

    for i in range(len(locs[0])):
        y, x = locs[0][i], locs[1][i]
        if np.array_equal(array[y:y+h, x:x+w], kernel):
            #print(f"kernel was found at x={x}, y={y}.")
            return [x,y]

    #print(f"kernel was not found in array.")

可以通过检查其他值(如小矩阵的最后一项)来增强此策略。尽管如此,在病理情况下,该算法的效率并不比初始算法更高。
问题是,仅靠Numpy不能有效地做到这一点。一个非常有效的解决方案是使用Numba来完成:

import numba as nb

@nb.njit(inline="always")
def match_subimage(kernel, array, ay, ax):
    kh, kw = kernel.shape
    ah, aw = array.shape

    if ay + kh > ah or ax + kw > aw:
        return False

    for ky in range(kh):
        for kx in range(kw):
            if array[ay+ky, ax+kx] != kernel[ky, kx]:
                return False

    return True

@nb.njit('(uint8[:,::1], uint8[:,::1])')
def search_image_fastest(kernel, array):
    kh, kw = kernel.shape
    ah, aw = array.shape
    assert kh+kw > 0

    kv = kernel[0, 0]

    # Note this is faster to iterate over the x location in the nested loop
    for y in range(ah):
        for x in range(aw):
            # Fast-fail optimization
            if array[y, x] != kv:
                continue

            if match_subimage(kernel, array, y, x):
                #print(f"kernel was found at x={x}, y={y}.")
                return (x, y)

    #print(f"kernel was not found in array.")
    return None

以下是我的i5-9600KF处理器上输入矩阵的时序:

search_image:          55.2 µs
search_image_faster:   14.7 µs
search_image_fastest:   0.5 µs

Numba的实施速度大约是最初实施的100倍。事实上,它的执行速度非常快,超过60%的执行时间只是从CPython启动Numba函数的时间,而不是实际的计算。
如果这还不够,您可以使用多个Numba线程来完成,但是使用多个线程可能会因为启动线程的时间而使分析小输入矩阵的速度变慢。
如果这仍然不够,则可以预先计算小矩阵的校验和并迭代大矩阵,以便计算部分校验和并将其与预计算的部分校验和进行比较。该策略旨在减少重匹配的数量,就像在第一个解决方案中一样,除了两个校验和相等的概率应该更小,因此在病理情况下可能更快。

相关问题