Recording 360 degree inverse panorama views of cylindrical objects

There are many objects, like vases or jugs, which have decoration all round their surface. A photo like below can only show one direction, and the rest is either foreshortened & distorted, or the back & sides are invisible.

By mounting the object on a stepper-motor turntable, it is easy to get the rotate-and-step sequence. I'm still experimenting whether it is better to use a webcam or digital casmera. Things will change once the Pi camera is released.

I'm also still seeing whether I can automate, via ImageMagick, the merging of the images. At present it is easy to automate the cropping, and the joining. Just needs choosing suitable strip size for a given object. Any necessary scaling & colour /contrast adjustment could also be automated. It may be a better idea to feather the edges and overlap them a bit???. But I've had little success using panorama-merging software.

In IM, the bulk creation of the strips from the photos & their placing consecutively in one image is as follows....

To select a 40 by 800 vertical strip at position 800 sideways and 20 down from all the files called A*.jpg......

    convert A*.jpg -crop 40x800+800+20 stripes%03d.jpg

To join a set of strips called I*.jpg horizontally sideways..

    convert I*.jpg +append joined.jpg

My solution is to photograph from 'every' direction; dissect out the vertical strip exactly faciing the camera; and then stiutch the results together as a 2D view of ALL the surface. For a proof-of-concept I got the following results- the camera was hand held & not very steady, and no attempt was made to get good lighting & focus. Encouraging, anyway.

Here are seven images replayed as an animated gif. ( it's so easy to make these in Java with 'GIFted Motion'.)

Here are 54 images replayed the same way.

This is one of the sequence of photos, with beside it the corresponding central strip... I took seven of these.

-

.... and this is the result of a quick manual overlap of seven photos.

Here is a second attempt. The original has 4x bigger resolution on each axis. The strip width I chose was too big, hence the matching is bad. I need also to start thinking more about lighting direction & consistency, and about background. And reverse the scanning rotation- IM was stitching them left-right reversed..

Worse lighting, hence bad colour cast. Shown here at full size so you can see where ther problems are. Got the strip size close but not quite right, and the lighting shows bad specular reflections highlights.


Practical details

The set-up used is shown here, with a Pi, USB lead to the EBB, and camera & turntable. I've lots of other ideas involving scanning cameras that I hope to implement, plus time-lapse & stop-motion applications.

The EiBotBoard accepts serial commands from any terminal or program. Send it 'sm,3000,0,500' and it will spend the next 1000 ms turning motor 2 by 500 MICRO-steps, where, by proportionally chopping the drive current, you effectively increase a 48 step motor to 48 *16 steps per revolution.

The Pi drives it in a few lines of Python, tho' in the past I always used Liberty BASIC under Microsoft or Wine/Linux. See also my eggbots, etc...

The following shows how easy it is to drive steppers or servos or digital I/O via the EBB. See EiBotBoard home page. Buy it from SparkFun. You can find my eggbots on Eggbots and my other liberty BASIC work via my web site I'm very greatful to Bruce Shapiro and Brian Schmalz for creative ideas & inspiration.


#!/usr/bin/env python

import time
from time import sleep

import serial

ser = serial.Serial( "/dev/ttyACM0", 115200)

#   check EBB & version number
ser.write( 'v' +"\r\n")
time.sleep( 1)
print ser.readline()

while 1:
    print "Rotate & take photo.."
    print "Step motor #1 120 steps, taking 1 seconds, then wait 1s more."
    #   1000ms, motor 1 120 steps, motor 2 none.
    ser.write( "sm,1000,120,0\r\n")
    s = ser.readline()
    #print s
    time.sleep( 2)

    print "Pen servo cw  on pin 4.  Delay 2 second."
    #   channel 1, duration in 12-millionths of a second, pin 4, not at slowed rate
    ser.write( "tp\r\n")
    s = ser.readline()
    #print s
    time.sleep( 2)

    print "Pen servo acw on pin 4.  Delay 2 second."
    ser.write( "tp\r\n")
    s = ser.readline()
    #print s
    time.sleep( 2)

    print ""
..and similar code as used in Liberty BASIC....

    '   *************************************************************************
    '   **                                                                     **
    '   **     inversePanoramas.bas     tenochtitlanuk      December 2012      **
    '   **                                                                     **
    '   **  Assumes UBW attached to COM18, stepper rotates camera platform     **
    '   **                                                                     **
    '   **  Camera stores images as F:\DCIM\143_PANA\     nnn.jpg              **
    '   **                                                                     **
    '   *************************************************************************

    '   To-dos and problems/ ideas for changes
    '       Make number of shots /revolution a user-entered variable.
    '       Use file dialog to select where images are..
    '       Add a rescaling stage & contrast/ colour enhancement.
    '       Make the waits shorter by looking for process completion.
    '       Display the end result.

open "COM18: 115200, 8, N, 1, RS, DS0, CS0" for random as #u

#u "v"  '   check EBB & version number
calldll #kernel32, "Sleep", 400 as long, re as void
print " "; input$( #u, lof( #u))
print

photo =1

while photo <36 '   take every 10 degrees
    print " Rotate & take photo # "; photo
    photo =photo +1

    print "    Step motor #1 120 steps, taking 1 seconds, then wait 1 second more. ";
    #u "sm,1000,120,0"
    calldll #kernel32, "Sleep", 2000 as long, re as void
    print input$( #u, lof( #u))

    '   Pen servo is used to depress & release shutter...
    print "    Pen servo cw  on pin 4.  Delay 2 second. ";
    #u "tp"
    calldll #kernel32, "Sleep", 2000 as long, re as void
    print input$( #u, lof( #u))

    print "    Pen servo acw on pin 4.  Delay 2 second. ";
    #u "tp"
    calldll #kernel32, "Sleep", 2000 as long, re as void
    print input$( #u, lof( #u))

    print ""
    scan
wend

notice "Switch off camera & insert memory card in computer as drive F:"

 'To select a 40 by 800 vertical strip at position 800 sideways and 20 down from all the files called A*.jpg......
    IM$ ="convert F:\DCIM\143_PANA\A*.jpg -crop 40x800+800+20 F:\DCIM\143_PANA\stripes%03d.jpg"
    '   photo names are A*.jpg & in memory card ( F:) root directory
    print IM$
    run "cmd.exe /c "; chr$( 34); IM$; chr$( 34), HIDE
        '   Give it 10 minutes ( !) to execute- may not be enough for large images.
    timer 600000, [on2]
    wait
  [on2]
    timer 0

 'To join a set of strips called I*.jpg on card F: horizontally sideways, adding on right..
    IM$ =" convert F:\DCIM\143_PANA\stripes*.jpg +append F:\DCIM\143_PANA\joined.jpg"
    '   photo names are stripes*.jpg & in memory card ( F:) root directory
    print IM$
    run "cmd.exe /c "; chr$( 34); IM$; chr$( 34), HIDE
        '   Give it 10 minutes ( !) to execute- may not be enough for large images.
    timer 600000, [on3]
    wait
  [on3]
    timer 0

Progress

Getting better. LB program selects a slice from each of 54 views & abuts them. Working direct on pixels, rather than my usual use of ImageMagick, as I want to be able to do overlapping merge at the joins in future. Still need to get illumination, colour balance & stability a bit better, then can try say every 2 degrees.

Right hand is the real object, lefthand is a cylinder of paper from my printer.