All formats

ruby-vips

LanguageRuby (MRI 2.x/3.x, JRuby)
LicenseMIT (libvips: LGPL-2.1-or-later)
Repositorygithub.com/libvips/ruby-vips
Documentationrubydoc.info/gems/ruby-vips, libvips jpegsave
Depends onlibvips 8.x (with libjpeg-turbo or mozjpeg), ruby-ffi
Read / writeyes / yes; progressive, subsampling, Huffman optimization, trellis quantization with mozjpeg; Exif, XMP, ICC

libvips is a demand-driven image library: it processes images in small regions across threads instead of loading them whole, so a resize of a large JPEG uses a few tens of MB instead of hundreds. Vips::Image.thumbnail combines shrink-on-load (libjpeg’s ½, ¼, 1/8 scaled decoding), resizing and auto-rotation from Exif in one call, and is the fastest way to make thumbnails in Ruby.

libvips keeps metadata (Exif, XMP, ICC) when saving unless you ask it not to. It exposes every libjpeg option, and when built against mozjpeg it adds trellis quantization and scan optimization.

Installation#

Shell
# Debian / Ubuntu
sudo apt install libvips42
# macOS
brew install vips
gem install ruby-vips

On Windows, the gem pulls in the MSYS2 libvips package.

Basic usage: auto-rotate, resize and save a JPEG#

Ruby
require "vips"

image = Vips::Image.new_from_file("photo.jpg")
puts "#{image.width} x #{image.height}, #{image.bands} bands"

image = image.autorot                 # apply the Exif orientation
image = image.resize(1200.0 / image.width)
image.jpegsave("out.jpg", Q: 85, optimize_coding: true)

new_from_file only reads the header; pixels are decoded when an operation needs them.

How to make a JPEG thumbnail quickly#

Ruby
# 300 px wide, height in proportion; decodes at reduced scale and auto-rotates
thumb = Vips::Image.thumbnail("photo.jpg", 300)
thumb.write_to_file("thumb.jpg", Q: 80)

# exact 200x200 square, cropped around the center
square = Vips::Image.thumbnail("photo.jpg", 200, height: 200, crop: :centre)
square.write_to_file("square.jpg")

How to save a progressive JPEG without chroma subsampling#

Ruby
image.jpegsave("out.jpg",
  Q: 90,
  interlace: true,          # progressive
  subsample_mode: :off,     # 4:4:4
  optimize_coding: true)

With a mozjpeg-based libvips you can add trellis_quant: true, optimize_scans: true, quant_table: 3 for smaller files.

How to read Exif data#

libvips exposes Exif tags as fields named exif-ifd0-*, exif-ifd2-* (Exif SubIFD) and exif-ifd3-* (GPS):

Ruby
image = Vips::Image.new_from_file("photo.jpg")
puts image.get("exif-ifd0-Model") if image.get_typeof("exif-ifd0-Model") != 0
puts image.get("exif-ifd2-DateTimeOriginal") if image.get_typeof("exif-ifd2-DateTimeOriginal") != 0
puts image.get("orientation") if image.get_typeof("orientation") != 0

image.get_fields.grep(/^exif-/).each { |name| puts "#{name}: #{image.get(name)}" }

Values are strings like Canon EOS R6 (Canon EOS R6, ASCII, 13 components, 13 bytes): the converted value first, then the raw form.

How to strip metadata and encode to memory#

Ruby
# keep: :none removes Exif, XMP, IPTC and ICC (libvips 8.15+); older versions use strip: true
data = image.jpegsave_buffer(Q: 80, keep: :none)
File.binwrite("clean.jpg", data)

from_memory = Vips::Image.new_from_buffer(data, "")

Tips#