Pixelizing images with ChunkyPNG
Ever heard of ChunkyPNG? It’s an amazing PNG manipulation library that is easy and fun to use. This week’s challenge is to pixelize the image below, so your resulting image is built up from blocks of 10 by 10 pixels. Remember: you can’t change the size of the image.

If you’ve never used ChunkyPNG before, check out the wiki, you can find some great examples in there.
Again, put your solution in a Gist, together with your resulting image, like this example (please don’t include the input image and don’t fork the example gist). You can’t add images using Gist’s web interface, so you’ll have to clone your Gist and add it using git.
Like last week, you have a week to get your entry in, so I’m sure you have enough time to write a great implementation. Good luck!
-
Finished in 1st place with a final score of 3.6/5. (View the Gist)output.png
solution.rbView full entryrequire 'chunky_png' module ChunkyPNG class Image def pixelize!(size=10) [[:row, height], [:column, width]].each do |orientation, length| for i in 0...length pixelated = []; send(orientation, i).each_slice(size) do |slice| pixelated += [Color.rgba(*[:r,:g,:b,:a].map{|chan| (slice.map{|c| Color.send(chan, c)}.inject(0.0){|sum, v| sum += v} / slice.length).round })] * slice.length end send("replace_#{orientation}!", i, pixelated) end end end end end image = ChunkyPNG::Image.from_file('input.png') image.pixelize! image.save('output.png')
-
Finished in 2nd place with a final score of 3.4/5. (View the Gist)output.png
pixelize.rbView full entryrequire 'chunky_png' module ChunkyPNG::Color def self.average(pixels) new_r = pixels.map{|p| r(p)}.inject(&:+) / pixels.size new_g = pixels.map{|p| g(p)}.inject(&:+) / pixels.size new_b = pixels.map{|p| b(p)}.inject(&:+) / pixels.size rgb(new_r,new_g,new_b) end end class BlockyImage < ChunkyPNG::Image def block(x,y,n) positions = block_positions(x,y,n) positions.map{|x,y| get_pixel(x,y)} end def set_block(x,y,n,color) positions = block_positions(x,y,n) positions.map{|x,y| set_pixel(x,y,color)} end def pixelize(n) xtimes = (width / n.to_f).ceil ytimes = (height / n.to_f).ceil positions = (0...xtimes).map{|x| x * n}.product((0...ytimes).map{|y| y * n}) positions.each do |x,y| color = ChunkyPNG::Color.average(block(x, y, n)) set_block(x, y, n, color) end end private def block_positions(x,y,n) nx = [width, x + n].min ny = [height, y + n].min (x...nx).to_a.product((y...ny).to_a) end end image = BlockyImage.from_file('input.png') image.pixelize(10) image.save('output.png')
-
Finished in 3rd place with a final score of 3.3/5. (View the Gist).gitignore
input.png
README.mdDoing the damn thing
Outputs two versions of the image: a simple pixelated version and a version inspired by Andy Warhol.
Run it
ruby derp.rboutput.png
warhol.png
derp.rbView full entryrequire 'chunky_png' module ChunkyPNG class PixelChunk attr_reader :image, :left, :top, :right, :bottom def initialize(image, left, top, right, bottom) @image, @left, @top, @right, @bottom = image, left, top, right, bottom end def points @points ||= [*left..right].product([*top..bottom]) end def pixels points.map { |x, y| image[x, y] } end def average pix = self.pixels red = pix.map { |p| Color.r p }.inject(:+) / pix.size green = pix.map { |p| Color.g p }.inject(:+) / pix.size blue = pix.map { |p| Color.b p }.inject(:+) / pix.size Color.rgb(red, green, blue) end def color=(color) points.each do |x, y| image[x, y] = color end end def pixelize! self.color = average end def quadrant(size) xs = (0..image.width).step(image.width / size).each_cons(2).map { |a,b| a..b } ys = (0..image.height).step(image.height / size).each_cons(2).map { |a,b| a..b } [xs.index { |x| x.cover? right }, ys.index { |y| y.cover? bottom }] end end class Image def pixelize(size) dup.pixelize!(size) end def pixelize!(size) pixel_chunks(size).each(&:pixelize!) self end def pixelize_with_warhol(size) dup.pixelize_with_warhol!(size) end def pixelize_with_warhol!(size) pixel_chunks(size).each_with_index do |chunk, index| avg = chunk.average red = Color.r(avg) green = Color.g(avg) blue = Color.b(avg) it = 0.5 case chunk.quadrant(3) when [0,0], [1,1], [2,2] green -= green * it when [1,0], [2,1], [0,2] blue -= blue * it else red -= red * it end chunk.color = Color.rgb(red.to_i, green.to_i, blue.to_i) end self end def pixel_chunks(size) unless @pixel_chunks xs = (0...width).step(size).to_a ys = (0...height).step(size).to_a @pixel_chunks = xs.product(ys).map { |x,y| PixelChunk.new(self, x, y, [x+size, width].min - 1, [y+size, height].min - 1) } end @pixel_chunks end end end image = ChunkyPNG::Image.from_file('input.png') image.pixelize(10).save('output.png') image.pixelize_with_warhol(10).save('warhol.png') `open output.png` rescue nil `open warhol.png` rescue nil
-
Finished in 4th place with a final score of 3.3/5. (View the Gist)rbg.png
brg.png
gbr.png
lightest.png
first.png
darkest.png
random-component.png
grb.png
average.png
random-pixel.png
bgr.png
lightest.rbrequire 'chunky_png' pixel_size = 10 image = ChunkyPNG::Image.from_file('input.png') (image.width.to_f/pixel_size).round.times do |i| (image.height.to_f/pixel_size).round.times do |j| reds = [] greens = [] blues = [] pixel_size.times do |k| pixel_size.times do |l| begin reds << ChunkyPNG::Color.r(image[i*pixel_size+k,j*pixel_size+l]) greens << ChunkyPNG::Color.g(image[i*pixel_size+k,j*pixel_size+l]) blues << ChunkyPNG::Color.b(image[i*pixel_size+k,j*pixel_size+l]) rescue end end end color = ChunkyPNG::Color.rgba(reds.max,greens.max,blues.max,255) pixel_size.times do |k| pixel_size.times do |l| begin image[i*pixel_size+k,j*pixel_size+l] = color rescue end end end end end image.save('lightest.png')
random-pixel.rbrequire 'chunky_png' pixel_size = 10 image = ChunkyPNG::Image.from_file('input.png') (image.width.to_f/pixel_size).round.times do |i| (image.height.to_f/pixel_size).round.times do |j| color = image[[(i*pixel_size+rand(pixel_size)),image.width-1].min,[(j*pixel_size+rand(pixel_size)),image.height-1].min] pixel_size.times do |k| pixel_size.times do |l| begin image[i*pixel_size+k,j*pixel_size+l] = color rescue end end end end end image.save('random-pixel.png')
grb.rbrequire 'chunky_png' pixel_size = 10 def average(array) array.inject(0) { |sum, el| sum + el } / array.size end image = ChunkyPNG::Image.from_file('input.png') (image.width.to_f/pixel_size).round.times do |i| (image.height.to_f/pixel_size).round.times do |j| reds = [] greens = [] blues = [] pixel_size.times do |k| pixel_size.times do |l| begin reds << ChunkyPNG::Color.r(image[i*pixel_size+k,j*pixel_size+l]) greens << ChunkyPNG::Color.g(image[i*pixel_size+k,j*pixel_size+l]) blues << ChunkyPNG::Color.b(image[i*pixel_size+k,j*pixel_size+l]) rescue end end end color = ChunkyPNG::Color.rgba(average(greens),average(reds),average(blues),255) pixel_size.times do |k| pixel_size.times do |l| begin image[i*pixel_size+k,j*pixel_size+l] = color rescue end end end end end image.save('grb.png')
darkest.rbrequire 'chunky_png' pixel_size = 10 image = ChunkyPNG::Image.from_file('input.png') (image.width.to_f/pixel_size).round.times do |i| (image.height.to_f/pixel_size).round.times do |j| reds = [] greens = [] blues = [] pixel_size.times do |k| pixel_size.times do |l| begin reds << ChunkyPNG::Color.r(image[i*pixel_size+k,j*pixel_size+l]) greens << ChunkyPNG::Color.g(image[i*pixel_size+k,j*pixel_size+l]) blues << ChunkyPNG::Color.b(image[i*pixel_size+k,j*pixel_size+l]) rescue end end end color = ChunkyPNG::Color.rgba(reds.min,greens.min,blues.min,255) pixel_size.times do |k| pixel_size.times do |l| begin image[i*pixel_size+k,j*pixel_size+l] = color rescue end end end end end image.save('darkest.png')
bgr.rbrequire 'chunky_png' pixel_size = 10 def average(array) array.inject(0) { |sum, el| sum + el } / array.size end image = ChunkyPNG::Image.from_file('input.png') (image.width.to_f/pixel_size).round.times do |i| (image.height.to_f/pixel_size).round.times do |j| reds = [] greens = [] blues = [] pixel_size.times do |k| pixel_size.times do |l| begin reds << ChunkyPNG::Color.r(image[i*pixel_size+k,j*pixel_size+l]) greens << ChunkyPNG::Color.g(image[i*pixel_size+k,j*pixel_size+l]) blues << ChunkyPNG::Color.b(image[i*pixel_size+k,j*pixel_size+l]) rescue end end end color = ChunkyPNG::Color.rgba(average(blues),average(greens),average(reds),255) pixel_size.times do |k| pixel_size.times do |l| begin image[i*pixel_size+k,j*pixel_size+l] = color rescue end end end end end image.save('bgr.png')
rbg.rbrequire 'chunky_png' pixel_size = 10 def average(array) array.inject(0) { |sum, el| sum + el } / array.size end image = ChunkyPNG::Image.from_file('input.png') (image.width.to_f/pixel_size).round.times do |i| (image.height.to_f/pixel_size).round.times do |j| reds = [] greens = [] blues = [] pixel_size.times do |k| pixel_size.times do |l| begin reds << ChunkyPNG::Color.r(image[i*pixel_size+k,j*pixel_size+l]) greens << ChunkyPNG::Color.g(image[i*pixel_size+k,j*pixel_size+l]) blues << ChunkyPNG::Color.b(image[i*pixel_size+k,j*pixel_size+l]) rescue end end end color = ChunkyPNG::Color.rgba(average(reds),average(blues),average(greens),255) pixel_size.times do |k| pixel_size.times do |l| begin image[i*pixel_size+k,j*pixel_size+l] = color rescue end end end end end image.save('rbg.png')
average.rbrequire 'chunky_png' pixel_size = 10 def average(array) array.inject(0) { |sum, el| sum + el } / array.size end image = ChunkyPNG::Image.from_file('input.png') (image.width.to_f/pixel_size).round.times do |i| (image.height.to_f/pixel_size).round.times do |j| reds = [] greens = [] blues = [] pixel_size.times do |k| pixel_size.times do |l| begin reds << ChunkyPNG::Color.r(image[i*pixel_size+k,j*pixel_size+l]) greens << ChunkyPNG::Color.g(image[i*pixel_size+k,j*pixel_size+l]) blues << ChunkyPNG::Color.b(image[i*pixel_size+k,j*pixel_size+l]) rescue end end end color = ChunkyPNG::Color.rgba(average(reds),average(greens),average(blues),255) pixel_size.times do |k| pixel_size.times do |l| begin image[i*pixel_size+k,j*pixel_size+l] = color rescue end end end end end image.save('average.png')
gbr.rbrequire 'chunky_png' pixel_size = 10 def average(array) array.inject(0) { |sum, el| sum + el } / array.size end image = ChunkyPNG::Image.from_file('input.png') (image.width.to_f/pixel_size).round.times do |i| (image.height.to_f/pixel_size).round.times do |j| reds = [] greens = [] blues = [] pixel_size.times do |k| pixel_size.times do |l| begin reds << ChunkyPNG::Color.r(image[i*pixel_size+k,j*pixel_size+l]) greens << ChunkyPNG::Color.g(image[i*pixel_size+k,j*pixel_size+l]) blues << ChunkyPNG::Color.b(image[i*pixel_size+k,j*pixel_size+l]) rescue end end end color = ChunkyPNG::Color.rgba(average(greens),average(blues),average(reds),255) pixel_size.times do |k| pixel_size.times do |l| begin image[i*pixel_size+k,j*pixel_size+l] = color rescue end end end end end image.save('gbr.png')
random-component.rbrequire 'chunky_png' pixel_size = 10 def random(array) array[rand(array.size)] end image = ChunkyPNG::Image.from_file('input.png') (image.width.to_f/pixel_size).round.times do |i| (image.height.to_f/pixel_size).round.times do |j| reds = [] greens = [] blues = [] pixel_size.times do |k| pixel_size.times do |l| begin reds << ChunkyPNG::Color.r(image[i*pixel_size+k,j*pixel_size+l]) greens << ChunkyPNG::Color.g(image[i*pixel_size+k,j*pixel_size+l]) blues << ChunkyPNG::Color.b(image[i*pixel_size+k,j*pixel_size+l]) rescue end end end color = ChunkyPNG::Color.rgba(random(reds),random(greens),random(blues),255) pixel_size.times do |k| pixel_size.times do |l| begin image[i*pixel_size+k,j*pixel_size+l] = color rescue end end end end end image.save('random-component.png')
brg.rbrequire 'chunky_png' pixel_size = 10 def average(array) array.inject(0) { |sum, el| sum + el } / array.size end image = ChunkyPNG::Image.from_file('input.png') (image.width.to_f/pixel_size).round.times do |i| (image.height.to_f/pixel_size).round.times do |j| reds = [] greens = [] blues = [] pixel_size.times do |k| pixel_size.times do |l| begin reds << ChunkyPNG::Color.r(image[i*pixel_size+k,j*pixel_size+l]) greens << ChunkyPNG::Color.g(image[i*pixel_size+k,j*pixel_size+l]) blues << ChunkyPNG::Color.b(image[i*pixel_size+k,j*pixel_size+l]) rescue end end end color = ChunkyPNG::Color.rgba(average(blues),average(reds),average(greens),255) pixel_size.times do |k| pixel_size.times do |l| begin image[i*pixel_size+k,j*pixel_size+l] = color rescue end end end end end image.save('brg.png')
first.rbView full entryrequire 'chunky_png' pixel_size = 10 image = ChunkyPNG::Image.from_file('input.png') (image.width.to_f/pixel_size).round.times do |i| (image.height.to_f/pixel_size).round.times do |j| pixel_size.times do |k| pixel_size.times do |l| begin image[i*pixel_size+k,j*pixel_size+l] = image[i*pixel_size,j*pixel_size] rescue end end end end end image.save('first.png')
-
Finished in 5th place with a final score of 3.3/5. (View the Gist)4bit_r.png
4bit_b.png
4bit_g.png
8bit.png
brawl2.rbView full entryrequire 'chunky_png' include ChunkyPNG # ten by ten pixles? # might as well go 8 bit # while we are at it def reduce_depth(color, bits) Color.rgb(*Color.to_truecolor_bytes(color). map{ |c| c.to_f/256 }.each_with_index.map { |c,i| (256.0 * (c*bits[i]).round / bits[i]).to_i }) end def reduce_image(output, bits) image = Image.from_file('input.png') (0..image.width-1).step(10).each do |x| (0..image.height-1).step(10).each do |y| color = reduce_depth image[x,y], bits 10.times {|i| 10.times {|j| image.set_pixel_if_within_bounds x+i, y+j, color }} end end image.save("#{output}.png") end reduce_image "8bit", [8,8,4] reduce_image "4bit_r", [4,2,2] reduce_image "4bit_g", [2,4,2] reduce_image "4bit_b", [2,2,4]
-
Finished in 6th place with a final score of 3.2/5. (View the Gist)output.png
solution.rbView full entryrequire 'chunky_png' class Pixelizer attr_reader :image, :block_width, :block_height def initialize(image, block_width, block_height) @image = image @block_width = block_width @block_height = block_height end def pixelize! each_block do |canvas, x, y| color = average_color canvas image.rect x, y, x + canvas.width, y + canvas.height, color, color end end private def average_color(canvas) sum_r, sum_g, sum_b = 0, 0, 0 canvas.pixels.each do |pixel| sum_r += ChunkyPNG::Color.r pixel sum_g += ChunkyPNG::Color.g pixel sum_b += ChunkyPNG::Color.b pixel end area = canvas.area ChunkyPNG::Color.rgb(sum_r / area, sum_g / area, sum_b / area) end def each_block rows = (image.height.to_f / block_height).ceil cols = (image.width.to_f / block_width).ceil rows.times do |row| cols.times do |col| x, y = col * block_width, row * block_height width = [block_width, image.width - x].min height = [block_height, image.height - y].min yield image.crop(x, y, width, height), x, y end end end end image = ChunkyPNG::Image.from_file('input.png') Pixelizer.new(image, 10, 10).pixelize! image.save('output.png')
-
Finished in 7th place with a final score of 3.2/5. (View the Gist)simple_output.png
output.png
simple_solution.rbrequire 'chunky_png' image = ChunkyPNG::Image.from_file('input.png') width, height = image.width, image.height image.resample_nearest_neighbor!(width/10, height/10) image.resample_nearest_neighbor!(width, height) image.save('output.png')
solution.rbView full entryrequire 'chunky_png' module ChunkyPNG class Canvas module BilinearResampling def resample_bilinear!(new_width, new_height) width_ratio = width.to_f / new_width.to_f height_ratio = height.to_f / new_height.to_f pixels = [] 1.upto(new_height) do |y| 1.upto(new_width) do |x| pixels << get_bilinear_pixel((x-0.5)*width_ratio, (y-0.5)*height_ratio, width_ratio, height_ratio) end end replace_canvas!(new_width, new_height, pixels) end private def get_bilinear_pixel(x, y, sample_width, sample_height) x = x - sample_width/2 y = y - sample_height/2 pixel_weights = [] (y.floor..[(y+sample_height).ceil, height-1].min).each do |image_y| weight_y = 1 offset = y - image_y weight_y = offset if offset > 0 offset = image_y - (y + sample_height) weight_y = offset if offset > 0 (x.floor..[(x+sample_width).ceil, width-1].min).each do |image_x| weight = weight_y offset = x - image_x weight = (weight_y ** 2 + offset ** 2) ** 0.5 if offset > 0 offset = image_x - (x + sample_width) weight = (weight_y ** 2 + offset ** 2) ** 0.5 if offset > 0 pixel_weights << [get_pixel(image_x, image_y), weight] end end total_weight = pixel_weights.inject(0){|acc,(_,w)| acc+w } ChunkyPNG::Color.rgba( (pixel_weights.inject(0){|acc,(p,w)| acc+ChunkyPNG::Color.r(p)*w }/total_weight).round, (pixel_weights.inject(0){|acc,(p,w)| acc+ChunkyPNG::Color.g(p)*w }/total_weight).round, (pixel_weights.inject(0){|acc,(p,w)| acc+ChunkyPNG::Color.b(p)*w }/total_weight).round, (pixel_weights.inject(0){|acc,(p,w)| acc+ChunkyPNG::Color.a(p)*w }/total_weight).round ) end end include BilinearResampling end end image = ChunkyPNG::Image.from_file('input.png') width, height = image.width, image.height image.resample_bilinear!(width/10, height/10) image.resample_nearest_neighbor!(width, height) image.save('output.png')
-
Finished in 8th place with a final score of 3.2/5. (View the Gist)output.png
pixelizer.rbView full entry#!/usr/bin/env ruby begin require 'chunky_png' rescue LoadError require 'rubygems' require 'chunky_png' end class Pixelizer def initialize(source, chunkSize = 10) @source = ChunkyPNG::Image.from_file(source) @output = ChunkyPNG::Image.new(@source.width, @source.height) @size = chunkSize end def pixelize (0...@source.width).step(@size) do |x| (0...@source.height).step(@size) do |y| color = average_of_area(x, y) @output.rect(x, y, x + @size, y + @size, color, color) end end end def in_bounds?(x,y) x.between?(0, @source.width) && y.between?(0, @source.height) end def save(file) @output.save(file) end private def average_of_area(x, y) colors = {:r => [], :g => [], :b => []} x.upto(x + @size) do |_x| y.upto(y + @size) do |_y| next unless in_bounds?(_x, _y) pixel = @source.get_pixel(_x, _y) colors.each_key { |c| colors[c] << ChunkyPNG::Color.send(c, pixel) } end end color = ChunkyPNG::Color.rgb( average_of_color(colors[:r]), average_of_color(colors[:g]), average_of_color(colors[:b]) ) end def average_of_color(values) return 0 unless values.size > 0 values.inject { |s,e| s + e } / values.size end end p = Pixelizer.new('input.png') p.pixelize p.save('output.png')
-
Finished in 9th place with a final score of 3.2/5. (View the Gist)output_cheat.png
output_average.png
output_nearest.png
chunkypixel_average.rb#! usr/bin/env ruby require 'chunky_png' class ChunkyPNG::Image # s: Integer (pixel size) def pixelize s = 10 temp = Array.new((height*1.0/s).ceil) {Array.new((width*1.0/s).ceil) {Array.new(3) {0}}} height.times {|j| width.times {|i| ChunkyPNG::Color.to_truecolor_bytes(get_pixel(i,j)).each.with_index {|e,k| temp[j/s][i/s][k] += e}}} png = ChunkyPNG::Image.new width, height sq = s**2 height.times {|j| width.times {|i| png.set_pixel(i,j, ChunkyPNG::Color.parse(ChunkyPNG::Color.rgb(*temp[j/s][i/s].map {|e| e/sq})))}} png end end image = ChunkyPNG::Image.from_file('input.png') image.pixelize.save('output_average.png')
chunkypixel_cheat.rb#! usr/bin/env ruby require 'chunky_png' # ChunkyPNG::Image#resize is already implemented image = ChunkyPNG::Image.from_file('input.png') w, h = image.width, image.height image.resize(w/10, h/10).resize(w, h).save('output_cheat.png')
chunkypixel_nearest.rbView full entry#! usr/bin/env ruby require 'chunky_png' class ChunkyPNG::Image # s: Integer (pixel size) def pixelize s = 10 png = ChunkyPNG::Image.new width, height height.times {|j| width.times {|i| png.set_pixel(i,j, get_pixel(i/s*s, j/s*s))}} png end end image = ChunkyPNG::Image.from_file('input.png') image.pixelize.save('output_nearest.png')
-
Finished in 10th place with a final score of 3.2/5. (View the Gist)output.png
solution.rbView full entryrequire 'chunky_png' image = ChunkyPNG::Image.from_file('input.png') pixel_size = 10 (0...image.width).group_by { |n| n / pixel_size }.values.each do |xs| (0...image.height).group_by { |n| n / pixel_size }.values.each do |ys| area = xs.length * ys.length avg_color = ChunkyPNG::Color.rgb(*( xs.inject([0,0,0]) { |sum, x| ys.inject(sum) { |subsum, y| %w(r g b).zip(subsum).map { |channel, n| n + ChunkyPNG::Color.send(channel, image[x,y]) } } }.map { |n| n / area } )) image.rect(xs.first, ys.first, xs.last, ys.last, avg_color, avg_color) end end image.save('output.png')
-
Finished in 11th place with a final score of 3.1/5. (View the Gist)chunky3.png
pixelize.rbView full entryrequire 'chunky_png' module Pixelize # Replace chunks of an image with a single color. # # @param size Number of pixels to consolidate # def pixelize!(size) # Step over each chunk horizontally and vertically, being careful # not to exceed the boundaries (hence subtracting one). 0.step(self.width-1, size) do |x1| x2 = x1+size-1 0.step(self.height-1, size) do |y1| y2 = y1+size-1 rect(x1, y1, x2, y2, ChunkyPNG::Color::TRANSPARENT, self[x1,y1]) end end end end class ChunkyPNG::Image include Pixelize end img = ChunkyPNG::Image.from_file('output.png') img.pixelize!(10) img.save('chunky3.png')
-
Finished in 12th place with a final score of 3.1/5. (View the Gist)output.png
solution.rbView full entryrequire 'chunky_png' module ChunkyPNG::Color def self.interpolate(*colors) r, g, b = 0, 0, 0 colors.each do |c| r += r(c) g += g(c) b += b(c) end r, g, b = [r, g, b].map {|v| v / colors.length } rgb(r, g, b) end end class ChunkyPNG::Image def pixelate!(block_size = 10) (0..width - 1).step(block_size) do |x0| (0..height - 1).step(block_size) do |y0| x1 = x0 + block_size - 1 y1 = y0 + block_size - 1 int_color = interpolate_rect(x0, y0, x1, y1) rect(x0, y0, x1, y1, ChunkyPNG::Color::TRANSPARENT, int_color) end end self end private def interpolate_rect(x0, y0, x1, y1) colors = [] (x0..x1).each do |x| (y0..y1).each do |y| colors << self[x, y] if include_xy?(x, y) end end ChunkyPNG::Color.interpolate(*colors) end end ChunkyPNG::Image.from_file('input.png').pixelate!.save('output.png')
-
Finished in 13th place with a final score of 3.1/5. (View the Gist)README
Pixelise! ========= This has two pixelising function, a straight average of the colour channels in a given block, or a weighted mean based upon a two dimensional normal distribution. The code uses the weighted version, but the other is kept on for display purposes. Name your image "input.png" and run the program.
output.png
pixelise.rbView full entryrequire 'chunky_png' # 2d gaussian # # @params x x coord # @params y y coord # @params w width of sample # @params h height of sample def twodguass(x,y,w,h) #distro constants big_a = 1 a = 0.5 b = 0 c = 0.5 x_0 = w/2 y_0 = h/2 p1 = a*((x-x_0)**2) p2 = 2*b*((x-x_0)*(y-y_0)) p3 = c*((y-y_0)**2) p = (p1+p2+p3)*-1 return big_a*(Math.exp(p)) end # Pixelise functions # Return the average colour of a given area # # @params image image to manipulate # @params x x coord # @params y y coord # @params w width of area # @params h height of area def av_area(image, x, y, w, h) # image meta for error checking i_w = image.width i_h = image.height # Total area tot = w*h # average bins av_r = 0 av_g = 0 av_b = 0 # fill bins (0...w).each do |i| (0...h).each do |j| if(x+i >= i_w || y+j >= i_h) tot -=1 else raw = image[x+i,y+j] av_r += ChunkyPNG::Color.r(raw) av_g += ChunkyPNG::Color.g(raw) av_b += ChunkyPNG::Color.b(raw) end end end # divide bins av_r /= tot av_g /= tot av_b /= tot return ChunkyPNG::Color.rgb(av_r, av_g, av_b) end # Return the weighted average colour of a given area # # @params image image to manipulate # @params x x coord # @params y y coord # @params w width of area # @params h height of area def w_av_area(image, x, y, w, h) # image meta for error checking i_w = image.width i_h = image.height # Total weight tot = 0 # average bins av_r = 0 av_g = 0 av_b = 0 # fill bins (0...w).each do |i| (0...h).each do |j| if(x+i >= i_w || y+j >= i_h) else weight = twodguass(i,j,w,h) tot += weight raw = image[x+i,y+j] av_r += ChunkyPNG::Color.r(raw) * weight av_g += ChunkyPNG::Color.g(raw) * weight av_b += ChunkyPNG::Color.b(raw) * weight end end end # divide bins av_r /= tot av_g /= tot av_b /= tot return ChunkyPNG::Color.rgb(av_r.to_i, av_g.to_i, av_b.to_i) end # Pixelise function # # @params image image to pixelise # @params b_w block width # @params b_h block height # # @return pixelised image def pixelise(image, b_w, b_h) # Get image size w = image.width h = image.height # Number of blocks nbx = (w/b_w.to_f).ceil nby = (h/b_h.to_f).ceil # Go through each "block" in the image (0...nbx).each do |p_x| (0...nby).each do |p_y| # Top left coord x = p_x * b_w y = p_y * b_h # Get the average colour of the block p_colour = w_av_area(image, x, y, b_w, b_h); # Fill in block (0...b_w).each do |x1| (0...b_h).each do |y1| # Fill in up to edges image[x+x1,y+y1] = p_colour if !(x+x1 >= w || y+y1 >= h) end end end end return image end ### Script # Grab image image = ChunkyPNG::Image.from_file('input.png') # Size of pixeled area b_w = 10 b_h = 10 # Run the pixelisation image = pixelise(image, b_w, b_h) # save image.save('output.png')
-
Finished in 14th place with a final score of 3.0/5. (View the Gist)output.png
pixelize.rbView full entryrequire 'chunky_png' image = ChunkyPNG::Image.from_file('input.png') image.height.times do |y| image.width.times do |x| # As we iterate over each pixel, copy only the color of the pixel # from the top left corner of this pixels 10x10 region # e.g. the pixel at [69,17] will get set to the color from [60,10] image[x, y] = image[x / 10 * 10, y / 10 * 10] end end image.save('output.png')
-
Finished in 15th place with a final score of 3.0/5. (View the Gist)output.png
pixelize.rbView full entryrequire 'chunky_png' module ChunkyPNG::Color def self.average(pixels) pixel_rgbs = pixels.map{|p| to_truecolor_bytes(p)} averaged_rgb = [0,0,0].zip(*pixel_rgbs).map{|colors| colors.inject(&:+) / pixels.size} rgb(*averaged_rgb) end end module Pixelizeable def pixilize!(pixel_width, pixel_height = pixel_width) collect_block!(pixel_height, pixel_width) do |block| new_color = ChunkyPNG::Color.average(block.pixels) new_pixels = (0...(block.width * block.height)).collect{new_color} ChunkyPNG::Canvas.new(block.width, block.height, new_pixels) end end def collect_block!(pixel_width, pixel_height) image_block_width = (width.to_f / pixel_width.to_f).ceil image_block_height = (height.to_f / pixel_height.to_f).ceil x_offsets = (0...image_block_width).map{|x| x * pixel_width} y_offsets = (0...image_block_height).map{|x| x * pixel_height} xy_offsets = x_offsets.product(y_offsets) xy_offsets.each do |x_offset, y_offset| block_width = [pixel_width, width - x_offset].min block_height = [pixel_height, height - y_offset].min new_block = yield crop(x_offset, y_offset, block_width, block_height) replace!(new_block, x_offset, y_offset) end end end ChunkyPNG::Image.send :include, Pixelizeable image = ChunkyPNG::Image.from_file('input.png') image.pixilize!(10) image.save('output.png')
-
Finished in 16th place with a final score of 3.0/5. (View the Gist)output.png
pixelize.rbView full entryrequire 'rubygems' require 'chunky_png' include ChunkyPNG class Pixelizer attr_reader :canvas def initialize(canvas, size=10) @canvas = canvas @size = size.to_i @rows = (@canvas.height.to_f / @size).ceil @columns = (@canvas.width.to_f / @size).ceil end # Fills each block with the average color of its pixels def pixelize blocks.each do |block| block.fill(block.average_color) end self end # An array of the blocks composing the canvas def blocks @blocks ||= [].tap do |blocks| @rows.times do |row| @columns.times do |column| blocks << Block.new(@canvas, @size * column, @size * row, @size, @size) end end end end class Block def initialize(canvas, x, y, width, height) @canvas = canvas @x0, @y0, @x1, @y1 = x, y, x + width, y + height end def average_color colors = [] for x in @x0...@x1 for y in @y0...@y1 colors << Color.to_truecolor_bytes(@canvas[x, y]) if @canvas.include_xy?(x, y) end end Color.rgb *colors.transpose.map { |c| c.inject(:+) / c.size } end def fill(color) @canvas.rect(@x0, @y0, @x1, @y1, Color::TRANSPARENT, color) end end end input = Image.from_file('input.png') output = Pixelizer.new(input).pixelize.canvas output.save('output.png')
-
Finished in 17th place with a final score of 3.0/5. (View the Gist)output.png
pixelate.rbView full entryrequire 'rubygems' require 'chunky_png' include ChunkyPNG BLOCK_SIZE = 10 def main in_file = 'input.png', out_file = 'output.png' image = Image.from_file in_file 0.step(image.width, BLOCK_SIZE) do |x| 0.step(image.height, BLOCK_SIZE) do |y| section = image.safe_crop x, y, BLOCK_SIZE, BLOCK_SIZE unless section.pixels.length <= 0 section.fill Color.rgb_average(*section.pixels) image.replace! section, x, y end end end image.save out_file end class Canvas def safe_crop x, y, crop_width, crop_height crop_width = [crop_width, self.width - x].min crop_height = [crop_height, self.height - y].min self.crop x, y, crop_width, crop_height end def fill color self.rect 0, 0, self.width-1, self.height-1, ChunkyPNG::Color::TRANSPARENT, color end end module Color def rgb_average *args r, g, b = [args.map{|c| r(c)}, args.map{|c| g(c)}, args.map{|c| b(c)}].map &:average rgb(r, g, b) end end class Array def average return self.inject(:+) / self.length end end main
-
Finished in 18th place with a final score of 2.9/5. (View the Gist)README.md
ChunkyImagePixelizer
A very blunt attempt - slice the image in blocks, determine normalized color as a mean of all pixels colors ( pixels that are closer to the block center have more "weight" in result ), then paint entire rectangle with new color. Supports arbitrary shape of blocks.
ChunkyImagePixelizer.pixelize_image("input.png", "output.png", :width => 10, :height => 10)
output_10.png
output_4x8.png
output_6.png
output_8.png
solution.rbView full entryrequire "chunky_png" module ChunkyImagePixelizer class PixelsBlock def initialize(image, left, top, right, bottom) @image = image @left, @top, @right, @bottom = left, top, right, bottom end def normalized_color center_x = (@left + @right) / 2.0 center_y = (@top + @bottom) / 2.0 colors = (@left..@right).collect do |x| (@top..@bottom).collect do |y| { :color => @image[x,y], :distance => (1 + Math.hypot((x - center_x).abs, (y - center_y).abs)) ** 2 } end end.flatten max_distance = colors.collect{|c| c[:distance]}.max * 1.1 new_color = [0, 0, 0, 0] new_color_count = colors.inject(0.0) do |i, c| weight = max_distance - c[:distance] ChunkyPNG::Color.to_truecolor_alpha_bytes(c[:color]).each_with_index do |c, i| new_color[i] += times * c end i + weight end ChunkyPNG::Color.rgba(*new_color.map{|c| (c / new_color_count).round}) end def normalize_color! color = normalized_color @image.rect(@left, @top, @right, @bottom, color, color) end end class Slicer include Enumerable def initialize(image, shape) @image = image @width, @height, @shape = image.width, image.height, shape @left = @width % shape[:width] / -2 @top = @height % shape[:height] / -2 @right = @width - @left @bottom = @height - @top end def each (@left..@right).step(@shape[:width]) do |x| (@top..@bottom).step(@shape[:height]) do |y| if x < @width and y < @height yield PixelsBlock.new( @image, [x, 0].max, [y, 0].max, [x + @shape[:width], @width].min - 1, [y + @shape[:height], @height].min - 1 ) end end end end end def self.pixelize!(image, options = {}) options = {:width => 10, :height => 10}.merge(options) Slicer.new(image, options).each(&:normalize_color!) image end def self.pixelize_image(input_filename, output_filename, options = {}) pixelize!(ChunkyPNG::Image.from_file(input_filename), options).save(output_filename) end end ChunkyImagePixelizer.pixelize_image("input.png", "output_10.png") ChunkyImagePixelizer.pixelize_image("input.png", "output_8.png", :width => 8, :height => 8) ChunkyImagePixelizer.pixelize_image("input.png", "output_6.png", :width => 6, :height => 6) ChunkyImagePixelizer.pixelize_image("input.png", "output_4x8.png", :width => 4, :height => 8)
-
Finished in 19th place with a final score of 2.9/5. (View the Gist)output.png
mosaic.rbView full entryrequire 'chunky_png' image = ChunkyPNG::Image.from_file('input.png') (0...image.width).each do |x| (0...image.height).each do |y| image[x, y] = image[x/10*10, y/10*10] end end image.save('output.png')
-
Finished in 20th place with a final score of 2.9/5. (View the Gist)output.png
pixelate.rbView full entry# Code for http://codebrawl.com/contests/pixelizing-images-with-chunkypng - @jamesu require 'rubygems' require 'open-uri' require 'chunky_png' open("https://gist.github.com/raw/5dace61b37de19a56637/032f70a3023e37a7dc1fe4a619cd8c4f970d1677/output.png") do |f| p=ChunkyPNG;image=p::Image.from_blob(f.read);col=p::Color (0...(image.width)).each_slice(10) {|x| (0...(image.height)).each_slice(10) {|y| sum = [0,0,0] x.each{|xp| y.each{|yp| p = image.get_pixel(xp,yp) sum[0]+=col.r(p); sum[1]+=col.g(p); sum[2]+=col.b(p) }} sum.map!{|c| c /= x.length*y.length} sum_color = ChunkyPNG::Color.rgb(sum[0],sum[1],sum[2]) image.rect(x[0],y[0],x[0]+9,y[0]+9,sum_color,sum_color) }} image.save('output.png') end
-
Finished in 21st place with a final score of 2.9/5. (View the Gist)output.png
pixelize.rbView full entryrequire 'chunky_png' image = ChunkyPNG::Image.from_file('input.png') BLOCK_SIZE = 10 (0...image.height).each_slice(BLOCK_SIZE) do |y_range| (0...image.width).each_slice(BLOCK_SIZE) do |x_range| block = x_range.map { |x| y_range.map { |y| [x, y] } }.flatten(1) r, g, b, a, size = 0, 0, 0, 0, block.size block.each do |x, y| m, n, o, p = ChunkyPNG::Color.to_truecolor_alpha_bytes(image[x, y]) r, g, b, a = r + m, g + n, b + o, a + p end avg = ChunkyPNG::Color.rgba(r / size, g / size, b / size, a / size) block.each { |x, y| image[x, y] = avg } end end image.save('output.png')
-
Finished in 22nd place with a final score of 2.9/5. (View the Gist)README.md
Pixelizer entry for Codebrawl
This example needs Ruby 1.9.
Two ways to run this:
$ ./pixelizer.rb infile.png > outfile.png $ ./pixelizer.rb - > outfile.png < infile.pngoutput.png
pixelizer.rbView full entry#!/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
-
Finished in 23rd place with a final score of 2.8/5. (View the Gist)output.png
solution.rbView full entry# Solution for Codebrawl #2 # Author: Sam Pohlenz require 'chunky_png' class Pixelator attr_reader :resolution def initialize(resolution=10) @resolution = resolution end # Pixelates a ChunkyPNG Image (in place) according to the current resolution def pixelate(image) columns = (image.width / resolution.to_f).ceil rows = (image.height / resolution.to_f).ceil rows.times do |row| columns.times do |column| color = sample(image, column, row) paint(image, column, row, color) end end end # Finds the average color within a block determined by the current resolution def sample(image, column, row) pixels = 0 r = g = b = 0 each_pixel_in(image, column, row) do |x, y| color = image[x, y] r += ChunkyPNG::Color.r(color) g += ChunkyPNG::Color.g(color) b += ChunkyPNG::Color.b(color) pixels += 1 end raise ChunkyPNG::OutOfBounds, "Block #{column}, #{row} out of bounds" if pixels.zero? ChunkyPNG::Color.rgb(r / pixels, g / pixels, b / pixels) end # Fills a block with a given color def paint(image, column, row, color) each_pixel_in(image, column, row) do |x, y| image[x, y] = color end end private def each_pixel_in(image, column, row) resolution.times do |x| resolution.times do |y| image_x, image_y = column*resolution + x, row*resolution + y next unless image.include_xy?(image_x, image_y) yield image_x, image_y end end end end image = ChunkyPNG::Image.from_file('input.png') Pixelator.new(10).pixelate(image) image.save('output.png')
-
Finished in 24th place with a final score of 2.8/5. (View the Gist)output.png
solution.rbView full entryrequire 'chunky_png' image = ChunkyPNG::Image.from_file('input.png') chunks = [] #the tuples we're going to operate on [start-x, start-y, delta-x, delta-y] chunk_size = 10 #how many square pixels per normal sized chunk #dimensions w = image.dimension.width h = image.dimension.height #remainders wr = w % chunk_size hr = h % chunk_size #limits hl = h - hr wl = w - wr #grab each square chunk (0..(w-wr-chunk_size)).step(chunk_size).each do |x| (0..(h-hr-chunk_size)).step(chunk_size).each do |y| chunks << [x, y, chunk_size, chunk_size] end #if there's a remainder on the Y axis, add that if hr > 0 chunks << [x, hl, chunk_size, hr] #odd height chunk end end #if there's a remainder on the X axis if wr > 0 (0..(h-hr-chunk_size)).step(chunk_size).each do |y| chunks << [wl, y, wr, chunk_size] #odd width chunk end if hr > 0 #remainder on both X and Y chunks << [wl, hl, wr, hr] #bottom right corner chunk, odd width and height end end chunks.each do |chunk| c_r = c_g = c_b = 0 x,y,dx,dy = chunk pixels = dx*dy #sum the indvidual color values for all pixels in the chunk (x..(x + dx - 1)).each do |a| (y..(y + dy - 1)).each do |b| c_r += ChunkyPNG::Color.r(image[a,b]) c_g += ChunkyPNG::Color.g(image[a,b]) c_b += ChunkyPNG::Color.b(image[a,b]) end end #divide by total number of pixels c_r /= pixels c_g /= pixels c_b /= pixels color = ChunkyPNG::Color.rgb(c_r,c_g,c_b) #color to set the chunk to (x..(x + dx - 1)).each do |a| (y..(y + dy - 1)).each do |b| image[a,b] = color end end end image.save('output.png')
-
Finished in 25th place with a final score of 2.8/5. (View the Gist)Gemfile
source :rubygems gem "chunky_png", :git => "git://github.com/mariusgrigoriu/chunky_png.git"
out.png
operations.rb# Pixelizes the canvas # # This method will modify the canvas. To obtain a new canvas and leave the # current instance intact use {#pixelize} instead. # # @param [Integer] size_x The width of the block used to pixelize the image # @param [Integer] size_y The height of the block used to pixelize the image # @return [ChunkyPNG::Canvas] Returns itself, pixelized. # @see {#pixelize!} def pixelize!(size_x, size_y) Struct.new("RGBA", :r, :g, :b, :a, :count) bins = Array.new((height/size_y.to_f).ceil) do Array.new((width/size_x.to_f).ceil) { Struct::RGBA.new(0,0,0,0,0)} end for y in 0...height do for x in 0...width do bin = bins[y/size_y][x/size_x] bin.r += ChunkyPNG::Color.r(get_pixel(x,y)) bin.g += ChunkyPNG::Color.g(get_pixel(x,y)) bin.b += ChunkyPNG::Color.b(get_pixel(x,y)) bin.a += ChunkyPNG::Color.a(get_pixel(x,y)) bin.count += 1 end end for y in 0...height do for x in 0...width do bin = bins[y/size_y][x/size_x] divisor = bin.count.to_f set_pixel(x,y, ChunkyPNG::Color( (bin.r/divisor).round, (bin.g/divisor).round, (bin.b/divisor).round, (bin.a/divisor).round) ) end end return self end
pixelize-chunky_fork.rbView full entryrequire 'chunky_png' ChunkyPNG::Image.from_file('in.png').pixelize!(10, 10).save('out.png')
-
Finished in 26th place with a final score of 2.8/5. (View the Gist)output.png
pixelize.rbView full entryrequire 'chunky_png' image = ChunkyPNG::Image.from_file('input.png') original = image.dup block_size = 10 xs = 0.upto((image.width - 1) / block_size).to_a.map { |x| x * block_size } ys = 0.upto((image.height - 1) / block_size).to_a.map { |y| y * block_size } xs.each do |x| ys.each do |y| color = original[x, y] image.rect x, y, x + block_size, y + block_size, color, color end end image.save('output.png')
-
Finished in 27th place with a final score of 2.7/5. (View the Gist)output.png
pixelator.rbView full entryrequire 'rubygems' require 'chunky_png' # Pixelator will pixelify your pngs # defaults to a 10 pixel block, but will gladly accept # larger or smaller blocks class Pixelator include ChunkyPNG::Color def initialize(image_path) @image = ChunkyPNG::Image.from_file(image_path) end # anytime we set the block size the number of block rows # and block columns needs to be updated as well def set_block_size(blck_size) @block_size = blck_size @col_blocks = (@image.width.to_f / @block_size.to_f) @row_blocks = (@image.height.to_f / @block_size.to_f) end def pixelate(blck_size=10) set_block_size(blck_size) # iterate through each "block" as defined by the block size each_block do |x, y| red = [] green = [] blue = [] alpha = [] # setting current block size handles partial blocks at the ends of rows, columns, or both curr_x_block_size = x + 1 > @col_blocks ? @image.width - (@col_blocks.to_i * @block_size) : @block_size curr_y_block_size = y + 1 > @row_blocks ? @image.height - (@row_blocks.to_i * @block_size) : @block_size # iterate through the pixels in the current block and pull out all the color info each_pixel_from_block(curr_x_block_size, curr_y_block_size) do |xp, yp| pixel = @image[(x * @block_size) + xp, (y * @block_size) + yp] red << r(pixel) green << g(pixel) blue << b(pixel) alpha << a(pixel) end averages = [] # average each color channel [red, green, blue, alpha].each do |color| averages << color.inject{ |sum,i| sum + i } / color.size end # set every pixel from the current block to the averaged color value each_pixel_from_block(curr_x_block_size, curr_y_block_size) do |xp, yp| @image[(x * @block_size) + xp, (y * @block_size) + yp] = ChunkyPNG::Color.rgba(averages[0], averages[1], averages[2], averages[3]) end end @image end private def each_block @row_blocks.ceil.times do |y| @col_blocks.ceil.times do |x| yield(x,y) end end end def each_pixel_from_block(x_block_size, y_block_size) (0..x_block_size - 1).each do |xp| (0..y_block_size - 1).each do |yp| yield(xp, yp) end end end end Pixelator.new('input.png').pixelate(10).save("output.png")
-
Finished in 28th place with a final score of 2.7/5. (View the Gist)output.png
Pixelize.rbView full entryrequire 'chunky_png' include ChunkyPNG::Color include ChunkyPNG::Canvas::Drawing input = ChunkyPNG::Image.from_file('input.png') # Put your logic to averageize `image` here. BLOCK_SIZE = 10 height = input.height width = input.width heightIterations = height / BLOCK_SIZE widthIterations = width / BLOCK_SIZE canvas = ChunkyPNG::Canvas.new(width, height, WHITE) def averageArray(array) return (array.inject { |sum, el| sum + el }.to_f / array.size).to_i end heightIterations.times do |y| widthIterations.times do |x| reds, greens, blues = [], [], [] BLOCK_SIZE.times do |cy| BLOCK_SIZE.times do |cx| pixel = input[x * BLOCK_SIZE + cx, y * BLOCK_SIZE + cy] index = cy * BLOCK_SIZE + cx reds[index], greens[index], blues [index] = r(pixel), g(pixel), b(pixel) end end average = rgb(averageArray(reds), averageArray(greens), averageArray(blues)) canvas.rect(x * BLOCK_SIZE, y * BLOCK_SIZE, (x+1) * BLOCK_SIZE, (y+1) * BLOCK_SIZE, average, average) end end # Save image canvas.save('output.png')
-
Finished in 29th place with a final score of 2.6/5. (View the Gist)README.md
CodeBrawl #2: Pixelizing images with ChunkyPNG
Requirements
- ruby 1.9.x
- chunky_png gem
Usage
ruby pixelize.rb
output.png
pixelize.rbView full entryrequire 'chunky_png' class Pixelizer include ChunkyPNG def initialize(input, output, blk_size) @input_filename = input @output_filename = output @block_size = blk_size end # run it def run @input = Image.from_file(@input_filename) @input_h = @input.height @input_w = @input.width @output = Image.new(@input_w, @input_h) (0...@input_w).step(@block_size) do |x| (0...@input_h).step(@block_size) do |y| get_block(x,y) { |blk| get_pixels(blk) { |pixels| pixelize(pixels) { |color| write_block(x, y, color) } } } end end @output.save(@output_filename) end private # grab a block of the image starting at start_x, start_y with dimensions of @block_size def get_block(start_x, start_y) raise OutOfBounds if !@input.dimension.include?([start_x, start_y]) stop_x = @input.include_x?(start_x+@block_size-1) ? start_x+@block_size-1 : @input.width-1 stop_y = @input.include_y?(start_y+@block_size-1) ? start_y+@block_size-1 : @input.height-1 blk = [] (start_y..stop_y).each do |y| row = [] (start_x..stop_x).each do |x| row << @input[x,y] end blk << row end yield blk if block_given? blk end # grab the pixel values from the block, separating into rgba values def get_pixels(blk) pixels = blk.flatten.collect do |p| pix = p.to_s(16).rjust(8, "0") { :red => pix[0,2].hex, :green => pix[2,2].hex, :blue => pix[4,2].hex, :alpha => pix[6,2].hex } end yield pixels if block_given? pixels end # just avg the colors of the block def colorize(pixels, color) pixels.collect { |p| p[color] }.inject(0) { |sum, p| sum + p }/pixels.size end # determine the color of the block def pixelize(pixels) color = [:red, :green, :blue, :alpha].collect { |color| colorize(pixels, color) } yield color if block_given? color end # write the new pixelized block def write_block(x, y, color) (x...x+@block_size).each do |a| (y...y+@block_size).each do |b| @output.set_pixel_if_within_bounds(a, b, Color.rgba(color[0], color[1], color[2], color[3])) end end end end pix = Pixelizer.new('input.png', 'output.png', 10) pix.run
-
Finished in 30th place with a final score of 2.6/5. (View the Gist)output_1309375058.png
pngbrawl.rbView full entry#! /usr/bin/env ruby require 'rubygems' require 'chunky_png' image = ChunkyPNG::Image.from_file('input.png') h = image.dimension.height print "height #{h} \n" w = image.dimension.width print "width #{w} \n" def pixel_bomb(x, y, image) currentGreen = ChunkyPNG::Color.g(image[x,y]) color = ChunkyPNG::Color(image[x,y]) sz = case currentGreen when 0..63 rand(2) when 63..127 1 when 127..191 2 when 191..255 4 end (-sz..sz).to_a.each do |xn| (-sz..sz).to_a.each do |yn| p = x + xn q = y + yn image.set_pixel_if_within_bounds(p,q, color) end end end def back_to_xy(int, width, height) x = int % width y = (int / width) [x, y] end hash = Hash.new{|h, k| h[k] = []} (0..h-1).each do |y| (0..w-1).each do |x| this_one = (y * w) + x currentGreen = ChunkyPNG::Color.g(image[x,y]) hash[currentGreen] << this_one end end shash = hash.sort shash.each do |k, v| v.each do |pixel| x, y = back_to_xy(pixel, w, h) pixel_bomb(x, y, image) end end image.save('output_' + Time.now.to_i.to_s + '.png')
-
Finished in 31st place with a final score of 2.6/5. (View the Gist)output.png
chunky_pixels.rbView full entryrequire 'rubygems' require 'chunky_png' orig = ChunkyPNG::Image.from_file('input.png') rows = (orig.width / 10.0).ceil cols = (orig.height / 10.0).ceil canvas = ChunkyPNG::Canvas.new(rows * 10, cols * 10).replace(orig) (0...rows).each do |row| x = row * 10 (0...cols).each do |col| y = col * 10 chunk = canvas.crop(x, y, 10, 10).resample(100, 100).crop(45, 45, 10, 10) canvas.replace!(chunk, x, y) end end canvas.crop(0, 0, orig.width, orig.height).save('output.png')
-
Finished in 32nd place with a final score of 2.6/5. (View the Gist)output.png
pixelator.rbView full entryrequire 'chunky_png' class Pixelator def self.pixelate source_path, output_path, pixel_size = 10 source = ChunkyPNG::Image.from_file( source_path ) output = ChunkyPNG::Image.new( source.width, source.height, ChunkyPNG::Color::TRANSPARENT ) # output cols & rows cols = (source.height.to_f / pixel_size).ceil rows = (source.width.to_f / pixel_size).ceil cols.times do |y| rows.times do |x| # check for out of bounds pixel x_min = x * pixel_size y_min = y * pixel_size x_max = [x_min + pixel_size, source.width].min y_max = [y_min + pixel_size, source.height].min # get color color = source.get_pixel( x_min, y_min ) # draw output.rect( x_min, y_min, x_max, y_max, ChunkyPNG::Color::TRANSPARENT, color ) end end output.save( output_path ) end end Pixelator.pixelate( 'input.png', 'output.png' )
-
Finished in 33rd place with a final score of 2.6/5. (View the Gist)output.png
solution.rbView full entryrequire 'rubygems' require 'chunky_png' PIXEL_SIZE = 10 input = ChunkyPNG::Image.from_file('input.png') PIXELATED_width = (input.width.to_f/PIXEL_SIZE).ceil.to_i * PIXEL_SIZE PIXELATED_height = (input.height.to_f/PIXEL_SIZE).ceil.to_i * PIXEL_SIZE output = ChunkyPNG::Image.new(PIXELATED_width, PIXELATED_height, ChunkyPNG::Color::TRANSPARENT) # divide into pixel grid # deals with the remainder (i.e. output.width%PIXEL_SIZE) by cropping image afterwards (PIXELATED_width/PIXEL_SIZE).times do |x| (PIXELATED_height/PIXEL_SIZE).times do |y| w = x*PIXEL_SIZE h = y*PIXEL_SIZE # get square of pixels begin pxl_space = input.crop(w,h,PIXEL_SIZE,PIXEL_SIZE) rescue ChunkyPNG::OutOfBounds pxl_space = input.crop(w,h,input.width-w,input.height-h) end # determine output pixel colour pxl_r = pxl_space.pixels.inject(0) { |s,v| s+=ChunkyPNG::Color.r(v) }/pxl_space.pixels.count pxl_g = pxl_space.pixels.inject(0) { |s,v| s+=ChunkyPNG::Color.g(v) }/pxl_space.pixels.count pxl_b = pxl_space.pixels.inject(0) { |s,v| s+=ChunkyPNG::Color.b(v) }/pxl_space.pixels.count # paint pixel output.rect(w, h, w+PIXEL_SIZE, h+PIXEL_SIZE, ChunkyPNG::Color::TRANSPARENT, ChunkyPNG::Color.rgb(pxl_r,pxl_g,pxl_b)) end end # crop output.crop!(0,0, input.width, input.height) output.save('output.png')
-
Finished in 34th place with a final score of 2.6/5. (View the Gist)output.png
pixelit.rbView full entryrequire 'rubygems' require 'chunky_png' module Pixelization def pixelizing_replace!(size) # This method uses the (0,0) color value for the (size,size) grid # as the value throughout the pixelization (0..self.height-1).step(size) do |h| row = self.row(h) indices = (0..width-size).step(size).to_a indices.each do |i| row[i,size] = size.times.map { row[i] } end size.times do |h_delta| if (h + h_delta) < self.height self.replace_row!(h + h_delta, row) end end end end end image = ChunkyPNG::Image.from_file('input.png') image.extend Pixelization image.pixelizing_replace!(10) image.save("output.png")
-
Finished in 35th place with a final score of 2.6/5. (View the Gist)output.png
solution.rbView full entry#!/usr/bin/env ruby require 'chunky_png' image = ChunkyPNG::Image.from_file('input.png') # Size of the blocks in pixels by pixels blockSize = 10 # Process block by block w = 0 while w < image.width do h = 0 while h < image.height do sum = [0, 0, 0] # Sum of all the rgb values of the pixels in the block pixels = 0 # Number of pixels in the block (blocks at the right/bottom may be incomplete) avg = [0, 0, 0] # Average color for that block # Process all pixels in that block (blockSize x blockSize) 0.upto(blockSize) do |x| next if (w + x) >= image.width # Check bounds! 0.upto(blockSize) do |y| next if (h + y) >= image.height # Check bounds! # Get the RGB representation for that pixel color = ChunkyPNG::Color.to_truecolor_bytes(image[w + x, h + y]) # Aggregate pixel value to the accumulative array sum[0] += color[0] sum[1] += color[1] sum[2] += color[2] # Another pixel processed for this block pixels += 1 end end # Just calculate the average of the three components avg[0] = sum[0] / pixels avg[1] = sum[1] / pixels avg[2] = sum[2] / pixels # Update every pixel in the block with the same average color 0.upto(blockSize) do |x| next if (w + x) >= image.width 0.upto(blockSize) do |y| next if (h + y) >= image.height image[w + x, h + y] = ChunkyPNG::Color(avg[0], avg[1], avg[2]) end end # Next row h += blockSize end # Next column w += blockSize end image.save('output.png')
-
Finished in 36th place with a final score of 2.5/5. (View the Gist)output.png
solution.rbView full entryrequire 'chunky_png' image = ChunkyPNG::Image.from_file('input.png') # Not exactly elegant, but it gets the job done sample_y = 0 while sample_y < image.height do sample_x = 0 while sample_x < image.width do sample_color = image.get_pixel(sample_x + 4, sample_y + 4) (sample_x).upto(sample_x + 9) do |x| (sample_y).upto(sample_y + 9) do |y| image.set_pixel_if_within_bounds(x, y, sample_color) end end sample_x += 10 end sample_y += 10 end image.save('output.png')
-
Finished in 37th place with a final score of 2.5/5. (View the Gist)output.png
pixelize.rbView full entry#!/usr/bin/env ruby require 'chunky_png' # A chunky monkey patch... am I going to get sued by Ben and Jerry? class ChunkyPNG::Image < ChunkyPNG::Canvas def pixelize_area!(vector) red_vals = [] green_vals = [] blue_vals = [] vector.each do |pixel| red_vals << ChunkyPNG::Color.r(self[pixel.x, pixel.y]) green_vals << ChunkyPNG::Color.g(self[pixel.x, pixel.y]) blue_vals << ChunkyPNG::Color.b(self[pixel.x, pixel.y]) end pixel_color = ChunkyPNG::Color.rgb(avg(red_vals),avg(green_vals),avg(blue_vals)) self.rect(vector.min.x, vector.min.y, vector.max.x, vector.max.y, pixel_color, pixel_color) return self end def pixelize!(blocksize) grid_x = (0..self.dimension.width).reject{|x| x%blocksize != 0} grid_y = (0..self.dimension.height).reject{|y| y%blocksize != 0} grid_x.each_with_index do |x,i| grid_y.each_with_index do |y,j| next_x = [grid_x[i+1], self.dimension.width-1].reject{|i| i == nil}.min next_y = [grid_y[j+1], self.dimension.height-1].reject{|i| i == nil}.min width = next_x - x height = next_y - y unless width <= 0 || height <= 0 va = [] (x..next_x).each do |vx| (y..next_y).each do |vy| va << [vx, vy] end end vector = ChunkyPNG::Vector(*va) self.pixelize_area!(vector) end end end return self end private def avg(array) ((array.reduce{|sum,n| sum+n})/(array.size)).to_i end end image = ChunkyPNG::Image.from_file(ARGV[0]) image.pixelize!(ARGV[2] ? ARGV[2].to_i : 10) image.save(ARGV[1]) puts "done"
-
Finished in 38th place with a final score of 2.4/5. (View the Gist)output.png
pixelate.rbView full entryrequire 'chunky_png' module ChunkyPNG class Canvas module Pixelate # Pixelate image or part of the image # @param [Integer] block size for pixalating # @param [Integer] start x-coordinate for pixelating # @param [Integer] start y-coordinate for pixelating # @param [Integer] end x-coordinate for pixelating # @param [Integer] end y-coordinate for pixelating # @return [ChunkyPNG::Canvas] Itself, with pixelated applied def pixelate(block_size, x0 = 0, y0 = 0, x1 = self.width - 1, y1 = self.height - 1) half_block = block_size / 2 x = x0 y = y0 while y <= y1 while x <= x1 x_color_point = min(x + half_block, width - 1) y_color_point = min(y + half_block, height - 1) x_end = min(x + block_size - 1, x1) y_end = min(y + block_size - 1, y1) color = self.get_pixel(x_color_point, y_color_point) self.rect(x, y, x_end, y_end, ChunkyPNG::Color::TRANSPARENT, color) x += block_size end x = x0 y += block_size end self end private # internal simple function to calculate minimum of two values def min(a, b) return a if a < b return b end end # include module on Canvas include Pixelate end end image = ChunkyPNG::Image.from_file('input.png') image.pixelate(10) image.save('output.png')
-
Finished in 39th place with a final score of 2.4/5. (View the Gist)output.png
gistfile1.rbView full entryrequire 'chunky_png' @image = ChunkyPNG::Image.from_file('input.png') def pixelate(a, b) if a+10 > @image.dimension.width w = @image.dimension.width - a else w = 10 end if b+10 > @image.dimension.height h = @image.dimension.height - b else h = 10 end color1 = @image[a, b] w.times do |i| h.times do |j| color2 = @image[i+a, j+b] c1_r = ChunkyPNG::Color.r(color1) c2_r = ChunkyPNG::Color.r(color2) c1_g = ChunkyPNG::Color.g(color1) c2_g = ChunkyPNG::Color.g(color2) c1_b = ChunkyPNG::Color.b(color1) c2_b = ChunkyPNG::Color.b(color2) color1 = ChunkyPNG::Color.rgb(((c1_r + c2_r)/2), ((c1_g + c2_g)/2), ((c1_b + c2_b)/2)) end end w.times do |i| h.times do |j| @image[i+a, j+b] = color1 end end end w = dim.width h = dim.height i = 0 while (i < w) j = 0 while (j < h) pixelate(i, j) j = j + 10 end i = i + 10 end @image.save('output.png')
-
Finished in 40th place with a final score of 2.3/5. (View the Gist)output.png
pixellate.rbView full entryrequire 'chunky_png' def pixellate(tile_size = 10) tile_length = ((tile_size * tile_size) - 1) image = ChunkyPNG::Image.from_file('input.png') rework = image.resample_nearest_neighbor(image.width / tile_size, image.height / tile_size) rework.height.times do |y| rework.width.times do |x| flat_pixel = rework[x, y] (0..tile_length).each do |off| image[(x * tile_size) + (off / tile_size), (y * tile_size) + (off % tile_size)] = flat_pixel end end end image.save('output.png') end
-
Finished in 41st place with a final score of 2.3/5. (View the Gist)output.png
solution.rbView full entry# Basic requirements for pixelator require 'chunky_png' include ChunkyPNG::Color # Pixelator - a quick script to pixelate PNGs. # # @author Timmy Christensen class Pixelator # Initialize attributes attr_accessor :file_name, :pixel_size #################################################################### # Initialize #################################################################### # Create a new instance of Pixelator. # # @param [String] file_name The name of the image file in the current directory. # @param [Integer] pixel_size The square dimensions of the pixelation. def initialize(file_name, pixel_size) @file_name = file_name @pixel_size = pixel_size end #################################################################### # Pixelate #################################################################### # Create a pixelated copy of the image file. # # The pixelated image is saved as `output.png` in the current directory. def pixelate image = ChunkyPNG::Image.from_file(@file_name) hex = from_hex('#ffffffff') x = 0 y = 0 while y < image.height while x < image.row(y).length hex = to_hex image[x,y] 0.upto(@pixel_size - 1) { |i| 0.upto(@pixel_size - 1) { |j| if (x+i < image.width && y+j < image.height) image[x+i,y+j] = from_hex(hex) end } } x += @pixel_size end x = 0 y += @pixel_size end image.save('output.png') # Overwrites the file. end end #################################################################### # Usage #################################################################### # Call the script from the command using `ruby pixelator.rb <filename> <pixelsize>` p = Pixelator.new(ARGV[0] || 'input.png', ARGV[1].to_i < 2 ? 10 : ARGV[1].to_i) p.pixelate
-
Finished in 42nd place with a final score of 2.2/5. (View the Gist)output.png
pixelizer.rbView full entry#!/usr/bin/env ruby require 'chunky_png' @image = ChunkyPNG::Image.from_file('input.png') @pxSize = 10 def rectLoop(i, j) (0..@pxSize-1).each do |x| (0..@pxSize-1).each do |y| yield(i*@pxSize+x, j*@pxSize+y) end end end def savePixel(x, y) return unless @image.include_xy?(x, y) c = ChunkyPNG::Color.to_truecolor_alpha_bytes(@image[x,y]) (0..3).each do |i| @colorT[i] += c[i] end @countPixels += 1 end (0..(@image.width-1)/@pxSize).each do |i| (0..(@image.height-1)/@pxSize).each do |j| @colorT = [0,0,0,0] @countPixels = 0 rectLoop(i,j) do |x,y| savePixel(x, y) end @colorT.map! do |p| p/@countPixels end color = ChunkyPNG::Color.rgba(@colorT[0],@colorT[1],@colorT[2],@colorT[3],) rectLoop(i,j) do |x,y| @image[x,y] = color if @image.include_xy?(x, y) end end end @image.save('output.png')
-
Finished in 43rd place with a final score of 2.2/5. (View the Gist)README
____ ____ _ _ ____ __ ____ ___ ____ | . \|___\|\/_\| __\| | |___\| _\ | __\ | __/| / _><__| ]_| |__| / [__ \| ]_ |/ |/ |/\_/|___/|___/|/ |___/|___/ ---------------------------------------- Down and dirty solution to the Code Brawl 2 Problem. Execution: ruby pixelise.rb Input file should be named input.png
output.png
pixelate.rbView full entryrequire 'chunky_png' inputImage = ChunkyPNG::Image.from_file('input.png') newImage = ChunkyPNG::Image.new(inputImage.width, inputImage.height, ChunkyPNG::Color::TRANSPARENT) # Allocate starting anchor points pos_x = (inputImage.width - 1) pos_x_anchor = (inputImage.width - 1) pos_y_anchor = (inputImage.height - 1) while (!(pos_y_anchor < 0)) if (pos_x > 0) # Find the color at the anchor point pixel = inputImage[pos_x_anchor, pos_y_anchor] # Colour section while (pos_x > (pos_x_anchor - 10)) pos_y = pos_y_anchor while (pos_y > (pos_y_anchor - 10) ) newImage.set_pixel(pos_x, pos_y, pixel) pos_y -= 1 end pos_x -= 1 end # Move down the position of the image pos_x_anchor -= 10 pos_x = pos_x_anchor else # Reinitialise the anchor points and move to the next position pos_x = (inputImage.width - 1) pos_x_anchor = (inputImage.width - 1) pos_y_anchor -= 10 end end newImage.save('output.png', :interlace => true)
-
Finished in 44th place with a final score of 2.0/5. (View the Gist)pixelator.rbView full entry
require 'chunky_png' module Pixelator extend self # Go go go! def pixelize!(image) for_each_block_of image do |x, y| color = average_of all_colors_from block: [x, y], of: image fill block: [x, y], with: color, of: image end image end # Iterates through each block of an image def for_each_block_of(image, &blk) (0..image.width/ten).each do |x| (0..image.height/ten).each { |y| yield x, y } end end # Fill a block with a color def fill(options) x, y = options[:block] color = options[:with] image = options[:of] image.rect x*ten, y*ten, x*ten+ten, y*ten+ten, color, color if color end # Returns all colors in a given block range def all_colors_from(options) x, y = options[:block] image = options[:of] (0...ten * ten).map do |i| _x, _y = (x * ten + i/ten), (y * ten + i%ten) image[_x, _y] if image.include_xy?(_x, _y) end.compact end # Adds a color to a hash-color def add_color(hash, color) channels.each { |chan| hash[chan] ||= 0 hash[chan] += ChunkyPNG::Color.send(chan, color) } hash end # Divides a hash-color by a divisor def divide_color(hash, divisor) channels.each { |chan| hash[chan] /= divisor } hash end # Averages a list of colors def average_of(array) if array.length > 0 h = array.inject({}) { |h, color| add_color(h, color) } h = divide_color(h, array.length) ChunkyPNG::Color.rgba(h[:r], h[:g], h[:b], h[:a]) end end # Color channels def channels() [:r, :g, :b, :a]; end # Block size def ten() 10; end end image = Pixelator.pixelize! ChunkyPNG::Image.from_file('input.png') image.save('output.png')
-
Finished in 45th place with a final score of 1.9/5. (View the Gist)mw_chunky_pixelizer.rbView full entry
require 'chunky_png' require 'backports' unless RUBY_VERSION >= "1.8.7" include ChunkyPNG image = Image.from_file('input.png') xslices = (0...image.width).each_slice(10).to_a yslices = (0...image.height).each_slice(10).to_a xslices.product(yslices) do |xslice, yslice| tots = [0, 0, 0, 0] xslice.product(yslice).each do |x, y| rgba = Color.to_truecolor_alpha_bytes(image[x,y]) tots.map! { |t| t + rgba.shift } end weight = xslice.size * yslice.size pixelzd_color = Color.rgba(*tots.map { |t| (t.to_f / weight).round }) image.rect(xslice.first, yslice.first, xslice.last, yslice.last, Color::TRANSPARENT, pixelzd_color) end image.save('output.png')