0
votes

I'm working with some biomedical images with more than 3 channels. The images are rather large so I'd prefer to store them as pyramidal TIFF files with JPEG compression. This gives an order of magnitude compression ratio compared to other compression schemes (eg. LZW, Deflate).

I'm currently using pyvips's tiffsave function to save these images. With other compression schemes, I can save images with arbitrary number of channels. However, with JPEG compression, I've realized that the images saved can only have 1 or 3 channels.

If JPEG's algorithm can compress 1 channel, surely there must be ways to pack multiple JPEG compressed single channel images into one file?

Does anyone know a way to store n > 3 channel images into a single file with the following criteria?

  • JPEG compression (or an algorithm with comparable performance)
  • Easily viewable with GUI image viewing software (such as ImageJ)
  • I really don't want to store each channel as a separate file.
  • Images stored in pyramidal format
  • Python 3
1

1 Answers

2
votes

libvips 8.10 is out now and supports generation of OME-TIFF pyramids.

These TIFFs store each channel in a different page in the file with each page having its own pyramid held in a TIFF SUBIFD. I expect ImageJ can read these, though you might perhaps need a plugin.

You can generate them in pyvips like this:

#!/usr/bin/python3

import sys
import pyvips

def load(filename):
    return pyvips.Image.new_from_file(filename)

# load a couple of eg. three-band images to make a 6-band image
image = load(sys.argv[2]).bandjoin(load(sys.argv[3]))

# to convert to OME, we need a tall, thin mono image with page-height set to
# indicate where the joins are
ome = pyvips.Image.arrayjoin(image.bandsplit(), across=1)

# you must make a private copy before modifying image metadata
ome = ome.copy()
ome.set_type(pyvips.GValue.gint_type, 'page-height', image.height)

# now we can write as a pyramid
# libvips 8.10+ will put the pyramid layers into SUBIFDs
ome.tiffsave(sys.argv[1], pyramid=True, compression="jpeg", Q=90)

Run like this:

$ ./gen_ome.py x.tif ~/pics/1.tiff ~/pics/2.tiff 
$ tiffinfo x.tif 
TIFF Directory at offset 0x173f02 (1523458)
  Subfile Type: multi-page document (2 = 0x2)
  Image Width: 6048 Image Length: 4032
  Tile Width: 128 Tile Length: 128
  Resolution: 300, 300 pixels/inch
  Bits/Sample: 8
  Sample Format: unsigned integer
  Compression Scheme: JPEG
  Photometric Interpretation: min-is-black
  Orientation: row 0 top, col 0 lhs
  Samples/Pixel: 1
  Planar Configuration: single image plane
  Page Number: 0-6
  SubIFD Offsets: 2153288 2339670 2396406 2415400 2422262 2424960
  JPEG Tables: (73 bytes)
TIFF Directory at offset 0x3c3b2c (3947308)
  Subfile Type: multi-page document (2 = 0x2)
  Image Width: 6048 Image Length: 4032
  Tile Width: 128 Tile Length: 128
...

So you can see there are six pages for the six channels, and each page has a set of SUBIFDs containing the pyramid layers.