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