Fun mosaic effect with Go

A few months ago I saw a cool mosaic effect in a Wired ad for CA Technologies. Here’s what part of the ad looked like:

Photomosaic of people in an office

I liked the ad, so I wondered how they did it. Can you see out how to create a similar effect? Take a minute to figure it out as an exercise.

Here’s what I came up with: divide the image into tiles. For each tile, compute an average overall color for that tile. Then go back and blend every pixel in that tile with the average color. So if a tile is partly dark and partly blue, the average color is a dark blue, so the blue in that tile becomes even darker. I like that the effect is pretty simple once you figure out how to do it.

Of course, once I had an idea of how to do it, I wanted to write some code and see whether I could recreate the effect. Go has good libraries for handling images and I’ve been meaning to try Go. I ended up with about 70 lines of moderately-ghastly Go code that did the job.

For this Creative Commons image (thanks Fuelrefuel/Wikimedia Commons!)

Photo of people in an office

I ended up with a photomosaic like this:

Photomosaic of people in an office

As far as I can tell, that’s pretty much the same filter that ran in the ad. Here’s another example. First, a picture of me:

Matt Cutts

and here’s the resulting mosaic’ed image:

Matt Cutts in mosaic form

That’s all the interesting stuff. You can stop reading now.

This part is boring. Really. No need to keep reading. The code I came up with is really ugly, but the pseudo-code is pretty simple:


- Read the picture into a go image
- Number of horizontal tiles = image_width / desired_tile_width
- Number of vertical tiles = image_height / desired_tile_height
- Loop through tiles with nested vertical and horizontal for loops
- For each tile, loop over the tile's pixels to compute average RGB values
- Loop over the tile's pixels again & set new_color = (avg_color+curr_color)/2
- Write the image out as a new picture

That’s it! I wanted a quick and dirty test, so I didn’t worry about things like the leftover pixels if the tiles didn’t evenly divide the image.

Let’s see, what else. Things I liked about Go:
– It’s super-easy to read and write images, so I could concentrate on the fun stuff.
– I like that documentation like this gives a clear, easy way to set up your environment. The golang tour is great too. And installing Go on Ubuntu is easy: “sudo apt-get install golang” and you’re done.
– The language makes a lot of sense to me, in a C kind of way.

Some things didn’t make as much sense to me, or at least I need to do more reading:
– My initial program just read a JPEG and wrote it back out, and the output image was considerably dimmer. I was just using default encoding values, so maybe some gamma values got left out, but it was a little weird. I was expecting read->decode->encode->write to be a no-op.
– When I read the JPEG into an image and tried to write directly to that image, Go gave me an error. That was a little strange. I ended up copying the JPEG to a new image and then I could write.
– In the spirit of just doing stuff without reading the documentation, it seemed like Go images stored their At() component colors with 16 bits of range (from 0..65536). But when I wanted to write colors with Set() it seemed like Go wanted 8 bits in the example I found. So for a while I was casting stuff with (uint8) and getting totally random bits written into the image. That also generated a fun image:

Random mosaic from converting a 16 bit-range color to uint8

but it took me a few minutes to figure out what was going on. I’m sure some reading would clear things up, but.. who cares? I was also doing some weird float arithmetic to compute color averages. This was just quick/dirty code, and I can read more about the nitty gritty later. As soon as I got the effect I wanted, I rapidly lost interest. I even hard-coded image filenames because I couldn’t be bothered to search for go command-line flag info. All in good fun.
– Arrays and slices are cool, but allocating 2D arrays and slices seems a little verbose.
– I like that Go’s designers have opinions and enforce them, at least 99% of the time. When you’re hacking ugly code, it was annoying to get the “you didn’t use this variable” errors. But I understand the rationale and it’s probably a good idea for writing Real Code that’s not intended to be thrown away.
– I was all set to grouse about go fmt’s enforced indentations/spacing, but it actually looks pretty reasonable. Basically, each indent is a tab. Then if you’re a 3 or 4 space indent kind of guy, you can configure your editor like vim or emacs to change how the tab width is displayed.

Historically, Python is my language of choice to knock out a quick script thing–I love Python dictionaries. But with Go’s speed, support for dictionaries/maps, and capability to do HTTP servers very easily, I might end up switching to Go. I think I’ll use Go for my next little fun project.

Added: Thanks to Tom Madams who whipped a prototype of this filter in video using Shadertoy!

53 Responses to Fun mosaic effect with Go (Leave a comment)

  1. It’s good to see you getting back to your graphics roots Matt πŸ™‚

    Peter Bourgon has an excellent primer for Go on “How I Start”: https://howistart.org/posts/go/1

    Definitely recommended for those who want to get a Go environment up and running quickly.

  2. Your “fun image” looks like artwork by Gerhard Richter πŸ™‚

  3. AWESOME! Great read Matt, this is simply amazing.

  4. bill

    I just can’t get excited about Go until they have some sort of operator overloading for math classes and some sort of templates or generics so numerical libraries don’t have to go through fortran-style hoops and write float/double/complex versions of every single function.

  5. Reminds me of Jimmy Kimmel’s “Unnecessary Censorship” bit. Very cool, indeed.

  6. A few differences between yours and the ad, but I guess you had to simplify a little πŸ™‚

    The tiles in the ad are of different sizes and shapes.

    The tiles in the ad are overlapping.

    The faces in the ad has been brought back so there is not much being changed in the faces of the people.

    Except for that, I think you are on to something!

  7. Very cool!

    Probably this was just an excuse for playing with Go, but here are some ideas for possible improvements to your version of the effect:
    – Based on my prior experiences with averaging colors, averaging numerical rgb values is not the best strategy to get something aesthetically pleasing. Just using a different color encoding is usually a bit better. Better yet, take separate averages using all the common color encodings, then convert those results into one target encoding and average those for your final color. This is relatively easy and results are quite good usually.
    – In the original photo, it appears as if there is little/no tiling on faces, which I think creates a nice effect. Perhaps an artist did this manually in post-processing, but it would be interesting to automate this.

  8. I have liked your post, because it has maked me to remember a night more than 20 years ago, I was studying computer science, and I wanted to make a clone of a popular screensaver, do you remember the lines bouncing on borders and spreading the screen? It was a windows 3.1 screensaver.

    That year I used to make my applications with C++, I found a library to handle graphics, and that idea took me all night, I finished when my family were having the breakfast.

  9. Atishay

    it means, Google bot soon will start reading image content as well πŸ™‚ Is that an update for SEO or its just for fun on go πŸ™‚

  10. This is really cool technique to create some fun pictures. Though I would not prefer to use them in my webdesign, but nice stuff to amuse myself some times.

  11. Nigel Tao

    Modulo “In the spirit of just doing stuff without reading the documentation”, the http://golang.org/pkg/image/ package docs link to the http://blog.golang.org/go-image-package blog post which is worth reading if you’re doing any image manipulation in Go. In particular, see the paragraph starting, “There are three important subtleties about the return values… 100% red is represented by RGBA returning an r of 65535, not 255”.

    Note that the color.Color interface, which all color implementations satisfy, uses 16-bit color (100% == 65535), but the color.RGBA struct is the standard 8-bit color (100% == 255). There are other Color implementations, including the color.YCbCr struct used by JPEG.

    You can’t set the pixels of an image.YCbCr (i.e. a JPEG image) directly, because the underlying data doesn’t map 1:1 to pixels, due to chroma sub-sampling. If you know what you’re doing, and are aware that setting one Chroma sample can affect multiple pixels, then the image.YCbCr struct exposes enough to let you set pixels. But if you don’t know what you’re doing, then we figured it’d be less dangerous to disallow behavior (setting pixels) than have potentially surprising behavior.

    http://en.wikipedia.org/wiki/Chroma_subsampling

    I’m not sure why your image got dimmer. I suspect that the JPEG metadata has a color profile, which your image viewer honors but the Go decoder ignores for now. Other image viewers might not honor color profiles. If you mail me the source image and your code, I’m happy to poke at it.

  12. Must say a wow to your post on mosaic effect. The first image is well maintained with using the mosaic effect except the face portions. So the facial expressions are highlighted. There will be much difference in photo effects by the selection of size and mosaic shape. Highly interesting to read your image formatting thoughts . Good post.

  13. AWESOME! Great read Matt, this is simply amazing. πŸ˜€

  14. It’s actually a pretty interesting advertising strategy if you notice in the picture peoples faces aren’t actually mosaic or they are only lightly faded out which makes the viewer focus on these people. The 3 women are actually focused the most I wonder if these are important people to the company and faces they want you to remember. But you can do a lot with photoshop much of these mosaic type effects all really cool stuff!

  15. I do not understand this “wow how nice”. It is just random pixeling of the standard photo πŸ™‚

  16. Hi Matt, It’s wow .. Great to see after a long time, glad you at here, thanks

  17. The results don’t look that special to me. Though I would still want to do a picture of me πŸ™‚

  18. Matt –

    Thanks for the advice! I like how the mosaics is an interesting affect without making the photo too intrusive for easy viewership.

  19. Wow, that’s cool Matt. I will try it. Simply artistic.

  20. Hi Matt,

    In my opinion, the faces on ad (the 1st image) looks clear (not mosaic’ed).
    That’s the challenge: How to keep the faces looks clear !

    Thank you for sharing this fun mosaic article πŸ™‚

  21. My congratulations. It’s very cool!!

  22. HΓ© i think this is the way to place a less confronting selfportret! Thanks

  23. AWESOME Matt ! Great read, this is simply amazing~

  24. Wow thanks for sharing… I was remembered that pixel image was my first TV that I bought in 90’s

  25. amazing effects! will check out Go

  26. Matt,

    Why does Google prefer to link to the mobile version of most forums? Wouldn’t it be better for users to link to the full version? We would then be able to see the full conversation.

  27. That is so much quicker then trying to use photoshop for this effect. I can always use ways to spice things up on my site. Thanks

  28. That’s cool, I think second image has some 3D effects πŸ˜€

  29. Majed Atwi

    When you will be back we miss you, but we miss your videos more πŸ˜›

  30. that’s really nice matt.

  31. Very cool photo Matt! Thanks for the tutorial on how it’s done. It’s a future blog post feature image for sure!

    Cheers,
    Brendon

  32. amazing effects! will check out Go

  33. This is really cool, looked Jazzy.

  34. Wow! that’s really great indeed. And quite of fun too, Matt.

  35. Matt these photos are the best pixel art I have ever scenes. All of these images are looking stunning with the mosaic effect. I am now going to try the effect for my own pics πŸ™‚

  36. So inspiring! We can learn a lot from here.

  37. what a nice effect, thanks for sharing!!

  38. i am impress with the feedback of this post, Great article real enjoyable to read. thanks for sharing.

  39. This is a prime example of why I love nerds. While I wonder how a cool-looking thing is done, you can wonder and then actually figure it out, and I can watch the process and be envious of your abilities. It’s fascinating. I feel like if I knew how to do that, it would feel like magic. πŸ™‚

    I’m a nerd wannabe, and might have been a full nerd if I had been a wee bit younger; my tiny school didn’t have computers before I graduated in 1975, but did by the time my youngest brothers (seven and 10 years younger than I am) entered high school. I went into journalism. They went into computer science (and are now employed at levels I could only imagine reaching if I wrote a best-selling book), which I later wished I’d studied. I actually ended up effectively being a system administrator at my first newspaper only because no one else knew anything about computers. My brother said, “In the land of the blind, the one-eyed is king.”

    πŸ™‚

    Laurie

  40. I think you are on the right track. If you used a subtle image mask to remove some of the pixel effect over the faces, like in the original ad, you’d be spot on.

  41. Matt, Great, this is simple amazing.

    Thanks

  42. Thanks Matt, Its really amazing.

  43. The faces in the ad has been brought back so there is not much being changed in the faces of the people.

    very nice….

  44. Very nice effect! So can we wait on for google to launch its photo mosaic app soon?

  45. Awesome. Very cool Matt!

  46. Great Article. I love the way you write your articles.

  47. It’s looks too complected for me to be honest! lol
    But i think for your photo if the pixels or the boxes whatever the name, is smaller, it was going to be much better.
    But i liked the idea, and it make our eye stop on the photo and let our brain try to figure out how this made!
    Nice job Matt!

  48. love the contrast between the sharp and fuzzy pics!

css.php