# palettegen.rb # Copyright 2008 Michael Yacavone, myacavone at gmail.com # Released under the MIT license ("do whatever you want") # http://www.opensource.org/licenses/mit-license.php # Would love to receive improvement patches! # Version 1.0 # February 18, 2007 require 'rubygems' require 'RMagick' include Magick unless ARGV.length == 4 puts " Usage: ruby palettegen.rb InputImage OutputImage SwatchSize ApertureSize" puts " Example: ruby palettegen.rb Ocean.jpg OceanPalette.jpg 50 100" puts " Palettegen generates a 9x5 color palette from an input image." puts " SwatchSize is the output color-swatch size." puts " ApertureSize is the 'lens' size that picks up color from the image." puts " ApertureSize is at the end because you may want to experiment to " puts " find the optimal size." exit end # Read the image file img = Magick::Image::read(ARGV[0]).first # Set size of color swatches swatch = Integer(ARGV[2]) # Set the aperture of the target samples aperture = Integer(ARGV[3]) # Get the x/y coordinates of the five sample locations, # based on the rule of thirds and the center. This awesome idea via # Shelley Powers at http://burningbird.net/php/photographs.phps topy = (img.rows / 3) bottomy = ((img.rows / 3) * 2); leftx = (img.columns / 3); rightx = ((img.columns / 3) * 2); centery = (img.rows / 2); centerx = (img.columns / 2); # Create shades for blending black = Magick::Image.new(swatch, swatch) { self.background_color = '#000000' } white = Magick::Image.new(swatch, swatch) { self.background_color = '#ffffff' } # Create an imagelist to hold the blends blendedImages = Magick::ImageList.new # Blend each of five samples with a black or white shade, in various percentages, to generate a palette. # Process: at each x/y coordinate, crop out a rect based on the aperture size, # quantize this rect down to a single color, # set the background color to the quantized color, # put each blended image into the imagelist. # The following is total brute force; could use some block'd elegence.... src = img.crop(leftx, topy, aperture, aperture).quantize(1) src = Magick::Image.new(swatch, swatch) { self.background_color = src.pixel_color(1, 1) } blendedImages << black.blend(src, 0.2) blendedImages << black.blend(src, 0.4) blendedImages << black.blend(src, 0.6) blendedImages << black.blend(src, 0.8) blendedImages << src blendedImages << white.blend(src, 0.8) blendedImages << white.blend(src, 0.6) blendedImages << white.blend(src, 0.4) blendedImages << white.blend(src, 0.2) src = img.crop(rightx, topy, aperture, aperture).quantize(1) src = Magick::Image.new(swatch, swatch) { self.background_color = src.pixel_color(1, 1) } blendedImages << black.blend(src, 0.2) blendedImages << black.blend(src, 0.4) blendedImages << black.blend(src, 0.6) blendedImages << black.blend(src, 0.8) blendedImages << src blendedImages << white.blend(src, 0.8) blendedImages << white.blend(src, 0.6) blendedImages << white.blend(src, 0.4) blendedImages << white.blend(src, 0.2) src = img.crop(leftx, bottomy, aperture, aperture).quantize(1) src = Magick::Image.new(swatch, swatch) { self.background_color = src.pixel_color(1, 1) } blendedImages << black.blend(src, 0.2) blendedImages << black.blend(src, 0.4) blendedImages << black.blend(src, 0.6) blendedImages << black.blend(src, 0.8) blendedImages << src blendedImages << white.blend(src, 0.8) blendedImages << white.blend(src, 0.6) blendedImages << white.blend(src, 0.4) blendedImages << white.blend(src, 0.2) src = img.crop(rightx, bottomy, aperture, aperture).quantize(1) src = Magick::Image.new(swatch, swatch) { self.background_color = src.pixel_color(1, 1) } blendedImages << black.blend(src, 0.2) blendedImages << black.blend(src, 0.4) blendedImages << black.blend(src, 0.6) blendedImages << black.blend(src, 0.8) blendedImages << src blendedImages << white.blend(src, 0.8) blendedImages << white.blend(src, 0.6) blendedImages << white.blend(src, 0.4) blendedImages << white.blend(src, 0.2) src = img.crop(centerx, centery, aperture, aperture).quantize(1) src = Magick::Image.new(swatch, swatch) { self.background_color = src.pixel_color(1, 1) } blendedImages << black.blend(src, 0.2) blendedImages << black.blend(src, 0.4) blendedImages << black.blend(src, 0.6) blendedImages << black.blend(src, 0.8) blendedImages << src blendedImages << white.blend(src, 0.8) blendedImages << white.blend(src, 0.6) blendedImages << white.blend(src, 0.4) blendedImages << white.blend(src, 0.2) # Positon blended images on a canvas. # The tile is hard-coded to fit the nine swatches for each of the five samples. # The geometry sets the gap between swatches. montage = blendedImages.montage { self.tile = "9x5" self.geometry = "+2+2" self.background_color = "black" } # Write file to disk montage.write(ARGV[1]) # To-do: # Use blocks instead of the brute force swatch creation. # Write text onto the image with original file name, aperture size, and maybe hex color values? # Specify a global offset to the five target samples in case you want to move the sample targets around the image. # Build a UI and make it a web app.