Module: Vips

Extended by:
FFI::Library
Defined in:
lib/vips.rb,
lib/vips/size.rb,
lib/vips/align.rb,
lib/vips/angle.rb,
lib/vips/image.rb,
lib/vips/image.rb,
lib/vips/access.rb,
lib/vips/coding.rb,
lib/vips/extend.rb,
lib/vips/kernel.rb,
lib/vips/object.rb,
lib/vips/region.rb,
lib/vips/angle45.rb,
lib/vips/methods.rb,
lib/vips/version.rb,
lib/vips/direction.rb,
lib/vips/operation.rb,
lib/vips/bandformat.rb,
lib/vips/blend_mode.rb,
lib/vips/interesting.rb,
lib/vips/interpolate.rb,
lib/vips/operationmath.rb,
lib/vips/interpretation.rb,
lib/vips/operationmath2.rb,
lib/vips/operationround.rb,
lib/vips/operationboolean.rb,
lib/vips/operationcomplex.rb,
lib/vips/compass_direction.rb,
lib/vips/operationcomplex2.rb,
lib/vips/operationcomplexget.rb,
lib/vips/operationrelational.rb

Overview

This module provides a binding for the libvips image processing library.

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, precision: :integer

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, create images that wrap C-style memory arrays, or make images from constants.

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, precision: :integer

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. By default, it computes with a float mask, but :integer is fine for this case, and is much faster.

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 binding uses ruby-ffi to open the libvips shared library. When you call a method on the image class, it uses libvips introspection system (based on GObject) to search the library for an operation of that name, transforms the arguments to a form libvips can digest, and runs the operation.

This means ruby-vips always presents the API implemented by the libvips shared library. It should update itself as new features are added.

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. Any trailing keyword arguments are used to set options on the operation.

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 method 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.

Logging

Libvips uses g_log() to log warning, debug, info and (some) error messages.

https://developer.gnome.org/glib/stable/glib-Message-Logging.html

You can disable warnings by defining the VIPS_WARNING environment variable. You can enable info output by defining VIPS_INFO.

Exceptions

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

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 that operation expects.

Use the C API docs for more detail.

Enums

The libvips enums, such as VipsBandFormat appear in ruby-vips as Symbols like :uchar. They are documented as a set of classes for convenience, see the class list.

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, ArgumentClass, ArgumentClassPtr, ArgumentInstance, ArgumentInstancePtr, BandFormat, BlendMode, Coding, CompassDirection, Direction, Error, Extend, Image, IntStruct, Interesting, Interpolate, Interpretation, Kernel, Object, ObjectClass, Operation, OperationBoolean, OperationComplex, OperationComplex2, OperationComplexget, OperationMath, OperationMath2, OperationRelational, OperationRound, Region, Size, SizeStruct

Constant Summary collapse

LOG_DOMAIN =
"VIPS"
LIBRARY_VERSION =
Vips::version_string
MAX_COORD =

libvips has this arbitrary number as a sanity-check upper bound on image size. It's sometimes useful for know whan calculating image ratios.

10000000
IMAGE_TYPE =

some handy gtypes

GObject::g_type_from_name "VipsImage"
ARRAY_INT_TYPE =
GObject::g_type_from_name "VipsArrayInt"
ARRAY_DOUBLE_TYPE =
GObject::g_type_from_name "VipsArrayDouble"
ARRAY_IMAGE_TYPE =
GObject::g_type_from_name "VipsArrayImage"
REFSTR_TYPE =
GObject::g_type_from_name "VipsRefString"
BLOB_TYPE =
GObject::g_type_from_name "VipsBlob"
BAND_FORMAT_TYPE =
Vips::vips_band_format_get_type
INTERPRETATION_TYPE =
Vips::vips_interpretation_get_type
CODING_TYPE =
Vips::vips_coding_get_type
BLEND_MODE_TYPE =
nil
VERSION =
"2.0.16"

Class Method Summary collapse

Class Method Details

.at_least_libvips?(x, y) ⇒ Boolean

True if this is at least libvips x.y

Returns:

  • (Boolean)


585
586
587
588
589
590
# File 'lib/vips.rb', line 585

def self.at_least_libvips?(x, y)
  major = version(0)
  minor = version(1)

  major > x || (major == x && minor >= y)
end

.cache_set_max(size) ⇒ Object

Set the maximum number of operations that libvips should cache. Set 0 to disable the operation cache. The default is 1000.



542
543
544
# File 'lib/vips.rb', line 542

def self.cache_set_max size
  vips_cache_set_max size
end

.cache_set_max_files(size) ⇒ Object

Set the maximum number of files libvips should keep open in the operation cache. Set 0 to disable the operation cache. The default is 100.



555
556
557
# File 'lib/vips.rb', line 555

def self.cache_set_max_files size
  vips_cache_set_max_files size
end

.cache_set_max_mem(size) ⇒ Object

Set the maximum amount of memory that libvips should use for the operation cache. Set 0 to disable the operation cache. The default is 100mb.



548
549
550
# File 'lib/vips.rb', line 548

def self.cache_set_max_mem size
  vips_cache_set_max_mem size
end

.concurrency_set(n) ⇒ Object

Set the size of the libvips worker pool. This defaults to the number of hardware threads on your computer. Set to 1 to disable threading.



561
562
563
# File 'lib/vips.rb', line 561

def self.concurrency_set n
  vips_concurrency_set n
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


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
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
# File 'lib/vips/image.rb', line 1335

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

  # map gobject's type names to Ruby
  map_go_to_ruby = {
    "gboolean" => "Boolean",
    "gint" => "Integer",
    "gdouble" => "Float",
    "gfloat" => "Float",
    "gchararray" => "String",
    "VipsImage" => "Vips::Image",
    "VipsInterpolate" => "Vips::Interpolate",
    "VipsArrayDouble" => "Array<Double>",
    "VipsArrayInt" => "Array<Integer>",
    "VipsArrayImage" => "Array<Image>",
    "VipsArrayString" => "Array<String>",
  }

  generate_operation = lambda do |gtype, nickname, op|
    op_flags = op.get_flags
    return if (op_flags & OPERATION_DEPRECATED) != 0
    return if no_generate.include? nickname

    description = Vips::vips_object_get_description op

    # find and classify all the arguments the operator can take
    required_input = []
    optional_input = []
    required_output = []
    optional_output = []
    member_x = nil
    op.argument_map do |pspec, argument_class, _argument_instance|
      arg_flags = argument_class[:flags]
      next if (arg_flags & ARGUMENT_CONSTRUCT) == 0
      next if (arg_flags & ARGUMENT_DEPRECATED) != 0

      name = pspec[:name].tr("-", "_")
      # 'in' as a param name confuses yard
      name = "im" if name == "in"
      gtype = pspec[:value_type]
      fundamental = GObject::g_type_fundamental gtype
      type_name = GObject::g_type_name gtype
      if map_go_to_ruby.include? type_name
        type_name = map_go_to_ruby[type_name]
      end
      if fundamental == GObject::GFLAGS_TYPE ||
         fundamental == GObject::GENUM_TYPE
        type_name = "Vips::" + type_name[/Vips(.*)/, 1]
      end
      blurb = GObject::g_param_spec_get_blurb pspec
      value = {
        name: name,
        flags: arg_flags,
        gtype: gtype,
        type_name: type_name,
        blurb: blurb
      }

      if (arg_flags & ARGUMENT_INPUT) != 0
        if (arg_flags & ARGUMENT_REQUIRED) != 0
          # note the first required input image, if any ... we
          # will be a method of this instance
          if !member_x && gtype == Vips::IMAGE_TYPE
            member_x = value
          else
            required_input << value
          end
        else
          optional_input << value
        end
      end

      # MODIFY INPUT args count as OUTPUT as well
      if (arg_flags & ARGUMENT_OUTPUT) != 0 ||
         ((arg_flags & ARGUMENT_INPUT) != 0 &&
          (arg_flags & ARGUMENT_MODIFY) != 0)
        if (arg_flags & ARGUMENT_REQUIRED) != 0
          required_output << value
        else
          optional_output << value
        end
      end
    end

    print "# @!method "
    print "self." unless member_x
    print "#{nickname}("
    print required_input.map { |x| x[:name] }.join(", ")
    print ", " if required_input.length > 0
    puts "**opts)"

    puts "#   #{description.capitalize}."

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

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

    print "#   @return ["
    if required_output.length == 0
      print "nil"
    elsif required_output.length == 1
      print required_output.first[:type_name]
    else
      print "Array<"
      print required_output.map { |x| x[:type_name] }.join(", ")
      print ">"
    end
    if optional_output.length > 0
      print ", Hash<Symbol => Object>"
    end
    print "] "
    print required_output.map { |x| x[: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, _|
    nickname = Vips::nickname_find gtype

    if nickname
      begin
        # can fail for abstract types
        op = Vips::Operation.new nickname
      rescue Vips::Error
        nil
      end

      generate_operation.(gtype, nickname, op) if op
    end

    Vips::vips_type_map gtype, generate_class, nil
  end

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

  generate_class.(GObject::g_type_from_name("VipsOperation"), nil)

  puts "  end"
  puts "end"
end

.get_suffixes[String]

Get a list of all supported file suffixes.

Returns:

  • ([String])

    array of supported suffixes



595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
# File 'lib/vips.rb', line 595

def self.get_suffixes
  # vips_foreign_get_suffixes() was added in libvips 8.8
  return [] unless Vips.respond_to? :vips_foreign_get_suffixes

  array = Vips::vips_foreign_get_suffixes

  names = []
  p = array
  until (q = p.read_pointer).null?
    suff = q.read_string
    GLib::g_free q
    names << suff unless names.include? suff
    p += FFI::Type::POINTER.size
  end
  GLib::g_free array

  names
end

.leak_set(leak) ⇒ Object

Turn libvips leak testing on and off. Handy for debugging ruby-vips, not very useful for user code.



532
533
534
# File 'lib/vips.rb', line 532

def self.leak_set leak
  vips_leak_set((leak ? 1 : 0))
end

.set_debug(debug) ⇒ Object

Deprecated compatibility function.

Don't use this, instead change GLib::logger.level.



575
576
577
578
579
# File 'lib/vips.rb', line 575

def self.set_debug debug
  if debug
    GLib::logger.level = Logger::DEBUG
  end
end

.vector_set(enabled) ⇒ Object

Enable or disable SIMD and the run-time compiler. This can give a nice speed-up, but can also be unstable on some systems or with some versions of the run-time compiler.



568
569
570
# File 'lib/vips.rb', line 568

def self.vector_set enabled
  vips_vector_set_enabled(enabled ? 1 : 0)
end