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 if len(self) != len(other):
176 for d1, d2
in zip(self, other):
177 if d1.getBBox() != d2.getBBox():
183 return "Defects(" +
",".join(
str(d.getBBox())
for d
in self) +
")" 189 """Retrieve metadata associated with these `Defects`. 193 meta : `lsst.daf.base.PropertyList` 194 Metadata. The returned `~lsst.daf.base.PropertyList` can be 195 modified by the caller and the changes will be written to 201 """Store a copy of the supplied metadata with the defects. 205 metadata : `lsst.daf.base.PropertyList`, optional 206 Metadata to associate with the defects. Will be copied and 207 overwrite existing metadata. If not supplied the existing 208 metadata will be reset. 219 """Copy the defects to a new list, creating new defects from the 225 New list with new `Defect` entries. 229 This is not a shallow copy in that new `Defect` instances are 230 created from the original bounding boxes. It's also not a deep 231 copy since the bounding boxes are not recreated. 233 return self.__class__(d.getBBox()
for d
in self)
236 """Make a transposed copy of this defect list. 240 retDefectList : `Defects` 241 Transposed list of defects. 243 retDefectList = self.__class__()
245 bbox = defect.getBBox()
246 dimensions = bbox.getDimensions()
249 retDefectList.append(nbbox)
253 """Set mask plane based on these defects. 257 maskedImage : `lsst.afw.image.MaskedImage` 258 Image to process. Only the mask plane is updated. 259 maskName : str, optional 260 Mask plane name to use. 263 mask = maskedImage.getMask()
264 bitmask = mask.getPlaneBitMask(maskName)
266 bbox = defect.getBBox()
270 """Convert defect list to `~lsst.afw.table.BaseCatalog` using the 271 FITS region standard. 275 table : `lsst.afw.table.BaseCatalog` 276 Defects in tabular form. 280 The table created uses the 281 `FITS regions <https://fits.gsfc.nasa.gov/registry/region.html>`_ 282 definition tabular format. The ``X`` and ``Y`` coordinates are 283 converted to FITS Physical coordinates that have origin pixel (1, 1) 284 rather than the (0, 0) used in LSST software. 287 x = schema.addField(
"X", type=
"D", units=
"pix", doc=
"X coordinate of center of shape")
288 y = schema.addField(
"Y", type=
"D", units=
"pix", doc=
"Y coordinate of center of shape")
289 shape = schema.addField(
"SHAPE", type=
"String", size=16, doc=
"Shape defined by these values")
290 r = schema.addField(
"R", type="ArrayD", size=2, units="pix", doc="Extents")
291 rotang = schema.addField(
"ROTANG", type=
"D", units=
"deg", doc=
"Rotation angle")
292 component = schema.addField(
"COMPONENT", type=
"I", doc=
"Index of this region")
296 for i, defect
in enumerate(self.
_defects):
297 box = defect.getBBox()
299 table[i][x] = box.getCenterX() + 1.0
300 table[i][y] = box.getCenterY() + 1.0
301 width = box.getWidth()
302 height = box.getHeight()
304 if width == 1
and height == 1:
309 table[i][shape] = shapeType
310 table[i][r] = np.array([width, height], dtype=np.float64)
311 table[i][rotang] = 0.0
312 table[i][component] = i
317 metadata[SCHEMA_NAME_KEY] =
"FITS Region" 318 metadata[SCHEMA_VERSION_KEY] = 1
319 table.setMetadata(metadata)
324 """Write defect list to FITS. 329 Arguments to be forwarded to 330 `lsst.afw.table.BaseCatalog.writeFits`. 335 metadata = table.getMetadata()
336 now = datetime.datetime.utcnow()
337 metadata[
"DATE"] = now.isoformat()
338 metadata[
"CALIB_CREATION_DATE"] = now.strftime(
"%Y-%m-%d")
339 metadata[
"CALIB_CREATION_TIME"] = now.strftime(
"%T %Z").
strip()
341 table.writeFits(*args)
344 """Convert defects to a simple table form that we use to write 349 table : `lsst.afw.table.BaseCatalog` 350 Defects in simple tabular form. 354 These defect tables are used as the human readable definitions 355 of defects in calibration data definition repositories. The format 356 is to use four columns defined as follows: 359 X coordinate of bottom left corner of box. 361 Y coordinate of bottom left corner of box. 368 x = schema.addField(
"x0", type=
"I", units=
"pix",
369 doc=
"X coordinate of bottom left corner of box")
370 y = schema.addField(
"y0", type=
"I", units=
"pix",
371 doc=
"Y coordinate of bottom left corner of box")
372 width = schema.addField(
"width", type=
"I", units=
"pix",
373 doc=
"X extent of box")
374 height = schema.addField(
"height", type=
"I", units=
"pix",
375 doc=
"Y extent of box")
379 for i, defect
in enumerate(self.
_defects):
380 box = defect.getBBox()
381 table[i][x] = box.getBeginX()
382 table[i][y] = box.getBeginY()
383 table[i][width] = box.getWidth()
384 table[i][height] = box.getHeight()
389 metadata[SCHEMA_NAME_KEY] =
"Simple" 390 metadata[SCHEMA_VERSION_KEY] = 1
391 table.setMetadata(metadata)
396 """Write the defects out to a text file with the specified name. 401 Name of the file to write. The file extension ".ecsv" will 407 The name of the file used to write the data (which may be 408 different from the supplied name given the change to file 413 The file is written to ECSV format and will include any metadata 414 associated with the `Defects`. 419 table = afwTable.asAstropy()
421 metadata = afwTable.getMetadata()
422 now = datetime.datetime.utcnow()
423 metadata[
"DATE"] = now.isoformat()
424 metadata[
"CALIB_CREATION_DATE"] = now.strftime(
"%Y-%m-%d")
425 metadata[
"CALIB_CREATION_TIME"] = now.strftime(
"%T %Z").
strip()
427 table.meta = metadata.toDict()
430 path, ext = os.path.splitext(filename)
431 filename = path +
".ecsv" 432 table.write(filename, format=
"ascii.ecsv")
436 def _get_values(values, n=1):
437 """Retrieve N values from the supplied values. 441 values : `numbers.Number` or `list` or `np.array` 444 Number of values to retrieve. 448 vals : `list` or `np.array` or `numbers.Number` 449 Single value from supplied list if ``n`` is 1, or `list` 450 containing first ``n`` values from supplied values. 454 Some supplied tables have vectors in some columns that can also 455 be scalars. This method can be used to get the first number as 456 a scalar or the first N items from a vector as a vector. 459 if isinstance(values, numbers.Number):
468 """Construct a `Defects` from the contents of a 469 `~lsst.afw.table.BaseCatalog`. 473 table : `lsst.afw.table.BaseCatalog` 474 Table with one row per defect. 483 Two table formats are recognized. The first is the 484 `FITS regions <https://fits.gsfc.nasa.gov/registry/region.html>`_ 485 definition tabular format written by `toFitsRegionTable` where the 486 pixel origin is corrected from FITS 1-based to a 0-based origin. 487 The second is the legacy defects format using columns ``x0``, ``y0`` 488 (bottom left hand pixel of box in 0-based coordinates), ``width`` 491 The FITS standard regions can only read BOX, POINT, or ROTBOX with 492 a zero degree rotation. 497 schema = table.getSchema()
500 if "X" in schema
and "Y" in schema
and "R" in schema and "SHAPE" in schema:
504 elif "x0" in schema
and "y0" in schema
and "width" in schema
and "height" in schema:
509 raise ValueError(
"Unsupported schema for defects extraction")
512 record = r.extract(
"*")
520 shape = record[
"SHAPE"].upper()
525 elif shape ==
"POINT":
529 elif shape ==
"ROTBOX":
533 if math.isclose(rotang % 90.0, 0.0):
536 if math.isclose(rotang % 180.0, 0.0):
545 log.warning(
"Defect can not be defined using ROTBOX with non-aligned rotation angle")
548 log.warning(
"Defect lists can only be defined using BOX or POINT not %s", shape)
551 elif "x0" in record
and "y0" in record
and "width" in record
and "height" in record:
556 defectList.append(box)
558 defects =
cls(defectList)
559 defects.setMetadata(table.getMetadata())
562 metadata = defects.getMetadata()
563 for k
in (SCHEMA_NAME_KEY, SCHEMA_VERSION_KEY):
571 """Read defect list from FITS table. 576 Arguments to be forwarded to 577 `lsst.afw.table.BaseCatalog.writeFits`. 582 Defects read from a FITS table. 584 table = lsst.afw.table.BaseCatalog.readFits(*args)
589 """Read defect list from standard format text table file. 594 Name of the file containing the defects definitions. 599 Defects read from a FITS table. 601 table = astropy.table.Table.read(filename)
605 for colName
in table.columns:
606 schema.addField(colName, units=
str(table[colName].unit),
607 type=table[colName].dtype.type)
612 afwTable.resize(len(table))
613 for colName
in table.columns:
615 afwTable[colName] = table[colName]
619 for k, v
in table.meta.items():
621 afwTable.setMetadata(metadata)
628 """Read defects information from a legacy LSST format text file. 633 Name of text file containing the defect information. 642 These defect text files are used as the human readable definitions 643 of defects in calibration data definition repositories. The format 644 is to use four columns defined as follows: 647 X coordinate of bottom left corner of box. 649 Y coordinate of bottom left corner of box. 655 Files of this format were used historically to represent defects 656 in simple text form. Use `Defects.readText` and `Defects.writeText` 657 to use the more modern format. 661 defect_array = np.loadtxt(filename,
662 dtype=[(
"x0",
"int"), (
"y0",
"int"),
663 (
"x_extent",
"int"), (
"y_extent",
"int")])
667 for row
in defect_array)
671 """Compute a defect list from a footprint list, optionally growing 676 fpList : `list` of `lsst.afw.detection.Footprint` 677 Footprint list to process. 689 """Compute a defect list from a specified mask plane. 693 maskedImage : `lsst.afw.image.MaskedImage` 695 maskName : `str` or `list` 696 Mask plane name, or list of names to convert. 701 Defect list constructed from masked pixels. 703 mask = maskedImage.getMask()
705 lsst.afw.detection.Threshold.BITMASK)
def fromFootprintList(cls, fpList)
Defines the fields and offsets for a table.
def readText(cls, filename)
Encapsulate information about a bad portion of a detector.
def maskPixels(self, maskedImage, maskName="BAD")
Class for storing ordered metadata with comments.
A compact representation of a collection of pixels.
def toFitsRegionTable(self)
A Threshold is used to pass a threshold value to detection algorithms.
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)
Return a list of BBoxs, whose union contains exactly the pixels in the footprint, neither more nor le...
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)
Create a box centered as closely as possible on a particular point.
def _get_values(values, n=1)
def writeText(self, filename)
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...
An integer coordinate rectangle.
def fromMask(cls, maskedImage, maskName)