Thursday, January 19, 2012

Larger mosaics

Our mosaic effort is more proof of concept  than anything else. Although I do think you can get good results with a single tile image and grayscale, mosaics have to be large enough to see detail in the tiles. Even a 2K pixel image is too small, though it is what we can manage with our screen size. That said, I wanted to do something larger, so I took this 3264x2448 pixel image of my daughter:


and doubled each dimension to make a 5176x3520 grayscale:


Assume that if you wanted to print the image then you would want 300 dots per inch. At these dimensions the image would print 5176/300 --> 17.25 inches x 3520/300 --> 11.7 inches, assuming square pixels.

I grabbed this ( 214x290) as a tile (almost 1x1 at 300 dpi):


and then had some trouble.

The trouble was that JES reported a error from Java running our of heap space. The short explanation is that each program you run is allocated a certain amount of memory it can use. That memory is mostly in an area called the "heap", which allows the program to repurpose the memory. It is possible to tell your computer to give Java (more heap space, but the commands are specific to Java versions and and require administrative access.) Instead I decided to follow this plan:

  1. Break the large target image into 4 quadrants (each as large as the original image)
  2. Make a mosaic of each.
  3. Stitch the mosaics back into one final mosaic.
The advantage with regard to memory is that I'm never making a mosaic larger than the original image, and only one at a time, until I decide to stitch. But "stitching" just uses placeInCanvas().

In the end I got this, with the tiles only 38x61 (about 1/8 x 1/5 inches at 300 dpi). 


Here is the top left quadrant ready to be tiled:



Here is the top left tiled:


The dark line at the right edge is because the tile did not divide the quadrant evenly, so I cropped this down to what it should have been (dropping an entire column of tiles):



Here is the code. The top level function is called Lucy() -- it's a custom script for this problem, but easily adapted to other images.


def lucy():
  tileFile = pickAFile()
  tile = makePicture(tileFile)
  targetFile = pickAFile()
  
  gsFamily( tile, 'Tiles', 1 )
  tile = None
  split( makePicture( targetFile ) )
  for pic in [ 'tl.jpg', 'tr.jpg', 'bl.jpg', 'br.jpg' ]:
    f, target = grabPic( pic )
    gm = grayMap( target, 38, 61 )
    pm = doTiling( 'Tiles',gm, 38, 61 )
    writePictureTo( pm, 'pm'+pic )
  
  
def split( target ):
  w = getWidth(target)
  h = getHeight(target)
  lx1 = 0
  lx2 = w/2 + 1
  rx1 = lx2+1
  rx2 = w
  ty1 = 0
  ty2 = h/2 + 1
  by1 = ty2+1
  by2 = h
  tl = crop( target, lx1, ty1, lx2, ty2 )
  tr = crop( target, rx1, ty1, rx2, ty2)
  bl = crop( target, lx1, by1, lx2, by2)
  br = crop( target, rx1, by1, rx2, by2) 
  writePictureTo( tl, 'tl.jpg' )
  writePictureTo( tr, 'tr.jpg' )
  writePictureTo( bl, 'bl.jpg' )
  writePictureTo( br, 'br.jpg' )
  
  


  
def knit():
  f,p = grabPic('tlcrop.jpg')
  w = getWidth(p)
  h = getHeight(p)
  c = makeCanvas( 2*w, 2*h )
  placeInCanvas(p,c,0,0)
  f,p = grabPic('trcrop.jpg')
  placeInCanvas(p,c,w,0)
  f,p = grabPic( 'blcrop.jpg')
  placeInCanvas(p,c,0,h)
  f,p = grabPic( 'brcrop.jpg')
  placeInCanvas( p,c,w,h)
  writePictureTo(c,'pmknitted.jpg')
  return c

No comments:

Post a Comment