Created
May 8, 2012 09:19
-
-
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 file contains 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
/* | |
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