LSSTApplications  19.0.0-10-g920eed2,19.0.0-11-g48a0200+2,19.0.0-18-gfc4e62b+13,19.0.0-2-g3b2f90d+2,19.0.0-2-gd671419+5,19.0.0-20-g5a5a17ab+11,19.0.0-21-g2644856+13,19.0.0-23-g84eeccb+1,19.0.0-24-g878c510+1,19.0.0-25-g6c8df7140,19.0.0-25-gb330496+1,19.0.0-3-g2b32d65+5,19.0.0-3-g8227491+12,19.0.0-3-g9c54d0d+12,19.0.0-3-gca68e65+8,19.0.0-3-gcfc5f51+5,19.0.0-3-ge110943+11,19.0.0-3-ge74d124,19.0.0-3-gfe04aa6+13,19.0.0-30-g9c3fd16+1,19.0.0-4-g06f5963+5,19.0.0-4-g3d16501+13,19.0.0-4-g4a9c019+5,19.0.0-4-g5a8b323,19.0.0-4-g66397f0+1,19.0.0-4-g8278b9b+1,19.0.0-4-g8557e14,19.0.0-4-g8964aba+13,19.0.0-4-ge404a01+12,19.0.0-5-g40f3a5a,19.0.0-5-g4db63b3,19.0.0-5-gfb03ce7+13,19.0.0-6-gbaebbfb+12,19.0.0-61-gec4c6e08+1,19.0.0-7-g039c0b5+11,19.0.0-7-gbea9075+4,19.0.0-7-gc567de5+13,19.0.0-71-g41c0270,19.0.0-9-g2f02add+1,19.0.0-9-g463f923+12,w.2020.22
LSSTDataManagementBasePackage
defects.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 #
4 # Copyright 2008-2017 AURA/LSST.
5 #
6 # This product includes software developed by the
7 # LSST Project (http://www.lsst.org/).
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the LSST License Statement and
20 # the GNU General Public License along with this program. If not,
21 # see <https://www.lsstcorp.org/LegalNotices/>.
22 #
23 """Support for image defects"""
24 
25 __all__ = ("Defects",)
26 
27 import logging
28 import itertools
29 import collections.abc
30 import contextlib
31 import numpy as np
32 import copy
33 import datetime
34 import math
35 import numbers
36 import os.path
37 import warnings
38 import astropy.table
39 
40 import lsst.geom
41 import lsst.afw.table
42 import lsst.afw.detection
43 import lsst.afw.image
44 import lsst.afw.geom
45 from lsst.daf.base import PropertyList
46 
47 from . import Defect
48 
49 log = logging.getLogger(__name__)
50 
51 SCHEMA_NAME_KEY = "DEFECTS_SCHEMA"
52 SCHEMA_VERSION_KEY = "DEFECTS_SCHEMA_VERSION"
53 
54 
55 class Defects(collections.abc.MutableSequence):
56  """Collection of `lsst.meas.algorithms.Defect`.
57 
58  Parameters
59  ----------
60  defectList : iterable of `lsst.meas.algorithms.Defect`
61  or `lsst.geom.BoxI`, optional
62  Collections of defects to apply to the image.
63  metadata : `lsst.daf.base.PropertyList`, optional
64  Metadata to associate with the defects. Will be copied and
65  overwrite existing metadata, if any. If not supplied the existing
66  metadata will be reset.
67  normalize_on_init : `bool`
68  If True, normalization is applied to the defects in ``defectList`` to
69  remove duplicates, eliminate overlaps, etc.
70 
71  Notes
72  -----
73  Defects are stored within this collection in a "reduced" or "normalized"
74  form: rather than simply storing the bounding boxes which are added to the
75  collection, we eliminate overlaps and duplicates. This normalization
76  procedure may introduce overhead when adding many new defects; it may be
77  temporarily disabled using the `Defects.bulk_update` context manager if
78  necessary.
79  """
80 
81  _OBSTYPE = "defects"
82  """The calibration type used for ingest."""
83 
84  def __init__(self, defectList=None, metadata=None, *, normalize_on_init=True):
85  self._defects = []
86 
87  if defectList is not None:
88  self._bulk_update = True
89  for d in defectList:
90  self.append(d)
91  self._bulk_update = False
92 
93  if normalize_on_init:
94  self._normalize()
95 
96  if metadata is not None:
97  self._metadata = metadata
98  else:
99  self.setMetadata()
100 
101  def _check_value(self, value):
102  """Check that the supplied value is a `~lsst.meas.algorithms.Defect`
103  or can be converted to one.
104 
105  Parameters
106  ----------
107  value : `object`
108  Value to check.
109 
110  Returns
111  -------
112  new : `~lsst.meas.algorithms.Defect`
113  Either the supplied value or a new object derived from it.
114 
115  Raises
116  ------
117  ValueError
118  Raised if the supplied value can not be converted to
119  `~lsst.meas.algorithms.Defect`
120  """
121  if isinstance(value, Defect):
122  pass
123  elif isinstance(value, lsst.geom.BoxI):
124  value = Defect(value)
125  elif isinstance(value, lsst.geom.PointI):
126  value = Defect(lsst.geom.Box2I(value, lsst.geom.Extent2I(1, 1)))
127  elif isinstance(value, lsst.afw.image.DefectBase):
128  value = Defect(value.getBBox())
129  else:
130  raise ValueError(f"Defects must be of type Defect, BoxI, or PointI, not '{value!r}'")
131  return value
132 
133  def __len__(self):
134  return len(self._defects)
135 
136  def __getitem__(self, index):
137  return self._defects[index]
138 
139  def __setitem__(self, index, value):
140  """Can be given a `~lsst.meas.algorithms.Defect` or a `lsst.geom.BoxI`
141  """
142  self._defects[index] = self._check_value(value)
143  self._normalize()
144 
145  def __iter__(self):
146  return iter(self._defects)
147 
148  def __delitem__(self, index):
149  del self._defects[index]
150 
151  def __eq__(self, other):
152  """Compare if two `Defects` are equal.
153 
154  Two `Defects` are equal if their bounding boxes are equal and in
155  the same order. Metadata content is ignored.
156  """
157  if not isinstance(other, self.__class__):
158  return False
159 
160  # checking the bboxes with zip() only works if same length
161  if len(self) != len(other):
162  return False
163 
164  # Assume equal if bounding boxes are equal
165  for d1, d2 in zip(self, other):
166  if d1.getBBox() != d2.getBBox():
167  return False
168 
169  return True
170 
171  def __str__(self):
172  return "Defects(" + ",".join(str(d.getBBox()) for d in self) + ")"
173 
174  def _normalize(self):
175  """Recalculate defect bounding boxes for efficiency.
176 
177  Notes
178  -----
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.
183  """
184  # In bulk-update mode, normalization is a no-op.
185  if self._bulk_update:
186  return
187 
188  # work out the minimum and maximum bounds from all defect regions.
189  minX, minY, maxX, maxY = float('inf'), float('inf'), float('-inf'), float('-inf')
190  for defect in self:
191  bbox = defect.getBBox()
192  minX = min(minX, bbox.getMinX())
193  minY = min(minY, bbox.getMinY())
194  maxX = max(maxX, bbox.getMaxX())
195  maxY = max(maxY, bbox.getMaxY())
196 
197  region = lsst.geom.Box2I(lsst.geom.Point2I(minX, minY),
198  lsst.geom.Point2I(maxX, maxY))
199 
200  mi = lsst.afw.image.MaskedImageF(region)
201  self.maskPixels(mi, maskName="BAD")
202  self._defects = Defects.fromMask(mi, "BAD")._defects
203 
204  @contextlib.contextmanager
205  def bulk_update(self):
206  """Temporarily suspend normalization of the defect list.
207  """
208  self._bulk_update = True
209  try:
210  yield
211  finally:
212  self._bulk_update = False
213  self._normalize()
214 
215  def insert(self, index, value):
216  self._defects.insert(index, self._check_value(value))
217  self._normalize()
218 
219  def getMetadata(self):
220  """Retrieve metadata associated with these `Defects`.
221 
222  Returns
223  -------
224  meta : `lsst.daf.base.PropertyList`
225  Metadata. The returned `~lsst.daf.base.PropertyList` can be
226  modified by the caller and the changes will be written to
227  external files.
228  """
229  return self._metadata
230 
231  def setMetadata(self, metadata=None):
232  """Store a copy of the supplied metadata with the defects.
233 
234  Parameters
235  ----------
236  metadata : `lsst.daf.base.PropertyList`, optional
237  Metadata to associate with the defects. Will be copied and
238  overwrite existing metadata. If not supplied the existing
239  metadata will be reset.
240  """
241  if metadata is None:
242  self._metadata = PropertyList()
243  else:
244  self._metadata = copy.copy(metadata)
245 
246  # Ensure that we have the obs type required by calibration ingest
247  self._metadata["OBSTYPE"] = self._OBSTYPE
248 
249  def copy(self):
250  """Copy the defects to a new list, creating new defects from the
251  bounding boxes.
252 
253  Returns
254  -------
255  new : `Defects`
256  New list with new `Defect` entries.
257 
258  Notes
259  -----
260  This is not a shallow copy in that new `Defect` instances are
261  created from the original bounding boxes. It's also not a deep
262  copy since the bounding boxes are not recreated.
263  """
264  return self.__class__(d.getBBox() for d in self)
265 
266  def transpose(self):
267  """Make a transposed copy of this defect list.
268 
269  Returns
270  -------
271  retDefectList : `Defects`
272  Transposed list of defects.
273  """
274  retDefectList = self.__class__()
275  for defect in self:
276  bbox = defect.getBBox()
277  dimensions = bbox.getDimensions()
278  nbbox = lsst.geom.Box2I(lsst.geom.Point2I(bbox.getMinY(), bbox.getMinX()),
279  lsst.geom.Extent2I(dimensions[1], dimensions[0]))
280  retDefectList.append(nbbox)
281  return retDefectList
282 
283  def maskPixels(self, maskedImage, maskName="BAD"):
284  """Set mask plane based on these defects.
285 
286  Parameters
287  ----------
288  maskedImage : `lsst.afw.image.MaskedImage`
289  Image to process. Only the mask plane is updated.
290  maskName : str, optional
291  Mask plane name to use.
292  """
293  # mask bad pixels
294  mask = maskedImage.getMask()
295  bitmask = mask.getPlaneBitMask(maskName)
296  for defect in self:
297  bbox = defect.getBBox()
298  lsst.afw.geom.SpanSet(bbox).clippedTo(mask.getBBox()).setMask(mask, bitmask)
299 
300  def toFitsRegionTable(self):
301  """Convert defect list to `~lsst.afw.table.BaseCatalog` using the
302  FITS region standard.
303 
304  Returns
305  -------
306  table : `lsst.afw.table.BaseCatalog`
307  Defects in tabular form.
308 
309  Notes
310  -----
311  The table created uses the
312  `FITS regions <https://fits.gsfc.nasa.gov/registry/region.html>`_
313  definition tabular format. The ``X`` and ``Y`` coordinates are
314  converted to FITS Physical coordinates that have origin pixel (1, 1)
315  rather than the (0, 0) used in LSST software.
316  """
317 
318  nrows = len(self._defects)
319 
320  schema = lsst.afw.table.Schema()
321  x = schema.addField("X", type="D", units="pix", doc="X coordinate of center of shape")
322  y = schema.addField("Y", type="D", units="pix", doc="Y coordinate of center of shape")
323  shape = schema.addField("SHAPE", type="String", size=16, doc="Shape defined by these values")
324  r = schema.addField("R", type="ArrayD", size=2, units="pix", doc="Extents")
325  rotang = schema.addField("ROTANG", type="D", units="deg", doc="Rotation angle")
326  component = schema.addField("COMPONENT", type="I", doc="Index of this region")
327  table = lsst.afw.table.BaseCatalog(schema)
328  table.resize(nrows)
329 
330  if nrows:
331  # Adding entire columns is more efficient than adding
332  # each element separately
333  xCol = []
334  yCol = []
335  rCol = []
336 
337  for i, defect in enumerate(self._defects):
338  box = defect.getBBox()
339  center = box.getCenter()
340  # Correct for the FITS 1-based offset
341  xCol.append(center.getX() + 1.0)
342  yCol.append(center.getY() + 1.0)
343 
344  width = box.width
345  height = box.height
346 
347  if width == 1 and height == 1:
348  # Call this a point
349  shapeType = "POINT"
350  else:
351  shapeType = "BOX"
352 
353  # Strings have to be added per row
354  table[i][shape] = shapeType
355 
356  rCol.append(np.array([width, height], dtype=np.float64))
357 
358  # Assign the columns
359  table[x] = np.array(xCol, dtype=np.float64)
360  table[y] = np.array(yCol, dtype=np.float64)
361 
362  table[r] = np.array(rCol)
363  table[rotang] = np.zeros(nrows, dtype=np.float64)
364  table[component] = np.arange(nrows)
365 
366  # Set some metadata in the table (force OBSTYPE to exist)
367  metadata = copy.copy(self.getMetadata())
368  metadata["OBSTYPE"] = self._OBSTYPE
369  metadata[SCHEMA_NAME_KEY] = "FITS Region"
370  metadata[SCHEMA_VERSION_KEY] = 1
371  table.setMetadata(metadata)
372 
373  return table
374 
375  def writeFits(self, *args):
376  """Write defect list to FITS.
377 
378  Parameters
379  ----------
380  *args
381  Arguments to be forwarded to
382  `lsst.afw.table.BaseCatalog.writeFits`.
383  """
384  table = self.toFitsRegionTable()
385 
386  # Add some additional headers useful for tracking purposes
387  metadata = table.getMetadata()
388  now = datetime.datetime.utcnow()
389  metadata["DATE"] = now.isoformat()
390  metadata["CALIB_CREATION_DATE"] = now.strftime("%Y-%m-%d")
391  metadata["CALIB_CREATION_TIME"] = now.strftime("%T %Z").strip()
392 
393  table.writeFits(*args)
394 
395  def toSimpleTable(self):
396  """Convert defects to a simple table form that we use to write
397  to text files.
398 
399  Returns
400  -------
401  table : `lsst.afw.table.BaseCatalog`
402  Defects in simple tabular form.
403 
404  Notes
405  -----
406  These defect tables are used as the human readable definitions
407  of defects in calibration data definition repositories. The format
408  is to use four columns defined as follows:
409 
410  x0 : `int`
411  X coordinate of bottom left corner of box.
412  y0 : `int`
413  Y coordinate of bottom left corner of box.
414  width : `int`
415  X extent of the box.
416  height : `int`
417  Y extent of the box.
418  """
419  schema = lsst.afw.table.Schema()
420  x = schema.addField("x0", type="I", units="pix",
421  doc="X coordinate of bottom left corner of box")
422  y = schema.addField("y0", type="I", units="pix",
423  doc="Y coordinate of bottom left corner of box")
424  width = schema.addField("width", type="I", units="pix",
425  doc="X extent of box")
426  height = schema.addField("height", type="I", units="pix",
427  doc="Y extent of box")
428  table = lsst.afw.table.BaseCatalog(schema)
429 
430  nrows = len(self._defects)
431  table.resize(nrows)
432 
433  if nrows:
434 
435  xCol = []
436  yCol = []
437  widthCol = []
438  heightCol = []
439 
440  for defect in self._defects:
441  box = defect.getBBox()
442  xCol.append(box.getBeginX())
443  yCol.append(box.getBeginY())
444  widthCol.append(box.getWidth())
445  heightCol.append(box.getHeight())
446 
447  table[x] = np.array(xCol, dtype=np.int64)
448  table[y] = np.array(yCol, dtype=np.int64)
449  table[width] = np.array(widthCol, dtype=np.int64)
450  table[height] = np.array(heightCol, dtype=np.int64)
451 
452  # Set some metadata in the table (force OBSTYPE to exist)
453  metadata = copy.copy(self.getMetadata())
454  metadata["OBSTYPE"] = self._OBSTYPE
455  metadata[SCHEMA_NAME_KEY] = "Simple"
456  metadata[SCHEMA_VERSION_KEY] = 1
457  table.setMetadata(metadata)
458 
459  return table
460 
461  def writeText(self, filename):
462  """Write the defects out to a text file with the specified name.
463 
464  Parameters
465  ----------
466  filename : `str`
467  Name of the file to write. The file extension ".ecsv" will
468  always be used.
469 
470  Returns
471  -------
472  used : `str`
473  The name of the file used to write the data (which may be
474  different from the supplied name given the change to file
475  extension).
476 
477  Notes
478  -----
479  The file is written to ECSV format and will include any metadata
480  associated with the `Defects`.
481  """
482 
483  # Using astropy table is the easiest way to serialize to ecsv
484  afwTable = self.toSimpleTable()
485  table = afwTable.asAstropy()
486 
487  metadata = afwTable.getMetadata()
488  now = datetime.datetime.utcnow()
489  metadata["DATE"] = now.isoformat()
490  metadata["CALIB_CREATION_DATE"] = now.strftime("%Y-%m-%d")
491  metadata["CALIB_CREATION_TIME"] = now.strftime("%T %Z").strip()
492 
493  table.meta = metadata.toDict()
494 
495  # Force file extension to .ecsv
496  path, ext = os.path.splitext(filename)
497  filename = path + ".ecsv"
498  table.write(filename, format="ascii.ecsv")
499  return filename
500 
501  @staticmethod
502  def _get_values(values, n=1):
503  """Retrieve N values from the supplied values.
504 
505  Parameters
506  ----------
507  values : `numbers.Number` or `list` or `np.array`
508  Input values.
509  n : `int`
510  Number of values to retrieve.
511 
512  Returns
513  -------
514  vals : `list` or `np.array` or `numbers.Number`
515  Single value from supplied list if ``n`` is 1, or `list`
516  containing first ``n`` values from supplied values.
517 
518  Notes
519  -----
520  Some supplied tables have vectors in some columns that can also
521  be scalars. This method can be used to get the first number as
522  a scalar or the first N items from a vector as a vector.
523  """
524  if n == 1:
525  if isinstance(values, numbers.Number):
526  return values
527  else:
528  return values[0]
529 
530  return values[:n]
531 
532  @classmethod
533  def fromTable(cls, table):
534  """Construct a `Defects` from the contents of a
535  `~lsst.afw.table.BaseCatalog`.
536 
537  Parameters
538  ----------
539  table : `lsst.afw.table.BaseCatalog`
540  Table with one row per defect.
541 
542  Returns
543  -------
544  defects : `Defects`
545  A `Defects` list.
546 
547  Notes
548  -----
549  Two table formats are recognized. The first is the
550  `FITS regions <https://fits.gsfc.nasa.gov/registry/region.html>`_
551  definition tabular format written by `toFitsRegionTable` where the
552  pixel origin is corrected from FITS 1-based to a 0-based origin.
553  The second is the legacy defects format using columns ``x0``, ``y0``
554  (bottom left hand pixel of box in 0-based coordinates), ``width``
555  and ``height``.
556 
557  The FITS standard regions can only read BOX, POINT, or ROTBOX with
558  a zero degree rotation.
559  """
560 
561  defectList = []
562 
563  schema = table.getSchema()
564 
565  # Check schema to see which definitions we have
566  if "X" in schema and "Y" in schema and "R" in schema and "SHAPE" in schema:
567  # This is a FITS region style table
568  isFitsRegion = True
569 
570  # Preselect the keys
571  xKey = schema["X"].asKey()
572  yKey = schema["Y"].asKey()
573  shapeKey = schema["SHAPE"].asKey()
574  rKey = schema["R"].asKey()
575  rotangKey = schema["ROTANG"].asKey()
576 
577  elif "x0" in schema and "y0" in schema and "width" in schema and "height" in schema:
578  # This is a classic LSST-style defect table
579  isFitsRegion = False
580 
581  # Preselect the keys
582  xKey = schema["x0"].asKey()
583  yKey = schema["y0"].asKey()
584  widthKey = schema["width"].asKey()
585  heightKey = schema["height"].asKey()
586 
587  else:
588  raise ValueError("Unsupported schema for defects extraction")
589 
590  for record in table:
591 
592  if isFitsRegion:
593  # Coordinates can be arrays (some shapes in the standard
594  # require this)
595  # Correct for FITS 1-based origin
596  xcen = cls._get_values(record[xKey]) - 1.0
597  ycen = cls._get_values(record[yKey]) - 1.0
598  shape = record[shapeKey].upper()
599  if shape == "BOX":
601  lsst.geom.Extent2I(cls._get_values(record[rKey],
602  n=2)))
603  elif shape == "POINT":
604  # Handle the case where we have an externally created
605  # FITS file.
606  box = lsst.geom.Point2I(xcen, ycen)
607  elif shape == "ROTBOX":
608  # Astropy regions always writes ROTBOX
609  rotang = cls._get_values(record[rotangKey])
610  # We can support 0 or 90 deg
611  if math.isclose(rotang % 90.0, 0.0):
612  # Two values required
613  r = cls._get_values(record[rKey], n=2)
614  if math.isclose(rotang % 180.0, 0.0):
615  width = r[0]
616  height = r[1]
617  else:
618  width = r[1]
619  height = r[0]
621  lsst.geom.Extent2I(width, height))
622  else:
623  log.warning("Defect can not be defined using ROTBOX with non-aligned rotation angle")
624  continue
625  else:
626  log.warning("Defect lists can only be defined using BOX or POINT not %s", shape)
627  continue
628 
629  else:
630  # This is a classic LSST-style defect table
631  box = lsst.geom.Box2I(lsst.geom.Point2I(record[xKey], record[yKey]),
632  lsst.geom.Extent2I(record[widthKey], record[heightKey]))
633 
634  defectList.append(box)
635 
636  defects = cls(defectList)
637  defects.setMetadata(table.getMetadata())
638 
639  # Once read, the schema headers are irrelevant
640  metadata = defects.getMetadata()
641  for k in (SCHEMA_NAME_KEY, SCHEMA_VERSION_KEY):
642  if k in metadata:
643  del metadata[k]
644 
645  return defects
646 
647  @classmethod
648  def readFits(cls, *args):
649  """Read defect list from FITS table.
650 
651  Parameters
652  ----------
653  *args
654  Arguments to be forwarded to
655  `lsst.afw.table.BaseCatalog.writeFits`.
656 
657  Returns
658  -------
659  defects : `Defects`
660  Defects read from a FITS table.
661  """
662  table = lsst.afw.table.BaseCatalog.readFits(*args)
663  return cls.fromTable(table)
664 
665  @classmethod
666  def readText(cls, filename):
667  """Read defect list from standard format text table file.
668 
669  Parameters
670  ----------
671  filename : `str`
672  Name of the file containing the defects definitions.
673 
674  Returns
675  -------
676  defects : `Defects`
677  Defects read from a FITS table.
678  """
679  with warnings.catch_warnings():
680  # Squash warnings due to astropy failure to close files; we think
681  # this is a real problem, but the warnings are even worse.
682  # https://github.com/astropy/astropy/issues/8673
683  warnings.filterwarnings("ignore", category=ResourceWarning, module="astropy.io.ascii")
684  table = astropy.table.Table.read(filename)
685 
686  # Need to convert the Astropy table to afw table
687  schema = lsst.afw.table.Schema()
688  for colName in table.columns:
689  schema.addField(colName, units=str(table[colName].unit),
690  type=table[colName].dtype.type)
691 
692  # Create AFW table that is required by fromTable()
693  afwTable = lsst.afw.table.BaseCatalog(schema)
694 
695  afwTable.resize(len(table))
696  for colName in table.columns:
697  # String columns will fail -- currently we do not expect any
698  afwTable[colName] = table[colName]
699 
700  # Copy in the metadata from the astropy table
701  metadata = PropertyList()
702  for k, v in table.meta.items():
703  metadata[k] = v
704  afwTable.setMetadata(metadata)
705 
706  # Extract defect information from the table itself
707  return cls.fromTable(afwTable)
708 
709  @classmethod
710  def readLsstDefectsFile(cls, filename):
711  """Read defects information from a legacy LSST format text file.
712 
713  Parameters
714  ----------
715  filename : `str`
716  Name of text file containing the defect information.
717 
718  Returns
719  -------
720  defects : `Defects`
721  The defects.
722 
723  Notes
724  -----
725  These defect text files are used as the human readable definitions
726  of defects in calibration data definition repositories. The format
727  is to use four columns defined as follows:
728 
729  x0 : `int`
730  X coordinate of bottom left corner of box.
731  y0 : `int`
732  Y coordinate of bottom left corner of box.
733  width : `int`
734  X extent of the box.
735  height : `int`
736  Y extent of the box.
737 
738  Files of this format were used historically to represent defects
739  in simple text form. Use `Defects.readText` and `Defects.writeText`
740  to use the more modern format.
741  """
742  # Use loadtxt so that ValueError is thrown if the file contains a
743  # non-integer value. genfromtxt converts bad values to -1.
744  defect_array = np.loadtxt(filename,
745  dtype=[("x0", "int"), ("y0", "int"),
746  ("x_extent", "int"), ("y_extent", "int")])
747 
748  return cls(lsst.geom.Box2I(lsst.geom.Point2I(row["x0"], row["y0"]),
749  lsst.geom.Extent2I(row["x_extent"], row["y_extent"]))
750  for row in defect_array)
751 
752  @classmethod
753  def fromFootprintList(cls, fpList):
754  """Compute a defect list from a footprint list, optionally growing
755  the footprints.
756 
757  Parameters
758  ----------
759  fpList : `list` of `lsst.afw.detection.Footprint`
760  Footprint list to process.
761 
762  Returns
763  -------
764  defects : `Defects`
765  List of defects.
766  """
767  # normalize_on_init is set to False to avoid recursively calling
768  # fromMask/fromFootprintList in Defects.__init__.
769  return cls(itertools.chain.from_iterable(lsst.afw.detection.footprintToBBoxList(fp)
770  for fp in fpList), normalize_on_init=False)
771 
772  @classmethod
773  def fromMask(cls, maskedImage, maskName):
774  """Compute a defect list from a specified mask plane.
775 
776  Parameters
777  ----------
778  maskedImage : `lsst.afw.image.MaskedImage`
779  Image to process.
780  maskName : `str` or `list`
781  Mask plane name, or list of names to convert.
782 
783  Returns
784  -------
785  defects : `Defects`
786  Defect list constructed from masked pixels.
787  """
788  mask = maskedImage.getMask()
789  thresh = lsst.afw.detection.Threshold(mask.getPlaneBitMask(maskName),
790  lsst.afw.detection.Threshold.BITMASK)
791  fpList = lsst.afw.detection.FootprintSet(mask, thresh).getFootprints()
792  return cls.fromFootprintList(fpList)
lsst::meas::algorithms.defects.Defects.__setitem__
def __setitem__(self, index, value)
Definition: defects.py:139
lsst::afw::image
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
Definition: imageAlgorithm.dox:1
lsst::meas::algorithms.defects.Defects._get_values
def _get_values(values, n=1)
Definition: defects.py:502
lsst::meas::algorithms.defects.Defects
Definition: defects.py:55
lsst::meas::algorithms.defects.Defects.__delitem__
def __delitem__(self, index)
Definition: defects.py:148
lsst::meas::algorithms.defects.Defects.__init__
def __init__(self, defectList=None, metadata=None, *normalize_on_init=True)
Definition: defects.py:84
lsst::geom::Box2I::makeCenteredBox
static Box2I makeCenteredBox(Point2D const &center, Extent const &size)
Create a box centered as closely as possible on a particular point.
Definition: Box.cc:97
lsst::meas::algorithms.defects.Defects._check_value
def _check_value(self, value)
Definition: defects.py:101
lsst::meas::algorithms.defects.Defects.bulk_update
def bulk_update(self)
Definition: defects.py:205
lsst::daf::base::PropertyList
Class for storing ordered metadata with comments.
Definition: PropertyList.h:68
lsst::meas::algorithms.defects.Defects.getMetadata
def getMetadata(self)
Definition: defects.py:219
lsst::meas::algorithms.defects.Defects._normalize
def _normalize(self)
Definition: defects.py:174
lsst::afw::table::Schema
Defines the fields and offsets for a table.
Definition: Schema.h:50
strip
bool strip
Definition: fits.cc:911
lsst::meas::algorithms.defects.Defects.__str__
def __str__(self)
Definition: defects.py:171
lsst::afw::geom.transform.transformContinued.cls
cls
Definition: transformContinued.py:33
lsst::meas::algorithms.defects.Defects.fromFootprintList
def fromFootprintList(cls, fpList)
Definition: defects.py:753
lsst::meas::algorithms.defects.Defects._OBSTYPE
string _OBSTYPE
Definition: defects.py:81
lsst::meas::algorithms.defects.Defects.readText
def readText(cls, filename)
Definition: defects.py:666
lsst::meas::algorithms.defects.Defects.toSimpleTable
def toSimpleTable(self)
Definition: defects.py:395
lsst::afw::detection::FootprintSet
A set of Footprints, associated with a MaskedImage.
Definition: FootprintSet.h:53
lsst::afw::image::DefectBase
Encapsulate information about a bad portion of a detector.
Definition: Defect.h:41
lsst::meas::algorithms.defects.Defects.writeFits
def writeFits(self, *args)
Definition: defects.py:375
lsst::afw::geom::SpanSet
A compact representation of a collection of pixels.
Definition: SpanSet.h:78
lsst::afw::detection::footprintToBBoxList
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...
Definition: Footprint.cc:352
lsst::meas::algorithms.defects.Defects.__iter__
def __iter__(self)
Definition: defects.py:145
lsst::meas::algorithms.defects.Defects._defects
_defects
Definition: defects.py:85
lsst::afw::detection::Threshold
A Threshold is used to pass a threshold value to detection algorithms.
Definition: Threshold.h:43
lsst::meas::algorithms.defects.Defects.__eq__
def __eq__(self, other)
Definition: defects.py:151
lsst::meas::algorithms.defects.Defects.fromTable
def fromTable(cls, table)
Definition: defects.py:533
max
int max
Definition: BoundedField.cc:104
lsst::afw::table
Definition: table.dox:3
lsst::meas::algorithms.defects.Defects.setMetadata
def setMetadata(self, metadata=None)
Definition: defects.py:231
lsst::meas::algorithms.defects.Defects.fromMask
def fromMask(cls, maskedImage, maskName)
Definition: defects.py:773
lsst::afw::detection
Definition: Footprint.h:50
lsst::meas::algorithms.defects.Defects.transpose
def transpose(self)
Definition: defects.py:266
lsst::geom
Definition: geomOperators.dox:4
lsst::meas::algorithms.defects.Defects.__getitem__
def __getitem__(self, index)
Definition: defects.py:136
lsst::daf::base
Definition: Utils.h:47
lsst::meas::algorithms.defects.Defects.maskPixels
def maskPixels(self, maskedImage, maskName="BAD")
Definition: defects.py:283
min
int min
Definition: BoundedField.cc:103
lsst::meas::algorithms.defects.Defects.readLsstDefectsFile
def readLsstDefectsFile(cls, filename)
Definition: defects.py:710
lsst::geom::Point< int, 2 >
lsst::meas::algorithms.defects.Defects.copy
def copy(self)
Definition: defects.py:249
lsst::geom::Box2I
An integer coordinate rectangle.
Definition: Box.h:55
lsst::meas::algorithms.defects.Defects.readFits
def readFits(cls, *args)
Definition: defects.py:648
lsst::meas::algorithms.defects.Defects._metadata
_metadata
Definition: defects.py:97
lsst::meas::algorithms.defects.Defects.__len__
def __len__(self)
Definition: defects.py:133
lsst::meas::algorithms.defects.Defects._bulk_update
_bulk_update
Definition: defects.py:88
lsst::meas::algorithms.defects.Defects.toFitsRegionTable
def toFitsRegionTable(self)
Definition: defects.py:300
lsst::afw::table::CatalogT< BaseRecord >
lsst::geom::Extent< int, 2 >
astshim.fitsChanContinued.iter
def iter(self)
Definition: fitsChanContinued.py:88
lsst::meas::algorithms.defects.Defects.insert
def insert(self, index, value)
Definition: defects.py:215
lsst::meas::algorithms.defects.Defects.writeText
def writeText(self, filename)
Definition: defects.py:461
lsst::afw::geom
Definition: frameSetUtils.h:40