aws.osml.metadata

Sensor model construction from raw image metadata. This package is the bridge between osml-imagery-io’s metadata dictionaries and the photogrammetry package’s sensor model instances. It provides the SensorModelFactory orchestrator, individual builder classes for each supported model type, and the convenience function load_sensor_model(reader) that handles the common end-to-end path from a DatasetReader to a ready-to-use SensorModel.

This package does not contain sensor model implementations (those live in photogrammetry) or any pixel-level operations (image_processing).

Dependencies

The metadata package imports from photogrammetry (SensorModel, ImageCoordinate, ChippedImageSensorModel, CompositeSensorModel) and uses the formats package parsers for SICD/SIDD XML deserialization. It uses defusedxml for safe XML parsing and optionally pyproj for CRS resolution from EPSG codes. It does not import image_processing or features.

Design

The package follows the builder pattern with a factory orchestrator:

  • SensorModelBuilder – abstract base class. Each metadata format has a concrete subclass whose build() method returns Optional[SensorModel]. Builders return None when the required metadata fields are absent or malformed; they never raise on missing data.

  • SensorModelFactory – accepts all available metadata sources (TRE dicts, DES XML strings, GeoTIFF affine transform, CRS WKT, ground control points) and tries builders in priority order: RSM > RPC > SICD/SIDD > projective > affine. When both a precision model (RSM/RPC/SICD) and an approximate model (projective/affine) are available, the factory wraps them in a CompositeSensorModel. If the image is a chip (ICHIPB TRE present), the factory wraps the result in a ChippedImageSensorModel.

  • load_sensor_model(reader) – top-level convenience that extracts metadata from an osml-imagery-io DatasetReader, normalizes format-specific metadata layouts, and delegates to SensorModelFactory. This is the primary public entry point for most callers.

Metadata is represented as flat Python dicts keyed by TRE name (NITF) or numeric TIFF tag ID (GeoTIFF).

        flowchart LR
    R[DatasetReader] --> L[load_sensor_model]
    L --> F[SensorModelFactory]
    F --> B1[RSMBuilder]
    F --> B2[RPCBuilder]
    F --> B3[SICD/SIDDBuilder]
    F --> B4[ProjectiveBuilder]
    F --> B5[AffineBuilder]
    B1 & B2 & B3 & B4 & B5 --> SM[SensorModel]
    

Contributing a new builder

To support a new metadata format:

  1. Create a SensorModelBuilder subclass in a new module under src/aws/osml/metadata/.

  2. Implement build() -> Optional[SensorModel]. Return None when required fields are missing – do not raise.

  3. Keep the builder stateless after construction (all state set in __init__).

  4. Register the new builder in SensorModelFactory.build() at the appropriate priority level relative to existing builders.

  5. Export any public types from __init__.py.

Convenience Functions

aws.osml.metadata.load_sensor_model(reader, asset_key=None)

Convenience function that extracts metadata from an osml-imagery-io DatasetReader and constructs the best available SensorModel.

This function handles all format-specific metadata extraction (NITF TREs, GeoTIFF tags, DES XML segments, IGEOLO corner coordinates) and passes normalized inputs to SensorModelFactory for model construction.

Parameters:
  • reader (Any) – an osml-imagery-io DatasetReader (from IO.open)

  • asset_key (Optional[str]) – specific image asset key to use (default: first “image:” asset)

Return type:

Optional[SensorModel]

Returns:

the best available SensorModel, or None if no model can be built

aws.osml.metadata.derive_geotiff_georeference(metadata_dict)

Derive geo_transform and/or GCPs from GeoTIFF tags.

Replicates GDAL’s GetGeoTransform() and GetGCPs() logic:

  1. If ModelPixelScale (33550) is present with a single ModelTiepoint (33922), compute the 6-coefficient affine geo_transform.

  2. If ModelTransformation (34264) is present (rotated/skewed image), extract the 6 affine coefficients from the 4x4 matrix.

  3. If multiple ModelTiepoints exist without ModelPixelScale, treat them as ground control points (GDAL’s behavior for multi-tiepoint GeoTIFFs).

Parameters:

metadata_dict (Dict[str, Any]) – image metadata as a flat dict

Return type:

tuple

Returns:

tuple of (geo_transform, ground_control_points) — either or both may be None

Factory

class aws.osml.metadata.SensorModelFactory(actual_image_width, actual_image_height, tre_dicts=None, des_xml_strings=None, geo_transform=None, proj_wkt=None, ground_control_points=None, selected_sensor_model_types=None)

Bases: object

This class encapsulates the logic necessary to construct SensorModels from imagery metadata provided by osml-imagery-io. Users initialize the factory by providing whatever metadata is available and this class will decide how to create the most accurate SensorModel from the available information.

build()

Constructs the sensor model from the available information. Note that in cases where not enough information is available to provide any solution this method will return None.

Return type:

Optional[SensorModel]

Returns:

the highest quality sensor model available given the information provided

class aws.osml.metadata.SensorModelTypes(value)

Bases: Enum

This enumeration defines the various sensor model types this factory can build.

AFFINE = 'AFFINE'
PROJECTIVE = 'PROJECTIVE'
RPC = 'RPC'
RSM = 'RSM'
SICD = 'SICD'
class aws.osml.metadata.sensor_model_factory.ChippedImageInfoFacade(ichipb_dict)

Bases: object

This is a facade class that can be initialized with an ICHIPB TRE dict. It provides accessors for the values so that they can easily be used to create a ChippedImageSensorModel.

Builder Interface

class aws.osml.metadata.SensorModelBuilder

Bases: ABC

Abstract base for all classes that construct SensorModels from various types of metadata.

abstract build()

Construct a sensor model from the available information.

In cases where not enough information is available to provide any solution, this method will return None.

Return type:

Optional[SensorModel]

Returns:

the sensor model if available in the metadata provided

Builder Implementations

class aws.osml.metadata.rpc_sensor_model_builder.RPCSensorModelBuilder(tre_dicts)

Bases: SensorModelBuilder

This builder constructs sensor models for images that have RPC TREs. The inputs are TRE metadata provided as Python dicts (from osml-imagery-io’s MetadataProvider) rather than GDAL XML elements.

This builder only supports the RPC00B format. Support for other TREs can be added in the future if we find ourselves working with imagery containing that metadata.

See STDI-0002 Volume 1 Appendix E for more detailed information.

build()

Examine the TRE metadata for RPC information, parse the necessary values out of those TREs, and construct an RPC sensor model.

Return type:

Optional[RPCSensorModel]

Returns:

an RPC SensorModel if one can be constructed, None otherwise

static build_rpc_sensor_model(rpc_dict)

Construct an RPC sensor model from an RPC00B TRE dict.

Parameters:

rpc_dict (dict) – the RPC00B TRE field dict

Return type:

RPCSensorModel

Returns:

the RPC sensor model

static build_rpc_polynomial(rpc_dict, polynomial_name)

Construct an RPC polynomial from coefficients found in the RPC00B TRE dict. There are 4 repeating groups of these coefficients for the polynomials associated with line or sample numerators and denominators.

Parameters:
  • rpc_dict (dict) – the RPC00B TRE field dict

  • polynomial_name (str) – the prefix of the polynomial coefficients to extract

Return type:

RPCPolynomial

Returns:

the RPC polynomial

class aws.osml.metadata.rsm_sensor_model_builder.RSMSensorModelBuilder(tre_dicts)

Bases: SensorModelBuilder

This builder constructs sensor models for images that have RSM TREs. The inputs are TRE metadata provided as Python dicts (from osml-imagery-io’s MetadataProvider) rather than GDAL XML elements.

The actual type and number of RSM TREs included with an image will vary depending on the type of RSM sensor model defined. In general all images with these sensor models must have an RSMIDA TRE that defines the overall context of the sensor model.

The polynomial based sensor models will then have at least one RSMPCA TRE and may have multiple. If there are multiple then a RSMPIA TRE will also be present to describe how the various polynomial models cover the overall image domain.

See STDI-0002 Volume 1 Appendix U for more detailed information.

build()

Examine the TRE metadata for RSM information, parse the necessary values out of those TREs, and construct an RSM sensor model.

Return type:

Optional[SensorModel]

Returns:

an RSM SensorModel if one can be constructed, None otherwise

aws.osml.metadata.sicd_sensor_model_builder.xyztype_to_ndarray(xyztype)

Convert the XYZType to a 1-d NumPy array.

Parameters:

xyztype (Union[XYZType, XYZType]) – the XYZType dataclass

Return type:

ndarray

Returns:

the NumPy array

aws.osml.metadata.sicd_sensor_model_builder.poly1d_to_native(poly1d)

Convert the Poly1DType to a NumPy Polynomial.

Parameters:

poly1d (Union[Poly1DType, XYZType]) – the Poly1D dataclass

Return type:

Polynomial

Returns:

the NumPy polynomial with matching coefficients

aws.osml.metadata.sicd_sensor_model_builder.poly2d_to_native(poly2d)

Convert the Poly2D dataclass to a Polynomial2D.

Parameters:

poly2d (Union[Poly2DType, Poly2DType]) – the Poly2D dataclass

Return type:

Polynomial2D

Returns:

the Polynomial2D with matching coefficients

aws.osml.metadata.sicd_sensor_model_builder.xyzpoly_to_native(xyzpoly)

Convert the XYZPoly dataclass into a PolynomialXYZ.

Parameters:

xyzpoly (Union[XYZPolyType, XYZPolyType]) – the XYZPoly dataclass

Return type:

PolynomialXYZ

Returns:

the PolynomialXYZ with matching coefficients

class aws.osml.metadata.sicd_sensor_model_builder.SICDSensorModelBuilder(sicd_xml)

Bases: SensorModelBuilder

This builder is used to create sensor models for images that have SICD metadata. The metadata is provided as XML that conforms to the SICD specifications. We intend to support multiple SICD versions but the current software was implemented using the v1.2.1 and v1.3.0 specifications.

build()

Attempt to build a precise SAR sensor model. This sensor model handles chipped images natively.

Return type:

Optional[SensorModel]

Returns:

the sensor model; if available

static from_dataclass(sicd)

This method constructs a SICD sensor model from the python dataclasses generated when parsing the XML.

Parameters:

sicd (Union[SICD, SICD]) – the dataclass object constructed from the XML

Return type:

Optional[SensorModel]

Returns:

the sensor model; if available

class aws.osml.metadata.sidd_sensor_model_builder.SIDDSensorModelBuilder(sidd_xml)

Bases: SensorModelBuilder

This builder is used to create sensor models for images that have SIDD metadata. The metadata is provided as XML that conforms to the SIDD specifications. We intend to support multiple SIDD versions but the current software was implemented using the v2.0.0 and v3.0.0 specifications.

Note that the SIDD sensor models rely heavily on the SICD projections so the class of the returned model will be a SICDSensorModel. Future versions may rename this to SISensorModel or SARSensorModel.

build()

Attempt to build a precise SAR sensor model. This sensor model handles chipped images natively.

Return type:

Optional[SensorModel]

Returns:

the sensor model; if available

static from_dataclass(sidd)

This method constructs a SIDD sensor model from the python dataclasses generated when parsing the XML. If the metadata shows that this is a chip then a ChippedImageSensorModel will be constructed to wrap the SICDSensorModel used for the full image.

Parameters:

sidd (Union[SIDD, SIDD, SIDD]) – the dataclass object constructed from the XML

Return type:

Optional[SensorModel]

Returns:

the sensor model; if available

class aws.osml.metadata.projective_sensor_model_builder.ProjectiveSensorModelBuilder(tre_dicts, full_image_width, full_image_height)

Bases: SensorModelBuilder

This builder constructs sensor models for images that have a CSCRNA TRE. The inputs are TRE metadata provided as Python dicts (from osml-imagery-io’s MetadataProvider) rather than GDAL XML elements.

The CSCRNA TRE contains corner coordinates (upper-left, upper-right, lower-right, lower-left) that define the geographic extent of the image. These are used to construct a projective sensor model that maps between image pixel coordinates and world coordinates.

See STDI-0002 Volume 1 Appendix AW for more detailed information.

build()

Examine the TRE metadata for CSCRNA corner coordinate information, parse the necessary values, and construct a projective sensor model.

Return type:

Optional[ProjectiveSensorModel]

Returns:

a ProjectiveSensorModel if one can be constructed, None otherwise

static build_projective_sensor_model(cscrna_dict, full_image_width, full_image_height)

Construct a projective sensor model from a CSCRNA TRE dict and image dimensions.

Parameters:
  • cscrna_dict (dict) – the CSCRNA TRE field dict

  • full_image_width (float) – the width of the image in pixels

  • full_image_height (float) – the height of the image in pixels

Return type:

ProjectiveSensorModel

Returns:

the projective sensor model

class aws.osml.metadata.affine_sensor_model_builder.AffineSensorModelBuilder(geo_transform, proj_wkt=None)

Bases: SensorModelBuilder

This builder constructs sensor models for images that have a 6-coefficient affine geo transform (e.g. from GeoTIFF). It produces an AffineSensorModel from the transform coefficients and an optional CRS projection string.

build()

Use the geo transform to construct an affine sensor model.

Return type:

Optional[AffineSensorModel]

Returns:

an AffineSensorModel, or None if geo_transform is None

class aws.osml.metadata.gcp_sensor_model_builder.GroundControlPoint(image_x, image_y, world_longitude, world_latitude, world_elevation=0.0)

Bases: object

A ground control point mapping image coordinates to world coordinates.

image_x: float
image_y: float
world_longitude: float
world_latitude: float
world_elevation: float = 0.0
class aws.osml.metadata.gcp_sensor_model_builder.GCPSensorModelBuilder(ground_control_points)

Bases: SensorModelBuilder

This builder constructs a ProjectiveSensorModel from a list of ground control point correspondences. It replaces the GDAL-based GCPSensorModelBuilder by accepting a list of GroundControlPoint dataclass instances instead of gdal.GCP objects.

Handles degenerate cases: - Fewer than 3 distinct world-coordinate points → returns None - Self-intersecting (bowtie) polygons for exactly 4 GCPs → returns None - Adjacent duplicate points (triangle patterns) → perturbs them apart before solving

build()

Use the GCPs to construct a projective sensor model.

Requires at least 4 ground control points to estimate the projective transform. Returns None if the list is None, empty, has fewer than 4 points, or has geometrically degenerate world coordinates.

Return type:

Optional[ProjectiveSensorModel]

Returns:

a ProjectiveSensorModel, or None if GCPs are insufficient or degenerate