Yesterday I played a bit with Ruby, RMagick and FlickrAPI. I have to admit that I'm very impressed by the ImageMagick library and the RMagick Ruby bindings seem to work very smooth. It would be interesting to see this combination in a project, maybe with some image caching mechanism in order to reduce the overload of image processing.
Initially I wanted to make a little ruby script that fetches some images from Flickr and creates a bigger image, with the Flickr images arranged in order. But as I was browsing the RMagick documentation, I discovered `ImageList#montage` function which seemed to do exactly what I wanted. Here's the result, with some comments in place. This script can be extended in order to provide some better looking results - for example it would be interesting to see the names of the image posters, or also some better image effects (shadows, slight rotations in order for the images to appear more natural - eg. polaroid effect, etc.)
For accessing the FlickrAPI, I used the flickraw library - one of the best FlickrAPI Ruby binding in my opinion. I tried other libraries in the past, but they were either obsolete, or just too buggy. Flickraw provides a low level access to the FlickrAPI, but with a Ruby style syntax. The library is just a wrapper for the FlickrAPI methods - which means that even if the API will change, the library will most likely continue to work. Many claps to Flickraw's author for choosing this great design.
#!/usr/bin/ruby require 'flickraw' require 'open-uri' require 'RMagick' # # Obtain the the photos from Flickr. # def get_photos( username, count = 9 ) begin user = flickr.people.findByUsername( :username => username ) photo_list = flickr.photos.getContactsPublicPhotos( :user_id => user.nsid, :count => 9 ) rescue Exception => ex puts ex return [] end return photo_list.slice(0, 9) end # # Construct the URL of the photo, given a photo object as returned by flickraw. # def photo_url ( photo ) return "http://farm#{photo.farm}.static.flickr.com/#{photo.server}/#{photo.id}_#{photo.secret}.jpg" end # # Download the specified photos. It will use a caching mechanism in order to save # bandwidth. # def download_photos( photo_list ) files = [] Dir.mkdir 'images' unless File.exists?( 'images' ) photo_list.each do |p| url = photo_url p fileName = 'images/' + File.basename( url ) if !File.exists?( fileName ) puts url.to_s + " => " + fileName open url do |remote| open(fileName, 'wb') { |local| local << remote.read } end else puts "Skipping #{url} because it already exists." end files << fileName end return files end # # Create the collage, using Magick::ImageList#montage # method. # def create_image( args = { } ) # Set some default values props = { :width => 1024, :height => 768, :files => nil, :username => 'unknown-user', :output => '', :tile_h => 3, :tile_v => 3 } args.each { |k,v| k = k.to_sym raise ArgumentError, "unknown property #{k}" unless props.key?(k) props[k] = v } props[:output] = props[:username] + '.jpg' unless props[:output].length > 0 # Construct the image list image_list = Magick::ImageList.new props[:files].each do |img| image = Magick::Image.read( img ).first # Add a nice, white border around each image image.border!(30, 30, "#ffffff") image.background_color = "none" image_list << image end # Compute the placement of the images. Not a very interesting part... padding, image_dim = Hash.new, Hash.new padding[:x], padding[:y] = props[:width] * 0.05, props[:height] * 0.05 image_dim[:width], image_dim[:height] = (props[:width] - 6*padding[:x]) / props[:tile_h], (props[:height] - 6*padding[:y]) / props[:tile_v] # Construct the montage. image_list = image_list.montage do self.tile = "#{props[:tile_h]}x#{props[:tile_v]}" self.background_color = '#000000' self.geometry = "#{image_dim[:width]}x#{image_dim[:height]}+#{padding[:x]}+#{padding[:y]}" self.shadow = true end # Flatten everything and write it to a file. image = image_list.flatten_images image.resize!( props[:width], props[:height] ) image.write( props[:output] ) end # Check the number of parameters if ARGV.length <= 0 puts "Not enough arguments." exit end # Main body username = ARGV.first photos = get_photos( username ) image_files = download_photos( photos ) create_image( :files => image_files, :username => username, :width => 400, :height => 300 )
Hello Cristi,
I was reading you used flickraw and i am trying to use it too but I am having errors.
May you help me trying to understand what I am doing wrong?
Thank you in advance.
Best,
Jorge
Hi Jorge,
Can you provide more details about those errors? I've just tried to the flickraw part from the posted script and it appears to be still working (albeit it's a bit old). Not sure about the RMagick part, since I didn't have enough time to install it.
Be sure to install the flickraw gem first:
Let me know if you're still having troubles.
Regards,
Cristi