8

Hy.What I have to do is to create a program (using C or C++), that takes as input a 24bits/pixel bitmap and a gathering of images and I have to create a mosaic image , similar to the input image using the library of images given(To create a mosaic Photo similar to the input).

So far I can access the input's image pixels and the colors from it but I'm kind of stuck. My question is Where should I start? I need a basic algorithm that could do such a thing. And I can't really find any(maybe I'm looking wrong). And also can someone tell me a random photo downloader, so that i can download small images for the project? Can someone help me? Please, tell me where to start and what to use.

easwee
  • 15,757
  • 24
  • 60
  • 83
Alexx
  • 311
  • 2
  • 8
  • 19
  • 1
    I'm sure it's described in great detail in the patent: http://v3.espacenet.com/publicationDetails/biblio?CC=US&NR=6137498&KC=&FT=E – Mark Ransom Mar 29 '11 at 20:34

3 Answers3

17

I've done this in Scala. The Dr Dobbs article was extremely useful to me.

Sample image:

Sample photomosaic

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

Erick G. Hagstrom
  • 4,873
  • 1
  • 24
  • 38
I82Much
  • 26,901
  • 13
  • 88
  • 119
  • Hy,i have done my implementation of the code it does something ,but it`s far from what you did (picture wise).What i did is ,got the opencv library and worked on C++.I made an array containing an image(litle tiles) and the average values for RGB,(each chanel separately).I got an image and for an rectangle area from it i made the average values for the RGB channels,(sum=(avgR+avgG_avgB)/3)where avgR=(ImgAvgR-tileAvgR)etc. and found the minimum in the vector ,and replace the rectangle selected with the tile.....but doesn`t look wright.can you help me? – Alexx Apr 02 '11 at 20:29
  • 1
    First thing you should do is determine whether your average color computation is correct. To do this, instead of replacing the subimage with the closest color image, just replace it with a solid swatch of the same color. If that works, then we can go from there. – I82Much Apr 02 '11 at 22:40
  • is there a way to include each picture at least once? for example if i have a collection of 400 pictures, each of them will be included in the mosaic at least once. – Jaka Apr 11 '13 at 07:18
  • 1
    You could do that. I could imagine a few approaches. 1) lay out using standard algorithm. At the end, find all unused images. For each of these images, find the best place in the image for it (overwriting whatever is there). Would have to ensure that you converge to use them all (obviously not possible if num_rows x num_columns < num_images 2) impose very high penalties in your weighting function for duplicate images so that diversity naturally comes out, at expense of image quality. This wouldn't guarantee all images are used – I82Much Apr 12 '13 at 04:19
4

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.

John
  • 15,990
  • 10
  • 70
  • 110
  • Hy,i have done my implementation of the code it does something ,but it`s far from what you did (picture wise).What i did is ,got the opencv library and worked on C++.I made an array containing an image(litle tiles) and the average values for RGB,(each chanel separately).I got an image and for an rectangle area from it i made the average values for the RGB channels,(sum=(avgR+avgG_avgB)/3)where avgR=(ImgAvgR-tileAvgR)etc. and found the minimum in the vector ,and replace the rectangle selected with the tile..... – Alexx Apr 02 '11 at 20:30
  • I have heard that it is better to use hue rather than rgb, what is your opinion? – Sycren Apr 26 '12 at 08:52
1
  1. Decrease the resolution of the input image
  2. For every image in the list compute the average value of every channel (3 numbers - RGB)
  3. For every pixel in the input image with values (r,g,b) do the following: Randomly sample 30 (just a number that works well) images from the list. For every such random image in the sample, compute the distance (*) between the rgb values and choose the image with smallest distance.

(*) The distance between (r1,g1,b1) and (r2,g2,b2) can be for example: (r1-r2)**2+(g1-g2)**2+(b1-b2)**2.

That's it. It works pretty well. There are two hyperparameters for the algorithm.

  • The new resolution of the input image
  • The number of images we sample for every pixel in the input image. You can play with both of them.
srishtigarg
  • 1,106
  • 10
  • 24