LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
LSST Data Management Base Package
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 ._cameraGeom 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 _LOG = lsst.log.Log.getLogger(__name__)
54 
55 
56 def prepareWcsData(wcs, amp, isTrimmed=True):
57  """Put Wcs from an Amp image into CCD coordinates
58 
59  Parameters
60  ----------
61  wcs : `lsst.afw.geom.SkyWcs`
62  The WCS object to start from.
63  amp : `lsst.afw.table.AmpInfoRecord`
64  Amp object to use
65  isTrimmed : `bool`
66  Is the image to which the WCS refers trimmed of non-imaging pixels?
67 
68  Returns
69  -------
70  ampWcs : `lsst.afw.geom.SkyWcs`
71  The modified WCS.
72  """
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  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() == afwCameraGeom.ReadoutCorner.LL:
223  markbbox.include(dbbox.getMin())
224  markbbox.include(dbbox.getMin() + lsst.geom.Extent2I(markSize, markSize))
225  elif amp.getReadoutCorner() == afwCameraGeom.ReadoutCorner.LR:
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() == afwCameraGeom.ReadoutCorner.UR:
230  cornerPoint = lsst.geom.Point2I(dbbox.getMax())
231  markbbox.include(cornerPoint)
232  markbbox.include(cornerPoint + lsst.geom.Extent2I(-markSize, -markSize))
233  elif amp.getReadoutCorner() == afwCameraGeom.ReadoutCorner.UL:
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  tbbox = amp.getRawBBox()
261  tbbox.shift(amp.getRawXYOffset())
262  bbox.include(tbbox)
263  return bbox
264 
265 
266 def makeImageFromCcd(ccd, isTrimmed=True, showAmpGain=True, imageFactory=afwImage.ImageU, rcMarkSize=10,
267  binSize=1):
268  """Make an Image of a CCD.
269 
270  Parameters
271  ----------
272  ccd : `lsst.afw.cameraGeom.Detector`
273  Detector to use in making the image.
274  isTrimmed : `bool`
275  Assemble a trimmed Detector image.
276  showAmpGain : `bool`
277  Use the per-amp gain to color the pixels in the image?
278  imageFactory : callable like `lsst.afw.image.Image`
279  Image type to generate.
280  rcMarkSize : `float`
281  Size of the mark to make in the amp images at the read corner.
282  binSize : `int`
283  Bin the image by this factor in both dimensions.
284 
285  Returns
286  -------
287  image : `lsst.afw.image.Image`
288  Image of the Detector (type returned by ``imageFactory``).
289  """
290  ampImages = []
291  index = 0
292  if isTrimmed:
293  bbox = ccd.getBBox()
294  else:
295  bbox = calcRawCcdBBox(ccd)
296  for amp in ccd:
297  if showAmpGain:
298  ampImages.append(makeImageFromAmp(
299  amp, imageFactory=imageFactory, markSize=rcMarkSize))
300  else:
301  ampImages.append(makeImageFromAmp(amp, imValue=(index + 1)*1000,
302  imageFactory=imageFactory, markSize=rcMarkSize))
303  index += 1
304 
305  if len(ampImages) > 0:
306  ccdImage = imageFactory(bbox)
307  for ampImage, amp in zip(ampImages, ccd):
308  if isTrimmed:
309  assembleAmplifierImage(ccdImage, ampImage, amp)
310  else:
311  assembleAmplifierRawImage(ccdImage, ampImage, amp)
312  else:
313  if not isTrimmed:
314  raise RuntimeError(
315  "Cannot create untrimmed CCD without amps with raw information")
316  ccdImage = imageFactory(ccd.getBBox())
317  ccdImage = afwMath.binImage(ccdImage, binSize)
318  return ccdImage
319 
320 
322  """A class to retrieve synthetic images for display by the show* methods
323 
324  Parameters
325  ----------
326  isTrimmed : `bool`
327  Should amps be trimmed?
328  verbose : `bool`
329  Be chatty?
330  background : `float`
331  The value of any pixels that lie outside the CCDs.
332  showAmpGain : `bool`
333  Color the amp segments with the gain of the amp?
334  markSize : `float`
335  Size of the side of the box used to mark the read corner.
336  markValue : `float`
337  Value to assign the read corner mark.
338  ampImValue : `float` or `None`
339  Value to assign to amps; scaleGain(gain) is used if `None`.
340  scaleGain : callable
341  Function to scale the gain by.
342  """
343  def __init__(self, isTrimmed=True, verbose=False, background=numpy.nan,
344  showAmpGain=True, markSize=10, markValue=0,
345  ampImValue=None, scaleGain=lambda gain: (gain*1000)//10):
346  self.isTrimmedisTrimmed = isTrimmed
347  self.verboseverbose = verbose
348  self.backgroundbackground = background
349  self.showAmpGainshowAmpGain = showAmpGain
350  self.markSizemarkSize = markSize
351  self.markValuemarkValue = markValue
352  self.ampImValueampImValue = ampImValue
353  self.scaleGainscaleGain = scaleGain
354 
355  def getCcdImage(self, det, imageFactory, binSize):
356  """Return a CCD image for the detector and the (possibly updated) Detector.
357 
358  Parameters
359  ----------
360  det : `lsst.afw.cameraGeom.Detector`
361  Detector to use for making the image.
362  imageFactory : callable like `lsst.afw.image.Image`
363  Image constructor for making the image.
364  binSize : `int`
365  Bin the image by this factor in both dimensions.
366 
367  Returns
368  -------
369  ccdImage : `lsst.afw.image.Image`
370  The constructed image.
371  """
372  ccdImage = makeImageFromCcd(det, isTrimmed=self.isTrimmedisTrimmed, showAmpGain=self.showAmpGainshowAmpGain,
373  imageFactory=imageFactory, binSize=binSize)
374  return afwMath.rotateImageBy90(ccdImage, det.getOrientation().getNQuarter()), det
375 
376  def getAmpImage(self, amp, imageFactory):
377  """Return an amp segment image.
378 
379  Parameters
380  ----------
381  amp : `lsst.afw.table.AmpInfoTable`
382  AmpInfoTable for this amp.
383  imageFactory : callable like `lsst.afw.image.Image`
384  Image constructor for making the image.
385 
386  Returns
387  -------
388  ampImage : `lsst.afw.image.Image`
389  The constructed image.
390  """
391  ampImage = makeImageFromAmp(amp, imValue=self.ampImValueampImValue, imageFactory=imageFactory,
392  markSize=self.markSizemarkSize, markValue=self.markValuemarkValue,
393  scaleGain=self.scaleGainscaleGain)
394  if self.isTrimmedisTrimmed:
395  ampImage = ampImage.Factory(ampImage, amp.getRawDataBBox())
396  return ampImage
397 
398 
400  """A class to return an Image of a given Ccd using the butler.
401 
402  Parameters
403  ----------
404  butler : `lsst.daf.persistence.Butler` or `None`
405  The butler to use. If `None`, an empty image is returned.
406  type : `str`
407  The type of image to read (e.g. raw, bias, flat, calexp).
408  isTrimmed : `bool`
409  If true, the showCamera command expects to be given trimmed images.
410  verbose : `bool`
411  Be chatty (in particular, log any error messages from the butler)?
412  background : `float`
413  The value of any pixels that lie outside the CCDs.
414  callback : callable
415  A function called with (image, ccd, butler) for every image, which
416  returns the image to be displayed (e.g. rawCallback). The image must
417  be of the correct size, allowing for the value of isTrimmed.
418  *args : `list`
419  Passed to the butler.
420  **kwargs : `dict`
421  Passed to the butler.
422 
423  Notes
424  -----
425  You can define a short named function as a callback::
426 
427  def callback(im, ccd, imageSource):
428  return cameraGeom.utils.rawCallback(im, ccd, imageSource, correctGain=True)
429  """
430  def __init__(self, butler=None, type="raw",
431  isTrimmed=True, verbose=False, background=numpy.nan,
432  callback=None, *args, **kwargs):
433  super(ButlerImage, self).__init__(*args)
434  self.isTrimmedisTrimmedisTrimmed = isTrimmed
435  self.typetype = type
436  self.butlerbutler = butler
437  self.kwargskwargs = kwargs
438  self.isRawisRaw = False
439  self.backgroundbackgroundbackground = background
440  self.verboseverboseverbose = verbose
441  self.callbackcallback = callback
442 
443  def _prepareImage(self, ccd, im, binSize, allowRotate=True):
444  if binSize > 1:
445  im = afwMath.binImage(im, binSize)
446 
447  if allowRotate:
449  im, ccd.getOrientation().getNQuarter())
450 
451  return im
452 
453  def getCcdImage(self, ccd, imageFactory=afwImage.ImageF, binSize=1, asMaskedImage=False):
454  """Return an image of the specified ccd, and also the (possibly updated) ccd"""
455 
456  log = _LOG.getChild("ButlerImage")
457 
458  if self.isTrimmedisTrimmedisTrimmed:
459  bbox = ccd.getBBox()
460  else:
461  bbox = calcRawCcdBBox(ccd)
462 
463  im = None
464  if self.butlerbutler is not None:
465  err = None
466  for dataId in [dict(detector=ccd.getId()), dict(ccd=ccd.getId()), dict(ccd=ccd.getName())]:
467  try:
468  im = self.butlerbutler.get(self.typetype, dataId, **self.kwargskwargs)
469  except FitsError as e: # no point trying another dataId
470  err = IOError(e.args[0].split('\n')[0]) # It's a very chatty error
471  break
472  except Exception as e: # try a different dataId
473  if err is None:
474  err = e
475  continue
476  else:
477  ccd = im.getDetector() # possibly modified by assembleCcdTask
478  break
479 
480  if im:
481  if asMaskedImage:
482  im = im.getMaskedImage()
483  else:
484  im = im.getMaskedImage().getImage()
485  else:
486  if self.verboseverboseverbose:
487  # Lost by jupyterlab.
488  print(f"Reading {ccd.getId()}: {err}")
489 
490  log.warning("Reading %s: %s", ccd.getId(), err)
491 
492  if im is None:
493  return self._prepareImage_prepareImage(ccd, imageFactory(*bbox.getDimensions()), binSize), ccd
494 
495  if self.typetype == "raw":
496  if hasattr(im, 'convertF'):
497  im = im.convertF()
498  if False and self.callbackcallback is None: # we need to trim the raw image
499  self.callbackcallback = rawCallback
500 
501  allowRotate = True
502  if self.callbackcallback:
503  try:
504  im = self.callbackcallback(im, ccd, imageSource=self)
505  except Exception as e:
506  if self.verboseverboseverbose:
507  log.error("callback failed: %s", e)
508  im = imageFactory(*bbox.getDimensions())
509  else:
510  allowRotate = False # the callback was responsible for any rotations
511 
512  return self._prepareImage_prepareImage(ccd, im, binSize, allowRotate=allowRotate), ccd
513 
514 
515 def rawCallback(im, ccd=None, imageSource=None,
516  correctGain=False, subtractBias=False, convertToFloat=False, obeyNQuarter=True):
517  """A callback function that may or may not subtract bias/correct gain/trim
518  a raw image.
519 
520  Parameters
521  ----------
522  im : `lsst.afw.image.Image` or `lsst.afw.image.MaskedImage` or `lsst.afw.image.Exposure`
523  An image of a chip, ready to be binned and maybe rotated.
524  ccd : `lsst.afw.cameraGeom.Detector` or `None`
525  The Detector; if `None` assume that im is an exposure and extract its Detector.
526  imageSource : `FakeImageDataSource` or `None`
527  Source to get ccd images. Must have a `getCcdImage()` method.
528  correctGain : `bool`
529  Correct each amplifier for its gain?
530  subtractBias : `bool`
531  Subtract the bias from each amplifier?
532  convertToFloat : `bool`
533  Convert ``im`` to floating point if possible.
534  obeyNQuarter : `bool`
535  Obey nQuarter from the Detector (default: True)
536 
537  Returns
538  -------
539  image : `lsst.afw.image.Image` like
540  The constructed image (type returned by ``im.Factory``).
541 
542  Notes
543  -----
544  If imageSource is derived from ButlerImage, imageSource.butler is available.
545  """
546  if ccd is None:
547  ccd = im.getDetector()
548  if hasattr(im, "getMaskedImage"):
549  im = im.getMaskedImage()
550  if convertToFloat and hasattr(im, "convertF"):
551  im = im.convertF()
552 
553  isTrimmed = imageSource.isTrimmed
554  if isTrimmed:
555  bbox = ccd.getBBox()
556  else:
557  bbox = calcRawCcdBBox(ccd)
558 
559  ampImages = []
560  for a in ccd:
561  if isTrimmed:
562  data = im[a.getRawDataBBox()]
563  else:
564  data = im
565 
566  if subtractBias:
567  bias = im[a.getRawHorizontalOverscanBBox()]
568  data -= afwMath.makeStatistics(bias, afwMath.MEANCLIP).getValue()
569  if correctGain:
570  data *= a.getGain()
571 
572  ampImages.append(data)
573 
574  ccdImage = im.Factory(bbox)
575  for ampImage, amp in zip(ampImages, ccd):
576  if isTrimmed:
577  assembleAmplifierImage(ccdImage, ampImage, amp)
578  else:
579  assembleAmplifierRawImage(ccdImage, ampImage, amp)
580 
581  if obeyNQuarter:
582  nQuarter = ccd.getOrientation().getNQuarter()
583  ccdImage = afwMath.rotateImageBy90(ccdImage, nQuarter)
584 
585  return ccdImage
586 
587 
588 def overlayCcdBoxes(ccd, untrimmedCcdBbox=None, nQuarter=0,
589  isTrimmed=False, ccdOrigin=(0, 0), display=None, binSize=1):
590  """Overlay bounding boxes on an image display.
591 
592  Parameters
593  ----------
594  ccd : `lsst.afw.cameraGeom.Detector`
595  Detector to iterate for the amp bounding boxes.
596  untrimmedCcdBbox : `lsst.geom.Box2I` or `None`
597  Bounding box of the un-trimmed Detector.
598  nQuarter : `int`
599  number of 90 degree rotations to apply to the bounding boxes (used for rotated chips).
600  isTrimmed : `bool`
601  Is the Detector image over which the boxes are layed trimmed?
602  ccdOrigin : `tuple` of `float`
603  Detector origin relative to the parent origin if in a larger pixel grid.
604  display : `lsst.afw.display.Display`
605  Image display to display on.
606  binSize : `int`
607  Bin the image by this factor in both dimensions.
608 
609  Notes
610  -----
611  The colours are:
612  - Entire detector GREEN
613  - All data for amp GREEN
614  - HorizontalPrescan YELLOW
615  - HorizontalOverscan RED
616  - Data BLUE
617  - VerticalOverscan MAGENTA
618  - VerticalOverscan MAGENTA
619  """
620  if not display: # should be second parameter, and not defaulted!!
621  raise RuntimeError("Please specify a display")
622 
623  if untrimmedCcdBbox is None:
624  if isTrimmed:
625  untrimmedCcdBbox = ccd.getBBox()
626  else:
627  untrimmedCcdBbox = lsst.geom.Box2I()
628  for a in ccd.getAmplifiers():
629  bbox = a.getRawBBox()
630  untrimmedCcdBbox.include(bbox)
631 
632  with display.Buffering():
633  ccdDim = untrimmedCcdBbox.getDimensions()
634  ccdBbox = rotateBBoxBy90(untrimmedCcdBbox, nQuarter, ccdDim)
635  for amp in ccd:
636  if isTrimmed:
637  ampbbox = amp.getBBox()
638  else:
639  ampbbox = amp.getRawBBox()
640  if nQuarter != 0:
641  ampbbox = rotateBBoxBy90(ampbbox, nQuarter, ccdDim)
642 
643  displayUtils.drawBBox(ampbbox, origin=ccdOrigin, borderWidth=0.49,
644  display=display, bin=binSize)
645 
646  if not isTrimmed:
647  for bbox, ctype in ((amp.getRawHorizontalOverscanBBox(), afwDisplay.RED),
648  (amp.getRawDataBBox(), afwDisplay.BLUE),
649  (amp.getRawVerticalOverscanBBox(),
650  afwDisplay.MAGENTA),
651  (amp.getRawPrescanBBox(), afwDisplay.YELLOW)):
652  if nQuarter != 0:
653  bbox = rotateBBoxBy90(bbox, nQuarter, ccdDim)
654  displayUtils.drawBBox(bbox, origin=ccdOrigin, borderWidth=0.49, ctype=ctype,
655  display=display, bin=binSize)
656  # Label each Amp
657  xc, yc = ((ampbbox.getMin()[0] + ampbbox.getMax()[0])//2,
658  (ampbbox.getMin()[1] + ampbbox.getMax()[1])//2)
659  #
660  # Rotate the amp labels too
661  #
662  if nQuarter == 0:
663  c, s = 1, 0
664  elif nQuarter == 1:
665  c, s = 0, -1
666  elif nQuarter == 2:
667  c, s = -1, 0
668  elif nQuarter == 3:
669  c, s = 0, 1
670  c, s = 1, 0
671  ccdHeight = ccdBbox.getHeight()
672  ccdWidth = ccdBbox.getWidth()
673  xc -= 0.5*ccdHeight
674  yc -= 0.5*ccdWidth
675 
676  xc, yc = 0.5*ccdHeight + c*xc + s*yc, 0.5*ccdWidth + -s*xc + c*yc
677 
678  if ccdOrigin:
679  xc += ccdOrigin[0]
680  yc += ccdOrigin[1]
681  display.dot(str(amp.getName()), xc/binSize,
682  yc/binSize, textAngle=nQuarter*90)
683 
684  displayUtils.drawBBox(ccdBbox, origin=ccdOrigin,
685  borderWidth=0.49, ctype=afwDisplay.MAGENTA, display=display, bin=binSize)
686 
687 
688 def showAmp(amp, imageSource=FakeImageDataSource(isTrimmed=False), display=None, overlay=True,
689  imageFactory=afwImage.ImageU):
690  """Show an amp in an image display.
691 
692  Parameters
693  ----------
694  amp : `lsst.afw.tables.AmpInfoRecord`
695  Amp record to use in display.
696  imageSource : `FakeImageDataSource` or `None`
697  Source for getting the amp image. Must have a ``getAmpImage()`` method.
698  display : `lsst.afw.display.Display`
699  Image display to use.
700  overlay : `bool`
701  Overlay bounding boxes?
702  imageFactory : callable like `lsst.afw.image.Image`
703  Type of image to display (only used if ampImage is `None`).
704  """
705  if not display:
706  display = _getDisplayFromDisplayOrFrame(display)
707 
708  ampImage = imageSource.getAmpImage(amp, imageFactory=imageFactory)
709  ampImSize = ampImage.getDimensions()
710  title = amp.getName()
711  display.mtv(ampImage, title=title)
712  if overlay:
713  with display.Buffering():
714  if ampImSize == amp.getRawBBox().getDimensions():
715  bboxes = [(amp.getRawBBox(), 0.49, afwDisplay.GREEN), ]
716  xy0 = bboxes[0][0].getMin()
717  bboxes.append(
718  (amp.getRawHorizontalOverscanBBox(), 0.49, afwDisplay.RED))
719  bboxes.append((amp.getRawDataBBox(), 0.49, afwDisplay.BLUE))
720  bboxes.append((amp.getRawPrescanBBox(),
721  0.49, afwDisplay.YELLOW))
722  bboxes.append((amp.getRawVerticalOverscanBBox(),
723  0.49, afwDisplay.MAGENTA))
724  else:
725  bboxes = [(amp.getBBox(), 0.49, None), ]
726  xy0 = bboxes[0][0].getMin()
727 
728  for bbox, borderWidth, ctype in bboxes:
729  if bbox.isEmpty():
730  continue
731  bbox = lsst.geom.Box2I(bbox)
732  bbox.shift(-lsst.geom.ExtentI(xy0))
733  displayUtils.drawBBox(
734  bbox, borderWidth=borderWidth, ctype=ctype, display=display)
735 
736 
737 def showCcd(ccd, imageSource=FakeImageDataSource(), display=None, overlay=True,
738  imageFactory=afwImage.ImageF, binSize=1, inCameraCoords=False):
739  """Show a CCD on display.
740 
741  Parameters
742  ----------
743  ccd : `lsst.afw.cameraGeom.Detector`
744  Detector to use in display.
745  imageSource : `FakeImageDataSource` or `None`
746  Source to get ccd images. Must have a ``getCcdImage()`` method.
747  display : `lsst.afw.display.Display`
748  image display to use.
749  overlay : `bool`
750  Show amp bounding boxes on the displayed image?
751  imageFactory : callable like `lsst.afw.image.Image`
752  The image factory to use in generating the images.
753  binSize : `int`
754  Bin the image by this factor in both dimensions.
755  inCameraCoords : `bool`
756  Show the Detector in camera coordinates?
757  """
758  display = _getDisplayFromDisplayOrFrame(display)
759 
760  ccdOrigin = lsst.geom.Point2I(0, 0)
761  nQuarter = 0
762  ccdImage, ccd = imageSource.getCcdImage(
763  ccd, imageFactory=imageFactory, binSize=binSize)
764 
765  ccdBbox = ccdImage.getBBox()
766  if ccdBbox.getDimensions() == ccd.getBBox().getDimensions():
767  isTrimmed = True
768  else:
769  isTrimmed = False
770 
771  if inCameraCoords:
772  nQuarter = ccd.getOrientation().getNQuarter()
773  ccdImage = afwMath.rotateImageBy90(ccdImage, nQuarter)
774  title = ccd.getName()
775  if isTrimmed:
776  title += "(trimmed)"
777 
778  if display:
779  display.mtv(ccdImage, title=title)
780 
781  if overlay:
782  overlayCcdBoxes(ccd, ccdBbox, nQuarter, isTrimmed,
783  ccdOrigin, display, binSize)
784 
785  return ccdImage
786 
787 
788 def getCcdInCamBBoxList(ccdList, binSize, pixelSize_o, origin):
789  """Get the bounding boxes of a list of Detectors within a camera sized pixel grid
790 
791  Parameters
792  ----------
793  ccdList : `lsst.afw.cameraGeom.Detector`
794  List of Detector.
795  binSize : `int`
796  Bin the image by this factor in both dimensions.
797  pixelSize_o : `float`
798  Size of the pixel in mm.
799  origin : `int`
800  Origin of the camera pixel grid in pixels.
801 
802  Returns
803  -------
804  boxList : `list` [`lsst.geom.Box2I`]
805  A list of bounding boxes in camera pixel coordinates.
806  """
807  boxList = []
808  for ccd in ccdList:
809  if not pixelSize_o == ccd.getPixelSize():
810  raise RuntimeError(
811  "Cameras with detectors with different pixel scales are not currently supported")
812 
813  dbbox = lsst.geom.Box2D()
814  for corner in ccd.getCorners(FOCAL_PLANE):
815  dbbox.include(corner)
816  llc = dbbox.getMin()
817  nQuarter = ccd.getOrientation().getNQuarter()
818  cbbox = ccd.getBBox()
819  ex = cbbox.getDimensions().getX()//binSize
820  ey = cbbox.getDimensions().getY()//binSize
821  bbox = lsst.geom.Box2I(
822  cbbox.getMin(), lsst.geom.Extent2I(int(ex), int(ey)))
823  bbox = rotateBBoxBy90(bbox, nQuarter, bbox.getDimensions())
824  bbox.shift(lsst.geom.Extent2I(int(llc.getX()//pixelSize_o.getX()/binSize),
825  int(llc.getY()//pixelSize_o.getY()/binSize)))
826  bbox.shift(lsst.geom.Extent2I(-int(origin.getX()//binSize),
827  -int(origin.getY())//binSize))
828  boxList.append(bbox)
829  return boxList
830 
831 
832 def getCameraImageBBox(camBbox, pixelSize, bufferSize):
833  """Get the bounding box of a camera sized image in pixels
834 
835  Parameters
836  ----------
837  camBbox : `lsst.geom.Box2D`
838  Camera bounding box in focal plane coordinates (mm).
839  pixelSize : `float`
840  Size of a detector pixel in mm.
841  bufferSize : `int`
842  Buffer around edge of image in pixels.
843 
844  Returns
845  -------
846  box : `lsst.geom.Box2I`
847  The resulting bounding box.
848  """
849  pixMin = lsst.geom.Point2I(int(camBbox.getMinX()//pixelSize.getX()),
850  int(camBbox.getMinY()//pixelSize.getY()))
851  pixMax = lsst.geom.Point2I(int(camBbox.getMaxX()//pixelSize.getX()),
852  int(camBbox.getMaxY()//pixelSize.getY()))
853  retBox = lsst.geom.Box2I(pixMin, pixMax)
854  retBox.grow(bufferSize)
855  return retBox
856 
857 
858 def makeImageFromCamera(camera, detectorNameList=None, background=numpy.nan, bufferSize=10,
859  imageSource=FakeImageDataSource(), imageFactory=afwImage.ImageU, binSize=1):
860  """Make an Image of a Camera.
861 
862  Put each detector's image in the correct location and orientation on the
863  focal plane. The input images can be binned to an integer fraction of their
864  original bboxes.
865 
866  Parameters
867  ----------
868  camera : `lsst.afw.cameraGeom.Camera`
869  Camera object to use to make the image.
870  detectorNameList : `list` [`str`]
871  List of detector names from ``camera`` to use in building the image.
872  Use all Detectors if `None`.
873  background : `float`
874  Value to use where there is no Detector.
875  bufferSize : `int`
876  Size of border in binned pixels to make around the camera image.
877  imageSource : `FakeImageDataSource` or `None`
878  Source to get ccd images. Must have a ``getCcdImage()`` method.
879  imageFactory : callable like `lsst.afw.image.Image`
880  Type of image to build.
881  binSize : `int`
882  Bin the image by this factor in both dimensions.
883 
884  Returns
885  -------
886  image : `lsst.afw.image.Image`
887  Image of the entire camera.
888  """
889  log = _LOG.getChild("makeImageFromCamera")
890 
891  if detectorNameList is None:
892  ccdList = camera
893  else:
894  ccdList = [camera[name] for name in detectorNameList]
895 
896  if detectorNameList is None:
897  camBbox = camera.getFpBBox()
898  else:
899  camBbox = lsst.geom.Box2D()
900  for detName in detectorNameList:
901  for corner in camera[detName].getCorners(FOCAL_PLANE):
902  camBbox.include(corner)
903 
904  pixelSize_o = camera[next(camera.getNameIter())].getPixelSize()
905  camBbox = getCameraImageBBox(camBbox, pixelSize_o, bufferSize*binSize)
906  origin = camBbox.getMin()
907 
908  camIm = imageFactory(int(math.ceil(camBbox.getDimensions().getX()/binSize)),
909  int(math.ceil(camBbox.getDimensions().getY()/binSize)))
910  camIm[:] = imageSource.background
911 
912  assert imageSource.isTrimmed, "isTrimmed is False isn't supported by getCcdInCamBBoxList"
913 
914  boxList = getCcdInCamBBoxList(ccdList, binSize, pixelSize_o, origin)
915  for det, bbox in zip(ccdList, boxList):
916  im = imageSource.getCcdImage(det, imageFactory, binSize)[0]
917  if im is None:
918  continue
919 
920  imView = camIm.Factory(camIm, bbox, afwImage.LOCAL)
921  try:
922  imView[:] = im
923  except pexExceptions.LengthError as e:
924  log.error("Unable to fit image for detector \"%s\" into image of camera: %s",
925  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` [`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
int min
int max
def __init__(self, butler=None, type="raw", isTrimmed=True, verbose=False, background=numpy.nan, callback=None, *args, **kwargs)
Definition: utils.py:432
def _prepareImage(self, ccd, im, binSize, allowRotate=True)
Definition: utils.py:443
def getCcdImage(self, ccd, imageFactory=afwImage.ImageF, binSize=1, asMaskedImage=False)
Definition: utils.py:453
def getAmpImage(self, amp, imageFactory)
Definition: utils.py:376
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:345
def getCcdImage(self, det, imageFactory, binSize)
Definition: utils.py:355
Class for storing generic metadata.
Definition: PropertySet.h:66
A floating-point coordinate rectangle geometry.
Definition: Box.h:413
An integer coordinate rectangle.
Definition: Box.h:55
static Log getLogger(Log const &logger)
Definition: Log.h:772
Reports attempts to exceed implementation-defined length limits for some classes.
Definition: Runtime.h:76
def assembleAmplifierRawImage(destImage, rawImage, amplifier)
def assembleAmplifierImage(destImage, rawImage, amplifier)
def rotateBBoxBy90(bbox, n90, dimensions)
def makeFocalPlaneWcs(pixelSize, referencePixel)
Definition: utils.py:1035
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
def showAmp(amp, imageSource=FakeImageDataSource(isTrimmed=False), display=None, overlay=True, imageFactory=afwImage.ImageU)
Definition: utils.py:689
def makeImageFromCcd(ccd, isTrimmed=True, showAmpGain=True, imageFactory=afwImage.ImageU, rcMarkSize=10, binSize=1)
Definition: utils.py:267
def makeImageFromCamera(camera, detectorNameList=None, background=numpy.nan, bufferSize=10, imageSource=FakeImageDataSource(), imageFactory=afwImage.ImageU, binSize=1)
Definition: utils.py:859
def overlayCcdBoxes(ccd, untrimmedCcdBbox=None, nQuarter=0, isTrimmed=False, ccdOrigin=(0, 0), display=None, binSize=1)
Definition: utils.py:589
def findAmp(ccd, pixelPosition)
Definition: utils.py:1067
def makeImageFromAmp(amp, imValue=None, imageFactory=afwImage.ImageU, markSize=10, markValue=0, scaleGain=lambda gain:(gain *1000)//10)
Definition: utils.py:186
def prepareWcsData(wcs, amp, isTrimmed=True)
Definition: utils.py:56
def getCcdInCamBBoxList(ccdList, binSize, pixelSize_o, origin)
Definition: utils.py:788
def rawCallback(im, ccd=None, imageSource=None, correctGain=False, subtractBias=False, convertToFloat=False, obeyNQuarter=True)
Definition: utils.py:516
def getCameraImageBBox(camBbox, pixelSize, bufferSize)
Definition: utils.py:832
def showCcd(ccd, imageSource=FakeImageDataSource(), display=None, overlay=True, imageFactory=afwImage.ImageF, binSize=1, inCameraCoords=False)
Definition: utils.py:738
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
std::shared_ptr< SkyWcs > makeSkyWcs(daf::base::PropertySet &metadata, bool strip=false)
Construct a SkyWcs from FITS keywords.
Definition: SkyWcs.cc:521
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:467
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
Statistics makeStatistics(lsst::afw::image::Image< Pixel > const &img, lsst::afw::image::Mask< image::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl=StatisticsControl())
Handle a watered-down front-end to the constructor (no variance)
Definition: Statistics.h:359
std::shared_ptr< ImageT > rotateImageBy90(ImageT const &image, int nQuarter)
Rotate an image by an integral number of quarter turns.
Definition: rotateImage.cc:39
std::shared_ptr< ImageT > binImage(ImageT const &inImage, int const binX, int const binY, lsst::afw::math::Property const flags=lsst::afw::math::MEAN)
Definition: binImage.cc:44
Definition: Log.h:717
Angle abs(Angle const &a)
Definition: Angle.h:106