【问题标题】:The python code to stack images runs extremely slow, looking for suggestions to speed it up堆叠图像的python代码运行速度极慢,寻找加快速度的建议
【发布时间】:2020-08-13 14:56:58
【问题描述】:

我编写了一些代码来读取大约 150 张图像(1000 像素 x 720 像素,裁剪和调整大小)的每个像素的 RGB 值。

import os
from PIL import Image
print("STACKING IMAGES...")
os.chdir('cropped')
images=os.listdir() #list all images present in directory
print("GETTING IMAGES...")
channelR=[]
channelG=[]
channelB=[]
print("GETTING PIXEL INFORMATION...")  #runs reasonably fast
for image in images:  #loop through each image to extract RGB channels as separate lists
    with Image.open(image) as img:
        if image==images[0]:
            imgSize=img.size
        channelR.append(list(img.getdata(0)))
        channelG.append(list(img.getdata(1)))
        channelB.append(list(img.getdata(2)))
print("PIXEL INFORMATIION COLLECTED.")
print("AVERAGING IN CHANNEL RED.") #average for each pixel in each channel
avgR=[round(sum(x)/len(channelR)) for x in zip(*channelR)] #unzip the each pixel from all ~250 images, average it, store in tuple, starts to slow
print("AVERAGING IN CHANNEL GREEN.")
avgG=[round(sum(x)/len(channelG)) for x in zip(*channelG)] #slower
print("AVERAGING IN CHANNEL BLUE.")
avgB=[round(sum(x)/len(channelB)) for x in zip(*channelB)] #progressively slower
print("MERGING DATA ACROSS THREE CHANNELS.")
mergedData=[(x) for x in zip(avgR, avgG, avgB)]  #merge averaged colour channels pixel by pixel, doesn't seem to end, takes eternity
print("GENERATING IMAGE.")
stacked=Image.new('RGB', (imgSize)) #create image
stacked.putdata(mergedData) #generate image
stacked.show()
os.chdir('..')
stacked.save('stacked.tif', 'TIFF') #save file
print("FINISHED STACKING !")

在我配备适中的计算机(Core2Duo、4GB RAM、Linux Mint OS)上运行它需要将近一个小时才能完成三个通道的平均,然后再用一个小时来合并各个平均像素(没有完成,我中止了这个过程)。我读过列表理解很慢,并且 zip() 函数占用了太多内存,但是修改这些会导致进一步的错误。我什至读过将程序划分为函数可能会加快速度。

对于可比较的性能,我恳请回答问题的人在来自https://github.com/rlvaugh/Impractical_Python_Projects/tree/master/Chapter_15/video_frames 的图像上运行代码。

我们将不胜感激地接受任何有关加快程序速度的帮助。它是否有机会在转向更强大的系统时大幅提高速度?

提前感谢您的帮助。

【问题讨论】:

  • 如果您不循环遍历每个图像中的每个像素两次,您可能会节省一些时间。您可以为每个通道维护一个值列表,并在读取通道时将所有像素的值除以图像数量,而不是仅仅将完整的通道附加到一个大列表中以供稍后分析。因此,您将一次性创建三个平均值列表。
  • 列表推导通常比构建没有推导的列表要慢。如果这些理解很慢,扩展它们可能会为您节省一些时间
  • @JacobSteinebronn, this 似乎另有说明,就像我看到的许多其他链接一样。特别是在附加到列表时。
  • 这很好!就个人而言,我已经尝试过,发现列表比较慢,也许它取决于应用程序?无论如何,它并没有那么慢很多,就像 20% 左右
  • 使用numpytifffileimagecodecs库尝试tifffile.imwrite('stacked.tif', numpy.stack([imagecodecs.imread(name) for name in glob.glob('*.jpg')]).mean(axis=0).round().astype('uint8'))。示例数据集大约需要2秒。

标签: python performance time-complexity


【解决方案1】:

追加到列表很慢。就像你可以在一个循环中做的事情有多个列表理解一样。您还可以使用 numpy 数组来加速它,使用 SIMD operations 而不是迭代 list

以下是一些图片的示例代码。您可以根据需要对其进行扩展。

import os
import numpy as np
import PIL

os.chdir('cropped')

imgfiles = ['MVI_6450 001.jpg', 'MVI_6450 002.jpg', 'MVI_6450 003.jpg', 'MVI_6450 004.jpg']

allimgs = None

for imgnum, imgfile in enumerate(imgfiles):
    img = PIL.Image.open(imgfile)
    imgdata = np.array(img.getdata()) # Nx3 array. columns: R, G, B channels
    
    if allimgs is None:
        allshape = list(imgdata.shape) # Size of one image
        allshape.append(len(imgfiles)) # Append number of images
        # allshape is now [num_pixels, num_channels, num_images]
        # so making an array of this shape will allow us to store all images
        # Axis 0: pixels. Axis 1: channels. Axis 2: images
        allimgs = np.zeros(allshape) 
    
    allimgs[:, :, imgnum] = imgdata # Set the imgnum'th image data
    

# Get the mean along the last axis 
#     average same pixel across all images for each channel
imgavg = np.mean(allimgs, axis=-1) 

# normalize so that max value is 255
# Also convert to uint8
imgavg = np.uint8(imgavg / np.max(imgavg) * 255)

imgavg_tuple = tuple(map(tuple, imgavg))

stacked = PIL.Image.new("RGB", img.size)
stacked.putdata(imgavg_tuple)
stacked.show()

os.chdir('..')

注意:我们在开始时创建一个 numpy 数组来保存所有图像,而不是在加载更多图像时追加,因为将 Jacob@987654323 附加到 numpy 数组是一个糟糕的糟糕想法@。这是因为 numpy array append 实际上创建了一个新数组,然后复制了两个数组的内容,所以这是一个 O(n^2) 操作。

【讨论】:

  • 非常重要的注意事项:附加到 NUMPY 数组是可怕的!附加到常规列表会更好。这是因为 numpy.append 不是就地的,所以要通过附加来构建一个包含 n 个项目的列表,你将有 O(n^2) 这显然非常糟糕。
  • 但我在我的代码中的任何位置追加到 numpy 数组?
  • 我没有说你这样做,只是向海报指出(因为他们对这种东西似乎相对较新)构建这样的 numpy 数组可能非常糟糕
  • @PranavHosangadi,Traceback(最近一次调用最后):文件“stackImg.py”,第 26 行,在 stacked.putdata(imgAvgTuple) 文件“/usr/lib/python3/dist- packages/PIL/Image.py",第 1626 行,在 putdata self.im.putdata(data, scale, offset) TypeError: integer argument expected, got float
  • putdata() 需要 int 中的像素值,但 mean() 以浮点数返回
猜你喜欢
  • 2017-12-20
  • 2018-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-29
  • 2016-02-29
相关资源
最近更新 更多