1

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?

dano
  • 91,354
  • 19
  • 222
  • 219

1 Answers1

0

How about using 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')
Joseph Sheedy
  • 6,296
  • 4
  • 30
  • 31
  • I'm currently looking at an hour-long webcam recording of Sydney, Australia. Separated to its constituent frames, the recording represents 54,000 frames of resolution 800x450. HDF5 might work, but I don't see a way to calculate medians with it, though many commands are analogous to numpy's. I suppose I could just do it a pixel at a time, that could work. – Classiest Medic Oct 20 '14 at 19:52
  • 1
    Check out the code I added above. I used it to take the median of a large number of images. The NetCDF file gets large, but RAM usage stays low. Interesting technique, I'd love to see the results you get. – Joseph Sheedy Oct 21 '14 at 17:08