|
require 'oily_png' |
|
require 'matrix' |
|
|
|
module ChunkyPNG |
|
class Image |
|
def selective_color!(color, threshold=3.5) |
|
scores = [] |
|
pixels.map! do |pixel| |
|
score = ChunkyPNG::Color.lab_difference(pixel, color) |
|
scores << score |
|
if score > threshold |
|
ChunkyPNG::Color.to_grayscale(pixel) |
|
else |
|
pixel |
|
end |
|
end |
|
puts "Difference ranges from #{scores.min} -> #{scores.max}" |
|
end |
|
end |
|
|
|
module Color |
|
SRGB_GAMMA = 2.2 |
|
# http://www.brucelindbloom.com/ |
|
SRGB_MATRIX = ::Matrix[[0.4124564, 0.3575761, 0.1804375], |
|
[0.2126729, 0.7151522, 0.0721750], |
|
[0.0193339, 0.1191920, 0.9503041]] |
|
K = 24389.0/27.0 |
|
E = 216.0/24389.0 |
|
# http://en.wikipedia.org/wiki/Illuminant_D65 |
|
REFERENCE_WHITE_D65 = [95.047, 100.00, 108.883] |
|
|
|
# Calculate Delta E difference score |
|
# http://en.wikipedia.org/wiki/Color_difference#Delta_E |
|
def lab_difference(color, other_color) |
|
lab_color = rgb_to_lab(color) |
|
other_lab_color = rgb_to_lab(other_color) |
|
score = (0..2).collect do |i| |
|
(lab_color[i] - other_lab_color[i]) ** 2 |
|
end.reduce(:+) |
|
score = Math.sqrt(score) |
|
score |
|
end |
|
|
|
def rgb_to_lab(color) |
|
xyz_to_lab(rgb_to_xyz(color)) |
|
end |
|
|
|
def rgb_to_xyz(color) |
|
linearized = linearize(color) |
|
result = SRGB_MATRIX * linearized |
|
result.column_vectors[0].to_a |
|
end |
|
|
|
def xyz_to_lab(channels) |
|
lab = channels.collect.with_index do |channel, i| |
|
xyz_channel_to_lab_partial(channel / REFERENCE_WHITE_D65[i]) |
|
end |
|
|
|
l = 116 * lab[0] - 16 |
|
a = 500 * (lab[0] - lab[1]) |
|
b = 200 * (lab[1] - lab[2]) |
|
[l, a, b] |
|
end |
|
|
|
def xyz_channel_to_lab_partial(x) |
|
if x > E |
|
x ** 1/3.0 |
|
else |
|
(K * x + 16) / 116 |
|
end |
|
end |
|
|
|
def linearize(color) |
|
::Matrix[[linearize_component(r(color))], |
|
[linearize_component(g(color))], |
|
[linearize_component(b(color))]] |
|
end |
|
|
|
def linearize_component(component) |
|
component / 255.0 |
|
end |
|
end |
|
end |
|
|
|
image = ChunkyPNG::Image.from_file('input.png') |
|
|
|
purple = ChunkyPNG::Color.rgb(57, 54, 166) |
|
blue = ChunkyPNG::Color.rgb(7, 127, 87) |
|
green = ChunkyPNG::Color.rgb(151, 207, 63) |
|
yellow = ChunkyPNG::Color.rgb(255, 214, 20) |
|
red = ChunkyPNG::Color.rgb(229, 41, 18) |
|
# 50, 68, 59 in LAB |
|
# 34, 19, 2 in XYZ |
|
|
|
image.selective_color!(green) |
|
image.save('output.png') |