0

I use libvips to convert HEIC images to a more tractable format and pipe the result to another process without writing to disk. I can do that using PNG as an intermediate format:

vips copy input.heic .png

However the next process in my chain only accepts either BMP images or raw RGB data. If I replace .png with .bmp in the above command I get this error:

input.heic: bad seek to 1811903
VipsForeignSave: ".bmp" is not a known target format

This happens with many other formats including the native .vips. The conversion works fine with all formats if I write to disk instead of to stdout.

It will help being able to convert either to BMP or to a list of integers with the RGB information.

Alreiber
  • 19
  • 4

2 Answers2

2

You can see the set of supported formats with vips -l. For 8.10, it's:

$ vips -l | grep _target
          VipsForeignSaveCsvTarget (csvsave_target), save image to csv (.csv), priority=0, mono
          VipsForeignSaveMatrixTarget (matrixsave_target), save image to matrix (.mat), priority=0, mono
          VipsForeignSavePpmTarget (ppmsave_target), save to ppm (.ppm, .pgm, .pbm, .pfm), priority=0, rgb
          VipsForeignSaveRadTarget (radsave_target), save image to Radiance target (.hdr), priority=0, rgb
          VipsForeignSavePngTarget (pngsave_target), save image to target as PNG (.png), priority=0, rgba
          VipsForeignSaveJpegTarget (jpegsave_target), save image to jpeg target (.jpg, .jpeg, .jpe), priority=0, rgb-cmyk
          VipsForeignSaveWebpTarget (webpsave_target), save image to webp target (.webp), priority=0, rgba-only
          VipsForeignSaveHeifTarget (heifsave_target), save image in HEIF format (.heic, .heif, .avif), priority=0, rgba-only

.v and .raw might be added in 8.11. .bmp is written by imagemagick rather than libvips and may not make it.

Another alternative is to use something like the Python interface, pyvips, rather than the CLI. For example:

import os
import pyvips

image = pyvips.Image.black(10, 10)
memory = image.write_to_memory()
os.write(1, memory)

Will write the raw bytes (100 zeros in this case) to stdout in binary mode.

To use BMP instead you can write:

memory = image.magicksave_buffer(format="BMP")
jcupitt
  • 10,213
  • 2
  • 23
  • 39
  • That Python interface is interesting, I will see if calling a `.py` does not slow down my chain. I was not aware of that subset of export formats for stdout. My intuition expected the same formats to be able to write to stdout and disk. Out of curiosity, is there any reason for this differentiation? – Alreiber Nov 27 '20 at 18:01
  • The write-to-filedescriptor system was only added last year and not everything has been revised to support it yet. pyvips can be quite a bit faster than the CLI since there's no need for any temporary files -- libvips is a streaming image processing library, so if you do a sequence of operations in one process they all execute at once, in parallel and with no intermediates. It's a bit like connecting processes with pipes, except it can do things like coordinate transforms too. – jcupitt Nov 27 '20 at 18:59
  • Some notes about the write-to-filedescriptor system: https://libvips.github.io/libvips/2019/11/29/True-streaming-for-libvips.html – jcupitt Nov 27 '20 at 19:00
  • Very educational reading! The ultimate target of my work is a HEIC importing function for Matlab. This is my current process: 1) Matlab calls java to execute `vips copy *.heic .ppm` (I found a direct call to vips from Matlab to take ages reading stdout), 2) another java call reads the stdout buffer in parallel and brings the data back to Matlab, 3) the PPM format is parsed with Matlab code and the RGB data is reshaped into the matrix format that Matlab understands. Do you think Python could speed up this process? – Alreiber Nov 28 '20 at 00:01
  • Some additional info: comparing the performance of the above process against Matlab's built-in image reader for popular image formats resulted in my `libvips` wrapper slower by a factor of x1.5~x2.5. The only exception is `.bmp` where the built-in reader is 7 times faster (uh!) than my wrapper. Maybe my approach is not the best... – Alreiber Nov 28 '20 at 00:09
  • You should be able to call directly into libvips.dll from matlab and get a big array of pixel values (pyvips works like this). I've not actually done it myself, mind. https://uk.mathworks.com/help/matlab/call-c-library-functions.html – jcupitt Nov 28 '20 at 08:02
  • I tried and I am afraid I have been unable to call the .dll from Matlab. Could not get past some preprocessor errors locating other .h files. – Alreiber Nov 29 '20 at 13:07
  • You'll need to set the preprocessor include path to point to the directory you unzipped the dll to. I don't know how matlab handles this, unfortunately. – jcupitt Nov 30 '20 at 00:10
1

Not sure if you are looking for a work-around, or hoping for a software update from John for libvips, or what exactly.

Anyway, I just wanted to say that if you want a work-around to convert vips output to BMP, you could use ppmtobmp which is part of the NetPBM suite.

So, to a file that would be:

vips copy image.heic .ppm | ppmtobmp - > result.bmp

and as a stream filter, without going to disk:

vips copy image.jpg .ppm | ppmtobmp | NextProcess

Note that ppm format is actually RGB with 3-4 lines of ASCII header at the start, which contains the dimensions - try it and see. So, if you can find a way in Windows to delete 3-4 lines of ASCII you can get RGB. Or if your image is 640x480 pixels at 3 bytes/pixel maybe you can find a way on Windows to get the last (640x480x3) bytes of a file, or stream and discard the PPM header that way.

Keywords: HEIC, vips, NetPBM, BMP

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • Ah I always forget about `ppmtobmp`, very neat. – jcupitt Nov 27 '20 at 17:49
  • These two workarounds are what I was looking for. I have slightly modified my code to manipulate the `ppm` and extract the RGB out of it, and it works wonders. I didn't know `ppm` was such an easy format to interpret. And writing `ppm` with `libvips` is damn fast too! – Alreiber Nov 27 '20 at 18:05