Create mosaic from photograph

Posted on 2014-10-13
Last Modified: 2014-10-27
I am building a tool in VB.Net to generate a photograph mosaic. The main image is 7000px X 7000px.

The individual tile sizes are 30px X 30px

I need help with the following:

1) The mosaic generation code should check for each tile color and decide the best place to put it on the main image. Presently, my code is just randomly placing images on the main picture

2) It should suitable shade the tile to match the back color of the spot it is placed on - my code is doing that but not very well

3) It should generate a TRUE mosaic. Not an overlay of the main image with transparent looking tiles on top - there should be no 'ghost' image peeping out from the tiles

Please help in showing me third party tools that can achieve this or help me with the code. The project is in VB.Net
Question by:RTKHOT
  • 5
  • 4
LVL 33

Expert Comment

ID: 40379964
some questions:
The individual tile sizes are 30px X 30px
7000 is not a multiple of 30. you may use 6990 or 7020 pixels instead or define the tiles 35x35.

The mosaic generation code should check for each tile color

Open in new window

can you make a sample - say with 3x3 tiles and a picture of 90x90?

it is unclear where the tiles come from and which color out of 900 pixels is the one you want? are the tiles unicolored?

 ... and decide the best place to put it on the main image

Open in new window

can you tell which criteria should be used for that. the picture may have 900 different colors for a spot (worst case) or may be black-and-white (another extreme case).

It should suitable shade the tile to match the back color of the spot it is placed on

Open in new window

can you define what you mean with that?


Author Comment

ID: 40380026
Thanks for your interest. Here are some replies:

1) I approximated it. But 6990 x 6990 is fine using 30 x 30 tiles

2) The main image will be a photograph that contains head and neck. it will be on a dark background

3) the tiles will also be photographs of people. The will be of multiple colors, shades and backgrounds. they may be indoor, outdoor, full photo, just face... anything. They will be a complete mixture of photos of people

4) Ideally, a light tile should be placed on a light area of the photo. IT should be an approximate placement. Similarly, a dark tile on a dark area of the photo. A tile that has more red should be around a red area of the photo. This is an ideal situation. I realise it may not be possible.

5) When a tile is being placed on the main image, and suppose tile is grey and it is being placed on a red area of main image. Then the tile should become slightly red so it gets properly camouflaged. The tiles should get a hue that matches where they are being placed.

Hope this answers everything! :)
LVL 33

Expert Comment

ID: 40380247
thanks. yes, i now have a good idea what you want to do.

two more questions:

- how many tile photos are available?
  i assume they are more than 230x230, or do you want to use some of them a multiple?

- you told it is project. are you interested in c++ code nevertheless? or are you interested in algorithms only?

generally, you would define a measure for all tiles (mosaic or background). for example the measure could compute a 3d-point in a 10x10x10 grid where the dimensions are brightness index (from light to dark), color index (white to black), and diversity (count of neighbored pixels which have same color index) of all 900 pixels of a tile (for example the measure { 2, 5, 9 } could mean light-grey-homogeny, while { 8, 0, 1} is a mostly white picture with many scattered colored pixels on it). you may define a simple distance function between two points by calculating the Euclidean distance. the measure allows to  find a suitable tile for a given background very quickly by using dictionaries. for the shading you would use smaller tiles - say 3x3 - which you compare with the background. then "correct" the colors of the overlay by an appropriate algorithm (for example by calculating the difference between two colors for all 9 pixels of a mini tile and use the standard deviation to correct all colors into same direction; two neighbored mini tiles should not be corrected into different directions).  

Problems using Powershell and Active Directory?

Managing Active Directory does not always have to be complicated.  If you are spending more time trying instead of doing, then it's time to look at something else. For nearly 20 years, AD admins around the world have used one tool for day-to-day AD management: Hyena. Discover why


Author Comment

ID: 40381495
we will have a very large collection of tiles. but its ok if some repeat. we will have approx 1000 tiles.

code would help. vb or C is ok. if not, then algorithm would be second choice.

LVL 33

Expert Comment

ID: 40386157
ok. first the algorithm. if it is ok, I can help with code as well.    

to be able to compare rgb colors, you would divide the rgb space by 4 for each color, thus getting 64 "color cubes" from 000 to 333 (base 4 numbers, or 0 to 63 decimal). for example the cube where red is in [192, 255] range and green in [128,191] range and blue in [0, 63] range could be named "dark yellow" for all its points. the reference for each cube color is the middle point of each cube, for the example it is rgb(223, 95, 31). put all cubes into an array of 64 elements.

then, define an array of colors (color groups) where each cube can be assigned to, for example { black, dark, red, green, blue, grey, yellow, orange, beige, brown, olive, pink, violet, cyan, lite, white }.  

for each of the 64 cubes you would determine the key (for example 0 to 63), optionally a name (for example "dark yellow", "lite blue green", ...), the color group index and the reference point and put all that into a structure (class). for each color group, you would make a list of all color cubes which belong to the group.  

for each color group, you would determine the "neighbored" color groups, for example red is neighbored to { orange, pink }. you would do that on a visual resemblance rather than based on rgb values.

for each of your tiles and each of the 900 pixels of a tile you determine the cube(s) where it belongs to. count the pixels for each for color cube and for each color group. sort both the counter lists and eliminate counts less than 30 for the cube counts and less than 90 for the color counts. for cube count list determine the cube with maximum pixels. triple the number and add all counts of cubes neighbored to the maximum cube. if the total is more than 600 points, you would add the tile index plus total points to a list associated to the cube. repeat the computation for the second maximum cube, until the total pixels in the cube is less than 45 (or any other threshold you want to define) or points are less than 450.

for the color count list you would do similar. start with the color that has the majority of colors assigned and where the initial count is greater 90.  triple the count and add counts of colors which are neighbored to the majority color. if total points greater 600 you would add the tile index plus points to a list associated to the color group. repeat that for next maximum while total points are greater than the half of the maximum points.

for example if you have (dark:50, brown:80, beige:120, orange:150, pink:150, red:350) you would add the tile to list of red with 1350 points (3*350 +150+150) and to orange with 880 points  (3*150 + 350 + 80) and pink with 850 points (2*150 + 350)  but not to brown which has only 560 points which is less than the half of the red points.

as a pair of tile index and points is added to the lists, we would be able to sort the lists by points and use the tile with the maximum points if there are multiple choices. all tiles which could not associated to a cube were added to one of four lists of "scattered" tiles. for that you would compute  average + standard deviation of the sum of the rgb values for each pixel. use both values to determine whether a tile belongs to dark-thinly-dispersed, dark-highly-dispersed, lite-highly-dispersed or lite-thinly-dispersed.

to determine which tile to use for a given spot of the picture compute the cubes and colors for each spot same way as you did it for the tiles. check the lists of tiles whether there are exact matches. if there are multiple matches use a random algorithm to make a choice where the probability for matches with higher points should give an advantage. increment a counter for each tile used for mosaic  such that you could prevent the same tile to be used too often. if no tiles would fit or tiles were used too much use the "scattered" lists.


Author Comment

ID: 40386198
thanks for the above. if you have code, please send that as well
LVL 33

Expert Comment

ID: 40386274
I am a c++ programmer and the collections mentioned in the algorithm would be standard collections (arrays, lists, sets, maps or dictionaries).

are you familiar with c++ collections and standard libraries?

if not, I would prefer to use pseudo code which easily could be elaborated to VB or C# code.

I could not give full working code as it would be too much work. I can make a design and help with code, though.


Author Comment

ID: 40391315
Either would be fine. Whatever is convenient to you and requires least of your effort.

Thank you kindly and appreciate it much.
LVL 33

Accepted Solution

sarabande earned 500 total points
ID: 40392291
to be able to compare rgb colors, you would divide the rgb space by 4 for each color, thus getting 64 "color cubes" from 000 to 333
#include <string>
#include <vector>
#include <algorithm>

typedef unsigned long COLORREF;

#ifndef RGB
#define RGB(r,g,b)          ((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))

// names for color cubes
// the reference color is in the middle of each cube, [123] -> red 31, green 95, blue 159
enum EColorCube {
      black_000               = 0,
      dark_blue_001,      // = 1
      blue_002,                // = 2
      lite_blue_003,         // = 3
      dark_green_010,    // = 4
      dark_cyan_011,      // = 5
      sea_blue_012,        // = 6
      //...                                             // please add a name for each color cube
      lite_cyan_233,         // = 62
      white_333,              // = 63
      MAX_CUBE             //  = 64

// used for readability 
const char * szCubeColors[MAX_CUBE] = { "black", "dark blue", "blue", "lite blue", /*..., */ "lite cyan", "white" }; 

Open in new window

define an array of colors (color groups) where each cube can be assigned to

// colors from 0 (black) to 15 (white)
enum EColorGroup
     { empty = 0, black, dark, red, green, blue, grey, yellow, orange, 
       beige, brown, olive, pink, violet, cyan, lite, white, MAX_COLOR};

const char * szColors[MAX_COLOR] = 
     "", "black", "dark", "red", "green", "blue", "grey", "yellow", "orange", 
          "beige", "brown", "olive", "pink", "violet", "cyan", "lite", "white", 

const int MAX_NEIGHBOR = 5;

const int colorNeighbors[MAX_COLOR][1 + MAX_NEIGHBOR] =
     {  black, dark, },                     // black
     {  dark,  black, brown }, 
     {  red,    orange, pink },
      //...                                         // please add neighbor relations for all colors

struct ColorCube
      int index;
      const char * szcolor;
      COLORREF refColor;
      int color;

const ColorCube cubeDefs[MAX_CUBE] = 
      { black_000,               szCubeColors[black_000],          RGB(31, 31, 31),         black, },
      { dark_blue_001,           szCubeColors[dark_blue_001],      RGB(31, 31, 95),         dark,  },
      { white_333,               szCubeColors[white_333],          RGB(223, 223, 223),      white, },

Open in new window

for each color group, you would make a list of all color cubes which belong to the group.

class ColorGroup
     int              index;
     std::string      color;
     std::vector<int> neighbors;
     std::vector<int> cubes;
     ColorGroup(int idx) 
            , neighbors(&colorNeighbors[idx][1], &colorNeighbors[idx][MAX_NEIGHBOR+1])
            std::vector<int>::iterator last = std::find(neighbors.begin(), neighbors.end(), 0); 
            if (last != neighbors.end()) neighbors.resize(last-neighbors.begin());
            // list of assigned cubes
            for (int n = 0; n < MAX_CUBE; ++n)
                  if (cubeDefs[n].color == idx)

class ColorGroups
       std::vector<ColorGroup> colors;
             for (int c = black; c <= white; ++c)

Open in new window

the above compiles in vc++ (unmanaged c++).

you may add the missing definitions and/or port the above to your preferred language.

to be continued ...


Featured Post

Optimizing Cloud Backup for Low Bandwidth

With cloud storage prices going down a growing number of SMBs start to use it for backup storage. Unfortunately, business data volume rarely fits the average Internet speed. This article provides an overview of main Internet speed challenges and reveals backup best practices.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
update textboxes with Streamreader 21 47
Visual Studio 2013 Shortcut (VB) 4 46
Help with solving error related to resources file 4 48
Get Top Visible Row of Datagridview 6 32
A while ago, I was working on a Windows Forms application and I needed a special label control with reflection (glass) effect to show some titles in a stylish way. I've always enjoyed working with graphics, but it's never too clever to re-invent …
The ECB site provides FX rates for major currencies since its inception in 1999 in the form of an XML feed. The files have the following format (reducted for brevity) (CODE) There are three files available HERE (…
This Micro Tutorial will teach you how to censor certain areas of your screen. The example in this video will show a little boy's face being blurred. This will be demonstrated using Adobe Premiere Pro CS6.
This Micro Tutorial hows how you can integrate  Mac OSX to a Windows Active Directory Domain. Apple has made it easy to allow users to bind their macs to a windows domain with relative ease. The following video show how to bind OSX Mavericks to …

773 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question