ijtiff

Rory Nolan

2018-04-19

TL;DR

ImageJ sometimes writes channel information in TIFF files in a peculiar way, meaning that most ordinary TIFF reading softwares don’t read this channel information correctly. ijtiff knows about ImageJ’s peculiarities, so it can be relied upon to read ImageJ-written TIFF files correctly.

Introduction

The ImageJ software (https://imagej.nih.gov/ij) is a widely-used image viewing and processing software, particularly popular in microscopy and life sciences. It supports the TIFF image format (and many others). It reads TIFF files perfectly, however it can sometimes write them in a peculiar way, meaning that when other softwares try to read TIFF files written by ImageJ, mistakes can be made.

The goal of the ijtiff R package is to correctly import TIFF files that were saved from ImageJ and to write TIFF files than can be correctly read by ImageJ. It may also satisfy some non-ImageJ TIFF requirements that you might have. This is not an extension of the original tiff package; it behaves differently. Hence, if this package isn’t satisfying your TIFF needs, it’s definitely worth checking out the original tiff package.

Frames and Channels in TIFF files

The Peculiarity of ImageJ TIFF files

Note: If you don’t care about the particulars of TIFF files or how this package works on the inside, feel free to skip this subsection.

It is common to use TIFFTAG_SAMPLESPERPIXEL to record the number of channels in a TIFF image, however ImageJ sometimes leaves TIFFTAG_SAMPLESPERPIXEL with a value of 1 and instead encodes the number of channels in TIFFTAG_IMAGEDESCRIPTION which might look something like
"ImageJ=1.51 images=16 channels=2 slices=8".

A conventional TIFF reader would miss this channel information (becaus it is in an unusual place). ijtiff does not miss it. We’ll see an example below.

Note: These peculiar ImageJ-written TIFF files are still bona fide TIFF files according to the TIFF specification. They just break with common conventions of encoding channel information.

Reading ImageJ TIFF files

path_2ch_ij <- system.file("img", "Rlogo-banana-red_green.tif", 
                           package = "ijtiff")

path_2ch_ij is the path to a TIFF file which was made in ImageJ from the R logo dancing banana GIF used in the README of Jeroen Ooms’ magick package. The TIFF is a time-stack containing only the red and green channels of the first, third and fifth frames of the original GIF. Here’s the full gif:

Here are the red and green channels of the first, third and fifth frames of the TIFF:

The original tiff package

When we import it with the original tiff package:

img <- tiff::readTIFF(path_2ch_ij, all = TRUE)
#> Warning in tiff::readTIFF(path_2ch_ij, all = TRUE): TIFFReadDirectory:
#> Unknown field with tag 50838 (0xc696) encountered
#> Warning in tiff::readTIFF(path_2ch_ij, all = TRUE): TIFFReadDirectory:
#> Unknown field with tag 50839 (0xc697) encountered
str(img)  # 10 images
#> List of 6
#>  $ : num [1:155, 1:200] 1 1 1 1 1 1 1 1 1 1 ...
#>  $ : num [1:155, 1:200] 1 1 1 1 1 1 1 1 1 1 ...
#>  $ : num [1:155, 1:200] 1 1 1 1 1 1 1 1 1 1 ...
#>  $ : num [1:155, 1:200] 1 1 1 1 1 1 1 1 1 1 ...
#>  $ : num [1:155, 1:200] 1 1 1 1 1 1 1 1 1 1 ...
#>  $ : num [1:155, 1:200] 1 1 1 1 1 1 1 1 1 1 ...
img[[1]][100:110, 50:60]  # print a section of the first image in the series
#>            [,1]      [,2]      [,3]      [,4]      [,5]      [,6]
#>  [1,] 0.6627451 0.6627451 0.6549020 0.6627451 0.7058824 0.9215686
#>  [2,] 0.6745098 0.6431373 0.6745098 0.6431373 0.6431373 0.6745098
#>  [3,] 0.6549020 0.6627451 0.6431373 0.6627451 0.6627451 0.6431373
#>  [4,] 0.6431373 0.6431373 0.6627451 0.6431373 0.6627451 0.6431373
#>  [5,] 0.6745098 0.6745098 0.6431373 0.6627451 0.6431373 0.6627451
#>  [6,] 0.6745098 0.6431373 0.6431373 0.6431373 0.6549020 0.6549020
#>  [7,] 0.6549020 0.6549020 0.6431373 0.6549020 0.6549020 0.6431373
#>  [8,] 0.6431373 0.6549020 0.6431373 0.6549020 0.6431373 0.6431373
#>  [9,] 0.6431373 0.6745098 0.6431373 0.6431373 0.6431373 0.6549020
#> [10,] 0.6431373 0.6549020 0.6431373 0.6549020 0.6431373 0.6431373
#> [11,] 0.6431373 0.6431373 0.6431373 0.6431373 0.6431373 0.6431373
#>            [,7]      [,8]      [,9]     [,10]     [,11]
#>  [1,] 1.0000000 1.0000000 1.0000000 1.0000000 1.0000000
#>  [2,] 0.8705882 1.0000000 1.0000000 1.0000000 1.0000000
#>  [3,] 0.6627451 0.7803922 1.0000000 1.0000000 1.0000000
#>  [4,] 0.6627451 0.6431373 0.7058824 0.9058824 1.0000000
#>  [5,] 0.6431373 0.6627451 0.6431373 0.6431373 0.8039216
#>  [6,] 0.6549020 0.6431373 0.6431373 0.6549020 0.6431373
#>  [7,] 0.6431373 0.6549020 0.6431373 0.6431373 0.6431373
#>  [8,] 0.6549020 0.6431373 0.6549020 0.6431373 0.6431373
#>  [9,] 0.6431373 0.6431373 0.6431373 0.6431373 0.6431373
#> [10,] 0.6431373 0.6431373 0.6431373 0.6431373 0.6431373
#> [11,] 0.6549020 0.6431373 0.6431373 0.6431373 0.6431373

The ijtiff package

When we import the same image with the ijtiff package:

Note

The original tiff package reads several types of TIFFs correctly, including many that are saved from ImageJ. This is just an example of a TIFF type that it doesn’t perform so well with.

Floating point TIFFs

The original tiff package could read but not write floating point (real-numbered) TIFF files. The ijtiff package can do both. It automatically decides which type is appropriate when writing.

Advice for all ImageJ users

Base ImageJ (similar to the tiff R package) does not properly open some perfectly good TIFF files1 (including some TIFF files written by the tiff and ijtiff R packages). Instead it often gives you the error message: imagej can only open 8 and 16 bit/channel images. These images in fact can be opened in ImageJ using the wonderful BioFormats plugin. See https://imagej.net/Bio-Formats.

No support for volumetric, time based images

The package supports volumetric (\(z\)-stack) and time-based (time-stack) images, but not both volume and time simultaneously. The fourth slot in an ijtiff_img is either for \(z\) or time.

Text Images

TIFF files are limited in which numbers they can represent (they can’t go outside the 32-bit range). Real-numbered TIFFs can also lack precision, having only the precision of a 32-bit floating point number. If TIFF isn’t good enough, you can use text images. Text images are just plain text files which are tab-separated arrays of pixel values2. Hence, they are unconstrained in the precision they can offer (but are very inefficient with memory).

library(ijtiff)
img[1] <- 2 ^ 99  # too high for TIFF
write_tif(img, "img")  # errors
#> Error in write_tif(img, "img"): The maximum value in 'img' is greater than 2 ^ 32 - 1 and therefore too high to be written to a TIFF file.
write_txt_img(img, "img")  # no problem

Writing TIFF Files with ijtiff

ijtiff::write_tif() writes TIFF files in the conventional manner, with the number of channels in TIFFTAG_SAMPLESPERPIXEL. It records in TIFFTAG_SOFTWARE that the TIFF file was written with the ijtiff R package. Otherwise, no metadata is recorded.

Acknowledgement

This package uses a lot of code from the original tiff package by Simon Urbanek.

ropensci_footer


  1. I think native ImageJ only likes 1, 3 and 4-channel images and complains about the rest, but I’m not sure about this.

  2. read_txt_img() and write_txt_img() are just wrappers of readr::read_tsv() and readr::write_tsv().