-
Notifications
You must be signed in to change notification settings - Fork 0
DM-54912: add photometric_scaling to VisitImage #39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9ccd070
89890e8
5af2408
d3847ab
2b5e30a
1f9b18d
88e671c
2ced257
3a6f596
bac0e7c
27ce0ea
ff38799
4a3c4db
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Added a `photometric_scaling` component to `VisitImage`. |
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,27 +16,32 @@ | |
| "FieldSerializationModel", | ||
| "field_from_legacy", | ||
| "field_from_legacy_background", | ||
| "field_from_legacy_photo_calib", | ||
| ) | ||
|
|
||
| from typing import TYPE_CHECKING, Annotated, Any | ||
|
|
||
| import astropy.units | ||
| import numpy as np | ||
| import pydantic | ||
|
|
||
| from .._geom import Bounds | ||
| from ._chebyshev import ChebyshevField, ChebyshevFieldSerializationModel | ||
| from ._product import ProductField, ProductFieldSerializationModel | ||
| from ._spline import SplineField, SplineFieldSerializationModel | ||
| from ._sum import SumField, SumFieldSerializationModel | ||
|
|
||
| if TYPE_CHECKING: | ||
| try: | ||
| from lsst.afw.image import PhotoCalib as LegacyPhotoCalib | ||
| from lsst.afw.math import BackgroundList as LegacyBackgroundList | ||
| from lsst.afw.math import BackgroundMI as LegacyBackground | ||
| from lsst.afw.math import BoundedField as LegacyBoundedField | ||
| except ImportError: | ||
| type LegacyBoundedField = Any # type: ignore[no-redef] | ||
| type LegacyBackground = Any # type: ignore[no-redef] | ||
| type LegacyBackgroundList = Any # type: ignore[no-redef] | ||
| type LegacyPhotoCalib = Any # type: ignore[no-redef] | ||
|
|
||
|
|
||
| # Since Sphinx can't handle doc links to type aliases, whenever we annotate | ||
|
|
@@ -61,29 +66,55 @@ | |
|
|
||
|
|
||
| def field_from_legacy( | ||
| legacy_bounded_field: LegacyBoundedField, unit: astropy.units.UnitBase | None = None | ||
| legacy_bounded_field: LegacyBoundedField, | ||
| bounds: Bounds | None = None, | ||
| unit: astropy.units.UnitBase | None = None, | ||
| ) -> Field: | ||
| """Convert a legacy `lsst.afw.math.BoundedField` subclass to a `BaseField` | ||
| object. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| legacy_bounded_field | ||
| Legacy field to convert. | ||
| bounds | ||
| The bounds of the returned field, if they should be different from | ||
| the bounding box of ``legacy``. | ||
| unit | ||
| The units of the returned field (`lsst.afw.math.BoundedField` | ||
| objects do not know their units). | ||
| """ | ||
| from lsst.afw.math import ChebyshevBoundedField, ProductBoundedField | ||
|
|
||
| match legacy_bounded_field: | ||
| case ChebyshevBoundedField(): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm 99% sure if you wanted to remove the (), you could just do
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would actually turn this into an |
||
| return ChebyshevField.from_legacy(legacy_bounded_field, unit=unit) | ||
| return ChebyshevField.from_legacy(legacy_bounded_field, unit=unit, bounds=bounds) | ||
| case ProductBoundedField(): | ||
| return ProductField.from_legacy(legacy_bounded_field, unit=unit) | ||
| return ProductField.from_legacy(legacy_bounded_field, unit=unit, bounds=bounds) | ||
| case _: | ||
| raise NotImplementedError( | ||
| f"Conversion from {type(legacy_bounded_field).__name__} is not supported." | ||
| ) | ||
|
|
||
|
|
||
| def field_from_legacy_background( | ||
| legacy_background: LegacyBackground | LegacyBackgroundList, unit: astropy.units.UnitBase | None = None | ||
| legacy_background: LegacyBackground | LegacyBackgroundList, | ||
| bounds: Bounds | None = None, | ||
| unit: astropy.units.UnitBase | None = None, | ||
| ) -> Field: | ||
| """Convert a legacy `lsst.afw.math.Background` or | ||
| `lsst.afw.math.BackgroundList` instance to a `BaseField` object. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| legacy_background | ||
| Legacy background object to convert. | ||
| bounds | ||
| The bounds of the returned field, if they should be different from | ||
| the bounding box of ``legacy_background``. | ||
| unit | ||
| The units of the returned field (`lsst.afw.math.Background` | ||
| objects do not know their units). | ||
| """ | ||
| from lsst.afw.math import ApproximateControl, BackgroundList | ||
|
|
||
|
|
@@ -95,3 +126,50 @@ def field_from_legacy_background( | |
| return SplineField.from_legacy_background(legacy_background, unit=unit) | ||
| else: | ||
| return ChebyshevField.from_legacy_background(legacy_background, unit=unit) | ||
|
|
||
|
|
||
| def field_from_legacy_photo_calib( | ||
| legacy_photo_calib: LegacyPhotoCalib, | ||
| bounds: Bounds, | ||
| instrumental_unit: astropy.units.UnitBase = astropy.units.electron, | ||
| ) -> Field | None: | ||
| """Convert a legacy `lsst.afw.image.PhotoCalib` into a `BaseField` object. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I strongly dislike the name
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is indeed the base class for all fields. It's confusing because In There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. but later if you need that concept in some other sense it gets ambiguous, and I can't really ask the interpreter (type) to understand what the thing is if I have one
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm just pretty confident we're not going to need that concept in the future. I don't think it's fundamentally different from using an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And you can't think of anytime there would be Field types that would not be appropriate for being a photoclaib?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There might be some that wouldn't be very useful - e.g. since it's multiplicative, I don't really see a sum of fields ever being used. But I don't think there could ever be one that actually couldn't be used because it would be problematic. The whole point of the |
||
|
|
||
| Parameters | ||
| ---------- | ||
| legacy_photo_calib | ||
| Calibration object to convert. | ||
| bounds | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if it is not too late in api, I think it would be better if this was the same
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are some real differences here:
But it probably makes sense to at least make |
||
| Bounds of the returned field. | ||
| instrumental_unit | ||
| The instrumental units the legacy calibration transforms from. These | ||
| will be used as the denominator of the units of the returned field, | ||
| with ``astropy.units.nJy`` as the numerator. | ||
|
|
||
| Returns | ||
| ------- | ||
| `BaseField` | `None` | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I kind of feels awkward that some objects will have an attribute you can call, and sometimes it will be
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I could pretty easily switch to a property that checks to see if the internal value is In fact, allowing it to be
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I gave this a shot - making the |
||
| A field that transforms instrumental units to ``nJy``, or `None` if | ||
| the given calibration object was an identity mapping for a legacy | ||
| image that already had ``nJy`` pixels. | ||
| """ | ||
| calibration_mean = legacy_photo_calib.getCalibrationMean() | ||
| if legacy_photo_calib._isConstant: | ||
| if calibration_mean == 1.0: | ||
| # This image's pixels have been calibrated to nJy | ||
| # already, which means the calibration *from* post-ISR | ||
| # electrons that we want is stored elsewhere. | ||
| return None | ||
| else: | ||
| return ChebyshevField( | ||
| bounds, | ||
| np.array([[calibration_mean]], dtype=np.float64), | ||
| unit=astropy.units.nJy / instrumental_unit, | ||
| ) | ||
| else: | ||
| normalized_field = field_from_legacy( | ||
| legacy_photo_calib.computeScaledCalibration(), | ||
| unit=astropy.units.nJy / instrumental_unit, | ||
| bounds=bounds, | ||
| ) | ||
| return normalized_field * calibration_mean | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -119,14 +119,28 @@ def _get_archive_tree_type( | |
|
|
||
| @staticmethod | ||
| def from_legacy( | ||
| legacy: LegacyProductBoundedField, unit: astropy.units.UnitBase | None = None | ||
| legacy: LegacyProductBoundedField, | ||
| unit: astropy.units.UnitBase | None = None, | ||
| bounds: Bounds | None = None, | ||
| ) -> ProductField: | ||
| """Convert from a legacy `lsst.afw.math.ProductBoundedField`.""" | ||
| """Convert from a legacy `lsst.afw.math.ProductBoundedField`. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| legacy | ||
| Legacy field to convert. | ||
| unit | ||
| The units of the returned field (`lsst.afw.math.BoundedField` | ||
| objects do not know their units). | ||
| bounds | ||
| The bounds of the returned field, if they should be different from | ||
| the bounding box of ``legacy``. | ||
| """ | ||
| from ._concrete import field_from_legacy | ||
|
|
||
| legacy_factors = legacy.getFactors() | ||
| operands = [field_from_legacy(f) for f in legacy_factors[:-1]] | ||
| operands.append(field_from_legacy(legacy_factors[-1], unit=unit)) | ||
| operands = [field_from_legacy(f, bounds=bounds) for f in legacy_factors[:-1]] | ||
| operands.append(field_from_legacy(legacy_factors[-1], unit=unit, bounds=bounds)) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This feels like an awkward asymmetry and some weird special caring that units must happen to be on the last field.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They can be anywhere; the problem is picking an arbitrary one to add them to when we don't know anything else about what each operand represents. |
||
| return ProductField(operands) | ||
|
|
||
| def to_legacy(self) -> LegacyProductBoundedField: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same complaint here