Python NumPy 2D阵列迭代速度
我有一个循环,它用PIL的像素信息填充二维NumPy数组,这个数组称为“阴影”。颜色不是白色就是蓝色。我想建立一个最终的形象,从这些地方的白色占主导地位。 i、 e.如果循环中的一个图像在坐标x、y上有一个蓝色像素,而循环中的另一个图像在同一坐标上有一个白色像素,则最终像素将为白色 这项工作目前由以下人员完成:Python NumPy 2D阵列迭代速度,python,arrays,python-imaging-library,Python,Arrays,Python Imaging Library,我有一个循环,它用PIL的像素信息填充二维NumPy数组,这个数组称为“阴影”。颜色不是白色就是蓝色。我想建立一个最终的形象,从这些地方的白色占主导地位。 i、 e.如果循环中的一个图像在坐标x、y上有一个蓝色像素,而循环中的另一个图像在同一坐标上有一个白色像素,则最终像素将为白色 这项工作目前由以下人员完成: import math, random, copy import numpy as np from PIL import Image, ImageDraw colours = {0:
import math, random, copy
import numpy as np
from PIL import Image, ImageDraw
colours = {0: (255,255,255), 1: (0,0,255)}
#width and height of area of interest
w = 100 #100 meter
h = 200 #200 meter
NumberOfDots = 10
DotRadius = 20
NumberOfRuns = 3
Final = np.array([[colours[0] for x in range(w)] for y in range(h)])
Shadows = np.array([[colours[0] for x in range(w)] for y in range(h)])
for SensorNum in range(NumberOfRuns):
Shadows = np.array([[colours[0] for x in range(w)] for y in range(h)])
for dot in range(NumberOfDots):
ypos = random.randint(DotRadius, h-DotRadius)
xpos = random.randint(DotRadius, w-DotRadius)
for i in range(xpos - DotRadius, xpos + DotRadius):
for j in range(ypos - DotRadius, ypos + DotRadius):
if math.sqrt((xpos - i)**2 + (ypos - j)**2) < DotRadius:
Shadows[j][i] = colours[1]
im = Image.fromarray(Shadows.astype('uint8')).convert('RGBA')
im.save('result_test_image'+str(SensorNum)+'.png')
#This for loop below is the bottle-neck. Can its speed be improved?
if SensorNum > 0:
for i in range(w):
for j in range(h):
#White space dominates.
#(pixel by pixel) If the current images pixel is white and the unfinshed Final
#images pixel is blue then set the final pixel to white.
if np.all(Shadows[j][i]==colours[0]) and np.all(Final[j][i]==colours[1]):
Final[j][i] = colours[0]
else:
Final = copy.deepcopy(Shadows)
im = Image.fromarray(Final.astype('uint8')).convert('RGBA')
im.save('result_final_test.png')
导入数学、随机、复制
将numpy作为np导入
从PIL导入图像,ImageDraw
颜色={0:(255255),1:(0,0255)}
#感兴趣区域的宽度和高度
w=100#100米
h=200#200米
点数=10
点半径=20
运行次数=3
Final=np.数组([[颜色[0]表示范围内的x(w)]表示范围内的y(h)])
阴影=np.阵列([[范围内x的颜色[0](w)]范围内y的颜色[0](h)])
对于范围内的SensorNum(运行次数):
阴影=np.阵列([[范围内x的颜色[0](w)]范围内y的颜色[0](h)])
对于范围内的点(点数):
ypos=random.randint(点半径,h点半径)
xpos=random.randint(点半径,w点半径)
对于范围内的i(xpos-点半径,xpos+点半径):
对于范围内的j(ypos-点半径,ypos+点半径):
如果数学sqrt((xpos-i)**2+(ypos-j)**2)0:
对于范围(w)内的i:
对于范围(h)内的j:
#空白占主导地位。
#(逐像素)如果当前图像的像素为白色,且最终为未对齐的
#图像像素为蓝色,然后将最终像素设置为白色。
如果np.all(阴影[j][i]==颜色[0])和np.all(最终[j][i]==颜色[1]):
最终[j][i]=颜色[0]
其他:
最终=复制。深度复制(阴影)
im=Image.fromarray(Final.astype('uint8')).convert('RGBA'))
保存('result\u final\u test.png'))
最后一个嵌套for循环是我感兴趣的改进之处。
这很好,但是迭代是一个巨大的瓶颈。通过使用一些向量化等,是否可以更快地实现这一点?当然,可以对代码中的最后一个for循环进行向量化,因为每个迭代并不取决于之前迭代中计算的值。 但老实说,这并不像我想象的那么容易 我的方法比您当前的循环快800到1000倍。我使用下划线将大写数组和变量名替换为小写名称。大写通常保留给python中的类。这就是你问题中奇怪的代码着色的原因
if sensor_num > 0:
mask = ( # create a mask where the condition is True
((shadows[:, :, 0] == 255) & # R=255
(shadows[:, :, 1] == 255) & # G=255
(shadows[:, :, 2] == 255)) & # B=255
((final[:, :, 0] == 0) & # R=0
(final[:, :, 1] == 0) & # G=0
(final[:, :, 2] == 255))) # B=255
final[mask] = np.array([255, 255, 255]) # set Final to white where mask is True
else:
final = copy.deepcopy(shadows)
当然,RGB值可以替换为预定义值的查找,如使用颜色dict
。但我建议使用数组来存储颜色,特别是如果您计划使用数字对其进行索引:
colours = np.array([[255, 255, 255], [0, 0, 255]])
使遮罩看起来像:
mask = ( # create a mask where the condition is True
((shadows[:, :, 0] == colours[0, 0]) & # R=255
(shadows[:, :, 1] == colours[0, 1]) & # G=255
(shadows[:, :, 2] == colours[0, 2])) & # B=255
((final[:, :, 0] == colours[1, 0]) & # R=0
(final[:, :, 1] == colours[1, 1]) & # G=0
(final[:, :, 2] == colours[1, 2]))) # B=255
final[mask] = colours[0] # set Final to white where mask is True
当然,这也适用于使用dict
为了进一步加快速度,您可以将掩蔽中的RGC比较替换为与阵列本身的比较(模板计算)。对于您的数组大小,这大约要快5%,速度差随着数组大小的增加而增加,但是您只需更改颜色
数组/dict中的条目,就失去了比较其他颜色的灵活性。
具有模具操作的遮罩如下所示:
mask = ( # create a mask where the condition is True
((shadows[:, :, 0] == shadows[:, :, 1]) & # R=G
(shadows[:, :, 1] == shadows[:, :, 2]) & # G=B
(shadows[:, :, 2] == colours[0, 2])) & # R=G=B=255
((final[:, :, 0] == final[:, :, 1]) & # R=G
(final[:, :, 1] == colours[1, 1]) & # G=0
(final[:, :, 2] == colours[1, 2]))) # B=255
这将有助于大大加快计算速度
其他代码的部分也可以优化。当然,如果这不是瓶颈的话,这是值得的。
仅举一个例子:您可以调用一次并创建一个随机数组(以及+-DotRadius数组),然后在该数组上循环,而不是调用random.randint
每个循环:
ypos = np.random.randint(DotRadius, h-DotRadius, size=NumberOfDots)
ypos_plus_dot_radius = ypos + DotRadius
ypos_minus_dot_radius = ypos - DotRadius
xpos = np.random.randint(DotRadius, w-DotRadius, size=NumberOfDots)
xpos_plus_dot_radius = xpos + DotRadius
xpos_minus_dot_radius = xpos - DotRadius
for dot in range(NumberOfDots):
yrange = np.arange(ypos_minus_dot_radius[dot], ypos_plus_dot_radius[dot]) # make range instead of looping
# looping over xrange imho can't be avoided without further matrix operations
for i in range(xpos_minus_dot_radius[dot], xpos_plus_dot_radius[dot]):
# make a mask for the y-positions where the condition is true and
# index the y-axis of Shadows with this mask:
Shadows[yrange[np.sqrt((xpos[dot] - i)**2 + (ypos[dot] - yrange)**2) < DotRadius], i] = colours[1]
# colours[1] can of course be replaced with any 3-element array or single integer/float
ypos=np.random.randint(点半径,h点半径,大小=NumberOfDots)
ypos加上点半径=ypos+点半径
ypos_减去点_半径=ypos-点半径
xpos=np.random.randint(点半径,w点半径,大小=NumberOfDots)
xpos加上点半径=xpos加点半径
xpos\u减去\u点\u半径=xpos-点半径
对于范围内的点(点数):
yrange=np.arange(ypos_减去点半径[dot],ypos_加上点半径[dot])#生成范围而不是循环
#如果没有进一步的矩阵运算,就无法避免在xrange imho上循环
对于范围内的i(xpos_减点半径[dot],xpos_加点半径[dot]):
#为条件为真且正确的y位置制作遮罩
#使用此遮罩索引阴影的y轴:
阴影[yrange[np.sqrt((xpos[dot]-i)**2+(ypos[dot]-yrange)**2)
我无法复制您的代码。Numpy数组无法快速存储元组。因此,我猜与你所说的相比,阴影不是一个数组。你能给出一个包含所有导入模块的工作示例吗?我更新了代码,所以你应该能够运行它。除非我错过了什么,否则看起来Ndaray可以容纳元组:-)谢谢!当然,对于这种情况有一种矢量化方法。几分钟后我会发布一个解决方案。但否:ndarrays
无法容纳tuple
s。Numpy正在将元组
转换为具有形状(3,)
的数组。这种方法的计算时间是使用数组的近2倍?我的答案的性能提升有多大?很抱歉耽搁了。感谢您指出Ndarray不能容纳元组,我真的不知道…非常感谢您的支持,以及非常详细的解释:-)。它大大缩短了我的代码执行时间。这个瓶颈基本上已经完全消失了!改进的大致数量级是,如您所说,大约为x1000。这段代码大约花了100秒