Friday, March 27, 2009

Fun with RMagick - Thumbnails

In addition to programming web applications and sites, I have done a bit of digital image processing. Some years ago, I wrote some scripts to perform photo resizing and watermarking on batches of thousands of images at a time. I used Gimp-Perl. Gimp because I wasn't able to achieve the same quality with GD and because I liked being able to perform the steps in a GUI before writing the code to perform the steps.

I have recently stumbled upon some free time so I decided to play around a bit more. This time I used Ruby with RMagick. I wanted to get away from using the Gimp and I wanted to brush up on my Ruby skills. Obviously, Ruby isn't the most efficient (execution-time wise) language available but this is mostly just for fun.

I was interested in the possibility of analyzing a photograph, figuring out the most "interesting" bit, and saving that bit as a thumbnail. Searching for the most "interesting" bit was the obvious challenge.

My implementation is a bit simplistic. I divide the image into a grid of 200x200 squares (or however large I want the thumbnail to be). For each grid square, I get RGB readings of each pixel (actually, in this example, every five pixels in order to speed things up) and then figure out the standard deviation for each pixel. This is what the "histogram" tool in Gimp does. Once I had a script that replicated Gimp's histogram, I was a bit unmotivated to finish the program. Everything else was just grunt work and I wasn't too enthused about the program design. I know that there must be much smarter ways to do this task. Anyway, I finally got around to coding up the rest of it today and I am glad I did. It was fun to run it over a batch of eight images and see the results.

It is cool to run a program when you don't know what the results are going to be (besides it works/does not work). Short of manually clicking through Gimp and figuring out 100 histograms for each image, I couldn't know what this program would produce.

The images were just ones that I had laying around on my hard drive. I tried to pick some indoor and outdoor ones.

The square with the highest contrast wins out and sometimes this makes an interesting thumbnail and sometimes it doesn't.

Some other possible ideas would be to find the part of image with a lot of one primary color or a part that has a lot of a certain color, surrounded by a lot of contrast.

I haven't commented the code but it is pretty simple. The heart of it is just the standard deviation algorithm.
Code at github.

Sunday, August 24, 2008

Prepending content

I have been coding in Zend for a while now. I've mostly been using the framework's reference documentation to guide me. I've also Googled when I need help but I've been a little disappointed with the number of examples I've been able to find online.

Anyway, here is a problem I ran into while working on working on Photoplate. Images are collected into albums. As a navigational aid I wanted to, whenever a user looks at an image, display the appropriate album information. So, in the image display function, I need to be able to call the display function in the AlbumController and prepend that content. It took me a while to figure out how to do this. I had no trouble calling another controller and appending that content but I couldn't figure out how to prepend the content. I fumbled around with the actionStack for a bit until I figured out the solution: named segements.

So, now, in my ImageController, I do this:


$this->_helper->actionStack('view', 'album', null, array('id' => $album_id, 'parent' => true));


This calls the view action in the AlbumController. Then, in my controller base class, I have the following code:


function postDispatch()
{
     // if the parent parameter is set to true, the requested action is rendered to the parent segement
     $parent = $this->_request->getParam('parent');
     if($parent) {
         $response = $this->getResponse();
         $view = $this->initView();
         $script = $this->getViewScript();
         // make sure to prepend this content to any previous parent content
         $previous_parent_body = $response->getBody('parent');
         $response->setBody($view->render($script), 'parent');
         $response->appendBody($previous_parent_body, 'parent');
         $this->_helper->viewRenderer->setNoRender();
    }
}


That's it! Because I have this postDispatch() function set up in my base class, I can prepend the content from any controller by adding a single line of code. Whenever the 'parent' parameter is set, the view is rendered to the 'parent' segement. In the above code, you can see that I fetch the current 'parent' content, swap in the newly generated content, and append the previous content. This is because the function calls work upwards from the bottom of the hierarchy and so the order of the content needs to be reversed. The above code would be a little simpler if the response object offered a prependBody() method and I may subclass the base class to add that method.