LSSTApplications  10.0-2-g4f67435,11.0.rc2+1,11.0.rc2+12,11.0.rc2+3,11.0.rc2+4,11.0.rc2+5,11.0.rc2+6,11.0.rc2+7,11.0.rc2+8
LSSTDataManagementBasePackage
utils.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 #
4 # LSST Data Management System
5 # Copyright 2008, 2009, 2010 LSST Corporation.
6 #
7 # This product includes software developed by the
8 # LSST Project (http://www.lsst.org/).
9 #
10 # This program is free software: you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation, either version 3 of the License, or
13 # (at your option) any later version.
14 #
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
19 #
20 # You should have received a copy of the LSST License Statement and
21 # the GNU General Public License along with this program. If not,
22 # see <http://www.lsstcorp.org/LegalNotices/>.
23 #
24 
25 """
26 Support for cameraGeom
27 """
28 from __future__ import division
29 import math
30 import numpy
31 import itertools
32 
33 import lsst.afw.geom as afwGeom
34 import lsst.afw.image as afwImage
35 import lsst.afw.math as afwMath
36 import lsst.daf.base as dafBase
37 
38 from .rotateBBoxBy90 import rotateBBoxBy90
39 from .assembleImage import assembleAmplifierImage, assembleAmplifierRawImage
40 from .cameraGeomLib import PUPIL, FOCAL_PLANE
41 
42 import lsst.afw.display as afwDisplay
43 import lsst.afw.display.utils as displayUtils
44 
45 try:
46  type(display)
47 except NameError:
48  display = False
49 
50 def prepareWcsData(wcs, amp, isTrimmed=True):
51  """!Put Wcs from an Amp image into CCD coordinates
52 
53  @param[in, out] wcs WCS object to modify in place
54  @param[in] amp Amp object to use
55  @param[in] isTrimmed Is the image to which the WCS refers trimmed of non-imaging pixels?
56  """
57  if not amp.getHasRawInfo():
58  raise RuntimeError("Cannot modify wcs without raw amp information")
59  if isTrimmed:
60  ampBox = amp.getRawDataBBox()
61  else:
62  ampBox = amp.getRawBBox()
63  wcs.flipImage(amp.getRawFlipX(), amp.getRawFlipY(), ampBox.getDimensions())
64  #Shift WCS for trimming
65  wcs.shiftReferencePixel(-ampBox.getMinX(), -ampBox.getMinY())
66  #Account for shift of amp data in larger ccd matrix
67  offset = amp.getRawXYOffset()
68  wcs.shiftReferencePixel(offset.getX(), offset.getY())
69 
70 def plotFocalPlane(camera, pupilSizeDeg_x, pupilSizeDeg_y, dx=0.1, dy=0.1, figsize=(10., 10.),
71  showFig=True, savePath=None):
72  """!Make a plot of the focal plane along with a set points that sample the Pupil
73 
74  @param[in] camera a camera object
75  @param[in] pupilSizeDeg_x Amount of the pupil to sample in x in degrees
76  @param[in] pupilSizeDeg_y Amount of the pupil to sample in y in degrees
77  @param[in] dx Spacing of sample points in x in degrees
78  @param[in] dy Spacing of sample points in y in degrees
79  @param[in] figsize matplotlib style tuple indicating the size of the figure in inches
80  @param[in] showFig Display the figure on the screen?
81  @param[in] savePath If not None, save a copy of the figure to this name
82  """
83  try:
84  from matplotlib.patches import Polygon
85  from matplotlib.collections import PatchCollection
86  import matplotlib.pyplot as plt
87  except ImportError:
88  raise ImportError("Can't run plotFocalPlane: matplotlib has not been set up")
89  pupil_gridx, pupil_gridy = numpy.meshgrid(numpy.arange(0., pupilSizeDeg_x+dx, dx) - pupilSizeDeg_x/2.,
90  numpy.arange(0., pupilSizeDeg_y+dy, dy) - pupilSizeDeg_y/2.)
91  xs = []
92  ys = []
93  pcolors = []
94  for pos in zip(pupil_gridx.flatten(), pupil_gridy.flatten()):
95  posRad = afwGeom.Point2D(math.radians(pos[0]), math.radians(pos[1]))
96  cp = camera.makeCameraPoint(posRad, PUPIL)
97  ncp = camera.transform(cp, FOCAL_PLANE)
98  xs.append(ncp.getPoint().getX())
99  ys.append(ncp.getPoint().getY())
100  dets = camera.findDetectors(cp)
101  if len(dets) > 0:
102  pcolors.append('w')
103  else:
104  pcolors.append('k')
105 
106 
107  colorMap = {0:'b', 1:'y', 2:'g', 3:'r'}
108 
109  patches = []
110  colors = []
111  plt.figure(figsize=figsize)
112  ax = plt.gca()
113  xvals = []
114  yvals = []
115  for det in camera:
116  corners = [(c.getX(), c.getY()) for c in det.getCorners(FOCAL_PLANE)]
117  for corner in corners:
118  xvals.append(corner[0])
119  yvals.append(corner[1])
120  colors.append(colorMap[det.getType()])
121  patches.append(Polygon(corners, True))
122  center = det.getOrientation().getFpPosition()
123  ax.text(center.getX(), center.getY(), det.getName(), horizontalalignment='center', size=6)
124 
125  patchCollection = PatchCollection(patches, alpha=0.6, facecolor=colors)
126  ax.add_collection(patchCollection)
127  ax.scatter(xs, ys, s=10, alpha=.7, linewidths=0., c=pcolors)
128  ax.set_xlim(min(xvals) - abs(0.1*min(xvals)), max(xvals) + abs(0.1*max(xvals)))
129  ax.set_ylim(min(yvals) - abs(0.1*min(yvals)), max(yvals) + abs(0.1*max(yvals)))
130  ax.set_xlabel('Focal Plane X (mm)')
131  ax.set_ylabel('Focal Plane Y (mm)')
132  if savePath is not None:
133  plt.savefig(savePath)
134  if showFig:
135  plt.show()
136 
137 def makeImageFromAmp(amp, imValue=None, imageFactory=afwImage.ImageU, markSize=10, markValue=0,
138  scaleGain = lambda gain: (gain*1000)//10):
139  """!Make an image from an amp object
140 
141  Since images are integer images by default, the gain needs to be scaled to give enough dynamic range
142  to see variation from amp to amp. The scaling algorithm is assignable.
143 
144  @param[in] amp Amp record to use for constructing the raw amp image
145  @param[in] imValue Value to assign to the constructed image scaleGain(gain) is used if not set
146  @param[in] imageFactory Type of image to construct
147  @param[in] markSize Size of mark at read corner in pixels
148  @param[in] markValue Value of pixels in the read corner mark
149  @param[in] scaleGain The function by which to scale the gain
150  @return an untrimmed amp image
151  """
152  if not amp.getHasRawInfo():
153  raise RuntimeError("Can't create a raw amp image without raw amp information")
154  bbox = amp.getRawBBox()
155  dbbox = amp.getRawDataBBox()
156  img = imageFactory(bbox)
157  if imValue is None:
158  img.set(scaleGain(amp.getGain()))
159  else:
160  img.set(imValue)
161  #Set the first pixel read to a different value
162  markbbox = afwGeom.Box2I()
163  if amp.getReadoutCorner() == 0:
164  markbbox.include(dbbox.getMin())
165  markbbox.include(dbbox.getMin()+afwGeom.Extent2I(markSize, markSize))
166  elif amp.getReadoutCorner() == 1:
167  cornerPoint = afwGeom.Point2I(dbbox.getMaxX(), dbbox.getMinY())
168  markbbox.include(cornerPoint)
169  markbbox.include(cornerPoint + afwGeom.Extent2I(-markSize, markSize))
170  elif amp.getReadoutCorner() == 2:
171  cornerPoint = afwGeom.Point2I(dbbox.getMax())
172  markbbox.include(cornerPoint)
173  markbbox.include(cornerPoint + afwGeom.Extent2I(-markSize, -markSize))
174  elif amp.getReadoutCorner() == 3:
175  cornerPoint = afwGeom.Point2I(dbbox.getMinX(), dbbox.getMaxY())
176  markbbox.include(cornerPoint)
177  markbbox.include(cornerPoint + afwGeom.Extent2I(markSize, -markSize))
178  else:
179  raise RuntimeError("Could not set readout corner")
180  mimg = imageFactory(img, markbbox, False)
181  mimg.set(markValue)
182  return img
183 
184 def calcRawCcdBBox(ccd):
185  """!Calculate the raw ccd bounding box
186 
187  @param[in] ccd Detector for with to calculate the un-trimmed bounding box
188  @return Box2I of the un-trimmed Detector,
189  or None if there is not enough information to calculate raw BBox
190  """
191  bbox = afwGeom.Box2I()
192  for amp in ccd:
193  if not amp.getHasRawInfo():
194  return None
195  tbbox = amp.getRawBBox()
196  tbbox.shift(amp.getRawXYOffset())
197  bbox.include(tbbox)
198  return bbox
199 
200 def makeImageFromCcd(ccd, isTrimmed=True, showAmpGain=True, imageFactory=afwImage.ImageU, rcMarkSize=10,
201  binSize=1):
202  """!Make an Image of a Ccd
203 
204  @param[in] ccd Detector to use in making the image
205  @param[in] isTrimmed Assemble a trimmed Detector image if True
206  @param[in] showAmpGain Use the per amp gain to color the pixels in the image
207  @param[in] imageFactory Image type to generate
208  @param[in] rcMarkSize Size of the mark to make in the amp images at the read corner
209  @param[in] binSize Bin the image by this factor in both dimensions
210  @return Image of the Detector
211  """
212  ampImages = []
213  index = 0
214  if isTrimmed:
215  bbox = ccd.getBBox()
216  else:
217  bbox = calcRawCcdBBox(ccd)
218  for amp in ccd:
219  if amp.getHasRawInfo():
220  if showAmpGain:
221  ampImages.append(makeImageFromAmp(amp, imageFactory=imageFactory, markSize=rcMarkSize))
222  else:
223  ampImages.append(makeImageFromAmp(amp, imValue=(index+1)*1000,
224  imageFactory=imageFactory, markSize=rcMarkSize))
225  index += 1
226 
227  if len(ampImages) > 0:
228  ccdImage = imageFactory(bbox)
229  for ampImage, amp in itertools.izip(ampImages, ccd):
230  if isTrimmed:
231  assembleAmplifierImage(ccdImage, ampImage, amp)
232  else:
233  assembleAmplifierRawImage(ccdImage, ampImage, amp)
234  else:
235  if not isTrimmed:
236  raise RuntimeError("Cannot create untrimmed CCD without amps with raw information")
237  ccdImage = imageFactory(ccd.getBBox())
238  ccdImage = afwMath.binImage(ccdImage, binSize)
239  return ccdImage
240 
241 class FakeImageDataSource(object):
242  """A class to retrieve synthetic images for display by the show* methods"""
243  def __init__(self, isTrimmed=True, showAmpGain=True, markSize=10, markValue=0,
244  ampImValue=None, scaleGain = lambda gain: (gain*1000)//10):
245  """!Construct a FakeImageDataSource
246 
247  @param[in] isTrimmed Should amps be trimmed?
248  @param[in] showAmpGain color the amp segments with the gain of the amp
249  @param[in] markSize size of the side of the box used to mark the read corner
250  @param[in] markValue value to assing the read corner mark
251  @param[in] ampImValue Value to assing to amps. scaleGain(gain) is used if None
252  @param[in] scaleGain function to scale the gain by
253  """
254  self.isTrimmed = isTrimmed
255  self.showAmpGain = showAmpGain
256  self.markSize = markSize
257  self.markValue = markValue
258  self.ampImValue = ampImValue
259  self.scaleGain = scaleGain
260 
261  def getCcdImage(self, det, imageFactory, binSize):
262  """!Return a CCD image for the detector
263 
264  @param[in] det: Detector to use for making the image
265  @param[in] imageFactory: image constructor for making the image
266  @param[in] binSize: number of pixels per bin axis
267  """
268  return makeImageFromCcd(det, isTrimmed=self.isTrimmed, showAmpGain=self.showAmpGain,
269  imageFactory=imageFactory, binSize=binSize)
270 
271  def getAmpImage(self, amp, imageFactory):
272  """!Return an amp segment image
273 
274  @param[in] amp AmpInfoTable for this amp
275  @param[in] imageFactory image constructor fo making the imag
276  """
277  ampImage = makeImageFromAmp(amp, imValue=self.ampImValue, imageFactory=imageFactory,
278  markSize=self.markSize,
279  markValue=self.markValue, scaleGain=self.scaleGain)
280  if self.isTrimmed:
281  ampImage = ampImage.Factory(ampImage, amp.getRawDataBBox(), False)
282  return ampImage
283 
284 def overlayCcdBoxes(ccd, untrimmedCcdBbox, nQuarter, isTrimmed, ccdOrigin, display, binSize):
285  """!Overlay bounding boxes on an image display
286 
287  @param[in] ccd Detector to iterate for the amp bounding boxes
288  @param[in] untrimmedCcdBbox Bounding box of the un-trimmed Detector
289  @param[in] nQuarter number of 90 degree rotations to apply to the bounding boxes
290  @param[in] isTrimmed Is the Detector image over which the boxes are layed trimmed?
291  @param[in] ccdOrigin Detector origin relative to the parent origin if in a larger pixel grid
292  @param[in] display image display to display on
293  @param[in] binSize binning factor
294  """
295  with afwDisplay.Buffering():
296  ccdDim = untrimmedCcdBbox.getDimensions()
297  ccdBbox = rotateBBoxBy90(untrimmedCcdBbox, nQuarter, ccdDim)
298  for amp in ccd:
299  if isTrimmed:
300  ampbbox = amp.getBBox()
301  else:
302  ampbbox = amp.getRawBBox()
303  ampbbox.shift(amp.getRawXYOffset())
304  if nQuarter != 0:
305  ampbbox = rotateBBoxBy90(ampbbox, nQuarter, ccdDim)
306 
307  displayUtils.drawBBox(ampbbox, origin=ccdOrigin, borderWidth=0.49,
308  display=display, bin=binSize)
309 
310  if not isTrimmed and amp.getHasRawInfo():
311  for bbox, ctype in ((amp.getRawHorizontalOverscanBBox(), afwDisplay.RED),
312  (amp.getRawDataBBox(), afwDisplay.BLUE),
313  (amp.getRawVerticalOverscanBBox(), afwDisplay.MAGENTA),
314  (amp.getRawPrescanBBox(), afwDisplay.YELLOW)):
315  if amp.getRawFlipX():
316  bbox.flipLR(amp.getRawBBox().getDimensions().getX())
317  if amp.getRawFlipY():
318  bbox.flipTB(amp.getRawBBox().getDimensions().getY())
319  bbox.shift(amp.getRawXYOffset())
320  if nQuarter != 0:
321  bbox = rotateBBoxBy90(bbox, nQuarter, ccdDim)
322  displayUtils.drawBBox(bbox, origin=ccdOrigin, borderWidth=0.49, ctype=ctype,
323  display=display, bin=binSize)
324  # Label each Amp
325  xc, yc = (ampbbox.getMin()[0] + ampbbox.getMax()[0])//2, (ampbbox.getMin()[1] +
326  ampbbox.getMax()[1])//2
327  #
328  # Rotate the amp labels too
329  #
330  if nQuarter == 0:
331  c, s = 1, 0
332  elif nQuarter == 1:
333  c, s = 0, -1
334  elif nQuarter == 2:
335  c, s = -1, 0
336  elif nQuarter == 3:
337  c, s = 0, 1
338  c, s = 1, 0
339  ccdHeight = ccdBbox.getHeight()
340  ccdWidth = ccdBbox.getWidth()
341  xc -= 0.5*ccdHeight
342  yc -= 0.5*ccdWidth
343 
344  xc, yc = 0.5*ccdHeight + c*xc + s*yc, 0.5*ccdWidth + -s*xc + c*yc
345 
346  if ccdOrigin:
347  xc += ccdOrigin[0]
348  yc += ccdOrigin[1]
349  display.dot(str(amp.getName()), xc/binSize, yc/binSize, textAngle=nQuarter*90)
350 
351  displayUtils.drawBBox(ccdBbox, origin=ccdOrigin,
352  borderWidth=0.49, ctype=afwDisplay.MAGENTA, display=display, bin=binSize)
353 
354 def showAmp(amp, imageSource=FakeImageDataSource(isTrimmed=False), display=None, overlay=True,
355  imageFactory=afwImage.ImageU):
356  """!Show an amp in an image display
357 
358  @param[in] amp amp record to use in display
359  @param[in] imageSource Source for getting the amp image. Must have a getAmpImage method.
360  @param[in] display image display to use
361  @param[in] overlay Overlay bounding boxes?
362  @param[in] imageFactory Type of image to display (only used if ampImage is None)
363  """
364 
365  if not display:
366  display = afwDisplay.getDisplay()
367 
368  ampImage = imageSource.getAmpImage(amp, imageFactory=imageFactory)
369  ampImSize = ampImage.getDimensions()
370  title = amp.getName()
371  display.mtv(ampImage, title=title)
372  if overlay:
373  with afwDisplay.Buffering():
374  if amp.getHasRawInfo() and ampImSize == amp.getRawBBox().getDimensions():
375  bboxes = [(amp.getRawBBox(), 0.49, afwDisplay.GREEN),]
376  xy0 = bboxes[0][0].getMin()
377  bboxes.append((amp.getRawHorizontalOverscanBBox(), 0.49, afwDisplay.RED))
378  bboxes.append((amp.getRawDataBBox(), 0.49, afwDisplay.BLUE))
379  bboxes.append((amp.getRawPrescanBBox(), 0.49, afwDisplay.YELLOW))
380  bboxes.append((amp.getRawVerticalOverscanBBox(), 0.49, afwDisplay.MAGENTA))
381  else:
382  bboxes = [(amp.getBBox(), 0.49, None),]
383  xy0 = bboxes[0][0].getMin()
384 
385  for bbox, borderWidth, ctype in bboxes:
386  if bbox.isEmpty():
387  continue
388  bbox = afwGeom.Box2I(bbox)
389  bbox.shift(-afwGeom.ExtentI(xy0))
390  displayUtils.drawBBox(bbox, borderWidth=borderWidth, ctype=ctype, display=display)
391 
392 def showCcd(ccd, imageSource=FakeImageDataSource(), display=None, overlay=True,
393  imageFactory=afwImage.ImageU, binSize=1, inCameraCoords=False):
394  """!Show a CCD on display
395 
396  @param[in] ccd Detector to use in display
397  @param[in] imageSource Source for producing images to display. Must have a getCcdImage method.
398  @param[in] display image display to use
399  @param[in] overlay Show amp bounding boxes on the displayed image?
400  @param[in] imageFactory The image factory to use in generating the images.
401  @param[in] binSize Binning factor
402  @param[in] inCameraCoords Show the Detector in camera coordinates?
403  """
404  ccdOrigin = afwGeom.Point2I(0,0)
405  nQuarter = 0
406  ccdImage = imageSource.getCcdImage(ccd, imageFactory=imageFactory, binSize=binSize)
407 
408  ccdBbox = ccdImage.getBBox()
409  if ccdBbox.getDimensions() == ccd.getBBox().getDimensions():
410  isTrimmed = True
411  else:
412  isTrimmed = False
413 
414  if inCameraCoords:
415  nQuarter = ccd.getOrientation().getNQuarter()
416  ccdImage = afwMath.rotateImageBy90(ccdImage, nQuarter)
417  title = ccd.getName()
418  if isTrimmed:
419  title += "(trimmed)"
420  display.mtv(ccdImage, title=title)
421 
422  if overlay:
423  overlayCcdBoxes(ccd, ccdBbox, nQuarter, isTrimmed, ccdOrigin, display, binSize)
424 
425 def getCcdInCamBBoxList(ccdList, binSize, pixelSize_o, origin):
426  """!Get the bounding boxes of a list of Detectors within a camera sized pixel grid
427 
428  @param[in] ccdList List of Detector
429  @param[in] binSize Binning factor
430  @param[in] pixelSize_o Size of the pixel in mm.
431  @param[in] origin origin of the camera pixel grid in pixels
432  @return a list of bounding boxes in camera pixel coordinates
433  """
434  boxList = []
435  for ccd in ccdList:
436  if not pixelSize_o == ccd.getPixelSize():
437  raise RuntimeError(
438  "Cameras with detectors with different pixel scales are not currently supported")
439 
440  dbbox = afwGeom.Box2D()
441  for corner in ccd.getCorners(FOCAL_PLANE):
442  dbbox.include(corner)
443  llc = dbbox.getMin()
444  nQuarter = ccd.getOrientation().getNQuarter()
445  cbbox = ccd.getBBox()
446  ex = cbbox.getDimensions().getX()//binSize
447  ey = cbbox.getDimensions().getY()//binSize
448  bbox = afwGeom.Box2I(cbbox.getMin(), afwGeom.Extent2I(int(ex), int(ey)))
449  bbox = rotateBBoxBy90(bbox, nQuarter, bbox.getDimensions())
450  bbox.shift(afwGeom.Extent2I(int(llc.getX()//pixelSize_o.getX()/binSize),
451  int(llc.getY()//pixelSize_o.getY()/binSize)))
452  bbox.shift(afwGeom.Extent2I(-int(origin.getX()//binSize), -int(origin.getY())//binSize))
453  boxList.append(bbox)
454  return boxList
455 
456 def getCameraImageBBox(camBbox, pixelSize, bufferSize):
457  """!Get the bounding box of a camera sized image in pixels
458 
459  @param[in] camBbox Camera bounding box in focal plane coordinates (mm)
460  @param[in] pixelSize Size of a detector pixel in mm
461  @param[in] bufferSize Buffer around edge of image in pixels
462  @return the resulting bounding box
463  """
464  pixMin = afwGeom.Point2I(int(camBbox.getMinX()//pixelSize.getX()),
465  int(camBbox.getMinY()//pixelSize.getY()))
466  pixMax = afwGeom.Point2I(int(camBbox.getMaxX()//pixelSize.getX()),
467  int(camBbox.getMaxY()//pixelSize.getY()))
468  retBox = afwGeom.Box2I(pixMin, pixMax)
469  retBox.grow(bufferSize)
470  return retBox
471 
472 def makeImageFromCamera(camera, detectorNameList=None, background=numpy.nan, bufferSize=10,
473  imageSource=FakeImageDataSource(), imageFactory=afwImage.ImageU, binSize=1):
474  """!Make an Image of a Camera
475 
476  @param[in] camera Camera object to use to make the image
477  @param[in] detectorNameList List of detector names to use in building the image.
478  Use all Detectors if None.
479  @param[in] background Value to use where there is no Detector
480  @param[in] bufferSize Size of border in binned pixels to make around the camera image
481  @param[in] imageSource Source to get ccd images. Must have a getCcdImage method
482  @param[in] imageFactory Type of image to build
483  @param[in] binSize bin factor
484  @return an image of the camera
485  """
486  if detectorNameList is None:
487  ccdList = camera
488  else:
489  ccdList = [camera[name] for name in detectorNameList]
490 
491  if detectorNameList is None:
492  camBbox = camera.getFpBBox()
493  else:
494  camBbox = afwGeom.Box2D()
495  for detName in detectorNameList:
496  for corner in camera[detName].getCorners(FOCAL_PLANE):
497  camBbox.include(corner)
498 
499  pixelSize_o = camera[camera.getNameIter().next()].getPixelSize()
500  camBbox = getCameraImageBBox(camBbox, pixelSize_o, bufferSize*binSize)
501  origin = camBbox.getMin()
502 
503  camIm = imageFactory(int(math.ceil(camBbox.getDimensions().getX()/binSize)),
504  int(math.ceil(camBbox.getDimensions().getY()/binSize)))
505  boxList = getCcdInCamBBoxList(ccdList, binSize, pixelSize_o, origin)
506  for det, bbox in itertools.izip(ccdList, boxList):
507  im = imageSource.getCcdImage(det, imageFactory, binSize)
508  nQuarter = det.getOrientation().getNQuarter()
509  im = afwMath.rotateImageBy90(im, nQuarter)
510  imView = camIm.Factory(camIm, bbox, afwImage.LOCAL)
511  imView <<= im
512 
513  return camIm
514 
515 def showCamera(camera, imageSource=FakeImageDataSource(), imageFactory=afwImage.ImageU,
516  detectorNameList=None, binSize=10, bufferSize=10, frame=None, overlay=True, title="",
517  ctype=afwDisplay.GREEN, textSize=1.25, originAtCenter=True, display=None, **kwargs):
518  """!Show a Camera on display, with the specified display
519 
520  The rotation of the sensors is snapped to the nearest multiple of 90 deg.
521  Also note that the pixel size is constant over the image array. The lower left corner (LLC) of each
522  sensor amp is snapped to the LLC of the pixel containing the LLC of the image.
523  if overlay show the IDs and detector boundaries
524 
525  @param[in] camera Camera to show
526  @param[in] imageSource Source to get Ccd images from. Must have a getCcdImage method.
527  @param[in] imageFactory Type of image to make
528  @param[in] detectorNameList List of names of Detectors to use. If None use all
529  @param[in] binSize bin factor
530  @param[in] bufferSize size of border in binned pixels to make around camera image.
531  @param[in] frame specify image display (@deprecated; new code should use display)
532  @param[in] overlay Overlay Detector IDs and boundaries?
533  @param[in] title Title in display
534  @param[in] ctype Color to use when drawing Detector boundaries
535  @param[in] textSize Size of detector labels
536  @param[in] originAtCenter Put origin of the camera WCS at the center of the image? Else it will be LL
537  @param[in] display image display on which to display
538  @param[in] **kwargs all remaining keyword arguments are passed to makeImageFromCamera
539  @return the mosaic image
540  """
541  display = displayUtils.getDisplay(display, frame)
542 
543  if binSize < 1:
544  binSize = 1
545  cameraImage = makeImageFromCamera(camera, detectorNameList=detectorNameList, bufferSize=bufferSize,
546  imageSource=imageSource, imageFactory=imageFactory, binSize=binSize,
547  **kwargs)
548 
549  if detectorNameList is None:
550  ccdList = [camera[name] for name in camera.getNameIter()]
551  else:
552  ccdList = [camera[name] for name in detectorNameList]
553 
554  if detectorNameList is None:
555  camBbox = camera.getFpBBox()
556  else:
557  camBbox = afwGeom.Box2D()
558  for detName in detectorNameList:
559  for corner in camera[detName].getCorners(FOCAL_PLANE):
560  camBbox.include(corner)
561  pixelSize = ccdList[0].getPixelSize()
562  if originAtCenter:
563  #Can't divide SWIGGED extent type things when division is imported
564  #from future. This is DM-83
565  ext = cameraImage.getBBox().getDimensions()
566 
567  wcsReferencePixel = afwGeom.PointI(ext.getX()//2, ext.getY()//2)
568  else:
569  wcsReferencePixel = afwGeom.Point2I(0,0)
570  wcs = makeFocalPlaneWcs(pixelSize*binSize, wcsReferencePixel)
571  if title == "":
572  title = camera.getName()
573  display.mtv(cameraImage, title=title, wcs=wcs)
574 
575  if overlay:
576  with afwDisplay.Buffering():
577  camBbox = getCameraImageBBox(camBbox, pixelSize, bufferSize*binSize)
578  bboxList = getCcdInCamBBoxList(ccdList, binSize, pixelSize, camBbox.getMin())
579  for bbox, ccd in itertools.izip(bboxList, ccdList):
580  nQuarter = ccd.getOrientation().getNQuarter()
581  # borderWidth to 0.5 to align with the outside edge of the pixel
582  displayUtils.drawBBox(bbox, borderWidth=0.5, ctype=ctype, display=display)
583  dims = bbox.getDimensions()
584  display.dot(ccd.getName(), bbox.getMinX()+dims.getX()/2, bbox.getMinY()+dims.getY()/2,
585  ctype=ctype, size=textSize, textAngle=nQuarter*90)
586 
587  return cameraImage
588 
589 def makeFocalPlaneWcs(pixelSize, referencePixel):
590  """!Make a WCS for the focal plane geometry (i.e. returning positions in "mm")
591 
592  @param[in] pixelSize Size of the image pixels in physical units
593  @param[in] referencePixel Pixel for origin of WCS
594  @return Wcs object for mapping between pixels and focal plane.
595  """
596 
597  md = dafBase.PropertySet()
598  if referencePixel is None:
599  referencePixel = afwGeom.PointD(0,0)
600  for i in range(2):
601  md.set("CRPIX%d"%(i+1), referencePixel[i])
602  md.set("CRVAL%d"%(i+1), 0.)
603  md.set("CDELT1", pixelSize[0])
604  md.set("CDELT2", pixelSize[1])
605  md.set("CTYPE1", "CAMERA_X")
606  md.set("CTYPE2", "CAMERA_Y")
607  md.set("CUNIT1", "mm")
608  md.set("CUNIT2", "mm")
609 
610  return afwImage.makeWcs(md)
611 
612 def findAmp(ccd, pixelPosition):
613  """!Find the Amp with the specified pixel position within the composite
614 
615  @param[in] ccd Detector to look in
616  @param[in] pixelPosition Point2I containing the pixel position
617  @return Amp record in which pixelPosition falls or None if no Amp found.
618  """
619 
620  for amp in ccd:
621  if amp.getBBox().contains(pixelPosition):
622  return amp
623 
624  return None
def getCcdImage
Return a CCD image for the detector.
Definition: utils.py:261
def __init__
Construct a FakeImageDataSource.
Definition: utils.py:244
def findAmp
Find the Amp with the specified pixel position within the composite.
Definition: utils.py:612
def showCamera
Show a Camera on display, with the specified display.
Definition: utils.py:517
boost::shared_ptr< ImageT > binImage(ImageT const &inImage, int const binsize, lsst::afw::math::Property const flags=lsst::afw::math::MEAN)
Definition: binImage.cc:39
def getCameraImageBBox
Get the bounding box of a camera sized image in pixels.
Definition: utils.py:456
def showCcd
Show a CCD on display.
Definition: utils.py:393
An integer coordinate rectangle.
Definition: Box.h:53
def makeImageFromCcd
Make an Image of a Ccd.
Definition: utils.py:201
def assembleAmplifierRawImage
Assemble the amplifier region of a raw CCD image.
def makeFocalPlaneWcs
Make a WCS for the focal plane geometry (i.e.
Definition: utils.py:589
def makeImageFromAmp
Make an image from an amp object.
Definition: utils.py:138
def getCcdInCamBBoxList
Get the bounding boxes of a list of Detectors within a camera sized pixel grid.
Definition: utils.py:425
def getAmpImage
Return an amp segment image.
Definition: utils.py:271
def rotateBBoxBy90
Rotate a bounding box by an integer multiple of 90 degrees.
Wcs::Ptr makeWcs(coord::Coord const &crval, geom::Point2D const &crpix, double CD11, double CD12, double CD21, double CD22)
Create a Wcs object from crval, crpix, CD, using CD elements (useful from python) ...
Definition: makeWcs.cc:141
def showAmp
Show an amp in an image display.
Definition: utils.py:355
ImageT::Ptr rotateImageBy90(ImageT const &image, int nQuarter)
Definition: rotateImage.cc:41
def calcRawCcdBBox
Calculate the raw ccd bounding box.
Definition: utils.py:184
def makeImageFromCamera
Make an Image of a Camera.
Definition: utils.py:473
def plotFocalPlane
Make a plot of the focal plane along with a set points that sample the Pupil.
Definition: utils.py:71
def assembleAmplifierImage
Assemble the amplifier region of an image from a raw image.
Class for storing generic metadata.
Definition: PropertySet.h:82
A floating-point coordinate rectangle geometry.
Definition: Box.h:271
def overlayCcdBoxes
Overlay bounding boxes on an image display.
Definition: utils.py:284
def prepareWcsData
Put Wcs from an Amp image into CCD coordinates.
Definition: utils.py:50