Last active
February 28, 2017 16:46
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment