一种优雅/快速的方法来找到图像上一条线的终点?

时间:2019-01-21 09:38:18

标签: python performance numpy for-loop numpy-ndarray

我一直在努力通过将数组操作的循环替换为适当的NumPy函数来提高代码的速度。

该函数旨在获取一条线的终点,这是仅有的255个像素中只有一个相邻像素的两个点。

有没有办法我可以从np那里得到两点呢?在有条件的情况下,或者某些我不熟悉的NumPy函数可以胜任?

def get_end_points(image):

x1=-1
y1=-1
x2=-1
y2=-1
    for i in range(image.shape[0]):
        for j in range(image.shape[1]):
            if image[i][j]==255 and neighbours_sum(i,j,image) == 255:
                if x1==-1:
                    x1 = j
                    y1 = i
                else:
                    x2=j
                    y2=i
return x1,y1,x2,y2

3 个答案:

答案 0 :(得分:1)

这是卷积的解决方案:

import numpy as np
import scipy.signal

def find_endpoints(img):
    # Kernel to sum the neighbours
    kernel = [[1, 1, 1],
              [1, 0, 1],
              [1, 1, 1]]
    # 2D convolution (cast image to int32 to avoid overflow)
    img_conv = scipy.signal.convolve2d(img.astype(np.int32), kernel, mode='same')
    # Pick points where pixel is 255 and neighbours sum 255
    endpoints = np.stack(np.where((img == 255) & (img_conv == 255)), axis=1)
    return endpoints

# Test
img = np.zeros((1000, 1000), dtype=np.uint8)
# Draw a line from (200, 130) to (800, 370)
for i in range(200, 801):
    j = round(i * 0.4 + 50)
    img[i, j] = 255
print(find_endpoints(img))
# [[200 130]
#  [800 370]]

编辑:

您也可以考虑为此使用Numba。该代码几乎就是您已经拥有的代码,因此可能不是特别“优雅”,但速度要快得多。例如,如下所示:

import numpy as np
import numba as nb

@nb.njit
def find_endpoints_nb(img):
    endpoints = []
    # Iterate through every row and column
    for i in range(img.shape[0]):
        for j in range(img.shape[1]):
            # Check current pixel is white
            if img[i, j] != 255:
                continue
            # Sum neighbours
            s = 0
            for ii in range(max(i - 1, 0), min(i + 2, img.shape[0])):
                for jj in range(max(j - 1, 0), min(j + 2, img.shape[1])):
                    s += img[ii, jj]
            # Sum including self pixel for simplicity, check for two white pixels
            if s == 255 * 2:
                endpoints.append((i, j))
                if len(endpoints) >= 2:
                    break
        if len(endpoints) >= 2:
            break
    return np.array(endpoints)

print(find_endpoints_nb(img))
# [[200 130]
#  [800 370]]

这在我的计算机上运行速度相对较快:

%timeit find_endpoints(img)
# 34.4 ms ± 64.4 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit find_endpoints_nb(img)
# 552 µs ± 4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

此外,它应该使用更少的内存。上面的代码假定只有两个端点。如果添加并行化,您也许可以使其更快(尽管您必须进行一些更改,因为您将无法从并行线程修改列表endpoints)。

答案 1 :(得分:0)

编辑:我没有注意到您有灰度图像,但是就这个想法而言,什么都没有改变

我无法为您提供确切的解决方案,但是我可以为您提供更快的找到所需内容的方法

1)a)查找白色[255,255,255]的索引(像素)

df1['quantity'] = df1['quantity'].where(df1['quantity'].ne('last observation'), 
                                        df2['quantity']).astype(float)

1)b)围绕这一点做循环 因为您没有执行无用的循环,所以速度更快


2)该解决方案应该非常快,但是很难编程

a)像1)一样找到索引

indice =np.where(np.all(image==255, axis=2))

b)在X轴上移动索引数组+1并将其添加到图像

indice =np.where(np.all(image==255, axis=2))

您必须对所有邻居都这样做-> x + 1,x-1,y + 1,y-1,x + 1,y + 1 .... 它将变得格外快

EDIT2:我能够制作一个应该执行此操作的脚本,但是您应该先对其进行测试

indices = =np.where(np.all(image==255, axis=2))
indices_up = # somehow add to all indexes in x dimension +1 (simply move it up)
add_up = image[indices]+image[indices_up]
# if in add_up matrix is array with(rgb channel) [510,510,510] # 255+255, then it has neightbour in x+1
# Note that you cant do it with image of dtype uint8, because 255 is max, adding up you will end up back at 255

答案 2 :(得分:0)

我认为我至少可以使用卷积提供一个优雅的解决方案。

我们可以通过将原始图像与3x3环进行卷积来寻找相邻像素的数量。然后,如果中心像素中也有白色像素,我们就可以确定行末端是否在那里。

>>> import numpy as np
>>> from scipy.signal import convolve2d
>>> a = np.array([[0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 1, 1, 0]])
>>> a
array([[0, 0, 0, 0, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 0],
       [0, 0, 1, 0, 0, 0, 0],
       [0, 0, 0, 1, 0, 0, 0],
       [0, 0, 0, 0, 1, 0, 0],
       [0, 0, 0, 0, 1, 1, 0]])

>>> c = np.full((3, 3), 1)
>>> c[1, 1] = 0
>>> c
array([[1, 1, 1],
       [1, 0, 1],
       [1, 1, 1]])

>>> np.logical_and(convolve2d(a, c, mode='same') == 1, a == 1).astype(int)
array([[0, 0, 0, 0, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0]])

可以随意查看各个组件产生的内容,但是为了简洁起见,我没有在此处包括它们。正如您可能已经注意到的那样,它确实正确地拒绝了行以两个相邻像素结尾的情况。

您当然可以将其转换为np.where的任意行尾索引:

np.array(np.where(result))