0

Photographic mosaic is a technique of re-generating an existing image as a mosaic of thumbnails. The color of the original pixels should roughly resemble the color of the covering tile.

For example, a role-playing gamer re-generated the world map from thumbnail images of users.

enter image description here

The source code for this image is shared on github, but it's pretty tailored for the specific world-map task.

Is there a general solution for re-generating an existing image as a collage/mosaic of a set of given thumbnails?

Community
  • 1
  • 1
Adam Matan
  • 128,757
  • 147
  • 397
  • 562
  • It can't be that hard. Tile the big image into tiles of the desired size and get the average color of each tile - that's one line of ImageMagick. Resize all thumbnails to the desired tile size and get each one's average colour - another line of ImageMagick.Go through the big image and for each tile in it, choose the nearest matching thumbnail for the mosaic output. – Mark Setchell Feb 08 '16 at 11:46
  • No, you're right, it really ain't that hard, but I don't like reinventing the wheel if there's a nice, existing, open source solution for that (to which I might contribute). – Adam Matan Feb 08 '16 at 11:48

1 Answers1

4

Proof of concept follows, as a simple bash script with ImageMagick to do the image processing work.

#!/bin/bash

# Take all JPEGS in current directory and get their average RGB color and name in "tiles.txt"
for f in *.jpg; do convert $f -depth 8 -resize 1x1! -format "%[fx:int(mean.r*255)] %[fx:int(mean.g*255)] %[fx:int(mean.b*255)] $f\n" info: ; done > tiles.txt

# Create empty black output canvas same size as original map
convert map.png -threshold 100% result.png

# Split map into tiles of 10x10 and get x,y coordinates of each tile and the average RGB colour
convert map.png -depth 8 -crop 10x10 -format "%X %Y %[fx:int(mean.r*255)] %[fx:int(mean.g*255)] %[fx:int(mean.b*255)]\n" info: | 
   while read x y r g b; do
      thumb=$(awk -v R=$r -v G=$g -v B=$b '
         NR==1{nearest=3*255*255*255;tile=$4}
         { 
            tr=$1;tg=$2;tb=$3
            # Calculate distance (squared actually but sqrt is slow)
            d=((R-tr)*(R-tr))+((G-tg)*(G-tg))+((B-tb)*(B-tb))
            if(d<nearest){nearest=d;tile=$4}
         }
         END{print tile}
      ' tiles.txt)
      echo $x $y $r $g $b $thumb
      convert result.png -draw "image copy $x,$y 10,10 \"$thumb\"" result.png
   done

enter image description here

I don't have an endless supply of thumbnails but the concept seems to work. The maths of distance between colours is done in awk and obviously could be done in a more perceptually uniform colorspace and also things could be speeded up considerably. Another thought, to avoid repetition, might be to bin the tiles into similar colours and then take one at random from the nearest bin rather than the absolute nearest one.

The file tiles.txt looks like this:

111 116 109 0.jpg
82 88 81 1.jpg
112 110 95 10.jpg
178 154 150 100.jpg
190 169 163 101.jpg
187 166 163 102.jpg
...
...
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • 1
    Wow! You've squeezed amazing things from imagemagick, awk and bash and put everything together to an extremely compact and elegant piece of code. Thank you! However, this really makes me wonder if there is any open source solution for that, and if not - makes me want to write one. – Adam Matan Feb 08 '16 at 22:17
  • 1
    Go for it! And add a link back here when you have done it to publicise it! – Mark Setchell Feb 15 '16 at 17:47