Module: Vips

Defined in:
lib/vips/image.rb,
lib/vips.rb,
lib/vips/call.rb,
lib/vips/align.rb,
lib/vips/angle.rb,
lib/vips/error.rb,
lib/vips/access.rb,
lib/vips/coding.rb,
lib/vips/extend.rb,
lib/vips/angle45.rb,
lib/vips/methods.rb,
lib/vips/version.rb,
lib/vips/argument.rb,
lib/vips/direction.rb,
lib/vips/operation.rb,
lib/vips/bandformat.rb,
lib/vips/demandstyle.rb,
lib/vips/interpolate.rb,
lib/vips/foreignflags.rb,
lib/vips/interpretation.rb

Overview

This module provides a set of overrides for the vips image processing library used via the gobject-introspection gem.

It needs vips-8.2 or later to be installed, and Vips-8.0.typelib, the vips typelib, needs to be on your GI_TYPELIB_PATH.

Example

require 'vips'

if ARGV.length < 2
    raise "usage: #{$PROGRAM_NAME}: input-file output-file"
end

im = Vips::Image.new_from_file ARGV[0], :access => :sequential

im *= [1, 2, 1]

mask = Vips::Image.new_from_array [
        [-1, -1, -1],
        [-1, 16, -1],
        [-1, -1, -1]], 8
im = im.conv mask

im.write_to_file ARGV[1]

This example loads a file, boosts the green channel (I'm not sure why), sharpens the image, and saves it back to disc again.

Reading this example line by line, we have:

im = Vips::Image.new_from_file ARGV[0], :access => :sequential

Image.new_from_file can load any image file supported by vips. In this example, we will be accessing pixels top-to-bottom as we sweep through the image reading and writing, so :sequential access mode is best for us. The default mode is :random, this allows for full random access to image pixels, but is slower and needs more memory. See Access for full details on the various modes available. You can also load formatted images from memory buffers or create images that wrap C-style memory arrays.

The next line:

im *= [1, 2, 1]

Multiplying the image by an array constant uses one array element for each image band. This line assumes that the input image has three bands and will double the middle band. For RGB images, that's doubling green.

Next we have:

mask = Vips::Image.new_from_array [
        [-1, -1, -1],
        [-1, 16, -1],
        [-1, -1, -1]], 8
im = im.conv mask

Image.new_from_array creates an image from an array constant. The 8 at the end sets the scale: the amount to divide the image by after integer convolution. See the libvips API docs for vips_conv() (the operation invoked by Image#conv) for details on the convolution operator.

Finally:

im.write_to_file ARGV[1]

Image#write_to_file writes an image back to the filesystem. It can write any format supported by vips: the file type is set from the filename suffix. You can also write formatted images to memory buffers, or dump image data to a raw memory array.

How it works

The C sources to libvips include a set of specially formatted comments which describe its interfaces. When you compile the library, gobject-introspection generates Vips-8.0.typelib, a file describing how to use libvips.

The gobject-introspection gem loads this typelib and uses it to let you call functions in libvips directly from Ruby. However, the interface you get from raw gobject-introspection is rather ugly, so the ruby-vips gem adds a set of overrides which try to make it nicer to use.

The API you end up with is a Ruby-ish version of the VIPS C API. Full documentation on the operations and what they do is there, you can use it directly. This document explains the extra features of the Ruby API and lists the available operations very briefly.

Automatic wrapping

ruby-vips adds a Image.method_missing handler to Image and uses it to look up vips operations. For example, the libvips operation add, which appears in C as vips_add(), appears in Ruby as Image#add.

The operation's list of required arguments is searched and the first input image is set to the value of self. Operations which do not take an input image, such as Image.black, appear as class methods. The remainder of the arguments you supply in the function call are used to set the other required input arguments. If the final supplied argument is a hash, it is used to set any optional input arguments. The result is the required output argument if there is only one result, or an array of values if the operation produces several results. If the operation has optional output objects, they are returned as a final hash.

For example, Image#min, the vips operation that searches an image for the minimum value, has a large number of optional arguments. You can use it to find the minimum value like this:

min_value = image.min

You can ask it to return the position of the minimum with :x and :y.

min_value, opts = min :x => true, :y => true
x_pos = opts['x']
y_pos = opts['y']

Now x_pos and y_pos will have the coordinates of the minimum value. There's actually a convenience function for this, Image#minpos.

You can also ask for the top n minimum, for example:

min_value, opts = min :size => 10, :x_array => true, :y_array => true
x_pos = opts['x_array']
y_pos = opts['y_array']

Now x_pos and y_pos will be 10-element arrays.

Because operations are member functions and return the result image, you can chain them. For example, you can write:

result_image = image.real.cos

to calculate the cosine of the real part of a complex image. There are also a full set of arithmetic operator overloads, see below.

libvips types are also automatically wrapped. The override looks at the type of argument required by the operation and converts the value you supply, when it can. For example, Image#linear takes a VipsArrayDouble as an argument for the set of constants to use for multiplication. You can supply this value as an integer, a float, or some kind of compound object and it will be converted for you. You can write:

result_image = image.linear 1, 3 
result_image = image.linear 12.4, 13.9 
result_image = image.linear [1, 2, 3], [4, 5, 6] 
result_image = image.linear 1, [4, 5, 6] 

And so on. A set of overloads are defined for Image#linear, see below.

It does a couple of more ambitious conversions. It will automatically convert to and from the various vips types, like VipsBlob and VipsArrayImage. For example, you can read the ICC profile out of an image like this:

profile = im.get_value "icc-profile-data"

and profile will be a byte array.

If an operation takes several input images, you can use a constant for all but one of them and the wrapper will expand the constant to an image for you. For example, Image#ifthenelse uses a condition image to pick pixels between a then and an else image:

result_image = condition_image.ifthenelse then_image, else_image

You can use a constant instead of either the then or the else parts and it will be expanded to an image for you. If you use a constant for both then and else, it will be expanded to match the condition image. For example:

result_image = condition_image.ifthenelse [0, 255, 0], [255, 0, 0]

Will make an image where true pixels are green and false pixels are red.

This is useful for Image#bandjoin, the thing to join two or more images up bandwise. You can write:

rgba = rgb.bandjoin 255

to append a constant 255 band to an image, perhaps to add an alpha channel. Of course you can also write:

result_image = image1.bandjoin image2
result_image = image1.bandjoin [image2, image3]
result_image = Vips::Image.bandjoin [image1, image2, image3]
result_image = image1.bandjoin [image2, 255]

and so on.

Automatic YARD documentation

The bulk of these API docs are generated automatically by generate_yard. It examines libvips and writes a summary of each operation and the arguments and options that operation expects.

Use the C API docs for more detail.

Exceptions

The wrapper spots errors from vips operations and raises the Error exception. You can catch it in the usual way.

Draw operations

Paint operations like Image#draw_circle and Image#draw_line modify their input image. This makes them hard to use with the rest of libvips: you need to be very careful about the order in which operations execute or you can get nasty crashes.

The wrapper spots operations of this type and makes a private copy of the image in memory before calling the operation. This stops crashes, but it does make it inefficient. If you draw 100 lines on an image, for example, you'll copy the image 100 times. The wrapper does make sure that memory is recycled where possible, so you won't have 100 copies in memory.

If you want to avoid the copies, you'll need to call drawing operations yourself.

Overloads

The wrapper defines the usual set of arithmetic, boolean and relational overloads on image. You can mix images, constants and lists of constants (almost) freely. For example, you can write:

result_image = ((image * [1, 2, 3]).abs < 128) | 4

Expansions

Some vips operators take an enum to select an action, for example Image#math can be used to calculate sine of every pixel like this:

result_image = image.math :sin

This is annoying, so the wrapper expands all these enums into separate members named after the enum. So you can write:

result_image = image.sin

Convenience functions

The wrapper defines a few extra useful utility functions: Image#get_value, Image#set_value, Image#bandsplit, Image#maxpos, Image#minpos, Image#median.

Defined Under Namespace

Classes: Access, Align, Angle, Angle45, Argument, BandFormat, Coding, DemandStyle, Direction, Error, Extend, ForeignFlags, Image, Interpolate, Interpretation, Loader, Operation

Constant Summary collapse

LOG_DOMAIN =
"VIPS"
VERSION =
"1.0.2"

Class Method Summary collapse

Class Method Details

.call(name, *args) ⇒ Object

This is the public entry point for the vips binding. call will run any vips operation, for example:

out = Vips::call "black", 100, 100, :bands => 12

will call the C function

vips_black( &out, 100, 100, "bands", 12, NULL );

There are Vips::Image#method_missing hooks which will run call for you on Image for undefined instance or class methods. So you can also write:

out = Vips::Image.black 100, 100, :bands => 12

Or perhaps:

x = Vips::Image.black 100, 100
y = x.invert

to run the vips_invert() operator.

There are also a set of operator overloads and some convenience functions, see Image.

If the operator needs a vector constant, call will turn a scalar into a vector for you. So for x.linear(a, b), which calculates x * a + b where a and b are vector constants, you can write:

x = Vips::Image.black 100, 100, :bands => 3
y = x.linear(1, 2)
y = x.linear([1], 4)
y = x.linear([1, 2, 3], 4)

or any other combination. The operator overloads use this facility to support all the variations on:

x = Vips::Image.black 100, 100, :bands => 3
y = x * 2
y = x + [1,2,3]
y = x % [1]

Similarly, whereever an image is required, you can use a constant. The constant will be expanded to an image matching the first input image argument. For example, you can write:

x = Vips::Image.black 100, 100, :bands => 3
y = x.bandjoin(255)

to add an extra band to the image where each pixel in the new band has the constant value 255.



298
299
300
# File 'lib/vips/call.rb', line 298

def self.call(name, *args)
    Vips::call_base name, nil, "", args
end

.const_missing(name) ⇒ Object



44
45
46
47
48
49
50
51
52
53
# File 'lib/vips.rb', line 44

def const_missing(name)
    log "Vips::const_missing: #{name}"

    init()
    if const_defined?(name)
        const_get(name)
    else
      super
    end
end

.generate_yardObject

This method generates yard comments for all the dynamically bound vips operations.

Regenerate with something like:

ruby > methods.rb require 'vips'; Vips::generate_yard ^D



1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
# File 'lib/vips/image.rb', line 1263

def self.generate_yard
    # these have hand-written methods, see above
    no_generate = ["bandjoin", "ifthenelse"]

    generate_operation = lambda do |op|
        flags = op.flags
        return if (flags & :deprecated) != 0
        nickname = Vips::nickname_find op.gtype

        return if no_generate.include? nickname

        all_args = op.get_args.select {|arg| not arg.isset}

        # separate args into various categories
 
        required_input = all_args.select do |arg|
            (arg.flags & :input) != 0 and
            (arg.flags & :required) != 0 
        end

        optional_input = all_args.select do |arg|
            (arg.flags & :input) != 0 and
            (arg.flags & :required) == 0 
        end

        required_output = all_args.select do |arg|
            (arg.flags & :output) != 0 and
            (arg.flags & :required) != 0 
        end

        # required input args with :modify are copied and appended to 
        # output
        modified_required_input = required_input.select do |arg|
            (arg.flags & :modify) != 0 
        end
        required_output += modified_required_input

        optional_output = all_args.select do |arg|
            (arg.flags & :output) != 0 and
            (arg.flags & :required) == 0 
        end

        # optional input args with :modify are copied and appended to 
        # output
        modified_optional_input = optional_input.select do |arg|
            (arg.flags & :modify) != 0 
        end
        optional_output += modified_optional_input

        # find the first input image, if any ... we will be a method of this
        # instance
        member_x = required_input.find do |x|
            x.gtype.type_is_a? GLib::Type["VipsImage"]
        end
        if member_x != nil
            required_input.delete member_x
        end

        print "# @!method "
        print "self." if not member_x 
        print "#{nickname}("
        print required_input.map(&:name).join(", ")
        puts ", opts = {})"

        puts "#   #{op.description.capitalize}."

        required_input.each do |arg| 
            puts "#   @param #{arg.name} [#{arg.type}] #{arg.blurb}"
        end

        puts "#   @param [Hash] opts Set of options"
        optional_input.each do |arg| 
            puts "#   @option opts [#{arg.type}] :#{arg.name} #{arg.blurb}"
        end
        optional_output.each do |arg| 
            print "#   @option opts [#{arg.type}] :#{arg.name}"
            puts " Output #{arg.blurb}"
        end

        print "#   @return ["
        if required_output.length == 0 
            print "nil" 
        elsif required_output.length == 1 
            print required_output[0].type
        elsif 
            print "Array<" 
            print required_output.map(&:type).join(", ")
            print ">" 
        end
        if optional_output.length > 0
            print ", Hash<Symbol => Object>"
        end
        print "] "
        print required_output.map(&:blurb).join(", ")
        if optional_output.length > 0
            print ", " if required_output.length > 0
            print "Hash of optional output items"
        end
        puts ""

        puts ""
    end

    generate_class = lambda do |gtype|
        begin
            # can fail for abstract types
            # can't find a way to get to #abstract? from a gtype
            op = Vips::Operation.new gtype.name
        rescue
            op = nil
        end

        generate_operation.(op) if op

        gtype.children.each do |x|
            generate_class.(x)
        end
    end

    puts "module Vips"
    puts "  class Image"
    puts ""

    # gobject-introspection 3.0.7 crashes a lot if it GCs while doing 
    # something
    GC.disable

    generate_class.(GLib::Type["VipsOperation"])

    puts "  end"
    puts "end"
end

.init(*argv) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/vips.rb', line 68

def init(*argv)
    log "Vips::init: #{argv}"

    class << self
        remove_method(:init)
        remove_method(:const_missing)
        remove_method(:method_missing)
    end

    loader = Loader.new(self, argv)
    begin
        loader.load("Vips")
    rescue 
        puts "Unable to load Vips"
        puts "  Check that the vips library has been installed and is"
        puts "  on your library path."
        puts "  Check that the typelib `Vips-8.0.typelib` has been "
        puts "  installed, and that it is on your GI_TYPELIB_PATH."
        raise
    end

    require 'vips/error'
    require 'vips/argument'
    require 'vips/operation'
    require 'vips/call'
    require 'vips/image'
    require 'vips/version'
end

.method_missing(name, *args, &block) ⇒ Object



56
57
58
59
60
61
62
63
64
65
# File 'lib/vips.rb', line 56

def method_missing(name, *args, &block)
    log "Vips::method_missing: #{name}, #{args}, #{block}"

    init()
    if respond_to?(name)
        __send__(name, *args, &block)
    else
        super
    end
end

.set_debug(dbg) ⇒ Object

Turn debug logging on and off.

Parameters:

  • dbg (Boolean)

    Set true to print debug log messages



38
39
40
# File 'lib/vips.rb', line 38

def self.set_debug dbg 
    $vips_debug = dbg
end