我有一个问题,我需要将一个非常大的2D阵列(磁盘上的文件)与一个适合内存的较小阵列进行卷积。当数组适合内存时,scipy.signal.fftconvolve
很好,但当数组不适合时,则无效。除了循环遍历每个数组中的所有点以手动计算卷积之外,还有其他合理的方法吗?我对数学不满意,但我想知道fftconvolve
是否可以分成几部分并重新组合一点点重叠?还有别的吗?
答案 0 :(得分:1)
我可以建议你采用两种不同的方法(虽然我不会冒险提供一些示例代码,但希望你不会介意把它搞清楚):
1)使用numpy.memmap
; " 内存映射文件用于访问磁盘上的大段文件,而无需将整个文件读入内存。 (...)memmap对象可以在接受ndarray的任何地方使用。"
2)将大数组拆分为切片,使用mode='full'
执行卷积,并叠加结果。对于每个瓷砖,您将获得一个"边界"在瓷砖周围,与卷积内核的宽度相同。
可以组合两种方法(例如,从memmapped文件中读取切片,并将结果叠加到另一个memmapped文件中,这是结果)。
答案 1 :(得分:1)
迫切需要heltonbiker的回应,快速完成它将非常重要。正如您的“瓷砖”的重新组装一样。如果您无法将大数组加载到内存中,则需要将其作为memmapped文件加载...但要将其作为memmapped文件加载,您需要先创建一个。
这是一个粗略的伪代码。
#you need to know how big your data matrix is
#todo: assign nrows and ncols based on your data.
fp = np.memmap('your_new_memmap_filename.dat', dtype='float32', mode='w+', shape=(nrows, ncols))#create file with appropriate dimensions
data = open('yourdatafile.txt', 'r')
i = 0
for line in data:
arr = map(float, line.strip().split(',')) #comma delimited? header row?
fp[i, :] = arr
i += 1
del fp #see documentation...del will flush the output and close the file.
现在要处理..可以继续或新脚本
convolve_matrix = somenumpyarray
fp_result = np.memmap('your_results_memmap_filename.dat', dtype='float32', mode='w+', shape=(nrows, ncols))
#open big file read only
fp = np.memmap('your_new_memmap_filename.dat', dtype='float32', mode='r', shape=(nrows, ncols))
chunksize = 10000 #?
for i in range(int(nrows/chunksize) - 1): #don't forget the remainder at the end
chunk = fp[i * chunksize: (i + 1) * chunksize, :]
res = fftconvolve(chunk, convolve_matrix)
fp_result[i * chunksize: (i + 1) * chunksize, :] = res
#deal with remainder
del fp_result
del fp
请注意,此伪代码不会重叠,您需要填补一些空白。此外,一旦您完成拼贴工作,请确保使用Joblib并并行处理拼贴。 https://pythonhosted.org/joblib/parallel.html 对不起,我不能提供更多代码,我有一个2-d tiler / reassembler我为gis制作但它不在这台电脑上。它可能甚至没有多大帮助,因为你的砖瓦不会返回实际的瓷砖,而是返回切片列表,可能是几个列表,它们在哪里抓取切片(在大数组上),在结果中放置它的位置(大结果数组)以及切片的位置切片的结果(从大数组中抓取的切片的结果)迭代切片列表并且处理将很容易。但是制作切片功能会很棘手。
for source_slice, result_slice, mini_slice in zip(source_slice, result_slice, mini_slice):
matrix2convolve = big_fp[source_slice[0]:source_slice[1], :]
convolve_result = fftconvolve(matrix2convolve, convolve_matrix)
big_result_fp[result_slice[0]:result_slice[1], :] = convolve_result[mini_slice[0]:mini_slice[1], :]
答案 2 :(得分:-1)
这听起来很幼稚,但如果你移动结果像素,它就是100%真实。
通常做的卷积是错误的。实际操作根本不需要内存。您只需读取文件,执行卷积,然后将其写回到同一位置。
执行算法时的缺陷是它要求我们有一个中心像素。如果您宁愿将结果像素放在左上角,请执行卷积。您可以简单地将卷积作为扫描线操作进行读取。由于此操作没有更改任何向下或向右的像素,因此结果是正确的。
一旦对先前像素的完全任意和毫无意义的依赖性被打破,你就可以做一些很酷的事情,比如组合卷积内核,并在当前正在读取的相同内存占用(或文件)内执行操作。 http://godsnotwheregodsnot.blogspot.com/2015/02/combining-convolution-kernels.html
请在此处查看来源http://pastebin.com/bk0A2Z5D
将像素置于中心的原因是它们不会移动。但是,它不值得。如果您在结果周围有相同的垃圾线,那么你真的可以将它们移回到同一个内存中的正确位置。实际上,如果你在右下角执行下一个卷积并在数组上向后迭代,那么你最终将结果返回到或多或少开始的地方,或者使用内核:
0,0
0,1
简而言之,问题的唯一原因是前段时间某人决定应该有一个中心像素。当您放弃这个愚蠢的想法时,您总是知道结果像素的位置,您可以进行有趣的操作,例如预编译卷积矩阵,从而使用组合内核立即执行所有卷积,并且,对您来说,纯粹通过从磁盘读取来执行操作。试想一下,你可能是第一个拥有美国宇航局仙女座星系图像模糊副本的人。
如果有人知道谁先决定要制作一个中心像素的历史,我有点想知道。因为没有它,卷积只是逐像素扫描线操作。