Home >

Batch Resizing Images with Ruby

01 Feb 2015

Working with image files is a common task in web development, and many great tools exist for advanced image manipulation. However, often all I need to do with a set of images is resize them to be more web friendly.

TL;DR

Here is the full gist.

Resizing in Ruby

I starting looking at how I could build a generalized script to handle batch resizing in Ruby, being my preferred language for scripting.

To handle the image manipulation I'll make use of the RMagick gem which wraps the ImageMagick command line tool.

To install RMagick and ImageMagick via Homebrew and Rubygems use the following commands:

$ brew update && brew doctor
$ brew install imagemagick
$ gem install rmagick

Ruby Executable

To make the script runnable without using the ruby command we'll create a file with a ruby shebang.

I've created mine in ~/.bin which is added to my $PATH so the script can be executed from anywhere. The file also needs to be executable using chmod.

$ echo "#!/usr/bin/ruby" > ~/.bin/resize.rb
$ chmod +x ~/.bin/resize.rb
$ echo 'export PATH="~/.bin:$PATH"' >> ~/.bash_profile

Note: If you use Rbenv to manage your ruby versions you'll need to update your script to use #!/usr/bin/env ruby for your shebang. See the Rbenv wiki for more info.

Resizing

The logic for resizing an image is pretty straight forward. We just need to open the image using RMagick and call the resize_to_fit command, which will scale the image to fit the width in pixels while maintaining current aspect ratio.

Because this is a dynamic script we will get the values for directory and width using ARGV.

require 'RMagick'
require 'pathname'

@directory = Pathname(File.expand_path(ARGV[0]))
@size      = ARGV.fetch(1) { 1025 }

def resize_image(file)
  img = Magick::Image.read(file).first

  resized = img.resize_to_fit(@size)

  path = @directory.join('resized', File.basename(file))
  resized.write(path) do
    self.quality = 100
  end

  # free up RAM
  img.destroy!
  resized.destroy!
end

Processing a Batch

Now that we have a method for resizing images we need a way to collect images for resizing and iterate over them. We can use the Dir.glob method to get an Array of all the jpg, png, and gif images in the specified directory and then call resize_image for each.

resize_dir = "#{@directory}/resized"
unless File.directory? resize_dir
  puts "Creating #{resize_dir}/"
  Dir.mkdir resize_dir
end

files = Dir.glob "#{@directory}/*.{jpg,png,gif}"

puts "Resizing #{files.size} images..."

files.each do |file|
  puts "Resizing #{file}"
  resize_image(file)
end

puts "Finished resizing #{files.size} images"

Using the Script

To use our new script we call it and pass a directory and image width. Here we will create a batch of 150px thumbnails from the current directory.

$ cd ~/Desktop
$ resize.rb . 150
Resizing 2 images...
Resizing /Users/caleb/Desktop/avatar.jpg
Resizing /Users/caleb/Desktop/header.png
Finished resizing 2 images

Customizing

Obviously this type of script could be used to do more than just resize images. Just take a look at the RMagick docs for ideas.