Python memory management with a median image stacker
Question: I've been playing with time-lapse photography lately, and using median image
stacking on a group of images, or extracted video frames. I've created a
little script that works well with a relative few images:

    
    
    from PIL import Image
    import os
    import numpy as np
    #Create a list of the images' data
    imglist = []
    for fname in os.listdir("input\\"):
        imglist.append(np.array(Image.open("input\\"+fname)))
    #Find the median of all image data in the stack, save it
    median = np.uint8(np.median(imglist, axis=0))
    Image.fromarray(median).save("median.png","PNG")
    

The obvious problem here is that if too many images are loaded into memory at
once, they fill up my meager 8GB of RAM. I have tried approaches which include
splitting the image data into chunks and calculating the median one chunk at a
time, and splitting the image data by color channel, but when there are that
many images to process, the amount of file calls it takes to save the data to
the disk image by image causes a massive slowdown.

I am wondering if there's any way I can use some weighting scheme to calculate
the median for a few images at a time, then repeat with the results, or use a
gimmick like virtual memory, memory-mapped files, or something else to
eliminate this excessive memory usage. Any ideas?


Answer: How about using [](http://www.h5py.org/ "h5py")<http://www.h5py.org/>, an HDF5
interface for Python? HDF5 is a data format designed for handling massive
gridded datasets, and with h5py you can manipulate that data with NumPy.
NetCDF is good too, and integrates HDF5.

What kind of resolution are the images, and how many of them are there?

Here is a solution to your problem which uses netCDF4 to slice out (400,400)
regions of a collection of images, adds them to a netCDF file, and takes the
median at the end:

    
    
    import glob
    from netCDF4 import Dataset
    import numpy as np
    from PIL import Image
    
    WIDTH = 800
    HEIGHT = 450
    
    root = Dataset('test.nc', 'w')
    
    root.createDimension('x', WIDTH)
    root.createDimension('y', HEIGHT)
    root.createDimension('channels', 3)
    t = root.createDimension('t', None)
    
    img = root.createVariable('image', 'u1', ('t','y','x','channels'))
    
    images = glob.glob('images/*')
    
    for i,fname in enumerate(images):
        im = Image.open(fname)
        im_array  = np.asarray(im)
        img[i] = im_array
    
    median = np.median(img, axis=0)
    im = Image.fromarray(np.uint8(median))
    im.save('out.png')
    

