gpt4 book ai didi

Python - 使用 Numpy 快速放大数组,不允许使用图像库

转载 作者:行者123 更新时间:2023-11-28 20:31:56 27 4
gpt4 key购买 nike

这个问题在这里已经有了答案:





How to repeat elements of an array along two axes?

(5 个回答)


3年前关闭。




关于重复消息的注意事项:

相似的主题,不完全重复。特别是因为循环仍然是最快的方法。谢谢。

目标:

将数组从 [small,small] 快速提升到 [big,big] 一个因子,不要使用图像库。很简单的缩放,一个小值会变成几个大值,对几个大值进行归一化后就变成了。换句话说,这是天文术语中的“通量守恒”——小数组中的 16 值扩展到大数组的 4 个值(2 的因数)将是 4 个 4,因此该值的数量已被保留。

问题:

我有一些工作代码可以进行放大,但与缩小相比,它们的工作速度不是很快。放大实际上比缩小更容易(在这种基本情况下,这需要很多和) - 放大只需要将已知数据放入预分配数组的大块中。

对于一个工作示例,[16,24;8,16] 的 [2,2] 数组:

16 , 24

8 , 16



对于 [4,4] 数组乘以因子 2 将具有以下值:

4 , 4 , 6 , 6

4 , 4 , 6 , 6

2 , 2 , 4 , 4

2 , 2 , 4 , 4



最快的实现是由 numba 的 jit & prange 加速的 for 循环。我想更好地利用 Numpy 的预编译函数来完成这项工作。我也会娱乐 Scipy 的东西 - 但不是它的调整大小功能。

对于强矩阵操作函数来说,这似乎是一个完美的问题,但我只是没有设法让它很快发生。

此外,单行 numpy 调用是 方式时髦,所以不要感到惊讶。但这就是让它正确对齐所需要的。

代码示例:

在下面查看更多优化调用 请注意,我这里的案例制作了一个 20480x20480 的 float64 数组,它可以占用相当多的内存 - 但可以炫耀一个方法是否过于占用内存(就像矩阵一样)。

环境:Python 3、Windows、i5-4960K @ 4.5 GHz。运行 for 循环代码的时间约为 18.9 秒,在所示示例中运行 numpy 代码的时间约为 52.5 秒。

% MAIN:运行这些
import timeit

timeitSetup = '''
from Regridder1 import Regridder1
import numpy as np

factor = 10;

inArrayX = np.float64(np.arange(0,2048,1));
inArrayY = np.float64(np.arange(0,2048,1));
[inArray, _] = np.meshgrid(inArrayX,inArrayY);
''';

print("Time to run 1: {}".format( timeit.timeit(setup=timeitSetup,stmt="Regridder1(inArray, factor,)", number = 10) ));

timeitSetup = '''
from Regridder2 import Regridder2
import numpy as np

factor = 10;

inArrayX = np.float64(np.arange(0,2048,1));
inArrayY = np.float64(np.arange(0,2048,1));
[inArray, _] = np.meshgrid(inArrayX,inArrayY);
''';

print("Time to run 2: {}".format( timeit.timeit(setup=timeitSetup,stmt="Regridder2(inArray, factor,)", number = 10) ));

% FUN: Regridder 1 - for 循环
import numpy as np
from numba import prange, jit

@jit(nogil=True)
def Regridder1(inArray,factor):
inSize = np.shape(inArray);
outSize = [np.int64(np.round(inSize[0] * factor)), np.int64(np.round(inSize[1] * factor))];

outBlockSize = factor*factor; #the block size where 1 inArray pixel is spread across # outArray pixels
outArray = np.zeros(outSize); #preallcoate
outBlocks = inArray/outBlockSize; #precalc the resized blocks to go faster
for i in prange(0,inSize[0]):
for j in prange(0,inSize[1]):
outArray[i*factor:(i*factor+factor),j*factor:(j*factor+factor)] = outBlocks[i,j]; #puts normalized value in a bunch of places

return outArray;

% 乐趣:Regridder 2 - numpy
import numpy as np

def Regridder2(inArray,factor):
inSize = np.shape(inArray);
outSize = [np.int64(np.round(inSize[0] * factor)), np.int64(np.round(inSize[1] * factor))];

outBlockSize = factor*factor; #the block size where 1 inArray pixel is spread across # outArray pixels

outArray = inArray.repeat(factor).reshape(inSize[0],factor*inSize[1]).T.repeat(factor).reshape(inSize[0]*factor,inSize[1]*factor).T/outBlockSize;

return outArray;

将不胜感激洞察加速这一点。希望代码是好的,在文本框中制定它。

当前最佳解决方案:

在我的比赛中,numba 的 jit for 循环实现 (Regridder1) 与 jit 仅应用于需要它可以在 18.0 秒运行 timeit 测试,而仅 numpy 实现 (Regridder2) 在 18.5 秒运行 timeit 测试。好处是在第一次调用时,numpy only 实现不需要等待 jit 编译代码。 Jit 的 cache=True 让它不会在后续运行中编译。其他调用(nogil、nopython、prange)似乎没有帮助,但似乎也没有伤害。也许在 future 的 numba 更新中,他们会做得更好或什么的。

对于简单性和便携性,Regridder2 是最佳选择。它几乎一样快,并且不需要安装 numba(对于我的 Anaconda 安装需要我去安装它) - 所以它有助于便携性。

% FUN: Regridder 1 - for 循环
import numpy as np

def Regridder1(inArray,factor):
inSize = np.shape(inArray);
outSize = [np.int64(np.round(inSize[0] * factor)), np.int64(np.round(inSize[1] * factor))];

outBlockSize = factor*factor #the block size where 1 inArray pixel is spread across # outArray pixels
outArray = np.empty(outSize) #preallcoate
outBlocks = inArray/outBlockSize #precalc the resized blocks to go faster
factor = np.int64(factor) #convert to an integer to be safe (in case it's a 1.0 float)

outArray = RegridderUpscale(inSize, factor, outArray, outBlocks) #call a function that has just the loop

return outArray;
#END def Regridder1

from numba import jit, prange
@jit(nogil=True, nopython=True, cache=True) #nopython=True, nogil=True, parallel=True, cache=True
def RegridderUpscale(inSize, factor, outArray, outBlocks ):
for i in prange(0,inSize[0]):
for j in prange(0,inSize[1]):
outArray[i*factor:(i*factor+factor),j*factor:(j*factor+factor)] = outBlocks[i,j];
#END for j
#END for i
#scales the original data up, note for other languages you need i*factor+factor-1 because slicing
return outArray; #return success
#END def RegridderUpscale

% FUN: Regridder 2 - numpy 基于@ZisIsNotZis 的回答
import numpy as np

def Regridder2(inArray,factor):
inSize = np.shape(inArray);
#outSize = [np.int64(np.round(inSize[0] * factor)), np.int64(np.round(inSize[1] * factor))]; #whoops

outBlockSize = factor*factor; #the block size where 1 inArray pixel is spread across # outArray pixels

outArray = np.broadcast_to( inArray[:,None,:,None]/outBlockSize, (inSize[0], factor, inSize[1], factor)).reshape(np.int64(factor*inSize[0]), np.int64(factor*inSize[1])); #single line call that gets the job done

return outArray;
#END def Regridder2

最佳答案

我使用 512x512 对此做了一些基准测试。字节图像(10 倍高档):

a = np.empty((512, 512), 'B')

重复两次
>>> %timeit a.repeat(10, 0).repeat(10, 1)
127 ms ± 979 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

重复一次 + reshape
>>> %timeit a.repeat(100).reshape(512, 512, 10, 10).swapaxes(1, 2).reshape(5120, 5120)
150 ms ± 1.72 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

以上两种方法都涉及复制两次,而下面两种方法都涉及复制一次。

花式索引

t可以重复使用(和预先计算),它是不定时的。
>>> t = np.arange(512, dtype='B').repeat(10)
>>> %timeit a[t[:,None], t]
143 ms ± 2.1 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

查看+ reshape
>>> %timeit np.broadcast_to(a[:,None,:,None], (512, 10, 512, 10)).reshape(5120, 5120)
29.6 ms ± 2.82 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

看来 查看 + reshape 获胜 (至少在我的机器上)。 2048x2048上的测试结果字节图像如下,其中 View + reshape 仍然获胜
2.04 s ± 31.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
2.4 s ± 18 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
2.3 s ± 25.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
424 ms ± 14.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

2048x2048 的结果 float64图像是
3.14 s ± 20.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
5.07 s ± 39.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
3.56 s ± 64.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
1.8 s ± 24.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

虽然 itemsize 大了 8 倍,但并没有花费更多的时间

关于Python - 使用 Numpy 快速放大数组,不允许使用图像库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53330908/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com