Vips::Process
Process your images with ruby-vips using an operation-oriented approach.
Inspired by carrierwave-vips and @jcupitt's gaussian blur example.
Made with <3 @UXtemple. :)
Vips is an open source and super fast image
processing library with a very low memory footprint.
You can use it as a replacement to ImageMagick
, MiniMagick
and the likes.
See the benchmarks here.
Installation
This gem requires libvips
in order to run. For instructions on how to get it in OS X and Linux, follow these installation guides.
Add this line to your application's Gemfile:
gem 'vips-process'
And then execute:
$ bundle
Or install it yourself as:
$ gem install vips-process
Usage
Define your image class and explicity include the processes you want to apply to it:
# example.rb
require 'vips-process'
require 'vips-process/gaussian-blur'
require 'vips-process/resize'
class MyImage
include Vips::Process
include Vips::Process::GaussianBlur
include Vips::Process::Resize
include Vips::Process::Quality
attr_accessor :src, :dst
def initialize(src, dst=nil)
@src = src
@dst = dst || src
end
version(:blurred) { gaussian_blur }
version(:thumb) { resize_to_fit 150, 150 }
version :blurred_thumb, [:thumb, :blurred]
version(:blurred_thumb, [:thumb, :blurred]) { quality 50 }
end
Vips::Process
also comes with a Base
helper class that takes care of defining src
, dst
and
including Vips::Process
for you. It's the recommended way to go about it and you can use it as follows
require 'vips-process/base'
require 'vips-process/gaussian-blur'
class MyImage < Vips::Process::Base
include Vips::Process::GaussianBlur
version(:blurred) { gaussian_blur }
end
Working with versions
Version dependencies are recursive. I.e., if you have:
class MyImage < Vips::Process::Base
# ...
version(:a) { ... }
version(:b, [:a])
version(:c, [:b])
# ...
end
Calling image.c_version
will actually call image.b_version
which will in turn call
image.a_version
and then run version b
's code.
MyImage.versions
will return an array with the list of versions your image supports.
Calling image.versions!
will process all versions at once and will return an array with tuples
like [version_name, output_path]
. E.g. with the image class above:
image = MyImage.new('/path/to/src.jpg')
image.versions! #=> [[:a, '/path/to/src-a.jpg'], [:b, '/path/to/src-b.jpg'], [:c, '/path/to/src-c.jpg']]
versions!
takes one optional argument: the base
destination. This could be either a directory
or a filename.
If you use a directory, all new files will be written to that directory
(which will be created recursively if it doesn't exist) with the version's name followed by the src
file extension. E.g.:
image = MyImage.new('/path/to/src.jpg')
image.versions!('/path/to/dir/') #=> [[:a, '/path/to/dir/a.jpg'], [:b, '/path/to/dir/b.jpg'], [:c, '/path/to/dir/c.jpg']]
If you use a filename, all new files will be written next to that file prepending the version's name and using its extension. E.g.:
image = MyImage.new('/path/to/src.jpg')
image.versions!('/path/to/dst.jpeg') #=> [[:a, '/path/to/dst-a.jpeg'], [:b, '/path/to/dest-b.jpeg'], [:c, '/path/to/dest-c.jpg']]
By default we use the src
's filename.
Supported processes
All examples live in the /examples folder of this repo.
AutoOrient
Read the camera EXIF data to determine orientation and adjust accordingly
Usage:
Include it in your image class include Vips::Process::AutoOrient
and
call it MyImage.new('/path/to/src').auto_orient
.
GaussianBlur
Applies a gaussian blur to an image.
Usage:
Include it in your image class include Vips::Process::GaussianBlur
and
call it MyImage.new('/path/to/src').gaussian_blur(5, 0.2)
.
The first argument is the radius
and the second one is the minimium amplitude
we consider,
which sets how far out the mask goes; it's optional and defaults to 0.2
.
Convert
Converts an image to a different format.
Usage:
Include it in your image class include Vips::Process::Convert
and
call it MyImage.new('/path/to/src.jpg').convert(:png)
.
The first argument is the format
we'll conver the file to (jpeg or png) and
the second one is an optional hash of options
to be passed to the converting function
(ie, :interlace => true for png).
Crop
Crop an image in the desired dimentions.
Usage:
Include it in your image class include Vips::Process::Crop
and
call it MyImage.new('/path/to/src.jpg').crop(height: 100, top: 0.5)
.
Because it's arguments can be used in so many different ways I've decided to make them keyword arguments with a default that would give you the same image back.
There are four possible arguments in total: width
, height
, left
and top
.
width
and height
do just that: they crop the resulting image in size.
left
and top
are slightly different as they operate in two modes: if you use an Integer it's
the just the offset. However, if a Float between 0 and 1 is passed it's used to position the cropping
mask accordingly to the width
and or height
respectively. This is probable best seen with an
example:
Given:
i=image cm=crop mask
________ ________
| | | |
| | | |
| | --------
| |
| |
________
crop height: cm.height, top: 0.0 will result in:
________
| final|
| img |
--------
| |
| x |
________
crop height: cm.height, top: 0.5 will result in:
________
| x |
--------
| final|
| img |
--------
| x |
________
crop height: cm.height, top: 1.0 will result in:
________
| |
| x |
--------
| final|
| img |
________
It's also very powerful when used together with resize.
E.g.: say you have an image that is 3000x2000 px.
image.resize_to_width(300).crop(height: 150, top: 0.5).process!
will first resize it to
300x200 px and then it will crop it using a 150 height mask positioned in the middle of the
resized image. It will give you an image of full width but with height starting at 25px and
finishing at 175px. Here's a graphical example:
If the cropping area is outside of the boundaries of the current image crop
will throw an
Exception. If you would like it to silently ignore that issue and return the image as it came
use crop!
instead.
### Quality
Changes quality of the image (if supported by the file format)
Usage:
Include it in your image class include Vips::Process::Quality
and
call it MyImage.new('/path/to/src.jpg').quality(50)
.
It takes the quality
as an argument which defaults to 75
. This value should be between 0 and 100.
Currently jpegs
are only supported. Using any other image formats will be a no-op.
### Resize
There are three resize methods. Each of them will sharpen the image after doing its work.
Usage:
Include it in your image class include Vips::Process::Resize
.
All of them take the same arguments: width
and height
.
resize_to_fit
Resize the image to fit within the specified dimensions while retaining the original aspect ratio. The image may be shorter or narrower than specified in the smaller dimension but will not be larger than the specified values.
Call it as follows: MyImage.new('/path/to/src.jpg').resize_to_fit(150, 150)
resize_to_fill
Resize the image to fit within the specified dimensions while retaining the aspect ratio of the original image. If necessary, crop the image in the larger dimension.
Call it as follows: MyImage.new('/path/to/src.jpg').resize_to_fill(150, 150)
resize_to_limit
Resize the image to fit within the specified dimensions while retaining the original aspect ratio. Will only resize the image if it is larger than the specified dimensions. The resulting image may be shorter or narrower than specified in the smaller dimension but will not be larger than the specified values.
Call it as follows: MyImage.new('/path/to/src.jpg').resize_to_limit(150, 150)
Strip
Remove all exif and icc data when writing to a file. This method does not actually remove any metadata but rather marks it to be removed when writing the file.
Usage:
Include it in your image class include Vips::Process::Strip
and
call it MyImage.new('/path/to/src.jpg').strip
.
Version definition
You can also define versions and compose them as you wish.
A version is defined by calling version
in your image class.
A version can have dependencies which will be executed sequentially. Of course composed versions can also take a block and do more things. Maybe this can grow into a set of common versions?
TODO
- Increment test coverage.
- Implement version caching.
- Implement more processes. Feel free to add what you may need.
Contributing
- Fork it ( https://github.com/[my-github-username]/vips-process/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request