xkcdize
xkcdize copied to clipboard
XKCD-like picture distortion in Ruby and RMagick
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