libVIPS

a demand-driven, horizontally threaded image processing library. Compared to similar libraries, libvips runs quickly and uses little memory. - libVIPS / github / blog / wikipedia

Compiling

sudo apt install build-essential ninja-build

It straitghforward through meson. But it requires additional libraries to support features:

  • libspng (recommended) - faster png operation
    • otherwise use libpng
  • mozjpeg (recommended) - compatible with the libjpeg API and ABI. It is intended to be a drop-in replacement for libjpeg. MozJPEG is a patch for libjpeg-turbo.
    • libjpeg-turbo - a JPEG image codec that uses SIMD instructions to accelerate baseline JPEG compression and decompression
    • libjpeg - fallback
  • libjxl - for jpegxl
  • libwebp

  • highway - SIMD support
  • orc - Optimized Inner Loops Runtime Compiler

nip2

A user interface for libvips.

see also

Doc / c++ / SO

C++ API

The libvips C++ API is a thin layer over the libvips GObject API. It adds automatic reference counting, exceptions, operator overloads, and automatic constant expansion.

You can drop down to the C API at any point, so all the C API docs also work for C++.

VIPS images are three-dimensional arrays, the dimensions being width, height and bands

  • crop synonym for extract_area - Extract an area from an image.
  • data - Arrange for the underlying object to be entirely in memory, then return a pointer to the first pixel.

see also

Iterating over Region

An image can be very large, much larger than the available memory, so you can’t just access pixels with a pointer *.

That will only work for 8-bit images, and I’ve not tried to handle errors or int overflow correctly. With a 10k x 10k pixel RGB JPEG I see: 700ms to decompress and scan a 300mb image, with a peak memory use of 150mb.

Code...

/* compile with:
 *
 * gcc -g -Wall try350.c `pkg-config vips --cflags --libs`
 */

#include <stdio.h>
#include <vips/vips.h>

int
main(int argc, char **argv)   
{
    VipsImage *image;

    if (VIPS_INIT(argv[0]))
        vips_error_exit(NULL);

    if (!(image = vips_image_new_from_file(argv[1], "access", VIPS_ACCESS_SEQUENTIAL, NULL)))
        vips_error_exit(NULL);

    VipsRegion *region = vips_region_new(image);
    int sum = 0;
    for (int y = 0; y < image->Ysize; y++) {
        if (vips_region_prepare(region, &(VipsRect) { 0, y, image->Xsize, 1 }))
            vips_error_exit(NULL);

        unsigned char *p = VIPS_REGION_ADDR(region, 0, y);
        int size = VIPS_REGION_SIZEOF_LINE(region);
        for (int x = 0; x < size; x++) 
            sum += p[x];
    }

    printf("sum = %d\n", sum);

    g_object_unref(region);
    g_object_unref(image);

    return 0;
}

see also

Colors

Image shrinking

Image arithmetic

Perform an arithmetic operation, such as addition, on every pixel in an image or a pair of images.

  • vips_project - the sum of every row of pixels, and the sum of every column of pixels.

Defining Matrix

int akernel[] = { -1, -1, -1,
                -1, 16, -1,
                -1, -1, -1 };

VImage kernel = VImage::new_from_memory( akernel, sizeof(akernel), 
                                        3, 3, 1, VIPS_FORMAT_INT);

VImage conv = in.colourspace(VIPS_INTERPRETATION_sRGB, 
                                VImage::option()
                                ->set ("source_space", VIPS_INTERPRETATION_B_W)
                                )
                    .conv( kernel );

Internals

pyvips

import pyvips

image = pyvips.Image.new_from_file('some-image.jpg', access='sequential')
image *= [1, 2, 1]
mask = pyvips.Image.new_from_array([[-1, -1, -1],
                                    [-1, 16, -1],
                                    [-1, -1, -1]
                                   ], scale=8)
image = image.conv(mask, precision='integer')
image.write_to_file('x.jpg')

Ruby

Full bindings are available for

Iterate over pixel

You can’t really iterate over pixels in libvips, since images don’t really exist. Everything is a delayed computation and pixels only exist on demand. You can either implement a new vips operation, or render the whole image to an area of memory and then treat it like any other array. Have a look at vips_image_write_to_memory().

For eg:

From version 8.4

uint64_t dhash( VImage hash ) {
    VImage cache = VImage::new_memory();
    hash[0].write( cache);
    auto* p = (uint8_t*) cache.data();

	uint64_t hash_value = 0;

    for (int j = 0; j < h; j++) {
        for (int i = 0; i < w; i++) {
            hash_value <<= 1;
            hash_value |= *p++ > 0 ? 1 : 0; 
        }
    }

    return hash_value;
}

before

uint64_t dhash( VImage hash ) {
    auto w = hash.width();
    auto h = hash.height();
    cout << "w: " << w << " h: " << h << "band: " << hash.bands() << "\n";

    size_t size;
    auto* p = (uint8_t*) hash[0].write_to_memory( &size);
    uint64_t hash_value = 0;
      for (int j = 0; j < h; j++) {
        for (int i = 0; i < w; i++) {
            hash_value <<= 1;
            //hash_value |= hash(i, j)[0] > 0 ? 1 : 0;    // 1001101100111001101011010110000010011000011000110000111
            hash_value |= *p++ > 0 ? 1 : 0;               // 1001101100111001101011010110000010011000011000110000111
        }
    }
    return hash_value;
}

Note: it is much faster to do that than accessing pixels through vips_getpoint()

VIPS History

background on its 30 years of development

Written on January 14, 2022, Last update on September 15, 2024
image-lib color jpeg jxl ruby c++ opencv transform