This is something that’s been mulling around in my mind for a while now. Unfortunately, I’ve been too busy and distracted with other things to give it any more than a passing thought. However, this is me [finally] taking action.
With the desire to get to meet anybody around the area that’s doing similar work, I’m taking the initiative to officially begin the Wichita Ruby Users Group. That’s right, Wichita. As in Kansas. As in Wyatt Earp. As in the freaking Wizard of Oz (but with no association with either the musical “Oz!” or the hit HBO prison drama “Oz”).
I realize there are [currently] very few people who read this blog of mine, but if you’re in Kansas (or, heck, northern Oklahoma), get in touch with me. And hey, if you know anyone in the area, direct them my way. I’d really appreciate any help I can get on spreading the word on this thing. So, even if you don’t live anywhere near, twitter about it, call around, ping people, email them and just generally annoy everyone you know about this thing. Help me get the word out! If you can, try to convince your Ruby friends to move to Kansas so they can attend the meet-ups; I guarantee they will be the most entertaining programmer meet-ups around, even if it means I have to kick-off every meeting with a live performance by the philharmonic.
We’ll take anyone working with / dabbling in / thinking about / looking at / lusting after / crying over Ruby! If I get desperate enough, we may even start to accept some Django-ers (blasphemy!), although highly doubtful.
And maybe, just maybe, one day Wichita will be home to a beautiful, thriving Ruby community.
To get in touch with me, you can…
Email me: peburrows[at]gmail[dot]com
Call me: 501.278.6321
Twitter me: @peburrows
Posted by Phil Burrows on May 31, 2008
You guys aren’t gonna know what to do with yourselves if I keep up this trend of writing a blog post more often than once a month. If if makes you feel any better, I’m not sure I’ll manage either.
Anwyay, on to the substance.
So, like I mentioned in my last post, I’m working on a site for a photographer friend. Obviously, the main purpose of the site is to display pictures. That being the case, the obvious choice for a Rails app needing to upload files is Rick Olson’s wonderful attachment_fu plugin. For the site, I’m going to need quite a few different image sizes, and attachment_fu handles that wonderfully (If you’ll notice, we’re also using Amazon’s S3 storage service—there was some hacking done on the plugin for some tweaks to that as well; maybe another blog post coming soon?). Check it out:
has_attachment :content_type => :image,
:max_size => 5.megabytes,
:resize_to => '800x800>',
:storage => :s3,
:s3_access => :authenticated_read,
:thumbnails => {
:thumb => '100x100>',
:small => '168x168>',
:mid => '400x400>'
}
So, there are a couple things to note here. First, we’re resizing the original image to fit within 800px x 800px; attachment_fu handles this just fine. Secondly, we’re also creating four different thumbnails.
At this point, those of you familiar with the attachment_fu plugin are thinking, “So what, I do this on every project.” Yes, yes you do. But here’s what you probably don’t do: compress your images on resize. And, if you do compress your images, your thumbnails probably aren’t very pretty—they probably have visible artifacts from the multiple-JPEG-compression.
“Multiple-JPEG-compression?” I hear you ask. Yes, multiple-JPEG-compression. Maybe it’s a phrase I’ve just made up, but “multiple-JPEG-compression” is not a good thing. JPEG is a lossy format, so every time you save a JPEG image with any kind of compression, you lose a bit of the quality. Eventually, you’ll start to notice. The issue with compressing images in attachment_fu is that 1) it doesn’t do it unless you change some code, and 2) if you changed your code, you’re probably compressing the original image and then creating compressed thumbnails from that compressed, resized original. And that, my friends, is what I would call multiple-JPEG-compression.
But first, let’s back up for a bit…
The reason I even started exploring this was only because of necessity. Originally I hadn’t even thought of compressing the images; I guess assumed anything that needed to be done on the resize would be taken care of by the attachment_fu plugin. And, for the most part it is. By default, it drops any color-profile that could be included with the image (this behavior can, of course, be over-written; just specify :keep_profile => true in the has_attachment options). This is a really good thing for a couple reasons: 1) color-profiles can really increase an image’s filesize 2) color-profiles aren’t really even taken into account by most browsers. But one thing the attachment_fu plugin doesn’t do by default is compress images. So, after checking out the RMagick documentation, I figured the solution would be an easy one; all I had to do was specify a quality in the Image#to_blob call, like so:
# in attachment_fu/lib/technoweenie/attachment_fu/processors/rmagick_processor.rb
def resize_image(img, size)
# # ...code not relevant to us right now...
# set
self.temp_path = write_to_temp_file(img.to_blob {self.quality = 75})
end
And that did exactly what it should: it compressed all my images on resize. But the issue I was having was the multiple-JPEG-compression; my thumbnails were turning out ugly, which is not acceptable for a photographer’s website. Here’s how they were turning out:

As you can see, this wasn’t gonna work. So, what could I do to fix it. Well, first I had to hunt down the root of the problem. After some digging, the issue was what I’ve mentioned before, the original was getting resized and compressed, and then the thumbnails were being resized and compressed from that initial resize and compress. Multiple-JPEG-compression. So, my first thought was “Oh, well I’ll just pass in the original, full-resolution image to the resize method so each thumbnail can be created from a fresh copy and will only be compressed once.”
Bad Idea. Ever heard anything about RMagick being a memory hog? Well, it’s never a good idea to feed RMagick’s addiction to hogged resources. And, having RMagick resize an image five time that could possibly be up to 5 megabytes in size is not a good idea. Duh, Phil.
So, what was the solution to the problem. After thinking about it for a bit, I arrived on the solution I moved forward with, and it appears to be working quite well. Here’s a run-through of the plan I formulated in my mind:
1) resize the original image, but without compression
2) send that resized, uncompressed image to the resize method for thumbnail creation
2b) make sure that the thumbnails are compressed (but maybe not as much as the original)
3) after all the thumbnails are created and compressed, compress the original image and save it as well
So, the next step was to figure out a way to implement my plan. Long story short, here are the re-written methods in the plugin (i’m not a fan of monkey-patching):
# in attachment_fu/lib/technoweenie/attachment_fu.rb
def after_process_attachment
if @saved_attachment
if respond_to?(:process_attachment_with_processing) &&
thumbnailable? &&
!attachment_options[:thumbnails].blank? &&
parent_id.nil?
temp_file = temp_path || create_temp_file
attachment_options[:thumbnails].each{|suffix, size|
create_or_update_thumbnail(temp_file, suffix, *size)
}
end
#############################
# now compress the original #
#############################
with_image do |temp_file|
resize_image_or_thumbnail!(temp_file, true)
end
save_to_storage
@temp_paths.clear
@saved_attachment = nil
callback :after_attachment_saved
end
end
#
#...
#
def resize_image_or_thumbnail!(img, compress=false)
if (!respond_to?(:parent_id) || parent_id.nil?) &&
attachment_options[:resize_to] # parent image
resize_image(img, attachment_options[:resize_to], compress)
elsif thumbnail_resize_options # thumbnail
# we want to always compress the thumbnails
resize_image(img, thumbnail_resize_options, true)
end
end
# in attachment_fu/lib/technoweenie/attachment_fu/processors/rmagick_processor.rb
# Performs the actual resizing operation for a thumbnail
def resize_image(img, size, compress=false)
size = size.first if size.is_a?(Array) && size.length == 1 && !size.first.is_a?(Fixnum)
if size.is_a?(Fixnum) || (size.is_a?(Array) && size.first.is_a?(Fixnum))
size = [size, size] if size.is_a?(Fixnum)
img.thumbnail!(*size)
else
img.change_geometry(size.to_s) {|cols, rows, image|
image.resize!(cols<1 ? 1 : cols, rows<1 ? 1 : rows)
}
end
if compress && !self.thumbnail
self.temp_path = write_to_temp_file(img.to_blob {self.quality = 75})
elsif self.thumbnail
self.temp_path = write_to_temp_file(img.to_blob {self.quality = 90})
else
self.temp_path = write_to_temp_file(img.to_blob)
end
end
This uses a compression level of 75 for the original full-size image and 90 for the thumbnails, compressing each image only a single time. And, after those changes, here’s the kind of thumbnails we get:

And now for a comparison:


Can you tell which is which? I sure can, and the difference in quality is well worth the time it took to figure out how to get things right.
Posted by Phil Burrows on May 03, 2008