From 9605fdb0468fd44d1eaa4c5a421f0c5bd5a7bd6a Mon Sep 17 00:00:00 2001 From: Jim Bosch Date: Wed, 13 May 2026 15:41:56 -0400 Subject: [PATCH] Add draft of doc section on transitioning from afw. --- doc/lsst.images/for-afw-users.rst | 156 ++++++++++++++++++++++++++++++ doc/lsst.images/index.rst | 8 ++ 2 files changed, 164 insertions(+) create mode 100644 doc/lsst.images/for-afw-users.rst diff --git a/doc/lsst.images/for-afw-users.rst b/doc/lsst.images/for-afw-users.rst new file mode 100644 index 00000000..35db074f --- /dev/null +++ b/doc/lsst.images/for-afw-users.rst @@ -0,0 +1,156 @@ +.. py:currentmodule:: lsst.images + +For `lsst.afw` Users +==================== + +The `lsst.images` package is heavily inspired by (and is intented to partially supersede) the `lsst.afw` and `lsst.geom` packages. +Most of the types in `lsst.images` have a direct counterpart in `lsst.afw`, with bidirectional conversions between them. +Despite these conceptual similarities, the interfaces are often quite different in detail, generally because this is an opportunity to make interface improvements that are now difficult to make in `lsst.afw` or `lsst.geom`. + +Geometry +-------- + +``lsst.geom.Point*``, `lsst.geom.Extent*`, and Coordinate Ordering +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +There are no true point or extent types in `lsst.images`. +The philosophy is instead that we will usually want to operate pairs of *arrays* of points, and hence the focus is on vectorized, ``ufunc``-like interfaces that take ``x`` and ``y`` arguments. + +Because of the ubiquity of both ``(x, y)`` ordering and ``(y, x)`` ordering in Astropy, NumPy, and other libraries we interoperate with, `lsst.images` does *not* impose a uniform consistent order for such pairs across all interfaces. +Instead, pairs of ``x`` and ``y`` arguments are almost always keyword-only, and functions that return coordinate pairs (or pairs of coordinate arrays) use the `XY` or `YX` named tuples, which should generally be unpacked via their ``x`` and ``y`` attributes but are still formally `tuple` objects, for cases (e.g. shapes of arrays) where a `tuple` is needed. + +.. note:: + + `XY` and `YX` are tuples, so their ``+`` and ``*`` operators correspond to the `collections.abc.Sequence` definitions (concatenation and duplication). + They are *not* point-like types with point-like operators, even though they are the closest thing `lsst.images` has to a point type. + +Intervals, Boxes, and Polygons +"""""""""""""""""""""""""""""" + +`lsst.geom.IntervalI` corresponds directly to `Interval`. + +`lsst.geom.Box2I` corresponds directly to `Box` (but the latter is immutable). + +`lsst.geom.IntervalD` and `lsst.geom.Box2D` do not have direct counterparts in `lsst.images`. +2-d floating-point boxes are represented as `Polygon` objects; the expectation is that - unlike an integer-coordinate `Box` - there is nothing special about a floating-point rectangle that necessitates a dedicated class. + +`lsst.geom.Angle` and `lsst.geom.SpherePoint` do not have direct counterparts in `lsst.images` itself, but the `astropy.units.Quantity` and `astropy.coordinates.SkyCoord` types are generally used in the same roles. + +`lsst.afw.geom.Polygon` corresponds to `Polygon` and its more general `Region` base class, which can represent arbitrary sets of polygons (with holes) in a Euclidean (e.g. pixel) coordinate system. + +`Box` and `Region` (and by extension, `Polygon`) all satisfy the `Bounds` `~typing.Protocol`, allowing them to be attached to various objects (e.g. `~psfs.PointSpreadFunction`, `Transform`) to specify the region where those objects are valid. +The `Bounds` system is not yet fully implemented in `lsst.images`, but the goal is to provide consistent control options (e.g. raise, warn, extrapolate) for handling out-of-bounds positions across the library. + +Coordinate Systems and Transforms +""""""""""""""""""""""""""""""""" + +`lsst.afw.geom.SkyWcs` corresponds directly to `Projection`. +Both types can be (but are not necessarily!) representable as FITS WCS, and are capable of carrying around their own FITS WCS approximation. + +`lsst.afw.geom.TransformPoint2ToPoint2` and other instantiations of the same underyling C++ template correspond directly to `Transform`. + +`Projection` and `Transform` differ from their `lsst.afw.geom` counterparts in that they can identify the frames they transform between (e.g. the pixels of a particular ``{visit, detector}`` and the ICRS sky), via an object that satisfies the `Frame` `~typing.Protocol`. +This additional information needs to be provided when creating an `lsst.images` type from an `lsst.afw.geom` one (e.g. via `Projection.from_legacy`). + +`lsst.afw.cameraGeom.TransformMap` corresponds directly to `CameraFrameSet`. + +General-Purpose Images +---------------------- + +All image-like objects in `lsst.images` inherit from `GeneralizedImage`, which allows any number of image planes that correspond to a single (optional) `Projection` and bounding `Box`. + +Pixel indexing conventions in the two libraries are the same: the center of the lower-left pixel of most images is ``(0, 0)`` (and always a pair of integers). + +The offset often referred to as ``xy0`` in `lsst.afw` is generally called ``start`` in `lsst.images` arguments and attributes, and the `lsst.afw.image.PARENT` coordinate system that is aware of this offset is used by default on all `lsst.images` types, and can be used more explicitly via the `GeneralizedImage.absolute` slicing proxy. + +As in `lsst.afw.image`, underlying `numpy.ndarray` view attributes do not know about this offset, and instead operate in what is called the `lsst.afw.image.LOCAL` coordinate system in `lsst.afw`. +The types in `lsst.image` can be sliced in this coordinate system via the `GeneralizedImage.local` slicing proxy. + +`lsst.images.Image` corresponds directly to `Image`, but the latter can also hold a `Projection`, flexible metadata, units (via `astropy.units`), and an `astro_metadata_translator.ObservationInfo`. + +`lsst.images.Mask` corresponds directly to `Mask`, but the latter can also hold a `Projection` and flexible metadata, and its backing array is 3-d `numpy.uint8` array with shape ``(height, width, N)``, where ``N`` can change depending on the number of mask planes (which is fully dynamic). +The planes of different `Mask` objects are not necessarily the same (as is enforced by global state in `lsst.afw.image.Mask`); instead, a separate `MaskSchema` object is used to manage shared mask plane definitions. + +`lsst.images.MaskedImage` corresponds directly to `MaskedImage`, but the latter can also hold a `Projection`, flexible metadata, and units. + +Single-Visit `lsst.afw.image.Exposure` Objects +---------------------------------------------- + +When used to represent a calibrated single-visit, single-detector image, `lsst.afw.image.Exposure` corresponds to `VisitImage`, which is a subclass of `MaskedImage`. + +Most `lsst.afw.image.Exposure` components have `VisitImage` counterparts: + +- ``wcs`` (`lsst.afw.geom.SkyWcs`) -> `VisitImage.projection` (`Projection`) +- ``psf`` (`lsst.afw.detection.Psf`) -> `VisitImage.psf` (`psfs.PointSpreadFunction`) +- ``validPolygon`` (`lsst.afw.geom.Polygon`) -> `VisitImage.bounds` (any `Bounds` implementation) +- ``visitInfo`` (`lsst.afw.image.VisitInfo`) -> `VisitImage.obs_info` (`astro_metadata_translator.ObservationInfo`) +- ``summaryStats`` (`lsst.afw.image.ExposureSummaryStats`) -> `VisitImage.summary_stats` (`ObservationSummaryStats`) +- ``detector`` (`lsst.afw.cameraGeom.Detector`) -> `VisitImage.detector` (`cameras.Detector`) +- ``apCorrMap`` (`lsst.afw.image.ApCorrMap`) -> `VisitImage.aperture_corrections` (`dict` of `fields.BaseField`) + +Photometric calibration objects are not yet included in `VisitImage`, but will be soon. + +Coadd Images +------------ + +Coadded images can be represented outside of `lsst.images` by any of the following three types: + +- `lsst.afw.image.Exposure`: traditional lazy-PSF coadds (including templates), as well as post-detection deep cell-based coadds (for compatibility with most coadd measurement tasks). + +- `lsst.cell_coadds.MultipleCellCoadd`: the immediate result of building a cell-based coadd. + +- `lsst.cell_coadds.StitchedCoadd`: an intermediate object that keeps all of the extra information in a cell-based coadd while having traditional full-patch arrays for the image planes and mask, but does not have any I/O support. + +The `cells.CellCoadd` most closely resembles `~lsst.cell_coadds.StitchedCoadd`; it inherits from `MaskedImage` and hence has full-array image and mask planes, but its PSF model, bounds, and provenance data structures are explicitly cell-based. +It can fully represent any `~lsst.cell_coadds.MultipleCellCoadd` or `~lsst.cell_coadds.StitchedCoadd`, and will also typically hold the additional mask information (e.g. the ``DETECTED`` plane) and backround offset held by downstream `lsst.afw.image.Exposure` datasets. + +Because image subtraction templates are now Rubin's only traditional lazy-PSF coadd data product, but the spatial varation of those coadd PSFs is not used by the image subtraction pipeline, we plan to convert these to the `~cells.CellCoadd` data structure by approximating their PSFs as cell-based, i.e. evaluating the traditional lazy coadd PSF model at the centers of cells. +This is roughly equivalent to a procedure that builds templates as "edgy" cell coadds, in which visit-cell combinations that do not wholly overlap a cell are nevertheless included in the coadd (which is what we may do in the future, when `lsst.images` types are used as direct pipeline outputs). + +The `~cells.CellCoadd` type has counterparts for only some of the components of `lsst.afw.image.Exposure`: + +- ``wcs`` (`lsst.afw.geom.SkyWcs`) -> `~cells.CellCoadd.projection` (`Projection`) +- ``psf`` (`lsst.afw.detection.Psf`) -> `~cells.CellCoadd.psf` (`~cells.CellPointSpreadFunction`) + +Cell coadds also have a `~cells.CellCoadd.bounds` attribute (`~cells.CellGridBounds`) that plays a similar role to the `VisitImage.bounds` attribute in that it represents the area where pixels and other objects (e.g. PSFs are valid), by recording which cells do and do not have data. + +Aperture corrections and background models for cell coadds will be added in the future. + +BoundedFields and Backgrounds +----------------------------- + +`lsst.afw.math.BoundedField` corresponds directly to the `fields.BaseField` base class, whose subclasses are closed to the ``fields.Field`` type-union (i.e. no external implementations are permitted; this greatly simplifies serialization). +All `fields.BaseField` objects can be associated with units (via `astropy.units`). + +The `lsst.afw.math.BackgroundMI` and `lsst.afw.math.BackgroundList` types are *also* mapped to the `fields.BaseField` hierarchy in `lsst.images`, since those are also essentially just calculated images. + +Implementations include: + + - `lsst.afw.math.ChebyshevBoundedField` -> `fields.ChebyshevField` + - `lsst.afw.math.ProductBoundedField` -> `fields.ProductField` + - `lsst.afw.math.BackgroundMI` (interpolate) -> `fields.SplineField` + - `lsst.afw.math.BackgroundMI` (approximate) -> `fields.ChebyshevField` + - `lsst.afw.math.BackgroundList` -> `fields.SumField`. + +The `fields.field_from_legacy` and `fields.field_from_legacy_background` free functions can be used to convert from `lsst.afw` when the exact type is unknown. + +PSF Models +---------- + +The `lsst.afw.detection.Psf` class corresponds directly to `psfs.PointSpreadFunction`. +There is no concept of "average position" in `~psfs.PointSpreadFunction`, so a position must always be used to evaluate the PSF model. +The `~psfs.PointSpreadFunction` interface also currently lacks a way to represent wavelength-dependent PSF models, as we do not want to rush the in-code definition of the independent spectral-dimension variable. + +Concrete PSF implementations include: + +- `lsst.meas.extensions.piff.PiffPsf` -> `psfs.PiffWrapper` +- `lsst.meas.extensions.psfex.PsfExPsf` -> `psfs.PSFExWrapper` (this actually just wraps `lsst.meas.extensions.psfex.PsfExPsf` and cannot be used when that cannot be imported) +- `lsst.afw.detection.SingleGaussianPsf` -> `psfs.GaussianPointSpreadFunction` +- `lsst.cell_coadds.StitchedPsf` -> `cells.CellPointSpreadFunction` + +Camera Geometry +--------------- + +The `lsst.afw.cameraGeom.Detector` class corresponds directly to `cameras.Detector`, but the latter is mutable and hence has no need for the various builders of the former. + +The `lsst.afw.cameraGeom.Amplifier` class corresponds directly to `cameras.Amplifier`, but the latter factors out into optional sub-objects the various section bounding boxes that are modified during assembly and the suite of electronic parameters are superseded (at least for Rubin Observatory data) by calibration datasets. diff --git a/doc/lsst.images/index.rst b/doc/lsst.images/index.rst index 7115375c..a3f20ad8 100644 --- a/doc/lsst.images/index.rst +++ b/doc/lsst.images/index.rst @@ -8,6 +8,14 @@ lsst.images Modern image types and serialization for Rubin Observatory data products. +User Guide +========== + +.. toctree:: + :maxdepth: 1 + + for-afw-users.rst + Changes =======