Luk*_*ber 6 python optimization performance numpy vectorization
我有以下细胞:
cells = np.array([[1, 1, 1],
[1, 1, 0],
[1, 0, 0],
[1, 0, 1],
[1, 0, 0],
[1, 1, 1]])
Run Code Online (Sandbox Code Playgroud)
我想计算水平和垂直邻接以获得这个结果:
# horizontal adjacency
array([[3, 2, 1],
[2, 1, 0],
[1, 0, 0],
[1, 0, 1],
[1, 0, 0],
[3, 2, 1]])
# vertical adjacency
array([[6, 2, 1],
[5, 1, 0],
[4, 0, 0],
[3, 0, 1],
[2, 0, 0],
[1, 1, 1]])
Run Code Online (Sandbox Code Playgroud)
实际的解决方案如下所示:
def get_horizontal_adjacency(cells):
adjacency_horizontal = np.zeros(cells.shape, dtype=int)
for y in range(cells.shape[0]):
span = 0
for x in reversed(range(cells.shape[1])):
if cells[y, x] > 0:
span += 1
else:
span = 0
adjacency_horizontal[y, x] = span
return adjacency_horizontal
def get_vertical_adjacency(cells):
adjacency_vertical = np.zeros(cells.shape, dtype=int)
for x in range(cells.shape[1]):
span = 0
for y in reversed(range(cells.shape[0])):
if cells[y, x] > 0:
span += 1
else:
span = 0
adjacency_vertical[y, x] = span
return adjacency_vertical
Run Code Online (Sandbox Code Playgroud)
该算法基本上是(对于水平邻接):
由于我需要对所有数组元素循环两次,这对于较大的数组(例如图像)来说很慢。
有没有办法使用矢量化或其他一些 numpy 魔法来改进算法?
概括:
Joni 和 Mark Setchell 提出了很好的建议!
我创建了一个小型 Repo,其中包含示例图像和带有比较的 python 文件。结果令人惊讶:
我用 Numba 进行了一次非常快速的尝试,但还没有彻底检查过,尽管结果似乎是正确的:
\n#!/usr/bin/env python3\n\n# /sf/ask/4889803481/\n# magick -size 1920x1080 xc:black -fill white -draw "circle 960,540 960,1040" -fill black -draw "circle 960,540 960,800" a.png\n\nimport cv2\nimport numpy as np\nimport numba as nb\n\ndef get_horizontal_adjacency(cells):\n adjacency_horizontal = np.zeros(cells.shape, dtype=int)\n for y in range(cells.shape[0]):\n span = 0\n for x in reversed(range(cells.shape[1])):\n if cells[y, x] > 0:\n span += 1\n else:\n span = 0\n adjacency_horizontal[y, x] = span\n return adjacency_horizontal\n\n@nb.jit(\'void(uint8[:,::1], int32[:,::1])\',parallel=True)\ndef nb_get_horizontal_adjacency(cells, result):\n for y in nb.prange(cells.shape[0]):\n span = 0\n for x in range(cells.shape[1]-1,-1,-1):\n if cells[y, x] > 0:\n span += 1\n else:\n span = 0\n result[y, x] = span\n return \n\n# Load image\nim = cv2.imread(\'a.png\', cv2.IMREAD_GRAYSCALE)\n\n%timeit get_horizontal_adjacency(im)\n\nresult = np.zeros((im.shape[0],im.shape[1]),dtype=np.int32)\n%timeit nb_get_horizontal_adjacency(im, result)\nRun Code Online (Sandbox Code Playgroud)\n如果工作正常的话,计时效果很好,显示出 4000 倍的加速:
\nIn [15]: %timeit nb_get_horizontal_adjacency(im, result)\n695 \xc2\xb5s \xc2\xb1 9.12 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 1000 loops each)\n\nIn [17]: %timeit get_horizontal_adjacency(im)\n2.78 s \xc2\xb1 44.2 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\nRun Code Online (Sandbox Code Playgroud)\n输入
\n输入图像以 1080p 尺寸创建,即 1920x1080,使用ImageMagick使用:
\nmagick -size 1920x1080 xc:black -fill white -draw "circle 960,540 960,1040" -fill black -draw "circle 960,540 960,800" a.png\nRun Code Online (Sandbox Code Playgroud)\n\n输出(对比度调整)
\n\n