python - Numpy Array Math 复制像素,而不是通过 Python 中的 For 循环进行迭代
问题描述
我正在提取 4000 宽 x 2000 高的地球平面地图 ( src_filename
) 并预先计算了存储在numpy.ndarray
( transform_array
, 2000, 2000, 2) 中的像素重定位 x、y 值。
重新定位的像素应该从 a 复制srcimg_array
到 2000x2000 polarimg_array
,然后保存到图像中。
我有一个 for 循环,它遍历transform_array
,读取每个位置的位置值,然后将像素 RGB 值从源复制到极坐标阵列 - 即:transform_array
[0, 0] 包含 [1414, 1500] 表示源图像[0, 0] 处的颜色值被复制到 [1414, 1500] 中polarimg_array
,[0, 1] 值被复制到 [1413, 1500] 等。
这个过程每张图像大约需要 30 秒,我正在努力加快这个过程。
如何使用 Python 实现这一点?(即:不是 C 或 Cython 等)。
import numpy as np
import imageio
global transform_array
transform_array = np.zeros((2000, 2000, 2), dtype='i')
# transform_array values are pre-calculated already
srcimg_array = np.array(imageio.imread(src_filename))
polarimg_array = np.zeros((outputHeight, outputWidth, 3), dtype='uint8')
for item in np.ndindex((2000, 2000)):
XYgrid = transform_array[item[0], item[1]]
polarimg_array[[item[1]], [outputHeight - item[0]]] = srcimg_array[XYgrid[0], XYgrid[1]]
解决方案
首先,就像sai在他的评论中提到的,如果你想根据你在问题中解释的那样将像素从 重新定位srcimg_array
到:polarimg_array
transform_array
我有一个 for 循环遍历 transform_array,读取每个位置的位置值,然后将像素 RGB 值从源复制到极坐标数组 - 即: transform_array [0, 0] 包含 [1414, 1500] 这意味着[0, 0] 处的源图像颜色值被复制到 polarimg_array 中的 [1414, 1500],[0, 1] 值被复制到 [1413, 1500] 等。
然后你会像这样使用 for 循环:
for item in np.ndindex((2000, 2000)):
XYgrid = transform_array[item[0], item[1]]
polarimg_array[item[0], item[1]] = srcimg_array[XYgrid[0], XYgrid[1]]
我不明白你想用你的 for 循环做什么:
for item in np.ndindex((2000, 2000)):
XYgrid = transform_array[item[0], item[1]]
polarimg_array[[item[1]], [outputHeight - item[0]]] = srcimg_array[XYgrid[0], XYgrid[1]]
似乎您正在尝试进行某种额外的转换。如果是这种情况,那么您应该应用该转换,transform_array
以便transform_array
包含您需要的所有转换,然后根据transform_array
使用上面编写的 for 循环重新定位像素(我的回答中提到的第一个 for 循环)。
此外,在这一行中:
polarimg_array[[item[1]], [outputHeight - item[0]]] = srcimg_array[XYgrid[0], XYgrid[1]]
你已经把索引item[1]
和outputHeight - item[0]
放在一个数组里面(所以你有[item[1]]
和[outputHeight - item[0]]
作为索引),这不仅是不必要的,而且(出于某种奇怪的原因)它显着减慢了 for 循环的执行。(在我的计算机上,当我删除不必要的方括号时,它的速度大约快了 5 倍)。所以,这一行应该是这样的:
polarimg_array[item[1], outputHeight - item[0]] = srcimg_array[XYgrid[0], XYgrid[1]]
最后回到主要问题,在这个 for 循环中复制像素可以更快:
for item in np.ndindex((2000, 2000)):
XYgrid = transform_array[item[0], item[1]]
polarimg_array[item[0], item[1]] = srcimg_array[XYgrid[0], XYgrid[1]]
您可以在 NumPy 中使用数组索引( sai在他的评论中也提到过)。您可以在此处找到 NumPy 中数组索引的详细信息和示例。简而言之:
整数数组索引允许基于它们的 N 维索引选择数组中的任意项。每个整数数组表示该维度的多个索引。
示例:
>>> x = np.array([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
>>> rows = np.array([[0, 0],
[3, 3]], dtype=np.intp)
>>> columns = np.array([[0, 2],
[0, 2]], dtype=np.intp)
>>> x[rows, columns]
array([[ 0, 2],
[ 9, 11]])
下一行将做我们想要的:
polarimg_array = srcimg_array[transform_array[:,:,0], transform_array[:,:,1]]
我将在一个只有 6 个像素的图像上的简单示例中解释它是如何工作的。
对于此图像srcimg_array
将是:
array([[[200, 0, 0],
[150, 0, 0],
[100, 0, 0]],
[[ 0, 0, 200],
[ 0, 0, 150],
[ 0, 0, 100]]], dtype=uint8)
如果我们想水平翻转图像,那么transform_array
将是:
>>> transform_array = np.array([[[j, 2 - i] for i in range(3)] for j in range(2)])
>>> transform_array
array([[[0, 2],
[0, 1],
[0, 0]],
[[1, 2],
[1, 1],
[1, 0]]])
变量transform_array[:,:,0]
将包含转换位置的高度,transform_array[:,:,1]
并将包含转换位置的宽度。(:,:,0
意味着我们从第一个维度选择所有内容(:
),:
从第二个维度选择所有内容(),以及在第三个维度中的位置元素0
。)
>>> transform_array[:,:,0]
array([[0, 0, 0],
[1, 1, 1]])
>>> transform_array[:,:,1]
array([[2, 1, 0],
[2, 1, 0]])
最后:
>>> polarimg_array = srcimg_array[transform_array[:,:,0], transform_array[:,:,1]]
>>> polarimg_array
array([[[100, 0, 0],
[150, 0, 0],
[200, 0, 0]],
[[ 0, 0, 100],
[ 0, 0, 150],
[ 0, 0, 200]]], dtype=uint8)
例如,polarimg_array[1, 2]
is [ 0, 0, 200]
because transform_array[:,:,0][1, 2]
is1
和transform_array[:,:,1][1, 2]
is 0
sopolarimg_array[1, 2]
等于srcimg_array[1, 0]
。
生成的图像是:
这是完整的代码示例:
import numpy as np
import imageio
transform_array = np.zeros((2000, 2000, 2), dtype='i')
# transform_array values are pre-calculated already
srcimg_array = np.array(imageio.imread(src_filename))
polarimg_array = srcimg_array[transform_array[:,:,0], transform_array[:,:,1]]
在我的电脑上,一张大小为 4000x2000 的图像,创建所需的时间polarimg_array
约为 0.1 秒。
编辑(在cpuguru的评论之后):
我查看了您的代码并设法创建了您需要的转换。(我编写getTransformation
了创建函数,transform_array
因此我们可以使用它srcimg_array[transform_array[:,:,0], transform_array[:,:,1]]
来获取polarimg_array
。)这是完整的简化代码示例。(我使用 numba 删除了参数解析、日志记录、多处理和性能改进):
import os
import time
import numpy as np
import imageio
def getTransformation(height, outputHeight, outputWidth):
ts_rows, ts_columns = np.indices((outputHeight, outputHeight))
ts_rows = ts_rows - outputHeight / 2
ts_columns = ts_columns - outputHeight / 2
transform_src = np.zeros((outputHeight, outputHeight, 2), dtype='int')
transform_src[:,:,0] = np.arctan2(ts_columns, ts_rows) * height / np.pi
transform_src[:,:,1] = np.sqrt(ts_rows*ts_rows + ts_columns*ts_columns) \
* height / outputHeight
tn_columns, tn_rows = np.indices((outputHeight, outputHeight))
tn_rows = outputHeight - 1 - tn_rows
transform_north = transform_src[tn_rows, tn_columns]
transform_north = transform_north[:,:,::-1]
ts_columns, ts_rows = np.indices((outputHeight, outputHeight))
ts_rows = ts_rows - outputHeight
transform_south = transform_src[ts_rows, ts_columns]
transform_south[:,:,1] = height - transform_south[:,:,1] - 1
transform_south = transform_south[:,:,::-1]
transform_array = np.zeros((outputHeight, outputWidth, 2), dtype='int')
transform_array[:,:outputHeight,:] = transform_north
transform_array[:,outputHeight-4:,:] = transform_south
return transform_array
def EquirectangularToPolar(src_filename, transform_array):
srcimg_array = np.array(imageio.imread(src_filename))
polarimg_array = srcimg_array[transform_array[:,:,0], transform_array[:,:,1]]
name = os.path.splitext(os.path.basename(src_filename))[0]
dst_filename = ''.join([name, '_pp.', 'jpg'])
imageio.imsave(dst_filename, polarimg_array)
print("Saving " + dst_filename)
return
def main():
starttime = time.time()
filename = 'wind_waves_0001.jpg'
height = 2000
width = 4000
outputHeight = height
outputWidth = width - 4
starttime = time.time()
transform_array = getTransformation(height, outputHeight, outputWidth)
EquirectangularToPolar(filename, transform_array)
print('File conversion took {0:.2f} seconds'.format(time.time() - starttime))
if __name__ == '__main__':
main()
函数getTransformation
创建transform_array
的不是函数中使用的,EquirectangularToPolar
以将转换应用于每个图像。在上面的代码中,我假设您的所有图像都具有相同的大小,因此您可以transform_array
在所有图像上使用相同的大小(或至少在transform_array
相同大小的图像上使用相同的大小),从而为您带来额外的性能提升。
函数如何getTransformation
工作的解释:
第一部分创建
transform_src
.transform_src
是一个大小(outputHeight, outputHeight, 2)
等于transform_src[x][y]
的数组findSrcXY(x, y, height, outputHeight)
。我们不是findSrcXY
为每个坐标(x, y)
计算它,而是为所有 x 坐标一起计算它,并为所有 y 坐标一起计算它。当对整个数组应用操作而不是对数组的每个元素单独应用该操作时,NumPy 非常快。在第一行:ts_rows, ts_columns = np.indices((outputHeight, outputHeight))
ts_rows
并且ts_columns
被启动,以便ts_rows
包含 x 坐标并ts_columns
包含大小数组中所有位置的 y 坐标 (outputHeight, outputHeight)。接下来两行:
ts_rows = ts_rows - outputHeight / 2 ts_columns = ts_columns - outputHeight / 2
替换此代码:
x = x - outputHeight / 2 y = y - outputHeight / 2
最后两行:
transform_src[:,:,0] = np.arctan2(ts_columns, ts_rows) * height / np.pi transform_src[:,:,1] = np.sqrt(ts_rows*ts_rows + ts_columns*ts_columns) * height / outputHeight
findSrcX
正在做与函数和相同的事情findSrcY
。在 function 的第二和第三部分,创建了
getTransformation
数组transform_north
和transform_south
。transform_north
是生成图像左侧所需的变换,并且transform_south
是图像右侧所需的变换。数组transform_north
替换这一行:polarimg_array[coord_target[1], outputHeight - coord_target[0]] = srcimg_array[coord_src[1], coord_src[0]]
再次在第一行
ts_rows
并ts_columns
启动:tn_columns, tn_rows = np.indices((outputHeight, outputHeight))
这次
ts_rows
andts_columns
被颠倒了,因为coord_target
被颠倒了polarimg_array[coord_target[1], outputHeight - coord_target[0]]
(在第一个索引上是coord_target[1]
,在第二个索引上是coord_target[0]
。)然后下一行:
tn_rows = outputHeight - 1 - tn_rows
替换.
outputHeight - coord_target[0]
_polarimg_array[coord_target[1], outputHeight - coord_target[0]]
(我添加-1
了结果图像从第一列(索引为零)开始,如果您查看图像(以 png 格式),您会看到第一列只是黑色,我认为这不是理想的结果.)在下一行:
transform_north = transform_src[tn_rows, tn_columns]
我们从
transform_src
正确的位置获得价值。在最后一行:
transform_north = transform_north[:,:,::-1]
我们反转位置(从 (x, y) 到 (y, x)),因为
coord_src
insrcimg_array[coord_src[1], coord_src[0]]
的位置是反转的。数组
transform_south
的创建类似于transform_north
.函数的最后一部分
getTransformation
将transform_north
and组合transform_south
在一个数组transform_array
中。
就像我说的那样,函数getTransformation
比以前的解决方案快得多,因为我们将所有操作应用于整个数组。这些是我电脑上部分程序所花费的(大约)时间:
- 0.8秒创建
transform_array
(带getTransformation
功能) - 0.2 秒创建
srcimg_array
(打开图像) - 0.3 秒创建
polarimg_array
(应用转换) - 函数0.2 秒
imageio.imsave
(保存结果图像)
使用您的代码(具有性能改进)转换大约需要 1 分钟。您可以尝试使用多处理和 numba 改进我的解决方案,但如果您的所有图像都具有相同的大小,那么您只创建transform_array
(call getTransformation
) 一次并EquirectangularToPolar
为每个图像调用一次函数(大约需要 0.7 秒)。鉴于打开和保存图像大约需要 0.4 秒,只有当您可以加快打开和保存图像的过程时,性能改进才有意义。
推荐阅读
- matlab - 自动响应提示内循环
- excel - 计算Excel中单元格中前导零的数量
- parsing - 如何用 antlr 捕获跳过的令牌?
- javascript - 给定计数的随机数
- python - DEBUG = False returns a Server Error (500) in Django
- servicestack-autoquery - How do I get second level public properties in ServiceStack
- php - Rewrite blade from package Laravel
- scala - 如何在加入 Spark 之前正确应用 HashPartitioner?
- java - JavaRDD
到 JavaRDD - django - 日期之间的Django差异