23 """Support for image defects""" 25 __all__ = (
"Defects",)
29 import collections.abc
30 from deprecated.sphinx
import deprecated
49 log = logging.getLogger(__name__)
51 SCHEMA_NAME_KEY =
"DEFECTS_SCHEMA" 52 SCHEMA_VERSION_KEY =
"DEFECTS_SCHEMA_VERSION" 55 @deprecated(reason=
"Policy defect files no longer supported (will be removed after v18)",
56 category=FutureWarning)
58 """Given a Policy file describing a CCD's bad pixels, return a vector of BadRegion::Ptr""" 60 badPixelsPolicy = policy.Policy.createPolicy(policyFile)
63 if badPixelsPolicy.exists(
"Defects"):
64 d = badPixelsPolicy.getArray(
"Defects")
67 width = reg.get(
"width")
73 if reg.exists(
"height"):
74 height = reg.get(
"height")
80 badPixels.append(Defect(bbox))
85 class Defects(collections.abc.MutableSequence):
86 """Collection of `lsst.meas.algorithms.Defect`. 90 defectList : iterable of `lsst.meas.algorithms.Defect` 91 or `lsst.geom.BoxI`, optional 92 Collections of defects to apply to the image. 96 """The calibration type used for ingest.""" 98 def __init__(self, defectList=None, metadata=None):
101 if metadata
is not None:
106 if defectList
is None:
113 def _check_value(self, value):
114 """Check that the supplied value is a `~lsst.meas.algorithms.Defect` 115 or can be converted to one. 124 new : `~lsst.meas.algorithms.Defect` 125 Either the supplied value or a new object derived from it. 130 Raised if the supplied value can not be converted to 131 `~lsst.meas.algorithms.Defect` 133 if isinstance(value, Defect):
136 value = Defect(value)
140 value = Defect(value.getBBox())
142 raise ValueError(f
"Defects must be of type Defect, BoxI, or PointI, not '{value!r}'")
152 """Can be given a `~lsst.meas.algorithms.Defect` or a `lsst.geom.BoxI` 163 """Compare if two `Defects` are equal. 165 Two `Defects` are equal if their bounding boxes are equal and in 166 the same order. Metadata content is ignored. 168 if not isinstance(other, self.__class__):
172 for d1, d2
in zip(self, other):
173 if d1.getBBox() != d2.getBBox():
179 return "Defects(" +
",".join(str(d.getBBox())
for d
in self) +
")" 185 """Retrieve metadata associated with these `Defects`. 189 meta : `lsst.daf.base.PropertyList` 190 Metadata. The returned `~lsst.daf.base.PropertyList` can be 191 modified by the caller and the changes will be written to 197 """Store a copy of the supplied metadata with the defects. 201 metadata : `lsst.daf.base.PropertyList`, optional 202 Metadata to associate with the defects. Will be copied and 203 overwrite existing metadata. If not supplied the existing 204 metadata will be reset. 215 """Copy the defects to a new list, creating new defects from the 221 New list with new `Defect` entries. 225 This is not a shallow copy in that new `Defect` instances are 226 created from the original bounding boxes. It's also not a deep 227 copy since the bounding boxes are not recreated. 229 return self.__class__(d.getBBox()
for d
in self)
232 """Make a transposed copy of this defect list. 236 retDefectList : `Defects` 237 Transposed list of defects. 239 retDefectList = self.__class__()
241 bbox = defect.getBBox()
242 dimensions = bbox.getDimensions()
245 retDefectList.append(nbbox)
249 """Set mask plane based on these defects. 253 maskedImage : `lsst.afw.image.MaskedImage` 254 Image to process. Only the mask plane is updated. 255 maskName : str, optional 256 Mask plane name to use. 259 mask = maskedImage.getMask()
260 bitmask = mask.getPlaneBitMask(maskName)
262 bbox = defect.getBBox()
266 """Convert defect list to `~lsst.afw.table.BaseCatalog` using the 267 FITS region standard. 271 table : `lsst.afw.table.BaseCatalog` 272 Defects in tabular form. 276 The table created uses the 277 `FITS regions <https://fits.gsfc.nasa.gov/registry/region.html>`_ 278 definition tabular format. The ``X`` and ``Y`` coordinates are 279 converted to FITS Physical coordinates that have origin pixel (1, 1) 280 rather than the (0, 0) used in LSST software. 283 x = schema.addField(
"X", type=
"D", units=
"pix", doc=
"X coordinate of center of shape")
284 y = schema.addField(
"Y", type=
"D", units=
"pix", doc=
"Y coordinate of center of shape")
285 shape = schema.addField(
"SHAPE", type=
"String", size=16, doc=
"Shape defined by these values")
286 r = schema.addField(
"R", type="ArrayD", size=2, units="pix", doc="Extents")
287 rotang = schema.addField(
"ROTANG", type=
"D", units=
"deg", doc=
"Rotation angle")
288 component = schema.addField(
"COMPONENT", type=
"I", doc=
"Index of this region")
292 for i, defect
in enumerate(self.
_defects):
293 box = defect.getBBox()
295 table[i][x] = box.getCenterX() + 1.0
296 table[i][y] = box.getCenterY() + 1.0
297 width = box.getWidth()
298 height = box.getHeight()
300 if width == 1
and height == 1:
305 table[i][shape] = shapeType
306 table[i][r] = np.array([width, height], dtype=np.float64)
307 table[i][rotang] = 0.0
308 table[i][component] = i
313 metadata[SCHEMA_NAME_KEY] =
"FITS Region" 314 metadata[SCHEMA_VERSION_KEY] = 1
315 table.setMetadata(metadata)
320 """Write defect list to FITS. 325 Arguments to be forwarded to 326 `lsst.afw.table.BaseCatalog.writeFits`. 331 metadata = table.getMetadata()
332 now = datetime.datetime.utcnow()
333 metadata[
"DATE"] = now.isoformat()
334 metadata[
"CALIB_CREATION_DATE"] = now.strftime(
"%Y-%m-%d")
335 metadata[
"CALIB_CREATION_TIME"] = now.strftime(
"%T %Z").strip()
337 table.writeFits(*args)
340 """Convert defects to a simple table form that we use to write 345 table : `lsst.afw.table.BaseCatalog` 346 Defects in simple tabular form. 350 These defect tables are used as the human readable definitions 351 of defects in calibration data definition repositories. The format 352 is to use four columns defined as follows: 355 X coordinate of bottom left corner of box. 357 Y coordinate of bottom left corner of box. 364 x = schema.addField(
"x0", type=
"I", units=
"pix",
365 doc=
"X coordinate of bottom left corner of box")
366 y = schema.addField(
"y0", type=
"I", units=
"pix",
367 doc=
"Y coordinate of bottom left corner of box")
368 width = schema.addField(
"width", type=
"I", units=
"pix",
369 doc=
"X extent of box")
370 height = schema.addField(
"height", type=
"I", units=
"pix",
371 doc=
"Y extent of box")
375 for i, defect
in enumerate(self.
_defects):
376 box = defect.getBBox()
377 table[i][x] = box.getBeginX()
378 table[i][y] = box.getBeginY()
379 table[i][width] = box.getWidth()
380 table[i][height] = box.getHeight()
385 metadata[SCHEMA_NAME_KEY] =
"Simple" 386 metadata[SCHEMA_VERSION_KEY] = 1
387 table.setMetadata(metadata)
392 """Write the defects out to a text file with the specified name. 397 Name of the file to write. The file extension ".ecsv" will 403 The name of the file used to write the data (which may be 404 different from the supplied name given the change to file 409 The file is written to ECSV format and will include any metadata 410 associated with the `Defects`. 415 table = afwTable.asAstropy()
417 metadata = afwTable.getMetadata()
418 now = datetime.datetime.utcnow()
419 metadata[
"DATE"] = now.isoformat()
420 metadata[
"CALIB_CREATION_DATE"] = now.strftime(
"%Y-%m-%d")
421 metadata[
"CALIB_CREATION_TIME"] = now.strftime(
"%T %Z").strip()
423 table.meta = metadata.toDict()
426 path, ext = os.path.splitext(filename)
427 filename = path +
".ecsv" 428 table.write(filename, format=
"ascii.ecsv")
432 def _get_values(values, n=1):
433 """Retrieve N values from the supplied values. 437 values : `numbers.Number` or `list` or `np.array` 440 Number of values to retrieve. 444 vals : `list` or `np.array` or `numbers.Number` 445 Single value from supplied list if ``n`` is 1, or `list` 446 containing first ``n`` values from supplied values. 450 Some supplied tables have vectors in some columns that can also 451 be scalars. This method can be used to get the first number as 452 a scalar or the first N items from a vector as a vector. 455 if isinstance(values, numbers.Number):
464 """Construct a `Defects` from the contents of a 465 `~lsst.afw.table.BaseCatalog`. 469 table : `lsst.afw.table.BaseCatalog` 470 Table with one row per defect. 479 Two table formats are recognized. The first is the 480 `FITS regions <https://fits.gsfc.nasa.gov/registry/region.html>`_ 481 definition tabular format written by `toFitsRegionTable` where the 482 pixel origin is corrected from FITS 1-based to a 0-based origin. 483 The second is the legacy defects format using columns ``x0``, ``y0`` 484 (bottom left hand pixel of box in 0-based coordinates), ``width`` 487 The FITS standard regions can only read BOX, POINT, or ROTBOX with 488 a zero degree rotation. 493 schema = table.getSchema()
496 if "X" in schema
and "Y" in schema
and "R" in schema and "SHAPE" in schema:
500 elif "x0" in schema
and "y0" in schema
and "width" in schema
and "height" in schema:
505 raise ValueError(
"Unsupported schema for defects extraction")
508 record = r.extract(
"*")
516 shape = record[
"SHAPE"].upper()
521 elif shape ==
"POINT":
525 elif shape ==
"ROTBOX":
529 if math.isclose(rotang % 90.0, 0.0):
532 if math.isclose(rotang % 180.0, 0.0):
541 log.warning(
"Defect can not be defined using ROTBOX with non-aligned rotation angle")
544 log.warning(
"Defect lists can only be defined using BOX or POINT not %s", shape)
547 elif "x0" in record
and "y0" in record
and "width" in record
and "height" in record:
552 defectList.append(box)
554 defects = cls(defectList)
555 defects.setMetadata(table.getMetadata())
558 metadata = defects.getMetadata()
559 for k
in (SCHEMA_NAME_KEY, SCHEMA_VERSION_KEY):
567 """Read defect list from FITS table. 572 Arguments to be forwarded to 573 `lsst.afw.table.BaseCatalog.writeFits`. 578 Defects read from a FITS table. 580 table = lsst.afw.table.BaseCatalog.readFits(*args)
585 """Read defect list from standard format text table file. 590 Name of the file containing the defects definitions. 595 Defects read from a FITS table. 597 table = astropy.table.Table.read(filename)
601 for colName
in table.columns:
602 schema.addField(colName, units=str(table[colName].unit),
603 type=table[colName].dtype.type)
608 afwTable.resize(len(table))
609 for colName
in table.columns:
611 afwTable[colName] = table[colName]
615 for k, v
in table.meta.items():
617 afwTable.setMetadata(metadata)
624 """Read defects information from a legacy LSST format text file. 629 Name of text file containing the defect information. 638 These defect text files are used as the human readable definitions 639 of defects in calibration data definition repositories. The format 640 is to use four columns defined as follows: 643 X coordinate of bottom left corner of box. 645 Y coordinate of bottom left corner of box. 651 Files of this format were used historically to represent defects 652 in simple text form. Use `Defects.readText` and `Defects.writeText` 653 to use the more modern format. 657 defect_array = np.loadtxt(filename,
658 dtype=[(
"x0",
"int"), (
"y0",
"int"),
659 (
"x_extent",
"int"), (
"y_extent",
"int")])
663 for row
in defect_array)
667 """Compute a defect list from a footprint list, optionally growing 672 fpList : `list` of `lsst.afw.detection.Footprint` 673 Footprint list to process. 685 """Compute a defect list from a specified mask plane. 689 maskedImage : `lsst.afw.image.MaskedImage` 691 maskName : `str` or `list` 692 Mask plane name, or list of names to convert. 697 Defect list constructed from masked pixels. 699 mask = maskedImage.getMask()
701 lsst.afw.detection.Threshold.BITMASK)
def fromFootprintList(cls, fpList)
def readText(cls, filename)
def maskPixels(self, maskedImage, maskName="BAD")
def toFitsRegionTable(self)
def setMetadata(self, metadata=None)
def _check_value(self, value)
def __setitem__(self, index, value)
def __init__(self, defectList=None, metadata=None)
def policyToBadRegionList(policyFile)
def readLsstDefectsFile(cls, filename)
def __getitem__(self, index)
std::vector< lsst::geom::Box2I > footprintToBBoxList(Footprint const &footprint)
def writeFits(self, args)
def __delitem__(self, index)
def fromTable(cls, table)
def insert(self, index, value)
static Box2I makeCenteredBox(Point2D const ¢er, Extent const &size)
def _get_values(values, n=1)
def writeText(self, filename)
def fromMask(cls, maskedImage, maskName)