Lesson #1 after using the iPhone: Automatic Orientation
Remember those days of downloading your photos from your camera, then going through them, one by one... and rotating each picture right-side up until you get drowsy or give up? Such tedious work! Oh, and did you click save after you rotated?
Apparently, pictures taken from the iPhone and many new digital cameras (though none from the world's largest camera manufacturer) comes embedded with orientation information! To put it simply, it means that these digital photos already know if they are landscape or portrait - How cool is that?
Unfortunately, not all software makes use of this piece of information to save you the labor. And this includes even cool sites like Flickr :-( What a shame. And it doesn't help that most image upload/resize/crop HOWTOs doesn't talk about it either.
SharedCopy is cooler than that. Though imaging is not our forte, and its just done for your little profile picture, things are set up such that your profile picture will be right-side up :-D And now, with the help of some monkey-patching, your Rails/Ruby-powered website can be as cool too!
RMagick users, simply include the code below (e.g. append to config/environment.rb):
require 'RMagick' unless defined?(Magick)
Magick::Image.class_eval do
class << self
def read_with_auto_upright(*args, &block)
self.read_without_auto_upright(*args, &block).collect do |img|
begin
# if there's nothing to rotate, auto_orient! will return nil
img.auto_orient! || img
rescue NotImplementedError
# Older versions of ImageMagick has no auto_orient!
case img.get_exif_by_entry("Orientation") && img["EXIF:Orientation"]
# img["EXIF:Orientation"] might not be ready until get_exif_by_entry() is called
when "6"
# Magick::RightTopOrientation
img.rotate!(90)
img["EXIF:Orientation"] = "1"
when "3"
# Magick::BottomRightOrientation
img.rotate!(180)
img["EXIF:Orientation"] = "1"
when "8"
# Magick::LeftBottomOrientation
img.rotate!(270)
img["EXIF:Orientation"] = "1"
end
img
end
end
end
# wrap around RMagick::Image#read method so that
# any images loaded in memory are already upright
alias :read_without_auto_upright :read
alias :read :read_with_auto_upright
# note: not using alias_method_chain so that this
# snippet can be used outside of Rails
end
end
ImageScience users, if you trust my C++ code, just place this patched image_science.rb in your vendor/ directory. Otherwise you can apply this patch manually:
--- lib/image_science.rb 2007-10-16 12:11:08.000000000 +0800
+++ lib/image_science.rb.new 2007-10-16 12:11:01.000000000 +0800
@@ -150,6 +150,21 @@
VALUE result = Qnil;
int flags = fif == FIF_JPEG ? JPEG_ACCURATE : 0;
if (bitmap = FreeImage_Load(fif, input, flags)) {
+ FITAG *tagValue = NULL;
+ FreeImage_GetMetadata(FIMD_EXIF_MAIN, bitmap, "Orientation", &tagValue);
+ switch (tagValue == NULL ? 0 : *((short *) FreeImage_GetTagValue(tagValue))) {
+ case 6:
+ bitmap = FreeImage_RotateClassic(bitmap, 270);
+ break;
+ case 3:
+ bitmap = FreeImage_RotateClassic(bitmap, 180);
+ break;
+ case 8:
+ bitmap = FreeImage_RotateClassic(bitmap, 90);
+ break;
+ default:
+ break;
+ }
result = wrap_and_yield(bitmap, self, fif);
}
return result;
What's done here (for both) is that images loaded into memory will be checked for orientation information and have its orientation corrected if possible (otherwise its left untouched). In memory. So all your image operations like "width", "resize" will be magically "correct".
No modifications required to your routine. Heck, you can even pretend the whole scenario never existed!
This is awesome. Just what I'm looking for. Thanks for sharing it.
Posted by: Wiriana Awiputra | January 15, 2008 at 03:40 PM
One more vote for awesome. Saved me a lot of trouble. Thanks!
Posted by: Damon | July 25, 2008 at 01:12 AM
Many thanks! Really great explanation, I also added another method to rotate on demand by n degrees.
##
# Rotates the image by rotate
# filter and yields the new image.
def rotate(degrees) # :yields: image
end
and in the inline builder bit:
builder.c <<-"END"
VALUE rotate(long d) {
GET_BITMAP(bitmap);
FIBITMAP *image = FreeImage_RotateClassic(bitmap, 270);
if (image) {
copy_icc_profile(self, bitmap, image);
return wrap_and_yield(image, self, 0);
}
return Qnil;
}
END
Posted by: Jason Green | October 13, 2008 at 09:59 AM
Note for ImageScience users:
Just place the patched version of image_science.rb in the /lib directory from the root of your rails app and it should work.
The instructions say to place it in your /vendor directory, and I don't think that is the right place to put it. Please correct me if I am wrong.
Posted by: adc | June 13, 2009 at 06:41 AM
@adc technically, both "lib" and "vendor" would work.
My own convention is to put project specific code that is written by team members into "lib". And code from anyone else (or for other projects) is placed inside "vendor".
in this way, should there be rights / licensing / permissions questions, the project owner can easily & quickly single out the stuff inside "vendor".
Posted by: choonkeat | June 13, 2009 at 07:24 AM