简单的一条线解决方案
你可以像这样在一行中做你想做的事:
new_img = (((image - color)**2).sum(axis=2)**.5 <= 10) * 255
优化的两线方案
上述行并不是执行 OP 想要的所有操作的最有效方式。这是一种明显更快的方法(感谢 Paul Panzer 提出的 cmets 优化建议,不保证可读性):
d = image - color
new_img = (np.einsum('...i, ...i', d, d) <= 100) * 255
时间安排:
给定一些 100x100 像素的测试数据:
import numpy as np
color = np.array([30, 70, 130])
# random data within [20,60,120]-[40,80,140] for demo purposes
image = np.random.randint(10*2 + 1, size=[100,100,3]) + color - 10
这是 OP 方法的时间安排和此答案中的解决方案的比较。单线解决方案比 OP 快约 100 倍,而完全优化的版本快约 300 倍:
%%timeit
# OP's code
img_dim = image.shape
new_img = np.zeros((img_dim[0],img_dim[1])) # no alpha channel replica
for c in range(img_dim[0]):
for r in range(img_dim[1]):
pixel = image[r,c,:3]
new_img[r,c] = L2_dist(color,pixel)
43.8 ms ± 502 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%%timeit
# one line solution
new_img = (((image - color)**2).sum(axis=2)**.5 <= 10) * 255
439 µs ± 13.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%%timeit
# fully optimized solution
d = image - color
new_img = (np.einsum('...i, ...i', d, d) <= 100) * 255
145 µs ± 2.29 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
简单的单行解法说明
作为第一个解决方案给出的简单单行将:
-
求image(形状为(m, n, 3)的数组)和color(形状为(3)的数组)中每个像素之间的欧几里得距离。
检查这些距离中的任何一个是否在10 范围内,并在满足条件的地方返回一个布尔数组True,否则返回False。
-
布尔数组实际上只是0s 和1s 的数组,因此我们将布尔数组乘以255 得到您想要的最终结果。
李>
优化方案说明
以下是使用的优化列表:
使用einsum 计算距离计算所需的平方和。在底层,einsum 使用 Numpy 包装的 BLAS 库来计算所需的 sum-product,因此它应该更快。
通过比较距离的平方与阈值的平方来跳过平方根。
-
我试图找到一种方法来最小化数组的分配/复制,但这实际上让事情变慢了。这是优化解决方案的一个版本,它恰好分配了两个数组(一个用于中间结果,一个用于最终结果)并且不制作其他副本:
%%timeit
# fully optimized solution, makes as few array copies as possible
scr = image.astype(np.double)
new_img = np.zeros(image.shape[:2], dtype=np.uint8)
np.multiply(np.less_equal(np.einsum('...i,...i', np.subtract(image, color, out=scr), scr, out=scr[:,:,0]), 100, out=new_img), 255, out=new_img)
232 µs ± 7.72 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)