LSSTApplications  18.0.0+53,19.0.0,19.0.0+1,19.0.0+15,19.0.0+16,19.0.0+18,19.0.0+23,19.0.0+3,19.0.0-1-g20d9b18+9,19.0.0-1-g425ff20,19.0.0-1-g5549ca4,19.0.0-1-g580fafe+9,19.0.0-1-g6fe20d0+2,19.0.0-1-g8c57eb9+9,19.0.0-1-gbfe0924,19.0.0-1-gdc0e4a7+14,19.0.0-1-ge272bc4+9,19.0.0-1-ge3aa853+1,19.0.0-14-gbb28fe44,19.0.0-16-g8258e2a,19.0.0-2-g0d9f9cd+16,19.0.0-2-g260436e,19.0.0-2-g9b11441+3,19.0.0-2-gd955cfd+22,19.0.0-3-g6513920,19.0.0-3-gc4f6e04,19.0.0-4-g41ffa1d+2,19.0.0-4-g725f80e+18,19.0.0-4-g75300c1e,19.0.0-4-ga8eba22,19.0.0-5-g0745e3f,19.0.0-6-g6637c4fb6,19.0.0-6-gb6b8b0a,19.0.0-7-gea0a0fe+5,w.2020.03
LSSTDataManagementBasePackage
utils.py
Go to the documentation of this file.
1 # This file is part of afw.
2 #
3 # Developed for the LSST Data Management System.
4 # This product includes software developed by the LSST Project
5 # (https://www.lsst.org).
6 # See the COPYRIGHT file at the top-level directory of this distribution
7 # for details of code ownership.
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 GNU General Public License
20 # along with this program. If not, see <https://www.gnu.org/licenses/>.
21 
22 """
23 Support for displaying cameraGeom objects.
24 """
25 
26 __all__ = ['prepareWcsData', 'plotFocalPlane', 'makeImageFromAmp', 'calcRawCcdBBox', 'makeImageFromCcd',
27  'FakeImageDataSource', 'ButlerImage', 'rawCallback', 'overlayCcdBoxes',
28  'showAmp', 'showCcd', 'getCcdInCamBBoxList', 'getCameraImageBBox',
29  'makeImageFromCamera', 'showCamera', 'makeFocalPlaneWcs', 'findAmp']
30 
31 import math
32 import numpy
33 
34 import lsst.geom
35 from lsst.afw.fits import FitsError
36 import lsst.afw.geom as afwGeom
37 import lsst.afw.image as afwImage
38 import lsst.afw.math as afwMath
39 import lsst.afw.cameraGeom as afwCameraGeom
40 import lsst.daf.base as dafBase
41 import lsst.log
42 import lsst.pex.exceptions as pexExceptions
43 
44 from .rotateBBoxBy90 import rotateBBoxBy90
45 from .assembleImage import assembleAmplifierImage, assembleAmplifierRawImage
46 from .cameraGeomLib import FIELD_ANGLE, FOCAL_PLANE
47 from lsst.afw.display.utils import _getDisplayFromDisplayOrFrame
48 from lsst.afw.cameraGeom import DetectorType
49 
50 import lsst.afw.display as afwDisplay
51 import lsst.afw.display.utils as displayUtils
52 
53 
54 def prepareWcsData(wcs, amp, isTrimmed=True):
55  """Put Wcs from an Amp image into CCD coordinates
56 
57  Parameters
58  ----------
59  wcs : `lsst.afw.geom.SkyWcs`
60  The WCS object to start from.
61  amp : `lsst.afw.table.AmpInfoRecord`
62  Amp object to use
63  isTrimmed : `bool`
64  Is the image to which the WCS refers trimmed of non-imaging pixels?
65 
66  Returns
67  -------
68  ampWcs : `lsst.afw.geom.SkyWcs`
69  The modified WCS.
70  """
71  if not amp.getHasRawInfo():
72  raise RuntimeError("Cannot modify wcs without raw amp information")
73  if isTrimmed:
74  ampBox = amp.getRawDataBBox()
75  else:
76  ampBox = amp.getRawBBox()
77  ampCenter = lsst.geom.Point2D(ampBox.getDimensions()/2.0)
78  wcs = afwGeom.makeFlippedWcs(wcs, amp.getRawFlipX(), amp.getRawFlipY(), ampCenter)
79  # Shift WCS for trimming
80  if isTrimmed:
81  trim_shift = ampBox.getMin() - amp.getBBox().getMin()
82  wcs = wcs.copyAtShiftedPixelOrigin(lsst.geom.Extent2D(-trim_shift.getX(), -trim_shift.getY()))
83  # Account for shift of amp data in larger ccd matrix
84  offset = amp.getRawXYOffset()
85  return wcs.copyAtShiftedPixelOrigin(lsst.geom.Extent2D(offset))
86 
87 
88 def plotFocalPlane(camera, fieldSizeDeg_x=0, fieldSizeDeg_y=None, dx=0.1, dy=0.1, figsize=(10., 10.),
89  useIds=False, showFig=True, savePath=None):
90  """Make a plot of the focal plane along with a set points that sample
91  the field of view.
92 
93  Parameters
94  ----------
95  camera : `lsst.afw.cameraGeom.Camera`
96  A camera object
97  fieldSizeDeg_x : `float`
98  Amount of the field to sample in x in degrees
99  fieldSizeDeg_y : `float` or `None`
100  Amount of the field to sample in y in degrees
101  dx : `float`
102  Spacing of sample points in x in degrees
103  dy : `float`
104  Spacing of sample points in y in degrees
105  figsize : `tuple` containing two `float`
106  Matplotlib style tuple indicating the size of the figure in inches
107  useIds : `bool`
108  Label detectors by name, not id?
109  showFig : `bool`
110  Display the figure on the screen?
111  savePath : `str` or `None`
112  If not `None`, save a copy of the figure to this name.
113  """
114  try:
115  from matplotlib.patches import Polygon
116  from matplotlib.collections import PatchCollection
117  import matplotlib.pyplot as plt
118  except ImportError:
119  raise ImportError(
120  "Can't run plotFocalPlane: matplotlib has not been set up")
121 
122  if fieldSizeDeg_x:
123  if fieldSizeDeg_y is None:
124  fieldSizeDeg_y = fieldSizeDeg_x
125 
126  field_gridx, field_gridy = numpy.meshgrid(
127  numpy.arange(0., fieldSizeDeg_x + dx, dx) - fieldSizeDeg_x/2.,
128  numpy.arange(0., fieldSizeDeg_y + dy, dy) - fieldSizeDeg_y/2.)
129  field_gridx, field_gridy = field_gridx.flatten(), field_gridy.flatten()
130  else:
131  field_gridx, field_gridy = [], []
132 
133  xs = []
134  ys = []
135  pcolors = []
136 
137  # compute focal plane positions corresponding to field angles field_gridx, field_gridy
138  posFieldAngleList = [lsst.geom.Point2D(x*lsst.geom.radians, y*lsst.geom.radians)
139  for x, y in zip(field_gridx, field_gridy)]
140  posFocalPlaneList = camera.transform(posFieldAngleList, FIELD_ANGLE, FOCAL_PLANE)
141  for posFocalPlane in posFocalPlaneList:
142  xs.append(posFocalPlane.getX())
143  ys.append(posFocalPlane.getY())
144  dets = camera.findDetectors(posFocalPlane, FOCAL_PLANE)
145  if len(dets) > 0:
146  pcolors.append('w')
147  else:
148  pcolors.append('k')
149 
150  colorMap = {DetectorType.SCIENCE: 'b', DetectorType.FOCUS: 'y',
151  DetectorType.GUIDER: 'g', DetectorType.WAVEFRONT: 'r'}
152 
153  patches = []
154  colors = []
155  plt.figure(figsize=figsize)
156  ax = plt.gca()
157  xvals = []
158  yvals = []
159  for det in camera:
160  corners = [(c.getX(), c.getY()) for c in det.getCorners(FOCAL_PLANE)]
161  for corner in corners:
162  xvals.append(corner[0])
163  yvals.append(corner[1])
164  colors.append(colorMap[det.getType()])
165  patches.append(Polygon(corners, True))
166  center = det.getOrientation().getFpPosition()
167  ax.text(center.getX(), center.getY(), det.getId() if useIds else det.getName(),
168  horizontalalignment='center', size=6)
169 
170  patchCollection = PatchCollection(patches, alpha=0.6, facecolor=colors)
171  ax.add_collection(patchCollection)
172  ax.scatter(xs, ys, s=10, alpha=.7, linewidths=0., c=pcolors)
173  ax.set_xlim(min(xvals) - abs(0.1*min(xvals)),
174  max(xvals) + abs(0.1*max(xvals)))
175  ax.set_ylim(min(yvals) - abs(0.1*min(yvals)),
176  max(yvals) + abs(0.1*max(yvals)))
177  ax.set_xlabel('Focal Plane X (mm)')
178  ax.set_ylabel('Focal Plane Y (mm)')
179  if savePath is not None:
180  plt.savefig(savePath)
181  if showFig:
182  plt.show()
183 
184 
185 def makeImageFromAmp(amp, imValue=None, imageFactory=afwImage.ImageU, markSize=10, markValue=0,
186  scaleGain=lambda gain: (gain*1000)//10):
187  """Make an image from an amp object.
188 
189  Since images are integer images by default, the gain needs to be scaled to
190  give enough dynamic range to see variation from amp to amp.
191  The scaling algorithm is assignable.
192 
193  Parameters
194  ----------
195  amp : `lsst.afw.table.AmpInfoRecord`
196  Amp record to use for constructing the raw amp image.
197  imValue : `float` or `None`
198  Value to assign to the constructed image, or scaleGain(gain) if `None`.
199  imageFactory : callable like `lsst.afw.image.Image`
200  Type of image to construct.
201  markSize : `float`
202  Size of mark at read corner in pixels.
203  markValue : `float`
204  Value of pixels in the read corner mark.
205  scaleGain : callable
206  The function by which to scale the gain (must take a single argument).
207 
208  Returns
209  -------
210  ampImage : `lsst.afw.image`
211  An untrimmed amp image, of the type produced by ``imageFactory``.
212  """
213  if not amp.getHasRawInfo():
214  raise RuntimeError(
215  "Can't create a raw amp image without raw amp information")
216  bbox = amp.getRawBBox()
217  dbbox = amp.getRawDataBBox()
218  img = imageFactory(bbox)
219  if imValue is None:
220  img.set(int(scaleGain(amp.getGain())))
221  else:
222  img.set(imValue)
223  # Set the first pixel read to a different value
224  markbbox = lsst.geom.Box2I()
225  if amp.getReadoutCorner() == afwCameraGeom.ReadoutCorner.LL:
226  markbbox.include(dbbox.getMin())
227  markbbox.include(dbbox.getMin() + lsst.geom.Extent2I(markSize, markSize))
228  elif amp.getReadoutCorner() == afwCameraGeom.ReadoutCorner.LR:
229  cornerPoint = lsst.geom.Point2I(dbbox.getMaxX(), dbbox.getMinY())
230  markbbox.include(cornerPoint)
231  markbbox.include(cornerPoint + lsst.geom.Extent2I(-markSize, markSize))
232  elif amp.getReadoutCorner() == afwCameraGeom.ReadoutCorner.UR:
233  cornerPoint = lsst.geom.Point2I(dbbox.getMax())
234  markbbox.include(cornerPoint)
235  markbbox.include(cornerPoint + lsst.geom.Extent2I(-markSize, -markSize))
236  elif amp.getReadoutCorner() == afwCameraGeom.ReadoutCorner.UL:
237  cornerPoint = lsst.geom.Point2I(dbbox.getMinX(), dbbox.getMaxY())
238  markbbox.include(cornerPoint)
239  markbbox.include(cornerPoint + lsst.geom.Extent2I(markSize, -markSize))
240  else:
241  raise RuntimeError("Could not set readout corner")
242  mimg = imageFactory(img, markbbox)
243  mimg.set(markValue)
244  return img
245 
246 
247 def calcRawCcdBBox(ccd):
248  """Calculate the raw ccd bounding box.
249 
250  Parameters
251  ----------
252  ccd : `lsst.afw.cameraGeom.Detector`
253  Detector for which to calculate the un-trimmed bounding box.
254 
255  Returns
256  -------
257  bbox : `lsst.geom.Box2I` or `None`
258  Bounding box of the un-trimmed Detector, or `None` if there is not enough
259  information to calculate raw BBox.
260  """
261  bbox = lsst.geom.Box2I()
262  for amp in ccd:
263  if not amp.getHasRawInfo():
264  return None
265  tbbox = amp.getRawBBox()
266  tbbox.shift(amp.getRawXYOffset())
267  bbox.include(tbbox)
268  return bbox
269 
270 
271 def makeImageFromCcd(ccd, isTrimmed=True, showAmpGain=True, imageFactory=afwImage.ImageU, rcMarkSize=10,
272  binSize=1):
273  """Make an Image of a CCD.
274 
275  Parameters
276  ----------
277  ccd : `lsst.afw.cameraGeom.Detector`
278  Detector to use in making the image.
279  isTrimmed : `bool`
280  Assemble a trimmed Detector image.
281  showAmpGain : `bool`
282  Use the per-amp gain to color the pixels in the image?
283  imageFactory : callable like `lsst.afw.image.Image`
284  Image type to generate.
285  rcMarkSize : `float`
286  Size of the mark to make in the amp images at the read corner.
287  binSize : `int`
288  Bin the image by this factor in both dimensions.
289 
290  Returns
291  -------
292  image : `lsst.afw.image.Image`
293  Image of the Detector (type returned by ``imageFactory``).
294  """
295  ampImages = []
296  index = 0
297  if isTrimmed:
298  bbox = ccd.getBBox()
299  else:
300  bbox = calcRawCcdBBox(ccd)
301  for amp in ccd:
302  if amp.getHasRawInfo():
303  if showAmpGain:
304  ampImages.append(makeImageFromAmp(
305  amp, imageFactory=imageFactory, markSize=rcMarkSize))
306  else:
307  ampImages.append(makeImageFromAmp(amp, imValue=(index + 1)*1000,
308  imageFactory=imageFactory, markSize=rcMarkSize))
309  index += 1
310 
311  if len(ampImages) > 0:
312  ccdImage = imageFactory(bbox)
313  for ampImage, amp in zip(ampImages, ccd):
314  if isTrimmed:
315  assembleAmplifierImage(ccdImage, ampImage, amp)
316  else:
317  assembleAmplifierRawImage(ccdImage, ampImage, amp)
318  else:
319  if not isTrimmed:
320  raise RuntimeError(
321  "Cannot create untrimmed CCD without amps with raw information")
322  ccdImage = imageFactory(ccd.getBBox())
323  ccdImage = afwMath.binImage(ccdImage, binSize)
324  return ccdImage
325 
326 
328  """A class to retrieve synthetic images for display by the show* methods
329 
330  Parameters
331  ----------
332  isTrimmed : `bool`
333  Should amps be trimmed?
334  verbose : `bool`
335  Be chatty?
336  background : `float`
337  The value of any pixels that lie outside the CCDs.
338  showAmpGain : `bool`
339  Color the amp segments with the gain of the amp?
340  markSize : `float`
341  Size of the side of the box used to mark the read corner.
342  markValue : `float`
343  Value to assign the read corner mark.
344  ampImValue : `float` or `None`
345  Value to assign to amps; scaleGain(gain) is used if `None`.
346  scaleGain : callable
347  Function to scale the gain by.
348  """
349  def __init__(self, isTrimmed=True, verbose=False, background=numpy.nan,
350  showAmpGain=True, markSize=10, markValue=0,
351  ampImValue=None, scaleGain=lambda gain: (gain*1000)//10):
352  self.isTrimmed = isTrimmed
353  self.verbose = verbose
354  self.background = background
355  self.showAmpGain = showAmpGain
356  self.markSize = markSize
357  self.markValue = markValue
358  self.ampImValue = ampImValue
359  self.scaleGain = scaleGain
360 
361  def getCcdImage(self, det, imageFactory, binSize):
362  """Return a CCD image for the detector and the (possibly updated) Detector.
363 
364  Parameters
365  ----------
366  det : `lsst.afw.cameraGeom.Detector`
367  Detector to use for making the image.
368  imageFactory : callable like `lsst.afw.image.Image`
369  Image constructor for making the image.
370  binSize : `int`
371  Bin the image by this factor in both dimensions.
372 
373  Returns
374  -------
375  ccdImage : `lsst.afw.image.Image`
376  The constructed image.
377  """
378  ccdImage = makeImageFromCcd(det, isTrimmed=self.isTrimmed, showAmpGain=self.showAmpGain,
379  imageFactory=imageFactory, binSize=binSize)
380  return afwMath.rotateImageBy90(ccdImage, det.getOrientation().getNQuarter()), det
381 
382  def getAmpImage(self, amp, imageFactory):
383  """Return an amp segment image.
384 
385  Parameters
386  ----------
387  amp : `lsst.afw.table.AmpInfoTable`
388  AmpInfoTable for this amp.
389  imageFactory : callable like `lsst.afw.image.Image`
390  Image constructor for making the image.
391 
392  Returns
393  -------
394  ampImage : `lsst.afw.image.Image`
395  The constructed image.
396  """
397  ampImage = makeImageFromAmp(amp, imValue=self.ampImValue, imageFactory=imageFactory,
398  markSize=self.markSize, markValue=self.markValue,
399  scaleGain=self.scaleGain)
400  if self.isTrimmed:
401  ampImage = ampImage.Factory(ampImage, amp.getRawDataBBox())
402  return ampImage
403 
404 
406  """A class to return an Image of a given Ccd using the butler.
407 
408  Parameters
409  ----------
410  butler : `lsst.daf.persistence.Butler` or `None`
411  The butler to use. If `None`, an empty image is returned.
412  type : `str`
413  The type of image to read (e.g. raw, bias, flat, calexp).
414  isTrimmed : `bool`
415  If true, the showCamera command expects to be given trimmed images.
416  verbose : `bool`
417  Be chatty (in particular, log any error messages from the butler)?
418  background : `float`
419  The value of any pixels that lie outside the CCDs.
420  callback : callable
421  A function called with (image, ccd, butler) for every image, which
422  returns the image to be displayed (e.g. rawCallback). The image must
423  be of the correct size, allowing for the value of isTrimmed.
424  *args : `list`
425  Passed to the butler.
426  **kwargs : `dict`
427  Passed to the butler.
428 
429  Notes
430  -----
431  You can define a short named function as a callback::
432 
433  def callback(im, ccd, imageSource):
434  return cameraGeom.utils.rawCallback(im, ccd, imageSource, correctGain=True)
435  """
436  def __init__(self, butler=None, type="raw",
437  isTrimmed=True, verbose=False, background=numpy.nan,
438  callback=None, *args, **kwargs):
439  super(ButlerImage, self).__init__(*args)
440  self.isTrimmed = isTrimmed
441  self.type = type
442  self.butler = butler
443  self.kwargs = kwargs
444  self.isRaw = False
445  self.background = background
446  self.verbose = verbose
447  self.callback = callback
448 
449  def _prepareImage(self, ccd, im, binSize, allowRotate=True):
450  if binSize > 1:
451  im = afwMath.binImage(im, binSize)
452 
453  if allowRotate:
455  im, ccd.getOrientation().getNQuarter())
456 
457  return im
458 
459  def getCcdImage(self, ccd, imageFactory=afwImage.ImageF, binSize=1, asMaskedImage=False):
460  """Return an image of the specified ccd, and also the (possibly updated) ccd"""
461 
462  log = lsst.log.Log.getLogger("afw.cameraGeom.utils.ButlerImage")
463 
464  if self.isTrimmed:
465  bbox = ccd.getBBox()
466  else:
467  bbox = calcRawCcdBBox(ccd)
468 
469  im = None
470  if self.butler is not None:
471  err = None
472  for dataId in [dict(detector=ccd.getId()), dict(ccd=ccd.getId()), dict(ccd=ccd.getName())]:
473  try:
474  im = self.butler.get(self.type, dataId, **self.kwargs)
475  except FitsError as e: # no point trying another dataId
476  err = IOError(e.args[0].split('\n')[0]) # It's a very chatty error
477  break
478  except Exception as e: # try a different dataId
479  if err is None:
480  err = e
481  continue
482  else:
483  ccd = im.getDetector() # possibly modified by assembleCcdTask
484  break
485 
486  if im:
487  if asMaskedImage:
488  im = im.getMaskedImage()
489  else:
490  im = im.getMaskedImage().getImage()
491  else:
492  if self.verbose:
493  print("Reading %s: %s" % (ccd.getId(), err)) # lost by jupyterLab
494 
495  log.warn("Reading %s: %s", ccd.getId(), err)
496 
497  if im is None:
498  return self._prepareImage(ccd, imageFactory(*bbox.getDimensions()), binSize), ccd
499 
500  if self.type == "raw":
501  if hasattr(im, 'convertF'):
502  im = im.convertF()
503  if False and self.callback is None: # we need to trim the raw image
504  self.callback = rawCallback
505 
506  allowRotate = True
507  if self.callback:
508  try:
509  im = self.callback(im, ccd, imageSource=self)
510  except Exception as e:
511  if self.verbose:
512  log.error("callback failed: %s" % e)
513  im = imageFactory(*bbox.getDimensions())
514  else:
515  allowRotate = False # the callback was responsible for any rotations
516 
517  return self._prepareImage(ccd, im, binSize, allowRotate=allowRotate), ccd
518 
519 
520 def rawCallback(im, ccd=None, imageSource=None,
521  correctGain=False, subtractBias=False, convertToFloat=False, obeyNQuarter=True):
522  """A callback function that may or may not subtract bias/correct gain/trim
523  a raw image.
524 
525  Parameters
526  ----------
527  im : `lsst.afw.image.Image` or `lsst.afw.image.MaskedImage` or `lsst.afw.image.Exposure`
528  An image of a chip, ready to be binned and maybe rotated.
529  ccd : `lsst.afw.cameraGeom.Detector` or `None`
530  The Detector; if `None` assume that im is an exposure and extract its Detector.
531  imageSource : `FakeImageDataSource` or `None`
532  Source to get ccd images. Must have a `getCcdImage()` method.
533  correctGain : `bool`
534  Correct each amplifier for its gain?
535  subtractBias : `bool`
536  Subtract the bias from each amplifier?
537  convertToFloat : `bool`
538  Convert ``im`` to floating point if possible.
539  obeyNQuarter : `bool`
540  Obey nQuarter from the Detector (default: True)
541 
542  Returns
543  -------
544  image : `lsst.afw.image.Image` like
545  The constructed image (type returned by ``im.Factory``).
546 
547  Notes
548  -----
549  If imageSource is derived from ButlerImage, imageSource.butler is available.
550  """
551  if ccd is None:
552  ccd = im.getDetector()
553  if hasattr(im, "getMaskedImage"):
554  im = im.getMaskedImage()
555  if convertToFloat and hasattr(im, "convertF"):
556  im = im.convertF()
557 
558  isTrimmed = imageSource.isTrimmed
559  if isTrimmed:
560  bbox = ccd.getBBox()
561  else:
562  bbox = calcRawCcdBBox(ccd)
563 
564  ampImages = []
565  for a in ccd:
566  if isTrimmed:
567  data = im[a.getRawDataBBox()]
568  else:
569  data = im
570 
571  if subtractBias:
572  bias = im[a.getRawHorizontalOverscanBBox()]
573  data -= afwMath.makeStatistics(bias, afwMath.MEANCLIP).getValue()
574  if correctGain:
575  data *= a.getGain()
576 
577  ampImages.append(data)
578 
579  ccdImage = im.Factory(bbox)
580  for ampImage, amp in zip(ampImages, ccd):
581  if isTrimmed:
582  assembleAmplifierImage(ccdImage, ampImage, amp)
583  else:
584  assembleAmplifierRawImage(ccdImage, ampImage, amp)
585 
586  if obeyNQuarter:
587  nQuarter = ccd.getOrientation().getNQuarter()
588  ccdImage = afwMath.rotateImageBy90(ccdImage, nQuarter)
589 
590  return ccdImage
591 
592 
593 def overlayCcdBoxes(ccd, untrimmedCcdBbox=None, nQuarter=0,
594  isTrimmed=False, ccdOrigin=(0, 0), display=None, binSize=1):
595  """Overlay bounding boxes on an image display.
596 
597  Parameters
598  ----------
599  ccd : `lsst.afw.cameraGeom.Detector`
600  Detector to iterate for the amp bounding boxes.
601  untrimmedCcdBbox : `lsst.geom.Box2I` or `None`
602  Bounding box of the un-trimmed Detector.
603  nQuarter : `int`
604  number of 90 degree rotations to apply to the bounding boxes (used for rotated chips).
605  isTrimmed : `bool`
606  Is the Detector image over which the boxes are layed trimmed?
607  ccdOrigin : `tuple` of `float`
608  Detector origin relative to the parent origin if in a larger pixel grid.
609  display : `lsst.afw.display.Display`
610  Image display to display on.
611  binSize : `int`
612  Bin the image by this factor in both dimensions.
613 
614  Notes
615  -----
616  The colours are:
617  - Entire detector GREEN
618  - All data for amp GREEN
619  - HorizontalPrescan YELLOW
620  - HorizontalOverscan RED
621  - Data BLUE
622  - VerticalOverscan MAGENTA
623  - VerticalOverscan MAGENTA
624  """
625  if not display: # should be second parameter, and not defaulted!!
626  raise RuntimeError("Please specify a display")
627 
628  if untrimmedCcdBbox is None:
629  if isTrimmed:
630  untrimmedCcdBbox = ccd.getBBox()
631  else:
632  untrimmedCcdBbox = lsst.geom.Box2I()
633  for a in ccd.getAmplifiers():
634  bbox = a.getRawBBox()
635  untrimmedCcdBbox.include(bbox)
636 
637  with display.Buffering():
638  ccdDim = untrimmedCcdBbox.getDimensions()
639  ccdBbox = rotateBBoxBy90(untrimmedCcdBbox, nQuarter, ccdDim)
640  for amp in ccd:
641  if isTrimmed:
642  ampbbox = amp.getBBox()
643  else:
644  ampbbox = amp.getRawBBox()
645  if nQuarter != 0:
646  ampbbox = rotateBBoxBy90(ampbbox, nQuarter, ccdDim)
647 
648  displayUtils.drawBBox(ampbbox, origin=ccdOrigin, borderWidth=0.49,
649  display=display, bin=binSize)
650 
651  if not isTrimmed and amp.getHasRawInfo():
652  for bbox, ctype in ((amp.getRawHorizontalOverscanBBox(), afwDisplay.RED),
653  (amp.getRawDataBBox(), afwDisplay.BLUE),
654  (amp.getRawVerticalOverscanBBox(),
655  afwDisplay.MAGENTA),
656  (amp.getRawPrescanBBox(), afwDisplay.YELLOW)):
657  if nQuarter != 0:
658  bbox = rotateBBoxBy90(bbox, nQuarter, ccdDim)
659  displayUtils.drawBBox(bbox, origin=ccdOrigin, borderWidth=0.49, ctype=ctype,
660  display=display, bin=binSize)
661  # Label each Amp
662  xc, yc = (ampbbox.getMin()[0] + ampbbox.getMax()[0])//2, (ampbbox.getMin()[1] +
663  ampbbox.getMax()[1])//2
664  #
665  # Rotate the amp labels too
666  #
667  if nQuarter == 0:
668  c, s = 1, 0
669  elif nQuarter == 1:
670  c, s = 0, -1
671  elif nQuarter == 2:
672  c, s = -1, 0
673  elif nQuarter == 3:
674  c, s = 0, 1
675  c, s = 1, 0
676  ccdHeight = ccdBbox.getHeight()
677  ccdWidth = ccdBbox.getWidth()
678  xc -= 0.5*ccdHeight
679  yc -= 0.5*ccdWidth
680 
681  xc, yc = 0.5*ccdHeight + c*xc + s*yc, 0.5*ccdWidth + -s*xc + c*yc
682 
683  if ccdOrigin:
684  xc += ccdOrigin[0]
685  yc += ccdOrigin[1]
686  display.dot(str(amp.getName()), xc/binSize,
687  yc/binSize, textAngle=nQuarter*90)
688 
689  displayUtils.drawBBox(ccdBbox, origin=ccdOrigin,
690  borderWidth=0.49, ctype=afwDisplay.MAGENTA, display=display, bin=binSize)
691 
692 
693 def showAmp(amp, imageSource=FakeImageDataSource(isTrimmed=False), display=None, overlay=True,
694  imageFactory=afwImage.ImageU):
695  """Show an amp in an image display.
696 
697  Parameters
698  ----------
699  amp : `lsst.afw.tables.AmpInfoRecord`
700  Amp record to use in display.
701  imageSource : `FakeImageDataSource` or `None`
702  Source for getting the amp image. Must have a ``getAmpImage()`` method.
703  display : `lsst.afw.display.Display`
704  Image display to use.
705  overlay : `bool`
706  Overlay bounding boxes?
707  imageFactory : callable like `lsst.afw.image.Image`
708  Type of image to display (only used if ampImage is `None`).
709  """
710  if not display:
711  display = _getDisplayFromDisplayOrFrame(display)
712 
713  ampImage = imageSource.getAmpImage(amp, imageFactory=imageFactory)
714  ampImSize = ampImage.getDimensions()
715  title = amp.getName()
716  display.mtv(ampImage, title=title)
717  if overlay:
718  with display.Buffering():
719  if amp.getHasRawInfo() and ampImSize == amp.getRawBBox().getDimensions():
720  bboxes = [(amp.getRawBBox(), 0.49, afwDisplay.GREEN), ]
721  xy0 = bboxes[0][0].getMin()
722  bboxes.append(
723  (amp.getRawHorizontalOverscanBBox(), 0.49, afwDisplay.RED))
724  bboxes.append((amp.getRawDataBBox(), 0.49, afwDisplay.BLUE))
725  bboxes.append((amp.getRawPrescanBBox(),
726  0.49, afwDisplay.YELLOW))
727  bboxes.append((amp.getRawVerticalOverscanBBox(),
728  0.49, afwDisplay.MAGENTA))
729  else:
730  bboxes = [(amp.getBBox(), 0.49, None), ]
731  xy0 = bboxes[0][0].getMin()
732 
733  for bbox, borderWidth, ctype in bboxes:
734  if bbox.isEmpty():
735  continue
736  bbox = lsst.geom.Box2I(bbox)
737  bbox.shift(-lsst.geom.ExtentI(xy0))
738  displayUtils.drawBBox(
739  bbox, borderWidth=borderWidth, ctype=ctype, display=display)
740 
741 
742 def showCcd(ccd, imageSource=FakeImageDataSource(), display=None, overlay=True,
743  imageFactory=afwImage.ImageF, binSize=1, inCameraCoords=False):
744  """Show a CCD on display.
745 
746  Parameters
747  ----------
748  ccd : `lsst.afw.cameraGeom.Detector`
749  Detector to use in display.
750  imageSource : `FakeImageDataSource` or `None`
751  Source to get ccd images. Must have a ``getCcdImage()`` method.
752  display : `lsst.afw.display.Display`
753  image display to use.
754  overlay : `bool`
755  Show amp bounding boxes on the displayed image?
756  imageFactory : callable like `lsst.afw.image.Image`
757  The image factory to use in generating the images.
758  binSize : `int`
759  Bin the image by this factor in both dimensions.
760  inCameraCoords : `bool`
761  Show the Detector in camera coordinates?
762  """
763  display = _getDisplayFromDisplayOrFrame(display)
764 
765  ccdOrigin = lsst.geom.Point2I(0, 0)
766  nQuarter = 0
767  ccdImage, ccd = imageSource.getCcdImage(
768  ccd, imageFactory=imageFactory, binSize=binSize)
769 
770  ccdBbox = ccdImage.getBBox()
771  if ccdBbox.getDimensions() == ccd.getBBox().getDimensions():
772  isTrimmed = True
773  else:
774  isTrimmed = False
775 
776  if inCameraCoords:
777  nQuarter = ccd.getOrientation().getNQuarter()
778  ccdImage = afwMath.rotateImageBy90(ccdImage, nQuarter)
779  title = ccd.getName()
780  if isTrimmed:
781  title += "(trimmed)"
782 
783  if display:
784  display.mtv(ccdImage, title=title)
785 
786  if overlay:
787  overlayCcdBoxes(ccd, ccdBbox, nQuarter, isTrimmed,
788  ccdOrigin, display, binSize)
789 
790  return ccdImage
791 
792 
793 def getCcdInCamBBoxList(ccdList, binSize, pixelSize_o, origin):
794  """Get the bounding boxes of a list of Detectors within a camera sized pixel grid
795 
796  Parameters
797  ----------
798  ccdList : `lsst.afw.cameraGeom.Detector`
799  List of Detector.
800  binSize : `int`
801  Bin the image by this factor in both dimensions.
802  pixelSize_o : `float`
803  Size of the pixel in mm.
804  origin : `int`
805  Origin of the camera pixel grid in pixels.
806 
807  Returns
808  -------
809  boxList : `list` [`lsst.geom.Box2I`]
810  A list of bounding boxes in camera pixel coordinates.
811  """
812  boxList = []
813  for ccd in ccdList:
814  if not pixelSize_o == ccd.getPixelSize():
815  raise RuntimeError(
816  "Cameras with detectors with different pixel scales are not currently supported")
817 
818  dbbox = lsst.geom.Box2D()
819  for corner in ccd.getCorners(FOCAL_PLANE):
820  dbbox.include(corner)
821  llc = dbbox.getMin()
822  nQuarter = ccd.getOrientation().getNQuarter()
823  cbbox = ccd.getBBox()
824  ex = cbbox.getDimensions().getX()//binSize
825  ey = cbbox.getDimensions().getY()//binSize
826  bbox = lsst.geom.Box2I(
827  cbbox.getMin(), lsst.geom.Extent2I(int(ex), int(ey)))
828  bbox = rotateBBoxBy90(bbox, nQuarter, bbox.getDimensions())
829  bbox.shift(lsst.geom.Extent2I(int(llc.getX()//pixelSize_o.getX()/binSize),
830  int(llc.getY()//pixelSize_o.getY()/binSize)))
831  bbox.shift(lsst.geom.Extent2I(-int(origin.getX()//binSize),
832  -int(origin.getY())//binSize))
833  boxList.append(bbox)
834  return boxList
835 
836 
837 def getCameraImageBBox(camBbox, pixelSize, bufferSize):
838  """Get the bounding box of a camera sized image in pixels
839 
840  Parameters
841  ----------
842  camBbox : `lsst.geom.Box2D`
843  Camera bounding box in focal plane coordinates (mm).
844  pixelSize : `float`
845  Size of a detector pixel in mm.
846  bufferSize : `int`
847  Buffer around edge of image in pixels.
848 
849  Returns
850  -------
851  box : `lsst.geom.Box2I`
852  The resulting bounding box.
853  """
854  pixMin = lsst.geom.Point2I(int(camBbox.getMinX()//pixelSize.getX()),
855  int(camBbox.getMinY()//pixelSize.getY()))
856  pixMax = lsst.geom.Point2I(int(camBbox.getMaxX()//pixelSize.getX()),
857  int(camBbox.getMaxY()//pixelSize.getY()))
858  retBox = lsst.geom.Box2I(pixMin, pixMax)
859  retBox.grow(bufferSize)
860  return retBox
861 
862 
863 def makeImageFromCamera(camera, detectorNameList=None, background=numpy.nan, bufferSize=10,
864  imageSource=FakeImageDataSource(), imageFactory=afwImage.ImageU, binSize=1):
865  """Make an Image of a Camera.
866 
867  Put each detector's image in the correct location and orientation on the
868  focal plane. The input images can be binned to an integer fraction of their
869  original bboxes.
870 
871  Parameters
872  ----------
873  camera : `lsst.afw.cameraGeom.Camera`
874  Camera object to use to make the image.
875  detectorNameList : `list` [`str`]
876  List of detector names from ``camera`` to use in building the image.
877  Use all Detectors if `None`.
878  background : `float`
879  Value to use where there is no Detector.
880  bufferSize : `int`
881  Size of border in binned pixels to make around the camera image.
882  imageSource : `FakeImageDataSource` or `None`
883  Source to get ccd images. Must have a ``getCcdImage()`` method.
884  imageFactory : callable like `lsst.afw.image.Image`
885  Type of image to build.
886  binSize : `int`
887  Bin the image by this factor in both dimensions.
888 
889  Returns
890  -------
891  image : `lsst.afw.image.Image`
892  Image of the entire camera.
893  """
894  log = lsst.log.Log.getLogger("afw.cameraGeom.utils.makeImageFromCamera")
895 
896  if detectorNameList is None:
897  ccdList = camera
898  else:
899  ccdList = [camera[name] for name in detectorNameList]
900 
901  if detectorNameList is None:
902  camBbox = camera.getFpBBox()
903  else:
904  camBbox = lsst.geom.Box2D()
905  for detName in detectorNameList:
906  for corner in camera[detName].getCorners(FOCAL_PLANE):
907  camBbox.include(corner)
908 
909  pixelSize_o = camera[next(camera.getNameIter())].getPixelSize()
910  camBbox = getCameraImageBBox(camBbox, pixelSize_o, bufferSize*binSize)
911  origin = camBbox.getMin()
912 
913  camIm = imageFactory(int(math.ceil(camBbox.getDimensions().getX()/binSize)),
914  int(math.ceil(camBbox.getDimensions().getY()/binSize)))
915  camIm[:] = imageSource.background
916 
917  assert imageSource.isTrimmed, "isTrimmed is False isn't supported by getCcdInCamBBoxList"
918 
919  boxList = getCcdInCamBBoxList(ccdList, binSize, pixelSize_o, origin)
920  for det, bbox in zip(ccdList, boxList):
921  im = imageSource.getCcdImage(det, imageFactory, binSize)[0]
922  if im is None:
923  continue
924 
925  imView = camIm.Factory(camIm, bbox, afwImage.LOCAL)
926  try:
927  imView[:] = im
928  except pexExceptions.LengthError as e:
929  log.error("Unable to fit image for detector \"%s\" into image of camera: %s" % (det.getName(), e))
930 
931  return camIm
932 
933 
934 def showCamera(camera, imageSource=FakeImageDataSource(), imageFactory=afwImage.ImageF,
935  detectorNameList=None, binSize=10, bufferSize=10, overlay=True, title="",
936  showWcs=None, ctype=afwDisplay.GREEN, textSize=1.25, originAtCenter=True, display=None,
937  **kwargs):
938  """Show a Camera on display, with the specified display.
939 
940  The rotation of the sensors is snapped to the nearest multiple of 90 deg.
941  Also note that the pixel size is constant over the image array. The lower
942  left corner (LLC) of each sensor amp is snapped to the LLC of the pixel
943  containing the LLC of the image.
944 
945  Parameters
946  ----------
947  camera : `lsst.afw.cameraGeom.Camera`
948  Camera object to use to make the image.
949  imageSource : `FakeImageDataSource` or `None`
950  Source to get ccd images. Must have a ``getCcdImage()`` method.
951  imageFactory : `lsst.afw.image.Image`
952  Type of image to make
953  detectorNameList : `list` [`str`] or `None`
954  List of detector names from `camera` to use in building the image.
955  Use all Detectors if `None`.
956  binSize : `int`
957  Bin the image by this factor in both dimensions.
958  bufferSize : `int`
959  Size of border in binned pixels to make around the camera image.
960  overlay : `bool`
961  Overlay Detector IDs and boundaries?
962  title : `str`
963  Title to use in display.
964  showWcs : `bool`
965  Include a WCS in the display?
966  ctype : `lsst.afw.display.COLOR` or `str`
967  Color to use when drawing Detector boundaries.
968  textSize : `float`
969  Size of detector labels
970  originAtCenter : `bool`
971  Put origin of the camera WCS at the center of the image?
972  If `False`, the origin will be at the lower left.
973  display : `lsst.afw.display`
974  Image display on which to display.
975  **kwargs :
976  All remaining keyword arguments are passed to makeImageFromCamera
977 
978  Returns
979  -------
980  image : `lsst.afw.image.Image`
981  The mosaic image.
982  """
983  display = _getDisplayFromDisplayOrFrame(display)
984 
985  if binSize < 1:
986  binSize = 1
987  cameraImage = makeImageFromCamera(camera, detectorNameList=detectorNameList, bufferSize=bufferSize,
988  imageSource=imageSource, imageFactory=imageFactory, binSize=binSize,
989  **kwargs)
990 
991  if detectorNameList is None:
992  ccdList = [camera[name] for name in camera.getNameIter()]
993  else:
994  ccdList = [camera[name] for name in detectorNameList]
995 
996  if detectorNameList is None:
997  camBbox = camera.getFpBBox()
998  else:
999  camBbox = lsst.geom.Box2D()
1000  for detName in detectorNameList:
1001  for corner in camera[detName].getCorners(FOCAL_PLANE):
1002  camBbox.include(corner)
1003  pixelSize = ccdList[0].getPixelSize()
1004 
1005  if showWcs:
1006  if originAtCenter:
1007  wcsReferencePixel = lsst.geom.Box2D(
1008  cameraImage.getBBox()).getCenter()
1009  else:
1010  wcsReferencePixel = lsst.geom.Point2I(0, 0)
1011  wcs = makeFocalPlaneWcs(pixelSize*binSize, wcsReferencePixel)
1012  else:
1013  wcs = None
1014 
1015  if display:
1016  if title == "":
1017  title = camera.getName()
1018  display.mtv(cameraImage, title=title, wcs=wcs)
1019 
1020  if overlay:
1021  with display.Buffering():
1022  camBbox = getCameraImageBBox(
1023  camBbox, pixelSize, bufferSize*binSize)
1024  bboxList = getCcdInCamBBoxList(
1025  ccdList, binSize, pixelSize, camBbox.getMin())
1026  for bbox, ccd in zip(bboxList, ccdList):
1027  nQuarter = ccd.getOrientation().getNQuarter()
1028  # borderWidth to 0.5 to align with the outside edge of the
1029  # pixel
1030  displayUtils.drawBBox(
1031  bbox, borderWidth=0.5, ctype=ctype, display=display)
1032  dims = bbox.getDimensions()
1033  display.dot(ccd.getName(), bbox.getMinX() + dims.getX()/2, bbox.getMinY() + dims.getY()/2,
1034  ctype=ctype, size=textSize, textAngle=nQuarter*90)
1035 
1036  return cameraImage
1037 
1038 
1039 def makeFocalPlaneWcs(pixelSize, referencePixel):
1040  """Make a WCS for the focal plane geometry
1041  (i.e. one that returns positions in "mm")
1042 
1043  Parameters
1044  ----------
1045  pixelSize : `float`
1046  Size of the image pixels in physical units
1047  referencePixel : `lsst.geom.Point2D`
1048  Pixel for origin of WCS
1049 
1050  Returns
1051  -------
1052  `lsst.afw.geom.Wcs`
1053  Wcs object for mapping between pixels and focal plane.
1054  """
1055  md = dafBase.PropertySet()
1056  if referencePixel is None:
1057  referencePixel = lsst.geom.PointD(0, 0)
1058  for i in range(2):
1059  md.set("CRPIX%d"%(i + 1), referencePixel[i])
1060  md.set("CRVAL%d"%(i + 1), 0.)
1061  md.set("CDELT1", pixelSize[0])
1062  md.set("CDELT2", pixelSize[1])
1063  md.set("CTYPE1", "CAMERA_X")
1064  md.set("CTYPE2", "CAMERA_Y")
1065  md.set("CUNIT1", "mm")
1066  md.set("CUNIT2", "mm")
1067 
1068  return afwGeom.makeSkyWcs(md)
1069 
1070 
1071 def findAmp(ccd, pixelPosition):
1072  """Find the Amp with the specified pixel position within the composite
1073 
1074  Parameters
1075  ----------
1076  ccd : `lsst.afw.cameraGeom.Detector`
1077  Detector to look in.
1078  pixelPosition : `lsst.geom.Point2I`
1079  The pixel position to find the amp for.
1080 
1081  Returns
1082  -------
1083  `lsst.afw.table.AmpInfoCatalog`
1084  Amp record in which ``pixelPosition`` falls or `None` if no Amp found.
1085  """
1086  for amp in ccd:
1087  if amp.getBBox().contains(pixelPosition):
1088  return amp
1089 
1090  return None
def makeImageFromAmp(amp, imValue=None, imageFactory=afwImage.ImageU, markSize=10, markValue=0, scaleGain=lambda gain:(gain *1000)//10)
Definition: utils.py:186
Angle abs(Angle const &a)
Definition: Angle.h:106
def makeFocalPlaneWcs(pixelSize, referencePixel)
Definition: utils.py:1039
A floating-point coordinate rectangle geometry.
Definition: Box.h:413
def plotFocalPlane(camera, fieldSizeDeg_x=0, fieldSizeDeg_y=None, dx=0.1, dy=0.1, figsize=(10., 10.), useIds=False, showFig=True, savePath=None)
Definition: utils.py:89
Reports attempts to exceed implementation-defined length limits for some classes. ...
Definition: Runtime.h:76
def getCcdInCamBBoxList(ccdList, binSize, pixelSize_o, origin)
Definition: utils.py:793
def getCcdImage(self, det, imageFactory, binSize)
Definition: utils.py:361
int min
def findAmp(ccd, pixelPosition)
Definition: utils.py:1071
def getCameraImageBBox(camBbox, pixelSize, bufferSize)
Definition: utils.py:837
Definition: Log.h:706
Statistics makeStatistics(lsst::afw::math::MaskedVector< EntryT > const &mv, std::vector< WeightPixel > const &vweights, int const flags, StatisticsControl const &sctrl=StatisticsControl())
The makeStatistics() overload to handle lsst::afw::math::MaskedVector<>
Definition: Statistics.h:520
def getAmpImage(self, amp, imageFactory)
Definition: utils.py:382
def __init__(self, isTrimmed=True, verbose=False, background=numpy.nan, showAmpGain=True, markSize=10, markValue=0, ampImValue=None, scaleGain=lambda gain:(gain *1000)//10)
Definition: utils.py:351
def _prepareImage(self, ccd, im, binSize, allowRotate=True)
Definition: utils.py:449
def makeImageFromCamera(camera, detectorNameList=None, background=numpy.nan, bufferSize=10, imageSource=FakeImageDataSource(), imageFactory=afwImage.ImageU, binSize=1)
Definition: utils.py:864
std::shared_ptr< SkyWcs > makeFlippedWcs(SkyWcs const &wcs, bool flipLR, bool flipTB, lsst::geom::Point2D const &center)
Return a copy of a FITS-WCS with pixel positions flipped around a specified center.
Definition: SkyWcs.cc:472
def __init__(self, butler=None, type="raw", isTrimmed=True, verbose=False, background=numpy.nan, callback=None, args, kwargs)
Definition: utils.py:438
def makeImageFromCcd(ccd, isTrimmed=True, showAmpGain=True, imageFactory=afwImage.ImageU, rcMarkSize=10, binSize=1)
Definition: utils.py:272
def rawCallback(im, ccd=None, imageSource=None, correctGain=False, subtractBias=False, convertToFloat=False, obeyNQuarter=True)
Definition: utils.py:521
def showCamera(camera, imageSource=FakeImageDataSource(), imageFactory=afwImage.ImageF, detectorNameList=None, binSize=10, bufferSize=10, overlay=True, title="", showWcs=None, ctype=afwDisplay.GREEN, textSize=1.25, originAtCenter=True, display=None, kwargs)
Definition: utils.py:937
int max
std::shared_ptr< ImageT > rotateImageBy90(ImageT const &image, int nQuarter)
Rotate an image by an integral number of quarter turns.
Definition: rotateImage.cc:39
def assembleAmplifierImage(destImage, rawImage, amplifier)
def showAmp(amp, imageSource=FakeImageDataSource(isTrimmed=False), display=None, overlay=True, imageFactory=afwImage.ImageU)
Definition: utils.py:694
static Log getLogger(Log const &logger)
Definition: Log.h:760
def showCcd(ccd, imageSource=FakeImageDataSource(), display=None, overlay=True, imageFactory=afwImage.ImageF, binSize=1, inCameraCoords=False)
Definition: utils.py:743
def prepareWcsData(wcs, amp, isTrimmed=True)
Definition: utils.py:54
std::shared_ptr< SkyWcs > makeSkyWcs(TransformPoint2ToPoint2 const &pixelsToFieldAngle, lsst::geom::Angle const &orientation, bool flipX, lsst::geom::SpherePoint const &boresight, std::string const &projection="TAN")
Construct a FITS SkyWcs from camera geometry.
Definition: SkyWcs.cc:536
Class for storing generic metadata.
Definition: PropertySet.h:67
std::shared_ptr< ImageT > binImage(ImageT const &inImage, int const binsize, lsst::afw::math::Property const flags=lsst::afw::math::MEAN)
Definition: binImage.cc:38
def assembleAmplifierRawImage(destImage, rawImage, amplifier)
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...
def overlayCcdBoxes(ccd, untrimmedCcdBbox=None, nQuarter=0, isTrimmed=False, ccdOrigin=(0, 0), display=None, binSize=1)
Definition: utils.py:594
An integer coordinate rectangle.
Definition: Box.h:55
def rotateBBoxBy90(bbox, n90, dimensions)
def getCcdImage(self, ccd, imageFactory=afwImage.ImageF, binSize=1, asMaskedImage=False)
Definition: utils.py:459