22  TIFF (.tif, .tiff) Images

Author

Data Curation Team

Published

December 18, 2025

22.1 Overview // Aperçu

The Tag Image File Format (TIFF) is the standard for scientific imaging such as microscopy, astronomy, and geospatial sciences. Unlike standard photos, TIFFs can hold high bit-depths and multi-page stacks.

//

Le format TIFF (Tag Image File Format) est la norme en matière d’imagerie scientifique, notamment en microscopie, en astronomie et dans les sciences géospatiales. Contrairement aux photos classiques, les fichiers TIFF peuvent prendre en charge des profondeurs de bits élevées et des séries d’images multipages.

NoteCuration Goal // Objectif de la curation

Validate scientific integrity and technical metadata. Detect hidden lossy compression (e.g., JPEG inside TIFF), inventory multi-page stacks, and extract embedded metadata (ImageJ tags) that are crucial for pixel-based analysis. // Vérifier l’intégrité scientifique et les métadonnées techniques. Détecter les compressions avec perte cachées (par exemple, un fichier JPEG à l’intérieur d’un fichier TIFF), recenser les piles de pages multiples et extraire les métadonnées intégrées (balises ImageJ) qui sont essentielles à l’analyse au niveau des pixels.

WarningIdentifying Risks // Identification des risques

Hidden lossy compression and the loss of proprietary metadata during format conversion are primary risks. Scientific TIFFs often appear black or empty in standard viewers, leading to accidental deletion or incorrect processing if not properly inspected. // La compression avec perte cachée et la perte de métadonnées propriétaires lors de la conversion de format constituent les principaux risques. Les fichiers TIFF scientifiques apparaissent souvent noirs ou vides dans les visionneuses standard, ce qui peut entraîner leur suppression accidentelle ou un traitement incorrect s’ils ne sont pas correctement vérifiés.

This notebook employs a multi-strategy approach to inspection:

  1. LibTIFF Extraction: Querying raw IFD headers for standard tags like compression.
  2. ImageMagick Fallback: Inferring properties for non-standard headers.
  3. Text Mining: Scanning generic “Description” fields for hidden scientific metadata.

//

Ce cahier adopte une approche multi-stratégies pour l’inspection :

  1. Extraction LibTIFF : analyse des en-têtes IFD bruts à la recherche de balises standard telles que celles relatives à la compression.
  2. Solution de secours ImageMagick : déduction des propriétés pour les en-têtes non standard.
  3. Exploration de texte : analyse des champs « Description » génériques à la recherche de métadonnées scientifiques cachées.

22.2 Setup // Configuration

We use the ijtiff package, which is designed for scientific image handling and correctly processes multi-page and high-bit-depth files.

//

Nous utilisons le package ijtiff, conçu pour le traitement d’images scientifiques et capable de traiter correctement les fichiers multipages et ceux à grande profondeur de bits.

22.2.1 R Packages // Packages R

The following R packages are required. If you don’t have these packages, uncomment this code and run it once in your R console:

//

Les packages R suivants sont requis. Si vous ne disposez pas de ces packages, décommentez ce code et exécutez-le une fois dans votre console R :

Code
# install.packages(c("tidyverse", "tiff", "magick", "stringr", "rstudioapi"))

22.2.2 Load libraries // Charger les bibliothèques

Code
library(tidyverse)
library(tiff)       # Raw LibTIFF
library(magick)     # ImageMagick
library(stringr)    # Text Mining
library(rstudioapi) # UI interaction

22.3 Select a target directory // Sélectionnez un répertoire de destination

This block allows for interactive selection of the image directory. If running in a non-interactive environment, it defaults to the path defined in the YAML header.

//

Ce bloc permet de sélectionner de manière interactive le répertoire contenant les images. En cas d’exécution dans un environnement non interactif, le chemin par défaut est celui défini dans l’en-tête YAML.

Code
if (interactive() && .Platform$OS.type == "windows") {
  selected_dir <- rstudioapi::selectDirectory(caption = "Select TIFF Directory")
} else {
  selected_dir <- NULL
}

if (!is.null(selected_dir)) {
  target_dir <- selected_dir
} else {
  target_dir <- params$target_dir
}

print(paste("Analyzing directory:", target_dir))
[1] "Analyzing directory: data/Inspect_tiff/"

22.4 Inventory of TIFF files // Liste des fichiers TIFF

We scan for .tif and .tiff files.

//

Nous recherchons les fichiers .tif et .tiff.

Code
tiff_files <- list.files(
  path = target_dir,
  pattern = "\\.tiff?$", 
  recursive = TRUE, 
  full.names = TRUE, 
  ignore.case = TRUE
)

print(paste("Found", length(tiff_files), "TIFF files."))
[1] "Found 6 TIFF files."

22.5 Deep Header Inspection // Inspection approfondie de l’en-tête

This function scans all file attributes for keywords (e.g., “depth”, “compression”) to find metadata even if it uses non-standard tag names.

//

Cette fonction analyse tous les attributs des fichiers à la recherche de mots-clés (par exemple, « depth », « compression ») afin de trouver des métadonnées, même si celles-ci utilisent des noms de balises non standard.

Code
message("Generating Deep Inspection Report...")

inspect_tiff_fuzzy <- function(fp) {
  fname <- basename(fp)
  
  tryCatch({
    # Read Image Header
    img <- image_read(fp)
    info <- image_info(img)
    attrs <- image_attributes(img)
    
    # Fuzzy Extraction: Bit Depth
    # Find ANY attribute containing "depth" or "bits" (case insensitive)
    depth_check <- attrs %>% 
      filter(grepl("depth|bits", property, ignore.case = TRUE)) %>% 
      pull(value)
    
    # Logic: Use found attribute, or fallback to info$depth, or "Unknown"
    final_depth <- if(length(depth_check) > 0) {
      paste(unique(depth_check), collapse = "/") 
    } else if ("depth" %in% names(info)) {
      as.character(info$depth[1])
    } else {
      "Unknown"
    }

    # Fuzzy Extraction: Compression
    # Find ANY attribute containing "compression"
    comp_check <- attrs %>% 
      filter(grepl("compression", property, ignore.case = TRUE)) %>% 
      pull(value)
      
    final_comp <- if(length(comp_check) > 0) comp_check[1] else "Unknown"

    # Fuzzy Extraction: Resolution
    res_check <- attrs %>% 
      filter(grepl("resolution|density", property, ignore.case = TRUE)) %>% 
      pull(value)
      
    final_res <- if(length(res_check) > 0) paste(res_check[1], "DPI") else paste(info$density[1], "DPI")

    # Build Row
    tibble(
      FileName = fname,
      Dimensions = paste(info$width[1], "x", info$height[1]),
      BitDepth = final_depth,
      Compression = final_comp,
      ColorSpace = info$colorspace[1],
      Resolution = final_res,
      FileSize_MB = round(file.size(fp) / 1024^2, 2),
      Status = "Success"
    )
    
  }, error = function(e) {
    # Error Handling
    message(paste("Failed on:", fname, "-", e$message))
    tibble(
      FileName = fname, Dimensions = NA, BitDepth = NA, Compression = NA, 
      ColorSpace = NA, Resolution = NA, FileSize_MB = NA,
      Status = paste("Error:", e$message)
    )
  })
}

# Execute Analysis
report <- map_dfr(tiff_files, inspect_tiff_fuzzy)

# Display
print("--- Deep Inspection Preview ---")
[1] "--- Deep Inspection Preview ---"
Code
print(head(report))
# A tibble: 6 × 8
  FileName     Dimensions BitDepth Compression ColorSpace Resolution FileSize_MB
  <chr>        <chr>      <chr>    <chr>       <chr>      <chr>            <dbl>
1 KL27_14D_KO… 1936 x 14… Unknown  Unknown     Gray       2x2 DPI           3.59
2 Td014_7D_MC… 2048 x 20… Unknown  Unknown     Gray       1x1 DPI           4   
3 Td014_7D_MC… 2048 x 20… Unknown  Unknown     Gray       26x26 DPI         4   
4 Td62_7D_MCA… 2048 x 20… Unknown  Unknown     Gray       6x6 DPI           4   
5 Td63_7D_MCA… 2048 x 20… Unknown  Unknown     Gray       6x6 DPI           4   
6 Td78_3D_MCA… 2048 x 20… Unknown  Unknown     Gray       6x6 DPI           4   
# ℹ 1 more variable: Status <chr>

22.6 Save Results // Enregistrer les résultats

Code
output_dir <- "Results/Inspect_tiff"
dir.create(output_dir, recursive = TRUE, showWarnings = FALSE)

output_file <- file.path(output_dir, paste0("TIFF_DeepScan_", format(Sys.Date(), "%Y%m%d"), ".csv"))

write.csv(report, output_file, row.names = FALSE)
print(paste("Report saved to:", output_file))
[1] "Report saved to: Results/Inspect_tiff/TIFF_DeepScan_20260703.csv"

22.7 Curation Insights // Aperçu de curation

Use this report to verify file integrity:

  • Bit Depth (16 or 32): These are true scientific images. Do not convert them to JPG or PNG, as those formats are typically limited to 8-bit and will discard data precision. If BitDepth remains “Unknown”, the file likely lacks a proper header.

  • Compression: Recommended (Uncompressed, LZW, Deflate, Packbits); Not recommended (JPEG, OldJPEG). If you see JPEG compression inside a TIFF, the data integrity for pixel-based analysis is compromised.

  • Color Space (Gray vs sRGB): Scientific microscopy data is typically Gray (intensity only). If a raw microscope file is sRGB, it may have been converted or taken from a screenshot, potentially losing its dynamic range.

//

Utilisez ce rapport pour vérifier l’intégrité des données :

  • Profondeur de bits (16 ou 32) : Il s’agit de véritables images scientifiques. Ne les convertissez pas au format JPG ou PNG, car ces formats sont généralement limités à 8 bits et entraîneraient une perte de précision des données. Si la profondeur de bits reste « Inconnue », le fichier ne dispose probablement pas d’un en-tête correct.

  • Compression : Recommandée (Non compressé, LZW, Deflate, Packbits) ; Non recommandée (JPEG, OldJPEG). Si vous constatez une compression JPEG dans un fichier TIFF, l’intégrité des données pour l’analyse au niveau des pixels est compromise.

  • Espace colorimétrique (Gris vs sRGB) : Les données de microscopie scientifique sont généralement en gris (intensité uniquement). Si un fichier brut de microscope est en sRGB, il a peut-être été converti ou provient d’une capture d’écran, ce qui peut entraîner une perte de sa plage dynamique.

22.8 Additional Tools // Outils supplémentaires

  • ImageJ / Fiji: Standard open-source software for viewing and analyzing scientific TIFFs (e.g. microscopy, astronomy). It handles multi-page stacks and high-bit-depth data that confuse standard viewers (Schindelin et al. 2012).

  • Bio-Formats: A library specifically designed to read proprietary life sciences image formats (e.g., .czi, .lif) and convert them to standard OME-TIFF (see the Bio-Formats documentation).

  • ExifTool: A robust command-line tool for reading and writing embedded metadata (EXIF, IPTC, XMP) in images (see the documentation).

//

  • ImageJ / Fiji : logiciel libre standard destiné à la visualisation et à l’analyse de fichiers TIFF scientifiques (par exemple, en microscopie ou en astronomie). Il prend en charge les piles d’images multipages et les données à grande profondeur de bits qui posent problème aux visionneuses standard (Schindelin et al. 2012).

  • Bio-Formats : Une bibliothèque spécialement conçue pour lire les formats d’image propriétaires des sciences de la vie (par exemple, .czi, .lif) et les convertir au format OME-TIFF standard (voir la documentation Bio-Formats.

  • ExifTool : Un outil en ligne de commande robuste permettant de lire et d’écrire des métadonnées intégrées (EXIF, IPTC, XMP) dans les images (voir la [documentation](https://exiftool.org/exiftool_pod.html

22.9 Using the Non-Interactive R Script // Utilisation du script R non interactif

For users who want to run this analysis on a server, in a batch job, or from the command line, here is a pure R script that performs the same process.

Download the R Script: Inspect_tiff_Script.R

//

Pour les utilisateurs qui souhaitent exécuter cette analyse sur un serveur, dans le cadre d’un traitement par lots ou depuis la ligne de commande, voici un script R pur qui effectue le même processus.

Télécharger le script R : Inspect_tiff_Script.R

22.9.1 Example HPC Submission Script // Exemple de script de soumission HPC

Inspect_tiff_submit.sh

#!/bin/bash
#SBATCH --job-name=tiff_check
#SBATCH --nodes=1
#SBATCH --ntasks=1
#SBATCH --time=00:30:00
#SBATCH --mem=16G
#SBATCH --output=logs/tiff_check_%j.log

# Load R Module
# Note: ImageMagick is often a system library, but some clusters require a module.
# Check with 'module avail imagemagick' if the script fails.
module load R
module load imagemagick/7.1  # Adjust version as needed for your cluster

# Define Target Directory
# Replace with the actual path to your microscopy/image data
DATA_DIR="/scratch/user/project_data/microscopy"

# Prepare Environment
mkdir -p Results/Inspect_tiff
mkdir -p logs

# Run Analysis
echo "Starting TIFF Deep Scan on $DATA_DIR"
Rscript Inspect_tiff_Script.R "$DATA_DIR"