XKCD-like picture distortion in Ruby and RMagick
Ruby
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
.gitignore
Gemfile
README.md
image-xkcd.png
image-xkcd2.png
image.png
rmagick_patch.rb
xkcd-distort-wolfram.png
xkcdize.rb
xkcdize2.rb

README.md

This is a small experimental script to play with Ruby and images.

It just takes some image and converts makes it "xkcd-like" (effect of handwritedness).

Before:

After:

The story behind the script is simple. I've just read an excellent blog post by Wolfram guy Vitaliy Kaurov, where he explains, how can you have xkcd-style charts in Wolfram Mathematica. Most of ideas there are fairly straighforward (set bold style for lines, add labels, use appropriate fonts), but there was some image distortion idea, which makes any graphics look "pencil-drawn".

And there, I've just thinking "Ruby is the best language evaaar, if I can do the same thing, reproduce this beautiful algo, with the same brevity and elegance.

Look for yourself, if I could!

Wolfram version:

Ruby version (not the best one, see below!):

def xkcdize(src)
  distorters = 2.times.map{
    Image.random(src.columns, src.rows).adaptive_blur(10, 5)
  }

  src.fx('p{i+15*(0.5-u[1]),j+15*(0.5-u[2])}', *distorters)
end

The algo is the same. Look at commented code into xkcdize.rb

To be fair, the solution was not easy. The ImageMagick fx-script (which is passed to Image#fx method) was hard to guess, and it's not a Ruby, just a bit of RMagick internal script inside mine.

Yet it is all still pretty clean and laconic.

New: Clean Ruby Version

I've almost invented pure-Ruby version (without dirty fx) hack, now it looks like clean and understandable and debuggable Ruby. The algo seems to be EXACTLY the same, yet the result is pretty ugly

Here IS working Ruby version:

def xkcdize(src, shift=20)
  distorters = 2.times.map{
    Image.random(src.columns, src.rows).adaptive_blur(10, 5)
  }

  src.zip(*distorters).map_to_image{|(s, dx, dy), col, row|
    src.pixel_color_f(col+shift*(0.5-dx.to_f), row+shift*(0.5-dy.to_f))
  }
end

The algo is EXACTLY the same, as well as the result:

Look at code: xkcdize2.rb

It seems, I also invented very useful functionality for RMagickImage:

src.pixel_color_f(col, row)

When provided with non-integer coordinates, it returns pixel, which is bilinear interpolation of pixels near the coordinates. (Though, to push it in RMagick, it should respect Image#pixel_interpolation_method setting).

Also, take a look at Image#zip(*other_images) and ImageList#map_to_image -- they also seem to be of general usefulness: rmagick_patch.rb