This example needs Ruby 1.9.
Two ways to run this:
$ ./pixelizer.rb infile.png > outfile.png
$ ./pixelizer.rb - > outfile.png < infile.png
#!/usr/bin/env ruby | |
require 'chunky_png' | |
class Pixelizer | |
include ChunkyPNG | |
# The ChunkyPNG::Image instance. | |
attr_reader :png | |
# The pixelizer accepts an IO object, like a file handle or $stdin. | |
def initialize(io) | |
@png = Image.from_io io | |
end | |
# Pixelizes using a very simple algorithm. Every block of 10x10 pixels | |
# is evaluated, and replaced by one single color closest to the set of | |
# colors within that block. You can overwrite the 10x10 pixels by setting | |
# the first argument. | |
def pixelize!(block_size = 10) | |
dimension = @png.dimension | |
@block_point = Point.new 0, 0 | |
while @block_point.within_bounds? dimension | |
y_delta = dimension.height - @block_point.y | |
@block_height = y_delta < block_size ? y_delta : block_size | |
while @block_point.within_bounds? dimension | |
x_delta = dimension.width - @block_point.x | |
@block_width = x_delta < block_size ? x_delta : block_size | |
pixelize_block! | |
@block_point.x += block_size | |
end | |
@block_point.x = 0 | |
@block_point.y += block_size | |
end | |
end | |
protected | |
# Replaces the block with a single color. | |
def pixelize_block! | |
new_color = blur_color | |
for block_y in 0...@block_height | |
for block_x in 0...@block_width | |
@png[@block_point.x + block_x, @block_point.y + block_y] = new_color | |
end | |
end | |
end | |
# Calculates the new color for a block based on surrounding colors. | |
# Completely transparent pixels don't count of course. | |
def blur_color | |
colors = {r: [], g: [], b: [], a: []} | |
for block_y in 0...@block_height | |
for block_x in 0...@block_width | |
color = @png[@block_point.x + block_x, @block_point.y + block_y] | |
unless Color.fully_transparent? color | |
colors.keys.each do |channel| | |
colors[channel] << Color.send(channel, color) | |
end | |
end | |
end | |
end | |
return Color::TRANSPARENT if colors[:r].empty? | |
Color.rgba *(colors.map do |chan, set| | |
set.inject(:+) / set.length | |
end) | |
end | |
end | |
unless ARGV[0] | |
$stderr.puts "Usage: #{$0} [input.png | -] > output.png" | |
exit 1 | |
end | |
begin | |
io = ARGV[0] == '-' ? $stdin : File.open(ARGV[0], 'rb') | |
pix = Pixelizer.new io | |
pix.pixelize! | |
pix.png.write $stdout | |
rescue | |
$stderr.puts "Sorry, something went wrong.\n#{$!}" | |
exit 1 | |
end |