Monday, August 01, 2011

Convert Fuji 3D MPO to Anaglyph

The cheap Fuji 3D camera has been lots of fun but viewing the pictures is a challenge. I ordered some Red/blue specs on ebay and have been figuring out how to automate conversion of the MPO files from the camera through to good quality anaglyph images. (If you have red/blue glasses this will look good, believe me). Dural trash n treasure The code is pillaged from a couple of places including here where it is mangled by the blogging software and doesn't work with the current bumpy. I've fixed it up and here it is for your entertainment:
"""Make a red-blue 3d Anaglyph image from an MPO file
Based on code from:

import Image, ImageOps
import numpy
import sys
import os

_magic = [0.299, 0.587, 0.114]
_zero = [0, 0, 0]
_ident = [[1, 0, 0],
[0, 1, 0],
[0, 0, 1]]

# anaglyph methods from here: (fantastic demos)

true_anaglyph = ([_magic, _zero, _zero], [_zero, _zero, _magic])
gray_anaglyph = ([_magic, _zero, _zero], [_zero, _magic, _magic])
color_anaglyph = ([_ident[0], _zero, _zero], [_zero, _ident[1], _ident[2]])
half_color_anaglyph = ([_magic, _zero, _zero], [_zero, _ident[1], _ident[2]])
optimized_anaglyph = ([[0, 0.7, 0.3], _zero, _zero], [_zero, _ident[1], _ident[2]])
methods = [true_anaglyph, gray_anaglyph, color_anaglyph, half_color_anaglyph, optimized_anaglyph]

def anaglyph(image1, image2, method=true_anaglyph):
    m1, m2 = [numpy.array(m).transpose() for m in method]
    im1, im2 = image_to_array(image1), image_to_array(image2)
    composite =, m1) +, m2)
    result = array_to_image(image1.mode, image1.size, composite)
    return result

def image_to_array(im):
    s = im.tostring()
    dim = len(im.getbands())
    return numpy.fromstring(s, 'uint8').reshape(len(s)/dim, dim)

def array_to_image(mode, size, a):
    return Image.fromstring(mode, size, a.reshape(len(a)*len(mode), 1).astype('uint8').tostring())

for filename in sys.argv[1:]:
    if filename.lower().endswith('.mpo'):
        print "reading %s" % filename
        basename = os.path.splitext(os.path.basename(filename))[0]
        file = open(filename, 'rb')
        data = # read both images
        offset = data.find('\xFF\xD8\xFF\xE1', 4)
        firstData = data[:offset - 4]
        leftFileName = '%s-left.jpg' % basename
        left = open(leftFileName, 'wb')

        rightData = data[offset:]
        rightFileName = '%s-right.jpg' % basename
        right = open(rightFileName, 'wb')

        nativeWidth = 3584
        nativeHeight = 2016

        desiredWidth = 1000
        fullHeight = desiredWidth * nativeHeight/ nativeWidth

        left =
        left = ImageOps.autocontrast(left)
        right =
        right = ImageOps.autocontrast(right)


        left = left.resize((desiredWidth, fullHeight), Image.ANTIALIAS)
        right = right.resize((desiredWidth, fullHeight), Image.ANTIALIAS)

        combined = anaglyph(left, right, half_color_anaglyph)
        outFileName = "%s_Anaglyph.jpg" % basename, "JPEG")
        print("Wrote %s" % outFileName)

You run it like this:
$ *.MPO
reading DSCF0005.MPO
Wrote DSCF0005_Anaglyph.jpg
reading DSCF0006.MPO
Wrote DSCF0006_Anaglyph.jpg
reading DSCF0007.MPO
Wrote DSCF0007_Anaglyph.jpg
DSCF0081 Anaglyph DSCF0077 Anaglyph DSCF0072 Anaglyph


Alan Yates said...

The pics look great, I recently got a cheap pair of anaglyph glasses from deal extreme. Quite comfortable and pretty good build quality, much better than the cheapy cardboard and cellophane ones you once got at the movies.

Peter Marks said...

I was surprised how well they work out, even in colour, I'm sure the old movies we used to see were pretty much black & white.

Thanks for the tip on the proper glasses, I'll order a pair. I've just got cheap cardboard ones that will soon fall apart.

Unknown said...

Also you can check "Stereo Master" software if you are using Mac.