Skip to content
Stanislaw Pankevich edited this page Jul 16, 2013 · 27 revisions

Examples

Color given jpg file to green (this is my favourite example).

You never iterate yourself through pixels one by one, it'd be much too slow.

Instead, imagine images are huge arrays of numbers and you have a library which can efficiently apply operations to those large matrices. Imagine what calculation the thing you want to do might correspond to, and just type it.

For example, to set the red and blue channels to zero and just leave a green image you might multiply r and b by zero and g by 1. A handy vips operation for this is "lin", meaning "linear transform").

out = in.lin(a, b)

sets every pixel in out to be

out = in * a + b

It lets you give an array of numbers for a and b and will use one array element per image channel. So therefore:

#!/usr/bin/ruby
require 'rubygems'
require 'vips'
include VIPS

im = Image.new 'mypic.jpg'
im = im.lin [0, 1, 0], [0, 0, 0]

im.write 'output.jpg'

Constant image

To make a constant image (where all pixels have the same constant value), make a 1x1 pixel zero image with Image::black, add some amount to it with .lin(), and expand it up to the size you need with .embed().

#!/usr/bin/ruby

require 'rubygems'
require 'vips'
include VIPS

im = Image.black 1, 1, 1
im = im.lin [0, 0, 0], [0, 0, 255]
im = im.embed :extend, 0, 0, 400, 400
im.write 'constant_blue.jpg'

You can speed this up a slightly. lin() makes a floating-point image (since the parameters can be floats), and jpeg-save is automatically casting this back down to 8 bits before writing. If you cast to 8 bits yourself before expanding the image you can avoid a lot of upcasting and downcasting.

im = Image.black 1, 1, 1
im = im.lin([0, 0, 0], [0, 0, 255]).clip2fmt(:UCHAR)
im = im.embed :extend, 0, 0, 400, 400
im.write 'constant_blue.jpg'

Gamma

To change image gamma you might try something like:

im = im.pow(0.5).lin(255 / 255 ** 0.5, 0)

Though that'll be a bit slow (it'll call pow() three times for each pixel), it'd be much faster to make a lookup table (see Basic concepts), run the pow() on that, then map the image through the table:

lut = Image.identity(1)
lut = lut.pow(0.5).lin(255 / 255 ** 0.5, 0)
im = im.maplut(lut)

Finally:

#!/usr/bin/ruby

require 'rubygems'
require 'vips'
include VIPS

im = Image.new 'mypic.jpg'

lut = Image.identity 1 

gamma = 0.8

lut = lut.pow(gamma).lin(255 / 255 ** gamma, 0).clip2fmt(:UCHAR)
im = im.maplut lut 

im.write 'output.jpg' 

Daltonize

Implementation of the daltonize algorithm for adjusting images to aid legibility for colour-blind people.

#!/usr/bin/ruby

# daltonize.rb - example implementation of the daltonize algorithm using ruby-vips
# 
# This example only implements Deuternope, but can easily be adapted to include Protanope and Tritanope
#
# Usage: ruby daltonize.rb <source.jpeg> <output.jpeg>
#
# requires: ruby-vips
#
# credits:
#   Written by John Cupitt - the maintainer of ruby-vips library
#   Yoav Aner - applied small fixes and tweaks
#
# Resources and other implementations:
#   javascript - http://www.daltonize.org/search/label/Javascript
#   python - http://moinmo.in/AccessibleMoin?action=AttachFile&do=view&target=daltonize.py
#   Daltonize.org - http://www.daltonize.org/
#   
#   "Analysis of Color Blindness" by Onur Fidaner, Poliang Lin and Nevran Ozguven.
#   http://scien.stanford.edu/class/psych221/projects/05/ofidaner/project_report.pdf
#           
#   "Digital Video Colourmaps for Checking the Legibility of Displays by Dichromats" by Françoise Viénot,
#   Hans Brettel and John D. Mollon
#   http://vision.psychol.cam.ac.uk/jdmollon/papers/colourmaps.pdf

require 'rubygems'
require 'vips'

im = VIPS::Image.new(ARGV[0])

# remove any alpha channel before processing
alpha = nil
if im.bands == 4
    alpha = im.extract_band(3)
    im = im.extract_band(0, 3)
end

begin
    # import to CIELAB with lcms
    # if there's no profile there, we'll fall back to the thing below
    lab = im.icc_import_embedded(:relative)
    xyz = lab.lab_to_xyz()
rescue VIPS::Error
    # nope .. use the built-in converter instead
    xyz = im.srgb_to_xyz()
end

# and now to bradford cone space (a variant of LMS)
brad = xyz.recomb([[0.8951,  0.2664, -0.1614],
                   [-0.7502,  1.7135,  0.0367],
                   [0.0389, -0.0685,  1.0296]])

# through the Deuteranope matrix
# we need rows to sum to 1 in Bradford space --- the matrix in the original
# Python code sums to 1.742
deut = brad.recomb([[1, 0, 0],
                    [0.7, 0, 0.3],
                    [0, 0, 1]])

# back to xyz (this is the inverse of the brad matrix above)
xyz = deut.recomb([[0.987, -0.147, 0.16],
                   [0.432, 0.5184, 0.0493],
                   [-0.0085, 0.04, 0.968]])

# .. and back to sRGB 
rgb = xyz.xyz_to_srgb()

# so this is the colour error 
err = im - rgb

# add the error back to other channels to make a compensated image
im = im + err.recomb([[0, 0, 0],
                      [0.7, 1, 0],
                      [0.7, 0, 1]])

# reattach any alpha we saved above
if alpha
    im = im.bandjoin(alpha.clip2fmt(im.band_fmt))
end

im.write(ARGV[1])

Watermarking

http://libvips.blogspot.co.uk/2013/07/watermarking-with-ruby-vips.html

#!/usr/bin/ruby

require 'rubygems'
require 'vips'

im = VIPS::Image.new(ARGV[0])

# first, make the watermark stencil
#
# we render the user's text into a 500-pixel wide block, fade it a bit,  
# add a large border, and replicate so as to cover the whole of the image we 
# are watermarking

# 500 pixels wide, 12-point text at 300 dpi
stencil = VIPS::Image.text(ARGV[2], "Sans 12", 500, 1, 300)

# text is 0 - 255 ... we want a fairly faint watermark, so scale it down
# .lin() makes a float image, clip back to uchar for speed
stencil = stencil.lin(0.3, 0).clip2fmt(:uchar)

# add a 100-pixel border of black pixels
stencil = stencil.embed(:black, 100, 100, 
    stencil.x_size() + 200, stencil.y_size() + 200)

# copy-paste across and down to make an image more than big enough to cover 
# the whole of im, then cut out exactly the bit we need
stencil = stencil.replicate(1 + im.x_size() / stencil.x_size(),
    1 + im.y_size() / stencil.y_size())
stencil = stencil.extract_area(0, 0, im.x_size(), im.y_size())

# make a background image ... a plain red colour
background = VIPS::Image.black(1, 1, 3).lin([1,1,1], [255,0,0]).clip2fmt(:uchar)
background = background.embed(:extend, 0, 0, im.x_size(), im.y_size())

# blend the background into the image with the stencil
im = stencil.blend(background, im)

im.write(ARGV[1])
Clone this wiki locally