image_processing.cr
image_processing.cr
Crystal port of the Ruby image_processing gem (v1.14.0): high-level image processing pipelines for libvips and ImageMagick.
require "image_processing"
processed = ImageProcessing::Vips
.source(File.open("input.jpg", "rb"))
.resize_to_limit(800, 800)
.convert("png")
.call
processed.path # => "/tmp/image_processing-…png"
processed.close!
Installation
Add the shard to your shard.yml:
dependencies:
image_processing:
github: OrelSokolov/image_processing.cr
The library links against libvips (>= 8.6; built and tested with 8.18) and shells out to ImageMagick 7 (magick/identify) for the MiniMagick processor. Both must be available on the system.
Vips
pipeline = ImageProcessing::Vips
.source("input.jpg")
.resize_to_limit(400, 400)
.saver(quality: 85)
.convert("png")
result = pipeline.call # writes to a Tempfile
result = pipeline.call("/path/to/output.png") # or an explicit destination
Supported chainable methods mirror the Ruby version: #source, #convert, #loader, #saver, #instrumenter, #apply, #custom, #operation, #call, plus all processor operations (#resize_to_limit, #resize_to_fit, #resize_to_fill, #resize_and_pad, #resize_to_cover, #rotate, #composite, #set, #set_type, #set_value, #remove, …). Appending ! to an operation runs the pipeline immediately.
Any libvips operation not defined as a processor method falls through to the accumulator (Vips::Image) like ruby-vips' method_missing does:
ImageProcessing::Vips.invert.call("input.jpg") # vips "invert" operation
ImageProcessing::Vips.shrink(2, 2).call("input.jpg")
MiniMagick
ImageProcessing::MiniMagick
.source("input.jpg")
.resize_to_limit(400, 400)
.convert("png")
.call
Arbitrary ImageMagick CLI options are available the same way (they become -option arguments on the convert command):
magick = ImageProcessing::MiniMagick
.source("input.jpg")
.auto_orient
.strip
.call(save: false) # => MiniMagick::Tool, command not executed
magick.args # => ["input.jpg", "-auto-orient", "-strip"]
Differences from the Ruby version
The API is a 1:1 port where Crystal allows it. Notable differences:
- The recursive option-value union is typed (
ImageProcessing::OptValue); values that flow through options and operation arguments can beVips::Value,String,Int,Float,Bool,Symbol,Hash,Array,File,Tempfile,PathorMiniMagick::Tool. - Module-level operations (e.g.
ImageProcessing::Vips.invert) are generated at compile time from the processors' public methods, because Crystal'smethod_missinghook does not fire for module-level calls. - Builder-level unknown operations go through
method_missing, which cannot accept arbitrary named arguments; named arguments used at a call site are compiled into the generated method, and a trailing optionsHashcan always be passed positionally instead. libvips8.18 removed theadd_alphaandcomposite2operations; the port implements#add_alphaviabandjoin_constand#compositevia the unifiedcompositeoperation.saver(strip: true)transparently maps to thekeepflags on libvips >= 8.15.
Development
crystal spec # full suite (compilation takes a few minutes)
The test suite is a port of the upstream Ruby tests (dhash-similarity assertions included). A few assertions are adapted for libvips 8.18 / ImageMagick 7 behavior; each deviation is marked with a comment in the spec.
License
MIT, same as the original gem.
image_processing.cr
- 0
- 0
- 0
- 0
- 0
- about 4 hours ago
- October 16, 2026
MIT License
Fri, 16 Oct 2026 12:21:32 GMT