There’s a new full libvips binding for .NET. It has a test-suite which passes with no memory leaks, it’s in NuGet, so it’s easy to install on Linux, macOS and Windows, and it has nice documentation:

https://kleisauke.github.io/net-vips

The README in the repository for the binding has more details, including some install notes and an example:

https://github.com/kleisauke/net-vips

But briefly, just get the libvips shared library on your system and enter:

Install-Package NetVips

Here’s an example program:

using NetVips;

// fast thumbnail generator
var image = Image.Thumbnail("somefile.jpg", 128);
image.WriteToFile("tiny.jpg");

// make a new image with some text rendered on it
image = Image.Text("Hello <i>World!</i>", dpi: 300);

// call a method
image = image.Invert();

// join images bandwise
image = image.Bandjoin(image);

// add a constant
image = image + 12;

// add a different value to each band
image = image + new[] {1, 2, 3};

// add two images
image = image + image;

// split bands up again
var images = image.Bandsplit();
var b1 = images[0];
var b2 = images[1];
var b3 = images[2];

// read a pixel from coordinate (10, 20)
var pixel = image.Getpoint(10, 20);
var r = pixel[0];
var g = pixel[1];
var b = pixel[2];

// make all pixels less than 128 bright blue
//    (image < 128) makes an 8-bit image where each band is 255 (true) if that 
//       value is less than 128, and 0 (false) if it's >= 128 ... you can use
//       images or new[] {1, 2, 3} constants as well as simple values
//    .BandAnd() joins all image bands together with bitwise AND, so you get a
//        one-band image which is true where all bands are true
//    condition.Ifthenelse(then, else) takes a condition image and uses true or
//        false values to pick pixels from the then or else images ... then and
//        else can be constants or images
image = (image < 128).BandAnd().Ifthenelse(new[] {0, 0, 255}, image);

// go to Yxy colourspace
image = image.Colourspace("yxy");

// pass options to a save operation
image.WriteToFile("x.png", new VOption
{
    {"compression", 9}
});

The repo includes benchmarks which test the performance against Magick.NET, the ImageMagick binding for .NET. On that test and on my pc, NetVips is 8x faster than Magick.NET.

How it works

This binding uses the P/Invoke (Platform Invocation Services) system in .NET to call directly into the libvips DLL. Once inside, it uses GObject and libvips’ introspection facilities to link operations on C# objects to operations in the libvips library.

All libvips operations were generated automatically to a PascalCase method in NetVips. For example, consider the C# code:

image = image.HoughCircle();

Which does a Hough transform. By taking advantage of nullable types (which allows you to omit any parameters in any position), we are able to call libvips operations that have optional arguments. See for example:

public Image HoughCircle(int? scale = null, int? minRadius = null, int? maxRadius = null)
{
    // Call hough_circle with the corresponding options.
}

When HoughCircle is called it jumps into libvips and searches for an operation called hough_circle (the non PascalCase variant). It then examines the operation and discovers what arguments it needs, what type they are, and what options the operation supports (no required args, but quite a few options, in this example).

It then walks the arguments that were supplied, setting what it can, and invokes the operation. On return, it extracts the results and repackages them for C#.

This dynamic approach via P/Invoke has several nice properties:

However, there is also a downside (due to limitation of C#):