从一系列图像中找到最大的 x 和 y 值

4
我有一堆位图图像(大约在2000-4000张之间),我要对它们进行最大强度投影的z-projection。因此,我需要从这个堆栈中得到每个x,y位置的最大值的2d数组。
我设计了一个简单的脚本将文件分成多个块,并使用multiprocessing.pool来计算该块的最大数组。然后比较这些数组,以找到堆栈的最大值。
虽然它能工作,但速度很慢。我的系统监视器显示我的CPU几乎没有运行。
请问有人可以给我一些建议如何加快处理速度吗?
import Image
import os
import numpy as np
import multiprocessing
import sys

#Get the stack of images
files = []
for fn in os.listdir(sys.argv[1]):
    if fn.endswith('.bmp'):
        files.append(os.path.join(sys.argv[1], fn))

def processChunk(filelist):
    first = True
    max_ = None
    for img in filelist:
        im = Image.open(img)
        array = np.array(im)
        if first:
            max_ = array
            first = False
        max_ = np.maximum(array, max_)
    return max_



if __name__ == '__main__':

    pool = multiprocessing.Pool(processes=8)

    #Chop list into chunks
    file_chunks = []
    chunk_size = 100
    ranges = range(0, len(files), chunk_size)

    for chunk_idx in ranges:
        file_chunks.append(files[chunk_idx:chunk_idx+chunk_size])

    #find the maximum x,y vals in chunks of 100
    first = True
    maxi = None
    max_arrays = pool.map(processChunk, file_chunks )

    #Find the maximums from the maximums returned from each process
    for array in max_arrays:
        if first:
            maxi = array
            first = False
        maxi = np.maximum(array, maxi)
    img = Image.fromarray(maxi)
    img.save("max_intensity.tif")

你可能在内存访问方面遇到了瓶颈,这就是为什么你的CPU没有达到最大值的原因。如果我正确地阅读了代码,那么每次选择x和y时,你都会获取一张图像。相反,创建一个与你的图像一样大的临时矩阵,并填充矩阵中的每个单元格。然后,加载下一张图像并进行z投影。如果你正在进行多核处理,你必须对哪个(x,y)单元格最强进行最终评估。 - SWPhantom
这是一个100%并行操作,所以如果你真的想在这里发疯,你可以尝试使用pycuda或pyopencl。对于相当大的图像,这将提供良好的性能。对于小图像,开销可能会抵消性能增益。 - M4rtini
1
只是一个想法:如果图像未经压缩,您的瓶颈可能是磁盘IO。除了解压缩图像格式之外,这似乎不是(CPU密集型操作)。无论如何,这可以解释低CPU利用率。 - Joe Kington
@JoeKington 我会尝试压缩图像。好主意。 - nrhorner
1个回答

4

编辑:

我对一些样本数据进行了小规模的基准测试,你是正确的。此外,通过仔细阅读你的代码,发现我的原始帖子大部分都是错误的。你实际上正在执行相同数量的迭代(略微多一些,但不是3倍)。我还发现

x = np.maximum(x, y)

比两者都要稍微快一点。

x[y > x] = y[y > x]
#or
ind = y > x
x[ind] = y[ind]

我会稍微修改你的代码,大概像这样:
```html

我接下来只会略微修改你的代码。就像这样:

```
import numpy as np
from multiprocessing import Pool

def process(chunk):
    max_ = np.zeros((4000, 4000))
    for im in chunk:
        im_array = np.array(Image.open(im))
        max_ = np.maximum(max_, im_array)
    return max_

if __name__ == "__main__":
    p = Pool(8)

    chunksize = 500 #4000/8 = 500, might have less overhead
    chunks = [files[i:i+chunksize]
              for i in range(0, len(files), chunksize)]

    # this returns an array of (len(files)/chunksize, 4000, 4000)
    max_arrays = np.array(p.map(process, chunks))
    maxi = np.amax(max_array, axis=0) #finds maximum along first axis
    img = Image.fromarray(maxi) #should be of shape (4000, 4000)

我认为这是最快的方法之一,尽管我有一个树形或锦标赛样式算法的想法,可能也是递归的。做得好。


图片有多大?足够小,可以同时加载两张图片到内存中吗?如果可以,那么你可以做一些像这样的事情:

maxi = np.zeros(image_shape) # something like (1024, 1024)

for im in files:
    im_array = np.array(Image.open(im))
    inds = im_array > maxi # find where image intensity > max intensity
    maxi[inds] = im_array[inds] # update the maximum value at each pixel 

max_im = Image.fromarray(maxi)
max_im.save("max_intensity.tif")

所有迭代完成后,maxi数组将包含每个(x, y)坐标的最大强度。不需要将其分成块。此外,只有一个for循环,而不是3个,因此速度更快,可能不需要多进程。


应该是 inds = im_array > maximaxi[inds] = im_array[inds] 吗? - wwii
应该可以。谢谢。我已经编辑了帖子以保持一致。 - wflynny
谢谢。这真的很简单。然而,我的原始脚本仍然更快。你的解决方案花了4分19秒,而我的只花了1分20秒。这些图像是4000 x 4000。 - nrhorner
@halex 实际上这些文件是20002000。我之前处理的是4k4k的文件。我也有SSD。干杯 - nrhorner
@user521469 好的,这改变了情况,打开和加载“仅”需要40秒来处理2000张2000x2000像素的图片。你的解决方案仍然非常快:). - halex
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接