TIFF File Management and OME Files

Authors
Dr. Sangeetha Nandakumar | Dr. Nicholas Del Grosso

Setup

Import Libraries

import numpy as np
import matplotlib.pyplot as plt
from tifffile import TiffFile, imread, imwrite
import json
from pprint import pprint
import xml.dom.minidom

Download Data

Run the below cells to download datasets for the first two sections of the notebook

This might take a while. In the meantime, feel free to read a bit about the datasets. We won’t do any analysis here.

  1. data_endoscope.tif 1-photon microendoscopic data from mouse dorsal striatum Reference .
  2. Sue_2x_3000_40_-46.tif (taken from CaImAn) dataset by Sue Koay and David Tank. 2-photon data from supragranular parietal cortex mouse during a virtual reality task.

TIFF files are widely used in scientific imaging due to their ability to store multiple image frames and rich metadata in a single container. In this notebook we explore how to read, inspect, and write TIFF files using Python. We begin by reading multi-page TIFF files using both imread and TiffFile, and measure performance when accessing subsets of frames. We then examine embedded metadata, learn how to extract specific fields, and compare metadata across frames. Finally, we write custom TIFF files and embed structured metadata in JSON format. This notebook builds foundational skills for working with microscopy data, especially in experiments that involve large image stacks or OME-TIFF standards.

Section 1: Reading TIFF File

TIFF, which stands for Tagged Image File Format, is a flexible and adaptable file format for storing images and data within a single file. It can be used to store multiple frames as pages.

Code Description
imread Reads TIFF files as NumPy arrays, suitable for straightforward image data.
TiffFile Provides more detailed control over reading TIFF files, including accessing metadata and handling complex file structures.
%%timeit Measures the execution time of code blocks, useful for performance optimization.
matplotlib for plotting Enables visualization of image data directly from the TIFF files, essential for data exploration and analysis.

Exercises

Example: Read data_endoscope.tif. How many frames does it have?

frames = imread('data/data_endoscope.tif')
frames.shape
(1000, 128, 128)

Exercise: Read Sue_2x_3000_40_-46.tif. How many frames does this data have?

Solution
frames = imread('data/Sue_2x_3000_40_-46.tif')
frames.shape
(3000, 170, 170)

Exercise: Read Sue_2x_3000_40_-46.tif. How long does it take?

Hint: Put %%timeit in the beginning of the cell

Solution
%%timeit
frames = imread('data/Sue_2x_3000_40_-46.tif')
156 ms ± 6.27 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Exercise: Read Sue_2x_3000_40_-46.tif. How long does it take to access the last frame?

Hint: Put %%timeit in the beginning of the cell

Solution
%%timeit
frames = imread('data/Sue_2x_3000_40_-46.tif')
frames[-1]
155 ms ± 3.65 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Example: Let’s see the plots. Plot the first frame

plt.imshow(frames[0], cmap='gray')

Exercise: Plot the last frame

Solution
plt.imshow(frames[-1], cmap='gray')

Example: Read only the first 3 frames from Sue_2x_3000_40_-46.tif

frames = imread('data/Sue_2x_3000_40_-46.tif', key=(0, 1, 2))
frames.shape
(3, 170, 170)

Exercise: Read only 1, 3, 5, 7, 9th frame from Sue_2x_3000_40_-46.tif

Solution
frames = imread('data/Sue_2x_3000_40_-46.tif', key=(0, 2, 4, 6, 8))
frames.shape
(5, 170, 170)

Exercise: Read only first 100 frames from Sue_2x_3000_40_-46.tif

Hint: use range(0, 100)

Solution
frames = imread('data/Sue_2x_3000_40_-46.tif', key=range(0, 100))
frames.shape
(100, 170, 170)

Example: Read data_endoscope.tif with TiffFile?

f = TiffFile('data/data_endoscope.tif')
frames = np.array([page.asarray() for page in f.pages])

Exercise: Read Sue_2x_3000_40_-46.tif with TiffFile

Solution
f = TiffFile('data/Sue_2x_3000_40_-46.tif')
frames = np.array([page.asarray() for page in f.pages])

Exercise: How long does it take to read Sue_2x_3000_40_-46.tif with TiffFile?

Solution
%%timeit
f = TiffFile('data/Sue_2x_3000_40_-46.tif')
frames = np.array([page.asarray() for page in f.pages])
1.04 s ± 164 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Exercise: How long does it take to access only the last frame with TiffFile?

Solution
%%timeit
f = TiffFile("data/data_endoscope.tif") 
f.pages[-1].asarray()
6.66 ms ± 65.8 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Section 2: Metadata handling and TIFF file writing

This section focuses on metadata handling and writing TIFF files with custom metadata, for managing and documenting image datasets in research. The methods demonstrated include extracting metadata from TIFF files, writing TIFF files with specified photometric interpretations, and embedding custom JSON-formatted metadata into TIFF files.

Code Description
TiffFile() Opens a TIFF file for reading, providing access to image data and metadata.
.pages[N].tags Accesses the tags (metadata) of the Nth page in the TIFF file.
imwrite() Writes image data to a TIFF file, with options for specifying photometric interpretation and embedding custom metadata.
json.dumps() Converts a Python dictionary into a JSON-formatted string suitable for embedding as metadata.

Exercises

Example: Read and print basic information about data_endoscope.tif

f = TiffFile('data/data_endoscope.tif')
print(f)
TiffFile 'data_endoscope.tif'  31.68 MiB  1000 Pages  uniform

Exercise: Read and print basic information about Sue_2x_3000_40_-46.tif

Solution
f = TiffFile('data/Sue_2x_3000_40_-46.tif')
print(f)
TiffFile 'Sue_2x_3000_40_-46.tif'  331.13 MiB  3000 Pages  shaped|uniform

Example: Print detailed information of first page of data_endoscope.tif

f = TiffFile('data/data_endoscope.tif')
print(f.pages[0].tags)
TiffTag 256 ImageWidth @33024 SHORT @33032 = 128
TiffTag 257 ImageLength @33036 SHORT @33044 = 128
TiffTag 258 BitsPerSample @33048 SHORT @33056 = 16
TiffTag 259 Compression @33060 SHORT @33068 = PACKBITS
TiffTag 262 PhotometricInterpretation @33072 SHORT @33080 = MINISBLACK
TiffTag 273 StripOffsets @33084 LONG[2] @33220 = (8, 16509)
TiffTag 274 Orientation @33096 SHORT @33104 = TOPLEFT
TiffTag 277 SamplesPerPixel @33108 SHORT @33116 = 1
TiffTag 278 RowsPerStrip @33120 SHORT @33128 = 64
TiffTag 279 StripByteCounts @33132 LONG[2] @33212 = (16501, 16512)
TiffTag 282 XResolution @33144 RATIONAL @33196 = (72, 1)
TiffTag 283 YResolution @33156 RATIONAL @33204 = (72, 1)
TiffTag 284 PlanarConfiguration @33168 SHORT @33176 = CONTIG
TiffTag 296 ResolutionUnit @33180 SHORT @33188 = INCH

Exercise: Print detailed information of 500th page of data_endoscope.tif. How is it same or different from that of the first page?

Solution
f = TiffFile('data/data_endoscope.tif')
print(f.pages[499].tags)
TiffTag 256 ImageWidth @16609952 SHORT @16609960 = 128
TiffTag 257 ImageLength @16609964 SHORT @16609972 = 128
TiffTag 258 BitsPerSample @16609976 SHORT @16609984 = 16
TiffTag 259 Compression @16609988 SHORT @16609996 = PACKBITS
TiffTag 262 PhotometricInterpretation @16610000 SHORT @16610008 = MINISBLACK
TiffTag 273 StripOffsets @16610012 LONG[2] @16610148 = (16576940, 16593437)
TiffTag 274 Orientation @16610024 SHORT @16610032 = TOPLEFT
TiffTag 277 SamplesPerPixel @16610036 SHORT @16610044 = 1
TiffTag 278 RowsPerStrip @16610048 SHORT @16610056 = 64
TiffTag 279 StripByteCounts @16610060 LONG[2] @16610140 = (16497, 16512)
TiffTag 282 XResolution @16610072 RATIONAL @16610124 = (72, 1)
TiffTag 283 YResolution @16610084 RATIONAL @16610132 = (72, 1)
TiffTag 284 PlanarConfiguration @16610096 SHORT @16610104 = CONTIG
TiffTag 296 ResolutionUnit @16610108 SHORT @16610116 = INCH

Exercise: Print detailed information of 1000th page of data_endoscope.tif. How is it same or different from that of the other two pages?

Solution
f = TiffFile('data/data_endoscope.tif')
print(f.pages[999].tags)
TiffTag 256 ImageWidth @33220494 SHORT @33220502 = 128
TiffTag 257 ImageLength @33220506 SHORT @33220514 = 128
TiffTag 258 BitsPerSample @33220518 SHORT @33220526 = 16
TiffTag 259 Compression @33220530 SHORT @33220538 = PACKBITS
TiffTag 262 PhotometricInterpretation @33220542 SHORT @33220550 = MINISBLACK
TiffTag 273 StripOffsets @33220554 LONG[2] @33220690 = (33187470, 33203979)
TiffTag 274 Orientation @33220566 SHORT @33220574 = TOPLEFT
TiffTag 277 SamplesPerPixel @33220578 SHORT @33220586 = 1
TiffTag 278 RowsPerStrip @33220590 SHORT @33220598 = 64
TiffTag 279 StripByteCounts @33220602 LONG[2] @33220682 = (16509, 16512)
TiffTag 282 XResolution @33220614 RATIONAL @33220666 = (72, 1)
TiffTag 283 YResolution @33220626 RATIONAL @33220674 = (72, 1)
TiffTag 284 PlanarConfiguration @33220638 SHORT @33220646 = CONTIG
TiffTag 296 ResolutionUnit @33220650 SHORT @33220658 = INCH

Example: Extract the value of ‘ImageWidth’ on 1000th page of data_endoscope

f = TiffFile('data/data_endoscope.tif')
print(f.pages[999].tags['ImageWidth'].value)
128

Exercise: Extract the value of ‘PhotometricInterpretation’ on 1000th page of data_endoscope

Solution
f = TiffFile('data/data_endoscope.tif')
print(f.pages[999].tags['PhotometricInterpretation'].value)
1

Exercise: Extract the value of ‘ResolutionUnit’ on 1000th page of data_endoscope

Solution
f = TiffFile('data/data_endoscope.tif')
print(f.pages[999].tags['ResolutionUnit'].value)
2