Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added fixtures/other/grayscale_no_samplesperpixel.tif
Binary file not shown.
8 changes: 8 additions & 0 deletions fixtures/other/readme.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
`grayscale_no_samplesperpixel.tif` is a minimal (114-byte) little-endian classic
TIFF: a 2x2 uint8 grayscale image, uncompressed, single strip. It deliberately
**omits the `SamplesPerPixel` tag (277)**, which the TIFF 6.0 specification
defines with a default of 1. It is a regression fixture for parsing files that
omit the tag (e.g. PerkinElmer/Phenix microscopy images), which previously
panicked with "samples_per_pixel not found". It was generated by hand-writing
the TIFF header, IFD and pixel bytes with Python's `struct` module.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we:

  1. Put this image fixture in https://github.com/developmentseed/geotiff-test-data instead of here
  2. Make the image creation reproducible. You say you hand-edited the TIFF header, and it would be nice to have a record on how that was done. You can see in the existing rasterio_generated directory how there's a Python script that creates the file.


`geogtowgs_subset_USGS_13_s14w171.tif` was created from "s3://prd-tnm/StagedProducts/Elevation/13/TIFF/current/s14w171/USGS_13_s14w171.tif" using these commands:

```bash
Expand Down
5 changes: 4 additions & 1 deletion src/ifd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,10 @@ impl ImageFileDirectory {
geo_key_directory = Some(GeoKeyDirectory::from_tags(tags)?);
}

let samples_per_pixel = samples_per_pixel.expect("samples_per_pixel not found");
// SamplesPerPixel (tag 277) defaults to 1 when the tag is absent.
// TIFF 6.0 spec, "SamplesPerPixel ... Default = 1": https://download.osgeo.org/libtiff/doc/TIFF6.pdf
// Many single-channel grayscale TIFFs (e.g. PerkinElmer/Phenix microscopy) omit it.
Comment on lines +375 to +377

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is verbose; just say

Suggested change
// SamplesPerPixel (tag 277) defaults to 1 when the tag is absent.
// TIFF 6.0 spec, "SamplesPerPixel ... Default = 1": https://download.osgeo.org/libtiff/doc/TIFF6.pdf
// Many single-channel grayscale TIFFs (e.g. PerkinElmer/Phenix microscopy) omit it.
// Samples per pixel defaults to 1
// https://web.archive.org/web/20240329145232/https://www.awaresystems.be/imaging/tiff/tifftags/samplesperpixel.html

let samples_per_pixel = samples_per_pixel.unwrap_or(1);
let planar_configuration = if let Some(planar_configuration) = planar_configuration {
planar_configuration
} else if samples_per_pixel == 1 {
Expand Down
23 changes: 23 additions & 0 deletions src/test/missing_samples_per_pixel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use crate::test::util::open_tiff;
use crate::tags::PhotometricInterpretation;

/// Regression test for TIFFs that omit the `SamplesPerPixel` tag (277).
///
/// The TIFF 6.0 specification defines a default of 1 for `SamplesPerPixel`, and
/// some single-channel grayscale TIFFs (e.g. PerkinElmer/Phenix microscopy
/// images) omit the tag entirely. Parsing such files used to panic with
/// "samples_per_pixel not found"; it must instead fall back to the spec default.
#[tokio::test]
async fn test_missing_samples_per_pixel_defaults_to_one() {
let filename = "other/grayscale_no_samplesperpixel.tif";
let (_reader, tiff) = open_tiff(filename).await;
let ifd = &tiff.ifds()[0];

assert_eq!(ifd.samples_per_pixel(), 1);
assert_eq!(ifd.image_width(), 2);
assert_eq!(ifd.image_height(), 2);
assert_eq!(
ifd.photometric_interpretation(),
PhotometricInterpretation::BlackIsZero
);
}
1 change: 1 addition & 0 deletions src/test/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod geo;
mod geotiff_test_data;
mod image_tiff;
mod missing_samples_per_pixel;
mod ome_tiff;
pub(crate) mod util;
Loading