Photo Mosaic Algorithm. How to create a mosaic photo given the basic image and a list of tiles?
I've done this in Scala. The Dr Dobbs article was extremely useful to me.
Sample image:
Here's my basic algorithm:
def createMosaic(targetImage:BufferedImage,
index:PhotoIndexer.PhotoIndex,
opacity:Float,
targetWidth:Int,
targetHeight:Int,
numRows:Int,
numColumns:Int, callback:PhotoMosaicCallback): ImageGrid = {
var indexCopy = index
// Map from the buffered image to that image's average color
var colorMap:Map[BufferedImage,Color] =
index.values.map(data => (data.thumbnail, data.avgColor)).toMap
// We look at rectangular regions of the target image, calculate their average
// colors, and then pick images that match those colors.
val sampleWidth = targetImage.getWidth / numColumns
val sampleHeight = targetImage.getHeight / numRows
// Used to report the progress of the process
var counter = 1
val numSubImages = numRows * numColumns
val imageGrid:ImageGrid = Array.fill(numRows, numColumns)(Nil)
// for each patch in the image
for (row <- 0 until numRows) {
for (column <- 0 until numColumns) {
val x = column * sampleWidth
val y = row * sampleHeight
// This is the small rectangular region of the target image that we're
// currently considering
val subImage = targetImage.getData(new Rectangle(x,y,sampleWidth,sampleHeight))
val avgImageColor = calculateColorFromRaster(subImage)
val nearest:Seq[BufferedImage] = getNearestColorImages(avgImageColor, colorMap)
// nearest is in sorted order; pick one of them and draw it to correct place in
// image
imageGrid(row)(column) = nearest
callback.photosCalculated(row, column, nearest)
val percent = 100.0 * counter / numSubImages
// TODO: for GUI version, use a display bar
if (counter % 100 == 0) {
println(percent + " completed (" + counter + " of" + numSubImages + ")")
}
counter+=1
}
}
imageGrid
}
My full sourcecode is available on github
Let's say your basic image is 100x100 pixels, and you have a bunch of 10x10 tiles.
You want to mosaic the basic image with 400 of the little tiles, so each tile comprises 5x5 pixels in the basic image.
For each 5x5 part in the basic image, determine the average RGB values for those pixels.
For each tile, determine the average RGB values.
Match up the average RGB values of each 5x5 part to the closest match from the tiles.
Then create your mosaic. You'll need to scale the tiles down to 5x5 to keep the image size the same, though.