Created
January 11, 2015 21:00
-
-
Save maximvl/ac0a36380914fc9804cd to your computer and use it in GitHub Desktop.
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
# 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 | |
# Version 1.1 | |
# January 11, 2014 | |
require 'RMagick' | |
include Magick | |
$just_print = false | |
if ARGV.length == 2 | |
$just_print = true | |
aperture = Integer(ARGV[1]) | |
elsif 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 | |
if not $just_print | |
# Set size of color swatches | |
$swatch = Integer(ARGV[2]) | |
# Set the aperture of the target samples | |
aperture = Integer(ARGV[3]) | |
# Create shades for blending | |
$black = Magick::Image.new($swatch, $swatch) { self.background_color = '#000000' } | |
$white = Magick::Image.new($swatch, $swatch) { self.background_color = '#ffffff' } | |
end | |
# 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 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. | |
def printColor(src) | |
color = src.pixel_color(1,1) | |
puts "#{color.red.to_s(16)}:#{color.green.to_s(16)}:#{color.blue.to_s(16)}" | |
end | |
def addImage(src, images) | |
src = Magick::Image.new($swatch, $swatch) { self.background_color = src.pixel_color(1, 1) } | |
images << $black.blend(src, 0.2) | |
images << $black.blend(src, 0.4) | |
images << $black.blend(src, 0.6) | |
images << $black.blend(src, 0.8) | |
images << src | |
images << $white.blend(src, 0.8) | |
images << $white.blend(src, 0.6) | |
images << $white.blend(src, 0.4) | |
images << $white.blend(src, 0.2) | |
end | |
def processImage(src, images) | |
if $just_print | |
printColor(src) | |
else | |
addImage(src, images) | |
end | |
end | |
# The following is total brute force; could use some block'd elegence.... | |
src = img.crop(leftx, topy, aperture, aperture).quantize(1) | |
processImage(src, blendedImages) | |
src = img.crop(rightx, topy, aperture, aperture).quantize(1) | |
processImage(src, blendedImages) | |
src = img.crop(leftx, bottomy, aperture, aperture).quantize(1) | |
processImage(src, blendedImages) | |
src = img.crop(rightx, bottomy, aperture, aperture).quantize(1) | |
processImage(src, blendedImages) | |
src = img.crop(centerx, centery, aperture, aperture).quantize(1) | |
processImage(src, blendedImages) | |
# 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. | |
if blendedImages.size > 0 | |
montage = blendedImages.montage { | |
self.tile = "9x5" | |
self.geometry = "+2+2" | |
self.background_color = "black" | |
} | |
# Write file to disk | |
montage.write(ARGV[1]) | |
end | |
# 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. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment