python - 堆叠图像的python代码运行速度极慢,寻找加快速度的建议
问题描述
我编写了一些代码来读取约 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的图像上运行代码。
我们将不胜感激地接受任何有关加快该计划的帮助。在转向更强大的系统时,它是否有机会大幅提高速度?
预先感谢您的任何帮助。
解决方案
附加到列表很慢。就像你可以在一个循环中做的事情有多个列表理解一样。您还可以使用numpy
数组来加速它,使用SIMD 操作而不是迭代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在下面的评论中提到的那样,追加到 numpy 数组是一个坏主意。这是因为 numpy array append 实际上创建了一个新数组,然后复制了两个数组的内容,所以这是一个 O(n^2) 操作。
推荐阅读
- javascript - Define interface by an array of values
- django - 在 Django 应用程序中的服务器启动期间将数据加载到内存中
- go - Golang fill slice in function not working
- django - 没有模型的django rest框架defaultRouter
- api - Salesforce 集成用户最佳实践
- javascript - 通过 Ajax 更新的 PayPal 智能按钮
- hadoop - 我如何启动 ambari hortonworks 服务?
- python - Python/BeautifulSoup - 如何将 html 代码分配给变量
- spring - 如何根据异常更改spring重试模板固定退避策略
- javascript - console.log 不显示类创建的函数