Skip to content

Apply TIFF Orientation tag (274) on read#1521

Open
brendancol wants to merge 1 commit intoxarray-contrib:mainfrom
brendancol:fix-1503-orientation
Open

Apply TIFF Orientation tag (274) on read#1521
brendancol wants to merge 1 commit intoxarray-contrib:mainfrom
brendancol:fix-1503-orientation

Conversation

@brendancol
Copy link
Copy Markdown
Contributor

Closes #1503.

The reader was ignoring tag 274. Files written with orientation 2-8 came back as silently flipped or rotated arrays. Pixel values were correct, geometry was wrong.

What changed

  • _header.py: parse tag 274 as IFD.orientation (default 1).
  • _reader.py: _apply_orientation remaps the decoded array per the TIFF 6.0 table. For orientations 5-8 the row and column axes swap, so the GeoTransform's pixel_width and pixel_height swap too, keeping y/x coord shapes consistent with the displayed array.
  • _reader.py: a windowed read with non-default orientation raises ValueError. The semantics of "window in file pixels vs. display pixels" are ambiguous and supporting it is its own design call.

Tests

23 new tests in tests/test_orientation.py:

  • All 8 orientations parametrized against a manually-computed expected output (tifffile's imread does not itself apply the tag, so the comparison target is computed from the source array).
  • y/x coord arrays match the post-orientation shape.
  • 5-8 specifically check the dim swap.
  • A file without the tag still defaults to no transform.
  • window= + non-default orientation raises ValueError; window= + orientation 1 still works.

Test plan

  • pytest xrspatial/geotiff/tests/test_orientation.py (23 passed)
  • pytest xrspatial/geotiff/tests/ (713 passed, 4 skipped, 3 pre-existing matplotlib palette failures unrelated)

The reader was ignoring tag 274 and returning the file's stored pixel
order regardless of which corner the data was meant to start from. Files
written with orientation 2-8 came back as silently flipped or rotated
arrays.

Parse the tag in `IFD.orientation` (default 1 = top-left, no transform)
and apply the TIFF 6.0 spec table after decode in `_apply_orientation`.
Orientations 5-8 swap rows and columns, so the GeoTransform's pixel_width
and pixel_height swap too -- coords end up with the right size on each
axis after the remap.

A windowed read on a non-default orientation has ambiguous semantics
(does the window refer to file pixels or display pixels?), so the
combination raises ValueError rather than silently picking one.

Tests cover all eight orientations against a manually-computed expected
output, the dim-swap for 5-8, default-tag behaviour, and the window
combination raising. tifffile's `imread` does not itself apply the tag,
so the comparison target is computed from the source array; that's the
only reliable way to verify the spec without depending on a second
oriented decoder.
@github-actions github-actions Bot added the performance PR touches performance-sensitive code label May 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance PR touches performance-sensitive code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Orientation tag (274) is silently ignored on read

1 participant