Mastodon

Image overlays with Rmagick and Rails

I knew it wouldn’t be long before I had another foray into Rmagick. This time is pretty similar to last time, except this time I needed to overlay one image on top of another image. There are a few people who have posted about how to do this but I found the information pretty sparse. As I was looking I noticed that some people were using the ImageList class and others the Image class. After ping-ponging between the various tutorials and finding the differences between them pretty confusing, I thought I would sort myself out by using both.

So here is the code that generated that stunning image overlay. I tend to figure this stuff out by sticking it in a controller and tinkeringĀ  with it. Once it works I will move it into a model. The code below is taken from my controller. First, the version using the Rmagick’s ImageList class:

def rubylist
ruby = ImageList.new(‘public/images/ruby.jpg’)
rails = ImageList.new(‘public/images/rails.png’)
ruby.gravity=EastGravity
ruby.geometry=’0x0+20+20′
rails_on_ruby = ruby.composite_layers(rails, Magick::OverCompositeOp)
rails_on_ruby.format = ‘jpeg’
send_data rails_on_ruby.to_blob, :stream => ‘false’, :filename => ‘test.jpg’, :type => ‘image/jpeg’, :disposition => ‘inline’
end

So what we have are two ImageList objects; ruby and rails. I am going to use ruby as my “destination” ImageList, meaning it will be the base image thatĀ  the other image is applied to. Setting the Gravity and Geometry attributes on the “destination” object means that Rmagick will position the “source” image according to those settings. Calling composite_layers on ruby and feeding it rails and a composite operator let Rmagick know what should be combine with the base image and how it should combine them. Since we already gave it the “where” part of the equation with the gravity and geometry, the rest is easy.

Doing the same thing with the Image class instead of ImageList is just different enough to trip you (read: Me) up:

def ruby
ruby = Image.read(‘public/images/ruby.jpg’)[0] #This returns an Array! Get the first element.
rails = Image.read(‘public/images/rails.png’)[0]
rails_on_ruby = ruby.composite(rails, Magick::EastGravity, 0, 0, Magick::OverCompositeOp) #the 0,0 is the x,y
rails_on_ruby.format = ‘jpeg’
send_data rails_on_ruby.to_blob, :stream => ‘false’, :filename => ‘test.jpg’, :type => ‘image/jpeg’, :disposition => ‘inline’
end

Rmagick pretty consistently works in a way that is the opposite of what I expect. Where I would expect Image.read to return, say, an image, instead it returns an Array. So after some head scratching and a little RTFM time I learned you could do what you see above. Alternately you could do “Image.read(‘public/images/rails.png’).first” if you think it’s prettier. The geometry stuff takes a little fooling around to get sorted out and to see how it works with the gravity. My understanding is that the x,y is just to offset the object from where the gravity setting alone would have placed it.

Like the image annotations before, its not difficult to do, but its a pain to figure out. While there are a bunch of other ways to write this code, this will at least give a running start.

Adding text to pictures with Rmagick and Rails.

Recently I needed to add some dynamically generated text to an image with Ruby on Rails. For some reason there do not seem to be many tutorials on how to work with RMagick so I thought I would share what little I now about it. Originally I looked at Image Science but it turns out that its very focused on simply resizing images. So for this I turned to RMagick. I was a little worried that the installation would be nightmarish but it turned out to be quite painless (at least on Ubuntu. I can’t imagine what it would be like on Windows. Probably bad.):

mike@sleepycat:~$ sudo aptitude install imagemagick librmagick-ruby libmagickwand-dev

Then install the gem:

mike@sleepycat:~$ sudo gem install rmagick

RMagick is a complex library that has tonnes of graphics functions and can transform images in many different ways.
It seems that RMagick works conceptually the same for both Vector graphics and bitmap images: create an image and add text objects to it or create a canvas and add vector objects to it (using Ruby Vector Graphics aka:RVG). So lets do that. So here is an image to start with:

To start playing with RMagick, you can stick this in one of your controllers:

require ‘RMagick’
include Magick

def lolcat

img = ImageList.new(‘public/computer-cat.jpg’)
txt = Draw.new

img.annotate(txt, 0,0,0,0, “In ur Railz, annotatin ur picz.”){
txt.gravity = Magick::SouthGravity
txt.pointsize = 25
txt.stroke = ‘#000000’
txt.fill = ‘#ffffff’
txt.font_weight = Magick::BoldWeight
}

img.format = ‘jpeg’
send_data img.to_blob, :stream => ‘false’, :filename => ‘test.jpg’, :type => ‘image/jpeg’, :disposition => ‘inline’

end

And here is the output of that script:

Although sticking that in a controller is a quick way to see the results of some tinkering and experimentation, this is the kind of stuff that should really be in a model. You would likely want to return img.to_blob from one of your model methods and then use send_data from there.

It’s pretty basic but it was all I needed to get my project working. RMagick does seem to be a little arcane. A lot of the things I tried along the way failed in pretty confusing ways and with pretty cryptic errors. While simple stuff like this is fine its going to take a lot more reading and fiddling to make me truly comfortable with RMagick. At least I have this written down for next time…