21"""Support for image defects"""
39from .calibType
import IsrCalib
41log = logging.getLogger(__name__)
43SCHEMA_NAME_KEY =
"DEFECTS_SCHEMA"
44SCHEMA_VERSION_KEY =
"DEFECTS_SCHEMA_VERSION"
48 """Calibration handler for collections of `lsst.meas.algorithms.Defect`.
52 defectList : iterable, optional
53 Collections of defects to apply to the image. Can be an iterable of
54 `lsst.meas.algorithms.Defect` or `lsst.geom.BoxI`.
55 metadata : `lsst.daf.base.PropertyList`, optional
56 Metadata to associate with the defects. Will be copied and
57 overwrite existing metadata, if any. If not supplied the existing
58 metadata will be reset.
59 normalize_on_init : `bool`
60 If True, normalization is applied to the defects in ``defectList`` to
61 remove duplicates, eliminate overlaps, etc.
65 Defects are stored within this collection in a "reduced" or "normalized"
66 form: rather than simply storing the bounding boxes which are added to the
67 collection, we eliminate overlaps and duplicates. This normalization
68 procedure may introduce overhead when adding many new defects; it may be
69 temporarily disabled using the `Defects.bulk_update` context manager if
72 The attributes stored in this calibration are:
74 _defects : `list` [`lsst.meas.algorithms.Defect`]
75 The collection of Defect objects.
78 """The calibration type used for ingest."""
83 def __init__(self, defectList=None, metadata=None, *, normalize_on_init=True, **kwargs):
86 if defectList
is not None:
99 """Check that the supplied value is a `~lsst.meas.algorithms.Defect`
100 or can be converted to one.
109 new : `~lsst.meas.algorithms.Defect`
110 Either the supplied value or a new object derived from it.
115 Raised if the supplied value can not be converted to
116 `~lsst.meas.algorithms.Defect`
118 if isinstance(value, Defect):
125 value =
Defect(value.getBBox())
127 raise ValueError(f
"Defects must be of type Defect, BoxI, or PointI, not '{value!r}'")
137 """Can be given a `~lsst.meas.algorithms.Defect` or a `lsst.geom.BoxI`
149 """Compare if two `Defects` are equal.
151 Two `Defects` are equal if their bounding boxes are equal and in
152 the same order. Metadata content is ignored.
160 if len(self) != len(other):
164 for d1, d2
in zip(self, other):
165 if d1.getBBox() != d2.getBBox():
172 return baseStr +
",".join(str(d.getBBox())
for d
in self) +
")"
175 """Recalculate defect bounding boxes for efficiency.
179 Ideally, this would generate the provably-minimal set of bounding
180 boxes necessary to represent the defects. At present, however, that
181 doesn't happen: see DM-24781. In the cases of substantial overlaps or
182 duplication, though, this will produce a much reduced set.
193 minX, minY, maxX, maxY = float(
'inf'), float(
'inf'), float(
'-inf'), float(
'-inf')
195 bbox = defect.getBBox()
196 minX = min(minX, bbox.getMinX())
197 minY = min(minY, bbox.getMinY())
198 maxX = max(maxX, bbox.getMaxX())
199 maxY = max(maxY, bbox.getMaxY())
206 self.
_defects = Defects.fromMask(mask,
"BAD")._defects
208 @contextlib.contextmanager
210 """Temporarily suspend normalization of the defect list.
228 """Copy the defects to a new list, creating new defects from the
234 New list with new `Defect` entries.
238 This is not a shallow copy in that new `Defect` instances are
239 created from the original bounding boxes. It's also not a deep
240 copy since the bounding boxes are not recreated.
245 """Make a transposed copy of this defect list.
249 retDefectList : `Defects`
250 Transposed list of defects.
254 bbox = defect.getBBox()
255 dimensions = bbox.getDimensions()
258 retDefectList.append(nbbox)
262 """Set mask plane based on these defects.
266 maskedImage : `lsst.afw.image.MaskedImage` or `lsst.afw.image.Mask`
267 Image to process. Only the mask plane is updated.
268 maskName : str, optional
269 Mask plane name to use.
272 if hasattr(mask,
"getMask"):
273 mask = mask.getMask()
274 bitmask = mask.getPlaneBitMask(maskName)
276 bbox = defect.getBBox()
280 """Convert defect list to `~lsst.afw.table.BaseCatalog` using the
281 FITS region standard.
285 table : `lsst.afw.table.BaseCatalog`
286 Defects in tabular form.
290 The table created uses the
291 `FITS regions <https://fits.gsfc.nasa.gov/registry/region.html>`_
292 definition tabular format. The ``X`` and ``Y`` coordinates are
293 converted to FITS Physical coordinates that have origin pixel (1, 1)
294 rather than the (0, 0) used in LSST software.
306 for i, defect
in enumerate(self.
_defects):
307 box = defect.getBBox()
308 center = box.getCenter()
310 xCol.append(center.getX() + 1.0)
311 yCol.append(center.getY() + 1.0)
316 if width == 1
and height == 1:
323 shapes.append(shapeType)
325 rCol.append(np.array([width, height], dtype=np.float64))
327 table = astropy.table.Table({
'X': xCol,
'Y': yCol,
'SHAPE': shapes,
328 'R': rCol,
'ROTANG': np.zeros(nrows),
329 'COMPONENT': np.arange(nrows)})
335 """Construct a calibration from a dictionary of properties.
337 Must be implemented by the specific calibration subclasses.
342 Dictionary of properties.
346 calib : `lsst.ip.isr.CalibType`
347 Constructed calibration.
352 Raised if the supplied dictionary is for a different
357 if calib._OBSTYPE != dictionary[
'metadata'][
'OBSTYPE']:
358 raise RuntimeError(f
"Incorrect crosstalk supplied. Expected {calib._OBSTYPE}, "
359 f
"found {dictionary['metadata']['OBSTYPE']}")
361 calib.setMetadata(dictionary[
'metadata'])
362 calib.calibInfoFromDict(dictionary)
364 xCol = dictionary[
'x0']
365 yCol = dictionary[
'y0']
366 widthCol = dictionary[
'width']
367 heightCol = dictionary[
'height']
369 with calib.bulk_update:
370 for x0, y0, width, height
in zip(xCol, yCol, widthCol, heightCol):
376 """Return a dictionary containing the calibration properties.
378 The dictionary should be able to be round-tripped through
384 Dictionary of properties.
390 outDict[
'metadata'] = metadata
400 box = defect.getBBox()
401 xCol.append(box.getBeginX())
402 yCol.append(box.getBeginY())
403 widthCol.append(box.getWidth())
404 heightCol.append(box.getHeight())
408 outDict[
'width'] = widthCol
409 outDict[
'height'] = heightCol
414 """Convert defects to a simple table form that we use to write
419 table : `lsst.afw.table.BaseCatalog`
420 Defects in simple tabular form.
424 These defect tables are used as the human readable definitions
425 of defects in calibration data definition repositories. The format
426 is to use four columns defined as follows:
429 X coordinate of bottom left corner of box.
431 Y coordinate of bottom left corner of box.
448 box = defect.getBBox()
449 xCol.append(box.getBeginX())
450 yCol.append(box.getBeginY())
451 widthCol.append(box.getWidth())
452 heightCol.append(box.getHeight())
454 catalog = astropy.table.Table({
'x0': xCol,
'y0': yCol,
'width': widthCol,
'height': heightCol})
456 outMeta = {k: v
for k, v
in inMeta.items()
if v
is not None}
457 catalog.meta = outMeta
458 tableList.append(catalog)
464 """Retrieve N values from the supplied values.
468 values : `numbers.Number` or `list` or `np.array`
471 Number of values to retrieve.
475 vals : `list` or `np.array` or `numbers.Number`
476 Single value from supplied list if ``n`` is 1, or `list`
477 containing first ``n`` values from supplied values.
481 Some supplied tables have vectors in some columns that can also
482 be scalars. This method can be used to get the first number as
483 a scalar or the first N items from a vector as a vector.
486 if isinstance(values, numbers.Number):
495 """Construct a `Defects` from the contents of a
496 `~lsst.afw.table.BaseCatalog`.
500 table : `lsst.afw.table.BaseCatalog`
501 Table with one row per defect.
502 normalize_on_init : `bool`, optional
503 If `True`, normalization is applied to the defects listed in the
504 table to remove duplicates, eliminate overlaps, etc. Otherwise
505 the defects in the returned object exactly match those in the
515 Two table formats are recognized. The first is the
516 `FITS regions <https://fits.gsfc.nasa.gov/registry/region.html>`_
517 definition tabular format written by `toFitsRegionTable` where the
518 pixel origin is corrected from FITS 1-based to a 0-based origin.
519 The second is the legacy defects format using columns ``x0``, ``y0``
520 (bottom left hand pixel of box in 0-based coordinates), ``width``
523 The FITS standard regions can only read BOX, POINT, or ROTBOX with
524 a zero degree rotation.
529 schema = table.columns
531 if "X" in schema
and "Y" in schema
and "R" in schema
and "SHAPE" in schema:
534 elif "x0" in schema
and "y0" in schema
and "width" in schema
and "height" in schema:
538 raise ValueError(
"Unsupported schema for defects extraction")
547 shape = record[
'SHAPE'].upper().rstrip()
552 elif shape ==
"POINT":
556 elif shape ==
"ROTBOX":
560 if math.isclose(rotang % 90.0, 0.0):
563 if math.isclose(rotang % 180.0, 0.0):
572 log.warning(
"Defect can not be defined using ROTBOX with non-aligned rotation angle")
575 log.warning(
"Defect lists can only be defined using BOX or POINT not %s", shape)
583 defectList.append(box)
585 defects = cls(defectList, normalize_on_init=normalize_on_init)
586 newMeta = dict(table.meta)
587 defects.updateMetadata(setCalibInfo=
True, **newMeta)
593 """Read defects information from a legacy LSST format text file.
598 Name of text file containing the defect information.
600 normalize_on_init : `bool`, optional
601 If `True`, normalization is applied to the defects listed in the
602 table to remove duplicates, eliminate overlaps, etc. Otherwise
603 the defects in the returned object exactly match those in the
613 These defect text files are used as the human readable definitions
614 of defects in calibration data definition repositories. The format
615 is to use four columns defined as follows:
618 X coordinate of bottom left corner of box.
620 Y coordinate of bottom left corner of box.
626 Files of this format were used historically to represent defects
627 in simple text form. Use `Defects.readText` and `Defects.writeText`
628 to use the more modern format.
632 defect_array = np.loadtxt(filename,
633 dtype=[(
"x0",
"int"), (
"y0",
"int"),
634 (
"x_extent",
"int"), (
"y_extent",
"int")])
638 for row
in defect_array)
640 return cls(defects, normalize_on_init=normalize_on_init)
644 """Compute a defect list from a footprint list, optionally growing
649 fpList : `list` of `lsst.afw.detection.Footprint`
650 Footprint list to process.
660 for fp
in fpList), normalize_on_init=
False)
664 """Compute a defect list from a specified mask plane.
668 mask : `lsst.afw.image.Mask` or `lsst.afw.image.MaskedImage`
670 maskName : `str` or `list`
671 Mask plane name, or list of names to convert.
676 Defect list constructed from masked pixels.
678 if hasattr(mask,
"getMask"):
679 mask = mask.getMask()
681 lsst.afw.detection.Threshold.BITMASK)
static Box2I makeCenteredBox(Point2D const ¢er, Extent const &size)
requiredAttributes(self, value)
updateMetadata(self, camera=None, detector=None, filterName=None, setCalibId=False, setCalibInfo=False, setDate=False, **kwargs)
fromTable(cls, tableList, normalize_on_init=True)
insert(self, index, value)
fromDict(cls, dictionary)
_check_value(self, value)
__setitem__(self, index, value)
maskPixels(self, mask, maskName="BAD")
fromMask(cls, mask, maskName)
readLsstDefectsFile(cls, filename, normalize_on_init=False)
fromFootprintList(cls, fpList)
__init__(self, defectList=None, metadata=None, *normalize_on_init=True, **kwargs)
std::vector< lsst::geom::Box2I > footprintToBBoxList(Footprint const &footprint)