Skip to content

Instantly share code, notes, and snippets.

@raelg
Last active February 28, 2017 16:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save raelg/ed6ec3a44a539d190c91 to your computer and use it in GitHub Desktop.
Save raelg/ed6ec3a44a539d190c91 to your computer and use it in GitHub Desktop.
Create a Photomosaic (https://en.wikipedia.org/wiki/Photographic_mosaic) from a directory of source images, and a given master image.
import java.awt.Color
import java.awt.image.BufferedImage
import java.io.{File, FilenameFilter}
import java.net.URL
import javax.imageio.ImageIO
import scala.collection.immutable.IndexedSeq
import scala.io.Source
import scala.sys.process._
object Collage extends App {
val imgDir = new File("flags") // collage source images directory
val hasAlpha = false
// determine the mean RGB of an image
def meanRgb(image: File): Color = {
val img = ImageIO.read(image)
val pixels = img.getWidth * img.getHeight
val (rTotal, gTotal, bTotal) = (for {
x <- 0 until img.getWidth
y <- 0 until img.getHeight
c = new Color(img.getRGB(x, y), hasAlpha)
} yield (c.getRed, c.getGreen, c.getBlue)).unzip3 match {
case (r, g, b) => (r.sum, g.sum, b.sum)
}
img.flush()
new Color(rTotal / pixels, gTotal / pixels, bTotal / pixels)
}
// Method 1: Map images to an array paired with its mean colour
def getImageToColorArray: Array[(File, Color)] = {
val images: Array[File] = imgDir.listFiles(new FilenameFilter {
override def accept(dir: File, name: String): Boolean = name.endsWith(".png")
})
images.map(f => (f, meanRgb(f)))
}
// Method 2: For every 24-bit colour, precompute the best image by closest mean RGB; Index corresponds to RGB value
def getColorToImageArray: Array[File] = {
val imageColors: Array[(File, Color)] = getImageToColorArray
(0 to 16777216).par.map { i =>
val rgb = new Color(i, hasAlpha)
val (bestImage, _) = imageColors.minBy { case (_, c) =>
// Compute the distance to the colour using 3D Pythagoras algorithm, and find the image with the min distance
math.sqrt(math.pow(c.getRed - rgb.getRed, 2) + math.pow(c.getBlue - rgb.getBlue, 2) + math.pow(c.getGreen - rgb.getGreen, 2))
}
bestImage
}.toArray
}
def makeCollage(): Unit = {
var start = System.currentTimeMillis()
val masterImg: BufferedImage = ImageIO.read(new File("Superman.png"))
val imageColors: Array[(File, Color)] = getImageToColorArray
//val colorToImages = getColorToImageArray (Method 2)
println("Pre-processing finished, time: " + (System.currentTimeMillis() - start))
start = System.currentTimeMillis()
val collage = new BufferedImage(masterImg.getWidth * 100, masterImg.getHeight * 100, 1)
val graphics = collage.getGraphics
(0 until masterImg.getHeight).par.foreach { h =>
(0 until masterImg.getWidth).foreach { w =>
val rgb = new Color(masterImg.getRGB(w, h), hasAlpha) // get RGB at each pixel in the source master image
// get the best image (Method 1):
val (bestImage, _) = imageColors.minBy { case (_, c) =>
math.sqrt(math.pow(c.getRed - rgb.getRed, 2) + math.pow(c.getBlue - rgb.getBlue, 2) + math.pow(c.getGreen - rgb.getGreen, 2))
}
//val bestImage = colorToImages(rgb.getRGB ^ (0xff << 24)) // Method 2
// draw the best image at the same coordinates
val img: BufferedImage = ImageIO.read(bestImage)
graphics.drawImage(img, w * 150, h * 100, img.getWidth, img.getHeight, null)
img.flush()
}
}
ImageIO.write(collage, "PNG", new File("combined5.png"))
println("Finished, duration: " + (System.currentTimeMillis() - start))
}
makeCollage()
}
@raelg
Copy link
Author

raelg commented Oct 10, 2015

combined3

@raelg
Copy link
Author

raelg commented Feb 28, 2017

Source image:
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment