Skip to content

Instantly share code, notes, and snippets.

@sihil
Created May 8, 2012 09:19
Show Gist options
  • Save sihil/2633827 to your computer and use it in GitHub Desktop.
Save sihil/2633827 to your computer and use it in GitHub Desktop.
FavIcon Cake - maps from a png to another png with a smaller palette (and counts how many pixels there are of each colour)
/*
This scala script takes in a small png file and converts it to a grayscale image with a smaller
palette size; counting the number of each pixels in the original and resulting image.
*/
import java.awt.image.BufferedImage
import java.io.File
import javax.imageio.ImageIO
import scala.collection.JavaConversions._
import scala.math.floor
object Pixel {
def luminance(lum: Int):Pixel = new Pixel((lum << 16) + (lum << 8) + lum)
}
case class Pixel(rgb: Int) extends Ordered[Pixel] {
val red: Int = (rgb >> 16) & 0xff
val green: Int = (rgb >> 8) & 0xff
val blue: Int = rgb & 0xff
val luminance: Int = floor((.3 * red) + (.59 * green) + (.11 * blue)).toInt
override def toString = "RGBL(%d,%d,%d,%d)".format(red, green, blue, luminance)
def compare(that: Pixel): Int = this.luminance.compare(that.luminance)
}
val paletteSize = 4
val faviconFileName = "/Users/shildrew/working/guardian-logo/src/favicon-blue.png"
val faviconURL = new File(faviconFileName)
val favicon = ImageIO.read(faviconURL)
val width = favicon.getWidth
val height = favicon.getHeight
val inputPixels = for ( x <- 0 until width;
y <- 0 until height )
yield { Pixel(favicon.getRGB(x, y)) }
val sortedPixels = inputPixels.distinct.sorted
// this "- 20" makes the distribution better for me - similar to levels in photoshop
val darkest = sortedPixels.head.luminance - 20
val lightest = sortedPixels.last.luminance
val range = darkest to lightest by ((lightest - darkest) / (paletteSize - 1))
val colourPalette = range.map{ Pixel.luminance(_) }
val remappedPixels = inputPixels.map { pixel =>
colourPalette.reduceLeft { (result, elem) =>
if (result.compare(pixel) < elem.compare(pixel)) {
result
} else {
elem
}
}
}
println("Distribution of remapped pixels (palette of "+paletteSize+"):")
println(remappedPixels.distinct.sorted.map{ pixel =>
"Luminance: %4s Count: %4s" format (pixel.luminance, remappedPixels.count( _ == pixel))
}.mkString("\n"))
val outputImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for ( x <- 0 until width;
y <- 0 until height) {
val pixel = remappedPixels.get(x*16 + y)
outputImage.setRGB(x,y,pixel.rgb)
}
val file = new File("output-new.png")
ImageIO.write(outputImage, "png", file);
println
println("Wrote to "+file.getAbsolutePath)
// Cake.write(outputImage)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment