Skip to content

Image Orientation Preflight

openmed.multimodal.orientation_preflight.check_image_orientation() compares the numeric EXIF/TIFF Orientation values of a stored image with the transform an image manifest declares. A disagreement between the two silently moves region coordinates to the wrong place after a rotation or mirror, so the check runs before any pixels are decoded.

The check reads no pixels and no metadata strings. It accepts integers, an optional declared transform, and optional dimensions, and returns a report with categorical and numeric fields only.

Transforms

ImageTransform(rotation_degrees, mirrored) describes how to display stored pixels upright: mirror horizontally first when mirrored is true, then rotate clockwise by 0, 90, 180, or 270 degrees. These eight combinations match the eight EXIF orientations one to one.

EXIF orientation Rotation Mirrored Width and height swap
1 0 no no
2 0 yes no
3 180 no no
4 180 yes no
5 270 yes yes
6 90 no yes
7 90 yes yes
8 270 no yes

ImageTransform.from_exif_orientation(6) returns the transform for a value, and transform.exif_orientation maps back.

Example

from openmed.multimodal.orientation_preflight import (
    ImageTransform,
    OrientationStatus,
    check_image_orientation,
)

report = check_image_orientation(
    [6],
    declared_transform=ImageTransform(rotation_degrees=90),
    stored_size=(4000, 3000),
    declared_size=(3000, 4000),
)
assert report.status is OrientationStatus.ALIGNED

report = check_image_orientation([6])
assert report.status is OrientationStatus.TRANSFORM_REQUIRED
assert report.reason_codes == ("transform_required",)

Statuses and reason codes

The status is the most severe verdict implied by the reason codes: aligned < transform_required < ambiguous < invalid. Reason codes are reported in this fixed order:

Reason code Status Meaning
orientation_value_limit invalid More than 8 orientation values were supplied.
orientation_value_invalid invalid A value is outside EXIF orientations 1 to 8.
dimensions_invalid invalid A width or height is not between 1 and 1,000,000.
orientation_conflict ambiguous Metadata sources report different orientations.
transform_conflict ambiguous The declared transform contradicts the orientation.
transform_without_orientation ambiguous A rotation or mirror was declared, but no orientation is recorded.
dimensions_inconsistent ambiguous Declared upright dimensions do not match the effective transform.
transform_not_applied transform_required The manifest declares no change, but the orientation needs one.
transform_required transform_required Nothing was declared and the orientation needs a transform.
orientation_duplicate aligned Several sources agree on the same orientation.
orientation_missing aligned No orientation is recorded, so the image is treated as upright.

When dimensions are supplied, the declared transform (or, if none, the transform the orientation requires) determines the expected upright oriented_width and oriented_height in the report.

Wrong argument types, such as strings, booleans, floats, or lists where a (width, height) tuple is expected, raise OrientationPreflightError. Its category is a stable code and the message never repeats the submitted value.

Serialization

report.to_dict() keeps a fixed field order, and report.to_json() returns compact JSON with sorted keys and the schema identifier openmed.multimodal.orientation_preflight.v1.

Out of scope

The preflight does not rotate or mirror pixels, run OCR, detect landmarks, or interpret images clinically.