RMagick and Image Manipulation

Hey all,

I’m currently working with Rmagick on an image editing feature for a
rails app. I’m adding an editing function which would allow the user to
rotate, add effects, etc…to uploaded images. The interface is worked
around ajax functions, So for ex: my rotate function is something like
this:

Controller:

def edit
@image = Image.find(params[:id])
end

def rotate
photo = Image.find(params[:id])
client = @session[‘client’].id
@image = photo

  image   = Magick::ImageList.new("public/client_images/#{client}/"
  • photo.filename)
    image = image.rotate(-90)
    image.write(“public/client_images/#{client}/” + photo.filename)

    #thumb
    thumb   =  (RAILS_ROOT + "/public/client_images/#{client}/" +
    

Image.thumbnail_name_for(photo.filename))
image = Magick::ImageList.new(thumb)
image = image.rotate(-90)
image.write(thumb)

  render :update do |page|
  page.replace_html('imageContainer', :partial => 'image')
  page.visual_effect :highlight, 'imageContainer'
end

end

View:
edit.rhtml:

<%= render :partial => "image" %> </div
Rotate | Crop | Resize

_image.rhtml partial:

What happens is the image gets rotated succesfully, but the RJS seems to
load before RMagick gets the image written to the filesystem, so when
the RJS re-loads is doesnt show the changes to the image.

Is there a way to allow the client to make changes, such as rotating
then cropping, then hit save, then RMagick writes the image? Like, how
would you keep the current image in a temporary memory?

Excuse the long post, but it really has me stumped.

Any help is very much appreciated.

Thanks,

If the image is really large, performing RMagick operations on it could
take
time. I was under the impression that this was blocking, but maybe I’m
wrong, but anyways, try moving your thumbnail manipulations before the
original image:

#thumb
thumb = (RAILS_ROOT + “/public/client_images/#{client}/” +
Image.thumbnail_name_for(photo.filename))
image = Magick::ImageList.new(thumb)
image = image.rotate(-90)
image.write(thumb)

before:

image = Magick::ImageList.new(“public/client_images/#{client}/”

  • photo.filename)
    image = image.rotate(-90)
    image.write(“public/client_images/#{client}/” + photo.filename)

Also, an implementation consideration: you could consider queueing up
the
effects applied (in a session variable array, [‘rotate:90’, ‘scale:2’]
etc.
perhaps) and only applying the effects to the original image when the
user
hits “save”. Do the operations only on the thumb meanwhile since only
that’s
getting displayed.

If the thumb is small, Firefox supports data urls, data:
http://www.andrewwooldridge.com/blog/2005/11/firefox-canvas-supports-data-urls.html
You can pass this inline in the index file, tho this is definitely
clunky.

Vish

Eric wrote:

What happens is the image gets rotated succesfully, but the RJS seems to
load before RMagick gets the image written to the filesystem, so when
the RJS re-loads is doesnt show the changes to the image.

Is there a way to allow the client to make changes, such as rotating
then cropping, then hit save, then RMagick writes the image? Like, how
would you keep the current image in a temporary memory?

Two things:

  1. The #to_blob method returns the image as a string.
  2. If you’re only dealing with 1 image at a time, use the Magick::Image
    class instead of the Magick::ImageList class.

Also take a look at:
http://www.rubyonrailsblog.com/articles/2006/09/08/ruby-on-rails-and-rmagick-crop-resize-rotate-thumbnail-and-upload-images

I’m having the same problem generating thumbnails (only I’m using
Magick::Image rather than ImageList). I’ve tried a bunch of stuff but
nothing seems to work so far. Incidently the problem seems to occur
regardless of the image size.

I’m probably going to resort to one of the following:

  • a spinner gif and an ajax request to see if the image is there yet
    or
  • checking if the file is on disk before returning the generated
    thumbnail path
    (and manually blocking the return until the file is written)
    both options make me cringe a little though…

Eric wrote:

I’m currently working with Rmagick on an image editing feature for a
rails app.

Here is an image-editing “Rails Engine” I wrote a few months ago:

http://svn.openprofile.net/plugins/p_sitsnot_engine/

It works pretty well – you might find it useful to have a look at the
code.

Or you can just use it as it – it only takes about ten minutes to build
a simple image-editing Rails app if you follow the README.

–Al Evans

Al Evans wrote:

Eric wrote:

I’m currently working with Rmagick on an image editing feature for a
rails app.

Here is an image-editing “Rails Engine” I wrote a few months ago:

http://svn.openprofile.net/plugins/p_sitsnot_engine/

It works pretty well – you might find it useful to have a look at the
code.

Or you can just use it as it – it only takes about ten minutes to build
a simple image-editing Rails app if you follow the README.

–Al Evans

Thanks for the advice Vishnu, and the Rails Engine Al (nice work btw).
It seemed my initial problem of the image not reloading after an action
was becuase it was being cached…a simple solution of adding a ?<%=
Time.now.to_i %> after the image path fixed it.

But it does seem more logical (and performace friendly) to queue up the
effects, or even make a copy of the image while it’s being edited to
allow revert/undo functions, etc…

Thanks again,

  • Eric

Hey,

I have been using your code to rotate images, but I get anerror:

Magick::ImageMagickError in MainController#rotate

unable to open image `/logos/0000/0001/300.jpg’: No such file or
directory

Any suggestions? Would really appreciate them.

<-- main_controller.rb -->

def edit
@image = Logo.find(params[:id])
end

def rotate
photo = Logo.find(params[:id])
@image = photo

    image   = Magick::ImageList.new(@image.public_filename)
    image   = image.rotate(90)
    image.write(@image.public_filename)

    render :update do |page|
    page.replace_html('imageContainer', :partial => 'image')
    page.visual_effect :highlight, 'imageContainer'
 end

end

<–edit.rhtml -->

Test

<%= render :partial => "image" %> </div
<%= link_to 'Rotate', :id => @image.id, :controller => :main, :action => 'rotate' %> | Crop | Resize

<-- _image.rhtml -->