Rotating Images With RMagick and Ruby on Rails
February 12th, 2006
On a current project for Christian Fellowship Center, a church in Sturgis, MI, I am creating a photo gallery. I’m using Sebastian Kanthak’s File Column plugin for the uploads and it is working great. I was testing with a few of the sample photos they provided for me when I realized that a few of the photos needed rotating (a common problem). Granted, I could easily do this, but they are not tech savy and thus would not know how to manipulate an image. I decided to mess around with RMagick a bit and create a way for them to rotate the photo though the administration area I was creating.
My photo model is fairly simple:
class Photo < ActiveRecord::Base
file_column :file,
:magick => {
:size => '440x330',
:crop => '4:3',
:versions => {
:square => {
:crop => '1:1',
:size => '100x100'}
}
}
end
Basically, this crops the uploaded photo so that it is 4:3 in ratio with a max width of 440 and a max height of 330. It also creates a square thumbnail version that is 100px on each side. That was simple and easy and after a tweak, uploading and resizing was working great. I figured that rotating an image would be simple, so I browsed the RMagick docs until I found rotate. Yep, it is simple.
I added an action to my photos controller named rotate like so:
def rotate
photo = Photo.find(params[:id])
degrees = if params[:direction] == 'left' then -90 else 90 end
#main photo
image = Magick::ImageList.new(photo.file)
image = image.rotate(degrees)
image.write(photo.file)
# thumb
thumb = RAILS_ROOT + "/public/photo/file/#{photo.id}/square/#{File.basename(photo.file)}"
image = Magick::ImageList.new(thumb)
image = image.rotate(degrees)
image.write(thumb)
redirect_to :action => 'list'
end
Rotating the main image
I’m new to ruby and to rails, so don’t take my methods as best practice, but they do work. The first line finds the photo based on the id passed. I also pass a direction (either left or right). Based on the direction, I either rotate the photo 90 degrees (clockwise) or -90 degrees (counter clockwise). photo.file is the path to the image I want to resize. After having determined the degrees, I created a new RMagick image object and rotated it by degrees. image.write(photo.file) simply saves the rotated file. One photo down, but one more rotation to go.
Rotating the square thumbnail
The square thumbnail was trickier. File column by default stores files in public/model_name/column_name/id/name_of_image.jpg. Different versions of the image are then stored inside a folder named the same as the version name. For example, because my model is ‘photo’ and the column name is ‘file’, an image with an id of 24 would be stored in public/photo/file/24/name_of_image.jpg and a version of that photo named square would be stored in public/photo/file/24/square/name_of_image.jpg. Knowing this, I just made the thumbnail file path the RAILS_ROOT plus the known path and file name. I then performed the same rotation on the square version as the original and saved the changes.
Nothing to complex, but I was feeling pretty good about the result. All that was left to do was add some fancy little arrows (as you saw above) and my rotation addition was complete. For those that are curious below is the code to show the arrow images and make them link to the rotation action is shown below. Happy rotating!
<%= link_to image_tag('admin/arrow_rotate_anticlockwise.gif', {:alt => 'Rotate Clockwise'}), :action => 'rotate', :id => photo.id, :direction => 'left' %>
<%= link_to image_tag('admin/arrow_rotate_clockwise.gif', {:alt => 'Rotate Clockwise'}), :action => 'rotate', :id => photo.id, :direction => 'right' %>
I have an additional article on this issue, saving the quality. It adds one tip to keep the quality of the rotated images high.
If you enjoyed this post, get free updates by email or RSS.
Nice touch for the user here. I too will soon be building an admin section that will need some picture management. I’ll certainly keep this one bookmarked.
I also like the icons you used. Where did you get them?
Thanks,
Jaime
@Jaime - The icons are fam fam. Fam fam is an amazing set of icons. I don’t even know how many there are but they work great with applications.
Awesome! thanks.. I’ll be doing this sort of thing later this week. :)
Thanks for sharing your experiences. One quick tip is to use the ternary operator. Instead of degrees = if … then … else … end, you can do degrees = params[:direction] == ‘left’ ? -90 : 90
@Jason - Nice. Thanks for the tip. I’ll be sure to do so from now on.
I have made several attempts to get the :crop function to work and have not been successful, no matter how I put it, I receive the same error. invalid geometry string `’ … which I cannot explain, even copying your code exactly does not work, or using any other example I’ve found. So far, I haven’t gotten much help from anyone yet as to why this is so. Anybody who has a solution please e-mail me. PsiliPharm [at] spymac.com
I hate it when this happens, as soon as I inquire about a problem, then I find a solution! Well, I would like to mention that if I used anything but the trunk version, I couldn’t get the crop to work, but I just installed version using
./script/plugin install
http://opensvn.csie.org/rails_file_column/
plugins/file_column/trunk
to install the plugin if you had previously installed it try the –force command afterwards…anyways, thanks again.
-James
@James - I know the feeling. Glad you got it working.
hi,
can you show me how you upload and save the file? coz i can’t make it work.
tnx!
@xtian - I used the file column plugin. That should get you going.
Just so everyone know, if you copy-n-paste the code directly into your model, controller or view, be careful. There are some screwy characters. Rewrite the surrounding quotes and make sure you manually type in the “x” between “100×100″, otherwise you’ll get an invalid geomtry error. Took me a while to figure that one out!
@Brian - Odd. Thanks for the tip.
Hi, I’ve been using file_column alongside rmagick to create thumb and medium versions of the images uploaded int my site.
The other day I ran into a problem when I tried to upload an animated gif, in both the thumb and medium versions of the gif only the first frames are saved.
This is the code on the model that I use to create the versions:
file_column :image, :magick => { :versions => {”tiny” => “75×75″, “medium” => “550×400″}}
Do you happen to know how I could get around this?
Not off the top of my head but I’m betting it’s an RMagick issue. I would search RMagick and animated gif’s on google and see if you can dig anything up.
Nice work. Rmagick can be a lot of fun and you show that here in your tutorial.
[...] Rotating Images With RMagick and Ruby on Rails This is archived from:Here [...]
Have you tried creating a rails action that returns an image as the HTTP response? I know how to do this in Java and C#, but as I’m new to Ruby & Rails, I’m out of my comfort zone!
I haven’t seen anywhere that I can control the response headers or response MIME type. I need to generate an image on-the-fly (possibly with caching, but I can tackle that later). I have RMagick installed, and can create my Image object. How do I get it on the response stream? There’s a ‘to_blob’ method on ‘Image’ that looks useful.
Any suggestions are appreciated :)
Well it seems I’ve solved my problem. For anyone else who’s interested, here is what I put in my controller:
require 'RMagick'
include Magick
def chord_chart
fill = GradientFill.new(100, 100, 100, 100,"yellow","red")
img = Image.new(200, 200, fill)
img.format = "GIF"
img_content = img.to_blob
render :text => img_content, :status => 200, :content_type => 'image/gif'
end
This creates a simple gradient filled image in the browser. Woo! Now, to make the image display something useful…
Thanks for sharing your findings with everyone else.
Hey ! Nice trick ;-)
However, I think , in my opinion, that rotate should be a method of Photo.
Here is what I did :
class Photo
@Julien - Agreed, I wrote this before I really understood what should be in the model and what in the controller.
Hello,
I want to do the following -
1. Upload an image,
2. Create an thumbnail of that image.
3. Save the original image in a folder.
4. Save the thumbnail of the image and the path of the original image in a database table.
5. Display the thumbnail and the original image.
How I can do all these ? (using Ruby on Rails)
Search for the attachment_fu rails plugin. It does everything that you are asking.
Hey,
I’m following your approach except that I’m using the attachment_fu plugin and storing images in the file system.
So far I have got up to:
def edit
@logo = Logo.find(params[:id])
image = @logo.public_filename
image = Magick::ImageList.new(image)
end
But I get an error saying:
Magick::ImageMagickError in MainController#edit
unable to open image `/logos/0000/0013/s.jpg’: No such file or directory
Any suggestions? Would appreciate any help.
Cheers
Quick tip for anyone wanting to do the same:
RMagick is capable of rotating jpegs in a lossless way, but can only do so for images whose height and width are multiples of 16.
This is why my original images were being rotated losslessly, but the thumbnails were rotated in lossy way. (And it really is noticeable after surprisingly few rotations)
So I changed:
:thumbnails => { :thumb => ‘100×100>’, :reduced => ‘500×500′ }
to:
:thumbnails => { :thumb => ‘128×128>’, :reduced => ‘512×512′ }
in my model. Hope that helps someone :-)
Thanks for sharing your knowledge
When using RMagick combined with file_column, after uploading a image using the resize method to created 3 different images my rails server gets a segmentation fault and dies. :(
When uploading without resizing then it is fine. :)
I have updated the latest RMagick but still have the same problem, my server is Centos Linux.
Would anyone know the answer to this?