Cropping Images With Rmagick
This post is a healthy mix of both ruby and rails methods for rmagick!
def submit_crop_image require 'RMagick' @user = curr_user tmpfile = "#{RAILS_ROOT}/public/images/users/tmp/#{@user.id}.jpg" img001 = "#{RAILS_ROOT}/public/images/users/#{@user.id}_001.jpg" img002 = "#{RAILS_ROOT}/public/images/users/#{@user.id}_002.jpg" img003 = "#{RAILS_ROOT}/public/images/users/#{@user.id}_003.jpg"
size001 = 100s size002 = 50 size003 = 16 top = params['top'].to_i left = params['left'].to_i width = params['width'].to_i height = params['height'].to_i begin img = Magick::Image::read(tmpfile).first unless width == 0 or height == 0 img.crop!(left,top,width,height) end img.resize! size001, size001 img.write img001 img.resize! size002, size002 img.write img002 img.resize! size003, size003 img.write img003 File.delete tmpfile rescue Exception => err @upload_image_error = 'Could not process your image file. Please try again.' + err render :action => 'edit_image' else @user.image_flag = 'Y' @user.save redirect_to :action => "profile", :username => @user.username end end
Thumbnail Images With Rmagick:
require 'RMagick' class PhotoController < ApplicationController [...snip...] def render_resized_image @photo=Photo.find(@params["id"]) maxw = @params["width"] != nil ? @params["width"].to_i : 90 maxh = @params["height"] != nil ? @params["height"].to_i : 90 aspectratio = maxw.to_f / maxh.to_f pic = Magick::Image.from_blob(@photo.image)[0] picw = pic.columns pich = pic.rows picratio = picw.to_f / pich.to_f if picratio > aspectratio then scaleratio = maxw.to_f / picw else scaleratio = maxh.to_f / pich end #breakpoint thumb = pic.resize(scaleratio) @response.headers["Content-type"]=@photo.mime end end
Thumbnailer Number Two With Rmagick!
require 'RMagick' maxwidth = 120 maxheight = 160 aspectratio = maxwidth.to_f / maxheight.to_f imgfile = 'world' pic = Magick::Image.read(imgfile + '.jpg').first imgwidth = pic.columns imgheight = pic.rows imgratio = imgwidth.to_f / imgheight.to_f imgratio > aspectratio ? scaleratio = maxwidth.to_f / imgwidth : scaleratio = maxheight.to_f / imgheight thumb = pic.resize(scaleratio) white_bg = Magick::Image.new(maxwidth, thumb.height) pic = white_bg.composite(thumb, Magick::CenterGravity, Magick::OverCompositeOp) pic.write(imgfile + '.thumb.jpg')
Database storage of uploaded images
class DbFile < ActiveRecord::Base IMAGE_TYPES = ['image/jpeg', 'image/pjpeg', 'image/gif', 'image/png', 'image/x-png'] before_validation :sanitize_filename validates_presence_of :size, :filename, :content_type class << self def new_file(file_data) content_type = file_data.content_type.strip (IMAGE_TYPES.include?(content_type) ? DbImage : DbFile).new \ :data => file_data.read, :filename => file_data.original_filename, :size => file_data.size, :content_type => content_type end end protected def sanitize_filename # NOTE: File.basename doesn't work right with Windows paths on Unix # get only the filename, not the whole path filename.gsub! /^.*(\\|\/)/, '' # Finally, replace all non alphanumeric, underscore or periods with underscore filename.gsub! /[^\w\.\-]/, '_' end end require 'rmagick' require 'base64' class DbImage < DbFile def data=(file_data) with_image(file_data, true) do |img| self.width = img.columns self.height = img.rows end end def with_image(file_data = nil, save_image = false, &block) img = Magick::Image::read_inline(Base64.b64encode(file_data || self.data)).first block.call(img) write_attribute('data', img.to_blob) if save_image img = nil GC.start end end
Controller Usage:
# returns DbImage if content_type matches db_file = DbFile.new_file(params[:file][:data]) db_file.save
Model Usage:
# raw binary image data File.open('my_file', 'w') { |f| f.write(db_file.data) } # Image resizing with rmagick # automatically creates RMagick::Image and # invokes GC.start db_file.with_image do |img| img.scale(.25) img.write('thumb.jpg') end
Rotating Images With RMagick and Ruby on Rails
This is archived from:Here
Photo model is fairly simple:
class Photo < ActiveRecord::Base file_column :file, :magick => { :size => ‘440×330', :crop => ‘4:3', :versions => { :square => { :crop => ‘1:1', :size => ‘100×100'} } } 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.
Add 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
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' %>
Rotating has an negative effect on the quality of the image. In order to rectify this, you have to add a block after each image.write call like so:
image.write(photo.file) {self.quality = 100}