Rotating Images With RMagick and Ruby on Rails

February 12th, 2006

Rotating Images Screenshot 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.

27 Responses to “Rotating Images With RMagick and Ruby on Rails”

  1. 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

  2. @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.

  3. Awesome! thanks.. I’ll be doing this sort of thing later this week. :)

  4. 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

  5. @Jason - Nice. Thanks for the tip. I’ll be sure to do so from now on.

  6. avatar James March 28th, 2006 10:15 pm

    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

  7. avatar James March 28th, 2006 10:54 pm

    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

  8. @James - I know the feeling. Glad you got it working.

  9. avatar xtian April 21st, 2006 8:08 am

    hi,

    can you show me how you upload and save the file? coz i can’t make it work.

    tnx!

  10. @xtian - I used the file column plugin. That should get you going.

  11. 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!

  12. @Brian - Odd. Thanks for the tip.

  13. avatar Bruno August 11th, 2006 2:15 pm

    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?

  14. 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.

  15. Nice work. Rmagick can be a lot of fun and you show that here in your tutorial.

  16. [...] Rotating Images With RMagick and Ruby on Rails This is archived from:Here [...]

  17. 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 :)

  18. 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…

  19. Thanks for sharing your findings with everyone else.

  20. avatar Julien Delgoulet August 2nd, 2007 4:11 pm

    Hey ! Nice trick ;-)
    However, I think , in my opinion, that rotate should be a method of Photo.
    Here is what I did :

    class Photo

  21. @Julien - Agreed, I wrote this before I really understood what should be in the model and what in the controller.

  22. avatar mahesh September 25th, 2007 7:10 am

    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)

  23. Search for the attachment_fu rails plugin. It does everything that you are asking.

  24. avatar Raj October 17th, 2007 6:35 am

    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

  25. avatar Matthew Flint March 2nd, 2008 12:59 pm

    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 :-)

  26. avatar Rajesh March 19th, 2008 6:34 am

    Thanks for sharing your knowledge

  27. 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?

About This Site

Addicted to New is the personal website of John Nunemaker (Noo-neh-maker), a Web Developer enamored of Ruby on Rails and a wide-eyed fan of all things new and cool.