I've compiled a mosaic of 2025 headshots from the avatars of the top Stack Overflow users.
(Click the image to view it full-size.)
Your task is to write an algorithm that will create an accurate photomosaic of another image using the 48×48 pixel avatars from this 45×45 grid of them.
Test Images
Here are the test images. The first is, of course, a light bulb!
(They are not full-sized here. Click an image to view it at full-size. Half-sized versions are available for The Kiss, A Sunday Afternoon..., Steve Jobs, and the spheres.)
Thanks to Wikipedia for all but the raytraced spheres.
At full-size these images all have dimensions divisible by 48. The larger ones had to be JPEGs so they could be compressed enough to upload.
Scoring
This is a popularity contest. The submission with mosaics that most accurately depict the original images should be voted up. I will accept the highest voted answer in a week or two.
Rules
Your photomosaics must be entirely composed of unaltered 48×48 pixel avatars taken from the mosaic above, arranged in a grid.
You may reuse an avatar in a mosaic. (Indeed for the larger test images you'll have to.)
Show your output, but keep in mind that the test images are very large, and StackExchange only allows posting of images up to 2MB. So compress your images or host them somewhere else and put smaller versions here.
To be confirmed the winner you must provide PNG versions of your light bulb or spheres mosaics. This is so I can validate them (see below) to ensure you are not adding extra colors to the avatars to make the mosaics look better.
Validator
This Python script can be used to check whether a completed mosaic really uses unaltered avatars. Just set toValidate
and allTiles
. It is unlikely to work for JPEGs or other lossy formats since it compares things exactly, pixel-for-pixel.
from PIL import Image, ImageChops
toValidate = 'test.png' #test.png is the mosaic to validate
allTiles = 'avatars.png' #avatars.png is the grid of 2025 48x48 avatars
def equal(img1, img2):
return ImageChops.difference(img1, img2).getbbox() is None
def getTiles(mosaic, (w, h)):
tiles = {}
for i in range(mosaic.size[0] / w):
for j in range(mosaic.size[1] / h):
x, y = i * w, j * h
tiles[(i, j)] = mosaic.crop((x, y, x + w, y + h))
return tiles
def validateMosaic(mosaic, allTiles, tileSize):
w, h = tileSize
if mosaic.size[0] % w != 0 or mosaic.size[1] % h != 0:
print 'Tiles do not fit mosaic.'
elif allTiles.size[0] % w != 0 or allTiles.size[1] % h != 0:
print 'Tiles do not fit allTiles.'
else:
pool = getTiles(allTiles, tileSize)
tiles = getTiles(mosaic, tileSize)
matches = lambda tile: equal(tiles[pos], tile)
success = True
for pos in tiles:
if not any(map(matches, pool.values())):
print 'Tile in row %s, column %s was not found in allTiles.' % (pos[1] + 1, pos[0] + 1)
success = False
if success:
print 'Mosaic is valid.'
return
print 'MOSAIC IS INVALID!'
validateMosaic(Image.open(toValidate).convert('RGB'), Image.open(allTiles).convert('RGB'), (48, 48))
Good luck everyone! I cant wait to see the results.
Note: I know photomosaic algorithms are easy to find online, but they aren't yet on this site. I'm really hoping we see something more interesting than the usual "average each tile and each grid space and match them up" algorithm.