Jekyll2026-02-25T12:30:00+00:00https://www.libvips.org/feed.xmllibvipsA fast image processing library with low memory needs.Patterns everywhere in nip42026-02-02T00:00:00+00:002026-02-02T00:00:00+00:00https://www.libvips.org/2026/02/02/nip4-pattern-matchingJanuary’s work on nip4 is out: v9.0.16 has more useful improvements to the programming language, with a few more minor changes.

This marks the end of work on nip4’s language. Next month will focus on the menu redesign.

For some background, there’s an older introduction to nip4’s programming language post.

More difficult row drag

Left-drag on the workspace background to scroll is very handy, but left-drag also moved rows around. I found myself often dragging rows accidentally while navigating large workspaces, and then putting the rows back in the right place was annoying.

You now have to hold down CTRL to drag rows around. Hopefully this will stop accidental row moves!

Patterns everywhere!

I’ve rewritten lambda expressions so they now support patterns and deconstruction. For example:

main = map (\[x, y] x + y) (zip2 [1..10] [11..20]);

So the lambda (the backslash character) expects a two element list with elements named as x and y and adds them together. The zip2 makes a list like [[1, 11], [2, 12], ..], so therefore main will have the value [12, 14, 16, ..].

This means that (finally!) you can use patterns and argument deconstruction everywhere. For example, you could write that lambda expression as:

main = [x + y :: [x, y] <- zip2 [1..10] [11..20]];

I’ve also rewritten list comprehensions so they have much better scoping behaviour.

The compiler is now lazy – everything is parsed during load, but code generation only happens when functions are evaluated. This improves startup time.

It’s now snip

And finally the programming language is officially renamed as snip, and the old nip4-batch program is now installed as snip. You can use it to write scripts with a shebang, perhaps:

#!/usr/bin/env snip

main = [x + y :: [x, y] <- zip2 [1..10] [11..20]];

You can run this and see:

$ ./try.def 
[12, 14, 16, 18, 20, 22, 24, 26, 28, 30]
$ 

… though that’s not quite correct, for now you need a print in there too, but this will be going away in the next version.

]]>
What’s new in libvips 8.182025-12-04T00:00:00+00:002025-12-04T00:00:00+00:00https://www.libvips.org/2025/12/04/What's-new-in-8.18Here’s a summary of what’s new in libvips 8.18. Check the ChangeLog if you need more details.

The headline features are support for UltraHDR, camera RAW images, and Oklab colourspace.

UltraHDR support

UltraHDR is a way of embedding a gainmap plus some extra metadata inside an ordinary SDR image. An SDR display can just show the regular SDR image, but an HDR display can extract the gainmap and use it to reconstruct a full HDR image. Having a single image file that can display well on both SDR and HDR devices is very valuable.

libvips 8.18 uses Google’s libultrahdr for UltraHDR load and save. The current version of this library only supports UltraHDR JPEG images; the next version is expected to add support for a wider range of image formats.

There are two main paths for UltraHDR images in libvips: as an SDR image with a separate gainmap, and as a full HDR image. The separate gainmap path is relatively fast but you will sometimes need to update the gainmap during processing. The full HDR path does not require gainmap updates, but can be slower, and will usually lose the original image’s tone mapping.

A new chapter in the libvips documentation introduces this feature and explains how to use it. As an example, you can use vipsthumbnail to resize UltraHDR images. This command:

$ vipsthumbnail ultra-hdr.jpg --size 1024

Makes this image:

ultrahdr thumbnail

If you view that image on an HDR display and with a web browser that supports UltraHDR images, the rocket exhaust should look very bright. It should also look nicely tonemapped on an SDR display.

We’d like to thank nlnet for their generous support while developing this feature.

Camera RAW support

Thanks to @lxsameer, libvips 8.18 now uses libraw to add support for most camera RAW formats. The new vips_dcrawload() operator will be used to automatically import images, for example:

$ vipsthumbnail IMG_3260.CR2 --size 1024

Makes this image

dcrawload thumbnail

Most time is spent in dcraw, so performance isn’t that much better than the previous solution with imagemagick:

$ /usr/bin/time -f %M:%e convert IMG_3260.CR2 -resize 500x x.jpg
442076:1.56
$ /usr/bin/time -f %M:%e vipsthumbnail IMG_3260.CR2 --size 500
359336:1.12

But it’s convenient to have it all in one thing.

Support for Oklab colourspace

Oklab and Oklch are new colourspaces that are more linear than CIELAB ‘76, faster to compute, and support HDR imaging. They have been added to CSS4 and are now implemented by all major web browsers.

libvips 8.18 supports them like any other colourspace, so you can use Oklab coordinates in image processing. For example, you could render a watermark in an Oklab colour like this:

#!/usr/bin/env python3

import sys
import pyvips

im = pyvips.Image.new_from_file(sys.argv[1], access="sequential")

# make the watermark
text = pyvips.Image \
    .text(sys.argv[3], width=500, dpi=600, align="centre") \
    .rotate(45)
colour = text \
    .cast("float") \
    .new_from_image([float(value) for value in sys.argv[4:]]) \
    .copy(interpretation="oklab") \
    .colourspace("srgb")

# use the text as the alpha, scaled down to make it semi-transparent
text = colour \
        .bandjoin((text * 0.8).cast("uchar")) \
        .copy_memory()

# replicate many times to cover the image
overlay = text \
    .replicate(1 + im.width / text.width, 1 + im.height / text.height) \
    .crop(0, 0, im.width, im.height)

# composite on top of the image
im = im.composite(overlay, "over")

im.write_to_file(sys.argv[2])

I can run it like this:

$ ./watermark-oklab.py ~/pics/theo.jpg x.jpg "in the beginning was the word" 0.7 0.2 -0.2

To generate:

watermark

A watermarked image, with the watermark colour specified in Oklab coordinates.

Improvements to the libvips core

The libvips core has seen some useful improvements, mostly driven by interactive use:

  • The mmap window size hadn’t been reviewed for a long time, and I think had been previously set after benchmarking on a 32-bit machine with limited VMEM. For 64-bit machines this is now much larger, improving random access speeds for many file formats.

  • vips_system() has a new "cache" argument which adds the command to the libvips operation cache. This makes nip4 much, much faster at issuing ImageMagick commands.

  • A new system for forcing the exit of worker threads makes threadpoool shutdown dramatically faster, greatly improving interactive performance.

  • Tiled image formats now set metadata to hint their cache size to downstream operations. This can help prevent retiling, again improving interactive performance.

  • vipsthumbnail has a new "path" argument. This gives you much more flexibility in how the output filename is constructed. The old -o option is still supported, but is deprecated.

Better file format support

As well as the RAW support above, the other file format operations have seen some improvements:

  • vips_pdfload() has a new "page-box" argument which lets you control which of the various media boxes you’d like to load.

  • vips_jxlload() has a new "bitdepth" argument which sets the depth at which the image should be loaded.

  • vips_webpsave() has a new "exact" argument which forces the RGB in RGBA to always be saved, even if the pixel is transparent. This can be important if you are using WebP to store data.

  • vips_heifsave() has a new "tune" parameter that lets you pass detailed options to the encoder. Handy for tuning output.

]]>
Multiple definitions in nip42025-11-22T00:00:00+00:002025-11-22T00:00:00+00:00https://www.libvips.org/2025/11/22/nip4-multiple-definitionsThere’s a new nip4 release, v9.0.15, with some useful improvements to nip4’s programming language. I thought I’d write a summary of this new feature.

For some background, there’s an older introduction to nip4 post.

nip4’s programming language

nip4 is an image processing spreadsheet and, like all spreadsheets, you can type equations into cells.

The equations you can type are in fact a tiny pure functional programming language, and this language is used to implement all of nip4’s menus and workspace widgets. You can use it to add things to nip4 yourself.

One way to experiment with it is with the Workspace definitions pane. If you right-click on the workspace background and pick Workspace definitions from the menu, a pane will slide in from the right:

nip4

This pane holds definitions local to this workspace tab. They are saved in the workspace file, so they are a great way to add small extra things that are useful for whatever you are working on.

Try entering:

fred = 99;

Pressing the ▶ (play) button will make nip4 parse and compile your definition. Back in the workspace, try entering fred at the bottom of column A. You should see:

nip4

If you go back to the Workspace definitions pane, edit it to fred = "banana";, and press ▶ again, A1 will update. Definitions are all live and if you change one, everything that uses it will also update.

If you right-click on the workspace background and select Edit toolkits you’ll get something more like a tiny development environment for the language, but Workspace definitions is handier for little experiments.

Multiple definitions

The big new feature in this new version is multiple definitions. You can define a function many times and during execution the first one which matches the arguments becomes the result of the function.

For example, you could write this to define a factorial function:

factorial 1 = 1;
factorial n = n * factorial (n - 1);

Now factorial 6 in a column will evaluate to 720.

This is not a great way to write a factorial function! But it does show the syntax. Something like:

factorial n = product [1..n];

would be better.

Deconstruction

A parallel new feature is function argument pattern matching and deconstruction.

Function arguments don’t have to be simple variables — they can be complex structure declarations. For example:

sum [] = 0;
sum a:x = a + sum x;

Square brackets mean lists and the colon (:) operator is infix CONS. The first definition of sum will match if the argument is the empty list, and the second for non-empty lists (lists which can be deconstructed with CONS), with a being bound to the head of the list and x to the tail.

Back in the workspace, sum [1..10] should be 55. The [1..10] is a list generator, it evaluates to [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].

This is a pretty bad way to define sum, a better solution is:

sum = foldr add 0

Where foldr is the standard right-associative list fold operator. You can define it very conveniently as:

foldr fn st [] = st;
foldr fn st x:xs = fn x (foldr fn st xs);

Other pattern matching features

Patterns can be simple constants, complex numbers, list constants, : (cons), classes and argument names. You can nest these in any way you like, so:

conj (x, y) = (x, -y);

Finds the complex conjugate, or:

banana [a, (x, y), c] = "a three element list, with a complex number as the middle element";

Or:

test (Image x) = "it's an image!";

This matches any x which is an instance of the Image class.

Next

The next step is to update the nip4 menus, hopefully making them more consistent and easier to use. We’ll see!

]]>
What’s new in libvips 8.172025-06-05T00:00:00+00:002025-06-05T00:00:00+00:00https://www.libvips.org/2025/06/05/What's-new-in-8.17Here’s a summary of what’s new in libvips 8.17. Check the ChangeLog if you need more details.

New documentation system

We were using gtk-doc for our documentation system. We’ve switched to its newer replacement, gi-docgen, and it should be a lot better.

docs

The most interesting improvements are:

  • A much better search system – try typing in the search box at the top left!

  • Better and more consistent display of optional arguments, see for example embed.

  • The class overview page includes a useful one-line description of each operator.

  • Revised and restructured Additional documentation.

  • Support for light and dark appearance.

  • More consistent markup should make it easier to automatically generate documentation for downstream projects.

Improvements to operators

To help compatibility, the old vips7 matrix multiply function is now available as a vips8 operator, matrixmultiply.

We rewrote the old vips7 remosaic function for vips8 years ago, but stupidly forgot to link it. We’ve fixed this, and remosaic is now proudly available. Similarly, quadratic has been there for years, but never worked properly. It’s been revised and should now (finally) be useful.

The so-called magic kernel is now supported for image resize.

ICC import and transform now has an auto option for rendering intent.

Better file format support

File format support has been improved (again). Highlights this time are:

  • gifsave has a new keep_duplicate_frames option

  • svgload has a new stylesheet option for custom CSS, and a high_bitdepth option for scRGB output.

  • heifload has a new unlimited flag to remove all load limits, has better alpha channel detection, and better detection of truncated files.

  • jp2kload has a new oneshot flag which can improve compatibility for older jp2k files.

  • jxlsave has much lower memory use for large images.

  • openslideload now shares and reuses connections to the underlying file format library, improving speed.

  • ppmload has a new buffer variant for convenience.

  • TIFF load and save has better error handling

  • A new “convert for save” system fixes a large number of minor bugs and inconsistencies in file format support.

General improvements

There have been some smaller libvips additions and improvements too.

  • The hough_line operator has better scaling to Hough space.

  • shrink is noticeably faster

  • The operation cache is a lot more reliable.

  • sink_screen has better scheduling and should render thumbnails much more quickly.

  • The ICC operators are better at detecting and rejecting corrupt profiles.

Plus some even more minor bugfixes and improvements.

]]>
Introduction to nip42025-03-20T00:00:00+00:002025-03-20T00:00:00+00:00https://www.libvips.org/2025/03/20/introduction-to-nip4nip4 is now mostly done so this post tries to introduce this strange tool for people who’ve not come across it before.

If you used nip2, there’s a nip4 for nip2 users post which runs though the main differences.

I’ll write a “nip4 for nerds” post next week introducing nip4’s programming language.

Background

nip4 is a free spreadsheet-like GUI for the libvips image processing library with binaries for Linux, Windows and Mac. You can use it to build image processing pipelines and then execute them on large datasets. These pipelines can get quite complex — I’ve made systems with over 10,000 operations chained together.

nip4

This workspace analyses pulmonary PET-CT scans. It fits a two compartment model to each lung voxel to estimate a rate constant for FDG uptake (a proxy for inflammation).

Here’s the workspace developed in the Charisma project.

nip4

This workspace standardises and automates technical imaging in museums. You can load visible, infrared, ultraviolet, UV-induced visible luminescence and visible-induced IR luminescence images, and it computes a set of derivatives. This screenshot shows calibrated visible and calibrated UV-induced visible luminescence with stray-light removal and Kubelka–Munk modelling of scatter.

Why is it useful?

Because the underlying image processing engine is libvips, it’s fast and does not need a lot of memory. The Charisma workspace above, for example, loads in about 5s on this PC, manipulates almost 100GB of images, but runs in under 1GB of RAM:

nip4

Everything is live. You can open an image view window on any of the cells in the workspace and watch pixels change as you edit the formula.

The whole systems is lazy and demand-driven. You can load enormous images (many 100s of gigabytes) and manipulate them interactively, since only the pixels needed to update the screen actually get processed. When you do a final save operation, it will take a while, of course.

nip4 comes with a separate program called snip. This is a batch-mode processor that can load nip4 workspace files and execute them on a set of inputs. You can use to apply a workspace you’ve developed in nip4 to a big collection of images.

The main window

When you start nip4 it looks something like this. I’ve loaded a test image (drag one in, use the folder button at the top left, or start nip4 from the command-line with nip4 my-great-image.jpg):

nip4

A is the current column, A1 is a row for the image you loaded, this is all in tab1. The thing down the left is the set of loaded toolkits.

The toolkit menu contains around 300 useful operations, and you can make more yourself. You can click to move in and out of toolkits, or you can click in the magnifying glass at the top and search for tools by keyword.

If you select Filter > Photographic Negative you’ll see:

nip4

Most tools take one argument, and they are applied to the bottom row in the current column. If you want to apply a tool to a row other than the bottom one, select it first by clicking on the row label.

If you open up A2 by clicking on the V down button next to the label, you’ll see the cell formula:

nip4

So the function Filter_negative_item.action has been applied to the row A1. You can edit the formula — click on it, enter 255 - A1, and you should see:

nip4

Which computes almost the same result.

Rows can contain many types of object. Click at the top of the toolkit bar to get back to the start position, then click Widgets > Scale to add a scale widget called A3 to the workspace. Now edit the formula to be A3 - A1 and try dragging the slider.

It looks a bit awkward with the result row A2 positioned before the scale. You can reorder columns by holding down Ctrl and dragging on the row label.

nip4

nip4 does not have an undo operation, instead it has fast and easy duplicate, merge and delete. If you make a copy of column A before you start changing it, you can’t lose any of your current work.

Duplicate column A by right-clicking on the column title and selecting Duplicate from the menu. Right-click on B3 and select Delete, so you have:

nip4

Now in the text box at the bottom of column B, enter this formula for Solarization:

if B1 < B2 then 255 * B1 / B2 else 255 * (255 - B1) / (255 - B2)

And try dragging scale B2. Hopefully you’ll see a solarized image. A version of this operation is also in the standard toolkits, next to Photographic Negative.

If you double-click on an image thumbnail, you’ll open an image view window. These are all live, so as you drag scales, they’ll all update. You can zoom in and watch the values of individual pixels change as you edit formula.

nip4

The main window has some other useful features. You can pan large workspaces by dragging on the background, the burger menu at the top-right has some useful options, there’s a context menu on the workspace tab, and another one on the workspace background.

The burger menu includes Recover After Crash, which lets you get any workspace back if something bad happens (I hope it doesn’t).

The image view window

The nip4 image view window has a lot of useful shortcuts.

  • Cursor keys to scroll around
  • Cursor keys plus shift to move by a screen size
  • Cursor keys plus Ctrl to move to image edges
  • Number keys to pick a particular magnification
  • Ctrl + number keys to pick a particular zoom out
  • 0 for best fit
  • 1 for 100%
  • d, to toggle debug rendering mode
  • i, + / o, - to zoom in and out
  • Ctrl-< / Ctrl-> for prev page, next page
  • Alt-Left / Alt-Right for prev image, next image
  • Mouse drag to pan
  • Mousewheel to zoom
  • Mousewheel + Shift/Ctrl to pan
  • Ctrl-O replace image
  • Ctrl-S save image
  • Alt-Enter show properties
  • Ctrl-C / Ctrl-V to copy paste of filenames, lists of filenames and images
  • Drag and drop filenames, lists of filenames and images
  • F11 fullscreen

If you select View > Display Control Bar, some widgets appear at the bottom. They let you flip pages, move to animation frames, set a scale and offset for each pixel (handy for scientific images), and a burger menu gives a set of useful visualisation options such as false colour, log scale, and colour management.

nip4

You can also mark features on images. If you hold down Ctrl and drag down and right, you’ll create a rectangular region, if you drag up and left you’ll mark an arrow, if you just Ctrl-click you’ll mark a point.

If you drag down and left you’ll mark a horizontal guide, if you drag up and right you’ll mark a vertical guide. Regions, arrows and points snap to guides, so they are handy for lining up groups of annotations.

nip4

Editing complex objects

All compound row objects can be edited. Back in the main window, try pressing the down arrow next to the region a few times. You should see something like this:

nip4

You can edit any of these member values. For example, try setting top to be a fixed value, like 900. Now go back to the image window and try dragging B8 — you’ll find it can only be dragged left-right, because the top edge is now fixed in place.

You can use any formula you like. Try setting height to be the formula width * 2 ** 0.5, that is, width times root two. Now you’ll find B8 can’t be sized vertically, but if you resize horizontally, the height will also change to keep the region in the “A” paper aspect ratio.

Reset

In that last example, three conflicting things were competing to set the value of B8.

First, at the bottom of B8, you’ll see the original equation, created when you initially dragged out the shape:

Region B7 1534 964 2880 2283

Next, as you moved the region with the mouse, the left, top, width and height members were updated. Finally, you edited top by hand to fix a value in place.

nip4 decides which of these ultimately sets the value of a row with two rules:

  1. Graphical edits (eg. dragging a region, or moving a scale) override formula.

  2. Inner edits override outer edits.

You can reset all changes to a row by right-clicking on a row and picking Reset from the menu. It will revert to the state of the top-most formula.

Overloading

Most operations will work on rows of any type and will produce a result of the expected type.

For example, if you make a new column, select Widgets > Scale twice to make two scale widgets, then select Math > Arithmetic > Add (or enter C1 + C2 in the formula box), you’ll get a third scale. Try dragging either of the top two scales and the third will update.

If you open up the new scale widget, you’ll see that from and to have been set appropriately for the possible range:

nip4

Just as with regions, you can also set the formula for any of these members.

Other features

Each tab can have a set of private definitions in nip4’s programming language. Right-click on the workspace background and select Workspace Definitions.

All the tools and toolkits are implemented in this language too. Right-click on the workspace background and select Edit Toolkits to change them. You can write your own tools and toolkits.

You can select many rows and group them. When you perform an operation on a group, it’ll automatically operate on every row in the group.

Once you’ve set up a pipeline, you can open the image view window of the first image and use the < and > buttons in the titlebar to step though all the other images in the same directory.

]]>
nip4 for nip2 users2025-03-12T00:00:00+00:002025-03-12T00:00:00+00:00https://www.libvips.org/2025/03/12/nip4-for-nip2-usersThe test release of nip4 is now out and it seems to work OK on Linux, Windows and macOS. I thought I’d write a few notes for users of nip2 outlining the big changes and the bits of it that are still missing.

If you’ve not used nip2, there’s another post with a general introduction to nip4.

Background

nip2 was mostly finished around 2000, with the final big feature going in in 2005 (I think). It still works fine, but it was becoming difficult to maintain since the major libraries it depends on (libvips-7 for image processing, gtk-2 for the user interface widgets, and goffice-0.8 for plotting) have long fallen out of maintenance.

It was also starting to look rather crude and old-fashioned! Something of a trip back in time, with brightly coloured elements and hard edges and corners everywhere. And nip2 is a “kitchen sink” program — it has lots of features which seemed like a good idea at the time, but which I never ended up using much.

nip2

nip4 is a rewrite of nip2 with the following aims:

  • can load and process all nip2 workspaces

  • add some missing language features

  • use the modern gtk-4 user-interface toolkit

  • a cleaner, simpler interface

  • use libvips-8 for image processing

  • use kplot for graphing

  • simple build and distribution process for Linux, macOS and Windows

And here is nip4, running the same very complex workspace:

nip4

New image window

One of the biggest changes is a completely new image view window. It understands most pyramidal image formats, it does smooth zooming, it runs on your GPU, it handles alpha, it supports animation and multipage images, and it should be a lot quicker.

The big changes from a user-interface point of view are:

  • Drag with the left mouse button to pan
  • Scrollwheel to zoom
  • Right-click (or “burger menu” in the top right) for the main menu, then View / Properties or Alt-Enter to view image metadata
  • Save as … to save as some file, with a dialog to set file format properties
  • View / Display control bar to get he scale and offset sliders, plus a useful menu of visualisation tools
  • The display control bar has a thing for the display mode for animated and multipage images – you can pause animation, view the pages or frames as a strip, and join separate-plane images into colour images
  • Ctrl-. and Ctrl-, for next page and previous page in multipage images, or to step though frames in an animated image
  • You can drag and drop or copy-paste images into and out of the image view window, so you can do Print Screen, then ^V to paste a desktop snapshot, for example, or crop an image the ^C and ^V into another image editor
  • Use the < and > buttons in the titlebar to step through the images in the same directory as this image

Most other things are the same, so number keys for zoom levels, i and o for zoom in and out, Ctrl-drag to mark regions, cursor keys to pan, and so on.

There are two new region create gestures: hold down Ctrl and drag down and left to great a horizontal guide, and drag up and right to make a vertical guide. Regions, arrows and points snap to guides, so they are handy for lining up groups of annotations.

Toolkit bar

The big change in the main window is the new toolkit bar down the left, replacing the old Toolkits menu.

nip4

It slides left and right as you select items or go back, and it stays open after clicking on a tool, so you can use several related tools together, which can be convenient.

It also supports keyword search. Click on the magnifying glass at the top-left, then type something related to what you want. Entering “lab”, for example, will show all the tools related to CIELAB colourspace, for example:

nip4

Searching is fuzzy, so you don’t need to be exact.

Workspace drag animations

The main workspace view has fancy animations for column and row dragging, which can help make it easier to see what’s going on.

You now need to hold down Ctrl before dragging a row, to make it harder to make accidental changes. You can now (finally!) drag rows between columns.

You can also left-drag on the workspace background to pan, phew.

No object edit dialogs

In nip2 there was a right-click menu with an Edit option on most objects. You couldf select this to edit basic object properties.

This is gone in nip4, you’re supposed to change properties by opening the row a few times and editing the members:

nip4

Recover after crash

nip4 has a much better recover after crash system. Select Recover after crash … from the main burger menu and you see a table of recent workspace auto-backups, sorted by the process ID and time. Doubleclick one of them to restore it.

nip4

New graphing window

The graphing window has been rewritten and now uses kplot.

nip4

It no longer supports bar plots, but everything else works, and it’s very fast.

Multiple select on rows

You can now range-select rows, then right-click on a row name and act on them all. This is useful for deleting or duplicating sets of rows.

Definitions

If you right-click on the workspace background, the workspace menu includes Workspace definitions and Edit toolkits ….

The toolkit editor is a bit basic, but does work.

What’s missing

The paintbox is probably the biggest missing feature, this might come back.

The github repository has a large TODO file with notes on bugs, ideas and other missing features.

]]>
nip4 January progress2025-01-31T00:00:00+00:002025-01-31T00:00:00+00:00https://www.libvips.org/2025/01/31/nip4-January-progressnip4 is pretty much done! It now loads the two largest workspaces I have correctly. I’ll do a bit more polishing and aim for an alpha release with pre-compiled binaries for flatpak and Windows by the end of February.

Here’s the per-voxel Patlak workspace I made for analyzing pulmonary FDG-PET scans:

FDG-PET workspace

The About nip4 menu item shows some stats:

Workspace stats

So this workspace has:

  • 8,400 rows
  • 15,000 images
  • over 100 GB of image data
  • 8,300 active operations
  • looking at top, it’s running in about 2.5 GB of ram

Here’s the Charisma workspace:

Charisma workspace

This thing is for museum imaging: you give it images taken under visible, infrared, and UV light with various filters and it aligns them and computes a range of useful derivatives, such as Kubelka–Munk-modelled UV-induced visible fluorescence with stray visible light removal.

Looking at About nip4 for this one, it’s only 300 rows, but has 14,000 images and over 120 GB of image data. It runs in about 1.2 GB of ram, according to top.

Other additions

I’ve implemented some other features:

Recover after crash

nip4 saves your work after every change, and keeps the last 10 saves from each workspace. These save files are normally deleted automatically on exit, but if there’s a crash (sadly inevitable in alpha software) they’ll still be there when it restarts. Click on Recover after crash and you get this window:

Recover after crash

You can see the saves from each nip4 workspace and select one to recover it. The Delete all backups button wipes the temp area if it’s getting too big.

Drag-drop and copy-paste

You can now drag-drop and copy-paste images and workspaces from your desktop and file manager. This is very handy for things like screen snapshots. You can even set nip4 as the image handler for your desktop (!!!).

Workspace definitions

Each tab in each workspace can have private definitions. Right click on the workspace background and select Workspace definitions and a panel opens on the right with all the local definitions. You can edit them and press the Play button to process your changes and update everything.

Workspace definitions

Edit toolkits

Right-click on the workspace background, pick Edit toolkits and you get the programming window:

Programming window

You can edit and compile any of the built-in toolkits, though it’s a bit barebones for now.

Stop offscreen image renders

nip4 now tracks which thumbnails are visible and starts and stops rendering as you move around a workspace. This saves a lot of memory and CPU!

]]>
nip4 November progress2024-11-26T00:00:00+00:002024-11-26T00:00:00+00:00https://www.libvips.org/2024/11/26/nip4-November-progressI’ve done another month on nip4 – plotting is in now, and I’ve done a first version of the new toolkit browser.

We were using goffice to render plots, but sadly goffice is still gtk3, with no progress towards a port. I did a bit of hacking and got it working with gtk4, but I don’t think that’s a very maintainable way forward. Instead, I’ve switched to kplot:

https://github.com/kristapsdz/kplot

This is a very simple library, but it can do everything nip4 needs, more or less. I made a fork and added better axis labeling and picking.

I’ve also mostly finished the new toolkit browser. I found the nip2 Toolkit menu difficult to use, so it’s now a scrolling bar down the left of the window. It also supports searching, so you can find things by name as well as by category.

There are some obvious missing features, and it’s still a bit crashy, but it’s now just about possible to use nip4 for work. I’ll do a bit more polishing, then try to make a first alpha release.

]]>
nip4 progress2024-10-19T00:00:00+00:002024-10-19T00:00:00+00:00https://www.libvips.org/2024/10/19/nip4-Progresslibvips.org OpenCollective has generously given me a year of funding to complete nip4. This is an update of the libvips GUI, nip2, to the gtk4 toolkit, and to the vips8 API.

nip2 was written between about 1997 and 2003 and depends on a lot of frameworks from the period. These are becoming extremely elderly now, and nip2 is getting difficult to maintain. It’s also looking increasingly old-fashioned.

Screenshot

nip4 is hoping to fix this. It has the following goals:

  1. Update from the gtk2 GUI toolkit to gtk4, the current version. This is a lot of work – the drawing model is completely different, and there are many, many changes in best practice. As a test, I updated vipsdisp to gtk4. The image display widget in this viewer is the thing that will display images in nip4.

  2. Switch to the vips8 API. We rewrote libvips to make vips8 between about 2010 and 2015, but nip2 was obviously stuck on the old vips7 API. nip4 has removed all the old vips7 code and redesigned for vips8. nip2 was the last large-scale user of vips7, as far as I know, so completing nip4 will let us build libvips with deprecated code removed by default.

  3. Complete backwards compatibility. nip4 aims to be able to run the whole of the nip2 test suite unmodified. 

  4. Prettier, simpler, faster, modernised.

nip4 is now loading the first 5 test nip2 workspaces correctly. The big change from the UI point of view is full animation for interactions. Everything slides around as you work:

Next steps:

  1. Get all the old nip2 test workspaces loading. This will involve implementing a few more workspace widgets. Plotting is probably the most work.

  2. Finish drag/drop and copy/paste. It’s only half-there right now.

  3. Add the toolkit menu and browser. This will probably be a bar down the left.

  4. Perhaps implement a cut down and polished version of the programming and debugging interface from nip2.

  5. UI for per-workspace definitions needs to go in.

  6. The image processing menus are all currently designed around vips7. A new set of vips8 menus could be a useful simplification.

  7. Do windows and mac builds. We have win and mac builds for vipsdisp done already.

And I think that would be enough for a release, hopefully in the first half of 2025.

]]>
What’s new in libvips 8.162024-10-10T00:00:00+00:002024-10-10T00:00:00+00:00https://www.libvips.org/2024/10/10/What's-new-in-8.16Here’s a summary of what’s new in libvips 8.16. Check the ChangeLog if you need more details.

Signed Distance Fields

libvips has a new vips_sdf() operator. This can efficiently generate a range of basic shapes as Signed Distance Fields – these are images where each pixel contains a signed value giving the distance to the closest edge. For example:

$ vips sdf x.v 512 512 circle --r=200 --a="256 256"

Makes a 512 x 512 pixel float image of a circle with radius 200, centered on 256 x 256. As you move out and away from the edge, values become increasingly positive, as you move within the circle, values become negative.

SDF circle

The great thing about SDFs is that they are quick to make and very easy to combine to make more complex shapes. For example, you could write:

#!/usr/bin/env python3

import sys
import pyvips

box = pyvips.Image.sdf(1000, 1000, "rounded-box",
                       a=[300, 400],
                       b=[700, 600],
                       corners=[100, 0, 0, 0])

circle = pyvips.Image.sdf(1000, 1000, "circle",
                          a=[500, 300],
                          r=100)

line = pyvips.Image.sdf(1000, 1000, "line",
                        a=[500, 500],
                        b=[600, 900])

# union
sdf = box.minpair(circle).minpair(line)

# make annular
sdf = sdf.abs() - 15

# render as an antialiased image
sdf.clamp().linear(-255, 255, uchar=True).write_to_file(sys.argv[1])

To make:

SDF boat

Hmmm, possibly a person rowing a boat. This uses three other new operators: vips_minpair() and vips_maxpair(), which given a pair of images find the pixel-wise max and min, and vips_clamp(), which constrains pixels to a range.

SDFs fit really well with libvips on-demand-evaluation. These things never really exist, they are just chains of delayed computation, so you can make them any size, and compute them in parallel.

Up until now we’ve used SVG rendering to generate masks for large images. SDFs are a lot faster and need much less memory – as long as you only need simple shapes, they should be a great replacement.

Better file format support

File format support has been improved (again). Highlights this time are:

  • JXL load and save now supports exif, xmp, and animation.

  • webpsave now has target_size parameter to set desired size in bytes and a passes parameter to set number of passes to achieve desired target size, plus a smart_deblock option for better edge rendering.

  • tiffload supports old-style JPEG compression.

  • tiffsave now lets you change the deflate compression level.

  • All paletteised images now have a palette metadata item.

  • PFM load and save now uses scRGB colourspace (ie. linear 0-1).

  • rawsave gets streaming support with vips_rawsave_target() and vips_rawsave_buffer().

General improvements

There have been some smaller libvips additions and improvements too.

  • libvips used to limit image dimensions to 10 million pixels. This is now configurable, see vips_max_coord_get().

  • There’s a new (trivial) vips_addalpha() operation.

  • vips_getpoint() has a new unpack_complex option.

  • vipsheader supports multiple -f field arguments.

  • libvips now includes basic g_auto support, making C programming slightly more convenient.

Plus some even more minor bugfixes and improvements.

]]>