22__all__ = [
"DetectorWrapper",
"CameraWrapper"]
32from ._cameraGeom
import CameraSys, PIXELS, TAN_PIXELS, FIELD_ANGLE, FOCAL_PLANE, ACTUAL_PIXELS, Orientation
33from ._cameraGeom
import Amplifier, ReadoutCorner
34from ._camera
import Camera
35from ._cameraGeom
import DetectorType
36from .cameraConfig
import DetectorConfig, CameraConfig
37from ._cameraFactory
import makeCameraFromAmpLists
38from ._makePixelToTanPixel
import makePixelToTanPixel
39from ._transformConfig
import TransformMapConfig
43 """A Detector and the data used to construct it
45 Intended for use
with unit tests, thus saves a copy of all input parameters.
46 Does
not support setting details of amplifiers.
50 name : `str` (optional)
54 detType : `lsst.afw.cameraGeom.DetectorType` (optional)
56 serial : `str` (optional)
59 Bounding box; defaults to (0, 0), (1024x1024).
60 numAmps : `int` (optional)
65 Dimensions of amplifier image bbox.
67 Orientation of CCC
in focal plane.
68 plateScale : `float` (optional)
69 Plate scale
in arcsec/mm; 20.0
is for LSST.
70 radialDistortion : `float` (optional)
71 Radial distortion,
in mm/rad^2.
72 The r^3 coefficient of the radial distortion polynomial
73 that converts FIELD_ANGLE
in radians to FOCAL_PLANE
in mm;
74 0.925
is the value Dave Monet measured
for lsstSim data
75 crosstalk : `iterable` (optional)
76 Crosstalk coefficient matrix. If
None, then no crosstalk correction
78 modFunc : `callable` (optional)
79 A function that can modify attributes just before constructing the
80 detector; modFunc receives one argument: a DetectorWrapper
with all
81 attributes
except detector set.
82 physicalType : `str` (optional)
83 The physical type of the device, e.g. CCD, E2V, HgCdTe
89 detType=DetectorType.SCIENCE,
93 pixelSize=(0.02, 0.02),
95 orientation=Orientation(),
97 radialDistortion=0.925,
120 radialDistortCoeffs = [0.0, 1.0/pScaleRad,
122 focalPlaneToField = afwGeom.makeRadialTransform(radialDistortCoeffs)
126 focalPlaneToField=focalPlaneToField,
129 tanPixelSys = CameraSys(TAN_PIXELS, self.
name)
130 actualPixelSys = CameraSys(ACTUAL_PIXELS, self.
name)
133 tanPixelSys: pixelToTanPixel,
134 actualPixelSys: afwGeom.makeRadialTransform([0, 0.95, 0.01]),
136 if crosstalk
is None:
137 crosstalk = [[0.0
for _
in range(numAmps)]
for _
in range(numAmps)]
140 if cameraBuilder
is None:
143 for i
in range(numAmps):
145 ampName = f
"amp {i + 1}"
146 ampBuilder.setName(ampName)
148 ampBuilder.setGain(1.71234e3)
149 ampBuilder.setReadNoise(0.521237e2)
150 ampBuilder.setReadoutCorner(ReadoutCorner.LL)
151 self.
ampList.append(ampBuilder)
154 detectorBuilder = cameraBuilder.add(self.
name, self.
id)
155 detectorBuilder.setType(self.
type)
156 detectorBuilder.setSerial(self.
serial)
158 detectorBuilder.setBBox(self.
bbox)
160 detectorBuilder.setPixelSize(self.
pixelSize)
161 detectorBuilder.setTransformFromPixelsTo(tanPixelSys, self.
transMap[tanPixelSys])
162 detectorBuilder.setTransformFromPixelsTo(actualPixelSys, self.
transMap[actualPixelSys])
163 detectorBuilder.setCrosstalk(np.array(self.
crosstalk, dtype=np.float32))
164 for ampBuilder
in self.
ampList:
165 detectorBuilder.append(ampBuilder)
166 camera = cameraBuilder.finish()
171 """A simple Camera and the data used to construct it
173 Intended for use
with unit tests, thus saves some interesting information.
178 Plate scale
in arcsec/mm; 20.0
is for LSST.
179 radialDistortion : `float`
180 Radial distortion,
in mm/rad^2.
181 The r^3 coefficient of the radial distortion polynomial
182 that converts FIELD_ANGLE
in radians to FOCAL_PLANE
in mm;
183 0.925
is the value Dave Monet measured
for lsstSim data.
185 Make repository products
with one raw image per amplifier (
True)
186 or with one raw image per detector (
False).
189 def __init__(self, plateScale=20.0, radialDistortion=0.925, isLsstLike=False):
190 afwDir = lsst.utils.getPackageDir(
"afw")
192 "cameraGeom",
"testData")
209 """Return the number of detectors"""
213 """Construct a list of DetectorConfig, one per detector
218 with open(detFile)
as fh:
219 names = fh.readline().rstrip().lstrip(
"#").split(
"|")
221 els = line.rstrip().split(
"|")
222 detectorProps = dict([(name, el)
223 for name, el
in zip(names, els)])
224 detectors.append(detectorProps)
226 for i, detector
in enumerate(detectors):
227 detectorId = (i + 1) * 10
228 detectorName = detector[
'name']
230 detConfig.name = detectorName
231 detConfig.id = detectorId
232 detConfig.bbox_x0 = 0
233 detConfig.bbox_y0 = 0
234 detConfig.bbox_x1 = int(detector[
'npix_x']) - 1
235 detConfig.bbox_y1 = int(detector[
'npix_y']) - 1
236 detConfig.serial =
str(detector[
'serial'])
237 detConfig.detectorType = int(detector[
'detectorType'])
238 detConfig.offset_x = float(detector[
'x'])
239 detConfig.offset_y = float(detector[
'y'])
240 detConfig.offset_z = float(detector[
'z'])
241 detConfig.refpos_x = float(detector[
'refPixPos_x'])
242 detConfig.refpos_y = float(detector[
'refPixPos_y'])
243 detConfig.yawDeg = float(detector[
'yaw'])
244 detConfig.pitchDeg = float(detector[
'pitch'])
245 detConfig.rollDeg = float(detector[
'roll'])
246 detConfig.pixelSize_x = float(detector[
'pixelSize'])
247 detConfig.pixelSize_y = float(detector[
'pixelSize'])
248 detConfig.transposeDetector =
False
249 detConfig.transformDict.nativeSys = PIXELS.getSysName()
250 detectorConfigs.append(detConfig)
253 return detectorConfigs
256 """Construct a dict of list of Amplifer, one list per detector.
261 Path to amplifier data file.
263 If True then there
is one raw image per amplifier;
264 if False then there
is one raw image per detector.
267 'LL': ReadoutCorner.LL,
268 'LR': ReadoutCorner.LR,
269 'UR': ReadoutCorner.UR,
270 'UL': ReadoutCorner.UL,
273 with open(ampFile)
as fh:
274 names = fh.readline().rstrip().lstrip(
"#").split(
"|")
276 els = line.rstrip().split(
"|")
277 ampProps = dict([(name, el)
for name, el
in zip(names, els)])
278 ampDataList.append(ampProps)
281 for ampData
in ampDataList:
282 if ampData[
'ccd_name']
in ampListDict:
283 ampList = ampListDict[ampData[
'ccd_name']]
284 self.
ampDataDict[ampData[
'ccd_name']][
'namps'] += 1
287 ampListDict[ampData[
'ccd_name']] = ampList
288 self.
ampDataDict[ampData[
'ccd_name']] = {
'namps': 1,
'linInfo': {}}
291 int(ampData[
'trimmed_ymin'])),
293 int(ampData[
'trimmed_ymax'])))
295 int(ampData[
'raw_ymin'])),
297 int(ampData[
'raw_ymax'])))
300 int(ampData[
'raw_data_ymin'])),
302 int(ampData[
'raw_data_ymax'])))
305 int(ampData[
'hoscan_ymin'])),
307 int(ampData[
'hoscan_ymax'])))
310 int(ampData[
'voscan_ymin'])),
312 int(ampData[
'voscan_ymax'])))
315 int(ampData[
'pscan_ymin'])),
317 int(ampData[
'pscan_ymax'])))
318 xoffset = int(ampData[
'x_offset'])
319 yoffset = int(ampData[
'y_offset'])
320 flipx = bool(int(ampData[
'flipx']))
321 flipy = bool(int(ampData[
'flipy']))
326 xExt = rawBbox.getDimensions().getX()
328 rawDataBbox.flipLR(xExt)
329 rawHOverscanBbox.flipLR(xExt)
330 rawVOverscanBbox.flipLR(xExt)
331 rawPrescanBbox.flipLR(xExt)
333 yExt = rawBbox.getDimensions().getY()
335 rawDataBbox.flipTB(yExt)
336 rawHOverscanBbox.flipTB(yExt)
337 rawVOverscanBbox.flipTB(yExt)
338 rawPrescanBbox.flipTB(yExt)
339 if not flipx
and not flipy:
341 elif flipx
and not flipy:
343 elif flipx
and flipy:
345 elif not flipx
and flipy:
348 raise RuntimeError(
"Couldn't find read corner")
352 rawBbox.shift(offext)
353 rawDataBbox.shift(offext)
354 rawHOverscanBbox.shift(offext)
355 rawVOverscanBbox.shift(offext)
356 rawPrescanBbox.shift(offext)
360 builder.setBBox(bbox)
361 builder.setRawXYOffset(offset)
362 builder.setName(
str(ampData[
'name']))
363 builder.setReadoutCorner(readoutMap[readcorner])
364 builder.setGain(float(ampData[
'gain']))
365 builder.setReadNoise(float(ampData[
'readnoise']))
366 linCoeffs = np.array([float(ampData[
'lin_coeffs']), ], dtype=float)
367 builder.setLinearityCoeffs(linCoeffs)
368 builder.setLinearityType(
str(ampData[
'lin_type']))
369 builder.setRawFlipX(flipx)
370 builder.setRawFlipY(flipy)
371 builder.setRawBBox(rawBbox)
372 builder.setRawDataBBox(rawDataBbox)
373 builder.setRawHorizontalOverscanBBox(rawHOverscanBbox)
374 builder.setRawVerticalOverscanBBox(rawVOverscanBbox)
375 builder.setRawPrescanBBox(rawPrescanBbox)
376 builder.setLinearityThreshold(float(ampData[
'lin_thresh']))
377 builder.setLinearityMaximum(float(ampData[
'lin_max']))
378 builder.setLinearityUnits(
str(ampData[
'lin_units']))
379 self.
ampDataDict[ampData[
'ccd_name']][
'linInfo'][ampData[
'name']] = \
380 {
'lincoeffs': linCoeffs,
'lintype':
str(ampData[
'lin_type']),
381 'linthresh': float(ampData[
'lin_thresh']),
'linmax': float(ampData[
'lin_max']),
382 'linunits':
str(ampData[
'lin_units'])}
383 ampList.append(builder)
387 """Make camera config and amp catalog dictionary, using default
388 detector and amp files.
393 If
True then there
is one raw image per amplifier;
394 if False then there
is one raw image per detector.
396 detFile = os.path.join(self._afwTestDataDir, "testCameraDetectors.dat")
399 ampListDict = self.
makeAmpLists(ampFile, isLsstLike=isLsstLike)
401 camConfig.name =
"testCamera%s"%(
'LSST' if isLsstLike
else 'SC')
402 camConfig.detectorList = dict((i, detConfig)
403 for i, detConfig
in enumerate(detectorConfigs))
406 radialDistortCoeffs = [0.0, 1.0/pScaleRad,
408 tConfig = afwGeom.TransformConfig()
409 tConfig.transform.name =
'inverted'
410 radialClass = afwGeom.transformRegistry[
'radial']
411 tConfig.transform.active.transform.retarget(radialClass)
412 tConfig.transform.active.transform.coeffs = radialDistortCoeffs
414 tmc.nativeSys = FOCAL_PLANE.getSysName()
415 tmc.transforms = {FIELD_ANGLE.getSysName(): tConfig}
416 camConfig.transformDict = tmc
417 return camConfig, ampListDict
422 """Compare two Point2D(Point2D) functions by evaluating them over a
427 dVal = (maxVal - minVal) / (nVal - 1)
428 for xInd
in range(nVal):
429 x = minVal + (xInd * dVal)
430 for yInd
in range(nVal):
431 y = minVal + (yInd * dVal)
433 res1 = func1(fromPoint)
434 res2 = func2(fromPoint)
435 self.assertPairsAlmostEqual(res1, res2)
440 """Compare two TransformMaps.
442 self.assertEqual(list(map1), list(map2))
445 with self.subTest(sysFrom=sysFrom, sysTo=sysTo):
446 transform1 = map1.getTransform(sysFrom, sysTo)
447 transform2 = map2.getTransform(sysFrom, sysTo)
448 self.compare2DFunctions(transform1.applyForward, transform2.applyForward, **kwds)
449 self.compare2DFunctions(transform1.applyInverse, transform2.applyInverse, **kwds)
454 self.assertEqual(amp1.getName(), amp2.getName())
455 self.assertEqual(amp1.getBBox(), amp2.getBBox())
456 self.assertFloatsEqual(amp1.getGain(), amp2.getGain(), ignoreNaNs=
True)
457 self.assertFloatsEqual(amp1.getReadNoise(), amp2.getReadNoise(), ignoreNaNs=
True)
458 self.assertFloatsEqual(amp1.getSaturation(), amp2.getSaturation(), ignoreNaNs=
True)
459 self.assertEqual(amp1.getReadoutCorner(), amp2.getReadoutCorner())
460 self.assertFloatsEqual(amp1.getSuspectLevel(), amp2.getSuspectLevel(), ignoreNaNs=
True)
461 self.assertEqual(amp1.getLinearityCoeffs().shape, amp2.getLinearityCoeffs().shape)
462 self.assertFloatsEqual(amp1.getLinearityCoeffs(), amp2.getLinearityCoeffs(), ignoreNaNs=
True)
463 self.assertEqual(amp1.getLinearityType(), amp2.getLinearityType())
464 self.assertFloatsEqual(amp1.getLinearityThreshold(), amp2.getLinearityThreshold(), ignoreNaNs=
True)
465 self.assertFloatsEqual(amp1.getLinearityMaximum(), amp2.getLinearityMaximum(), ignoreNaNs=
True)
466 self.assertEqual(amp1.getLinearityUnits(), amp2.getLinearityUnits())
467 self.assertEqual(amp1.getRawBBox(), amp2.getRawBBox())
468 self.assertEqual(amp1.getRawDataBBox(), amp2.getRawDataBBox())
469 self.assertEqual(amp1.getRawFlipX(), amp2.getRawFlipX())
470 self.assertEqual(amp1.getRawFlipY(), amp2.getRawFlipY())
471 self.assertEqual(amp1.getRawHorizontalOverscanBBox(), amp2.getRawHorizontalOverscanBBox())
472 self.assertEqual(amp1.getRawVerticalOverscanBBox(), amp2.getRawVerticalOverscanBBox())
473 self.assertEqual(amp1.getRawPrescanBBox(), amp2.getRawPrescanBBox())
478 """Compare two Detectors.
480 self.assertEqual(detector1.getName(), detector2.getName())
481 self.assertEqual(detector1.getId(), detector2.getId())
482 self.assertEqual(detector1.getSerial(), detector2.getSerial())
483 self.assertEqual(detector1.getPhysicalType(), detector2.getPhysicalType())
484 self.assertEqual(detector1.getBBox(), detector2.getBBox())
485 self.assertEqual(detector1.getPixelSize(), detector2.getPixelSize())
486 orientationIn = detector1.getOrientation()
487 orientationOut = detector2.getOrientation()
488 self.assertEqual(orientationIn.getFpPosition(), orientationOut.getFpPosition())
489 self.assertEqual(orientationIn.getReferencePoint(), orientationOut.getReferencePoint())
490 self.assertEqual(orientationIn.getYaw(), orientationOut.getYaw())
491 self.assertEqual(orientationIn.getPitch(), orientationOut.getPitch())
492 self.assertEqual(orientationIn.getRoll(), orientationOut.getRoll())
493 self.assertFloatsEqual(detector1.getCrosstalk(), detector2.getCrosstalk())
494 if compareTransforms:
495 self.assertTransformMapsEqual(detector1.getTransformMap(), detector2.getTransformMap(), **kwds)
496 self.assertEqual(len(detector1.getAmplifiers()), len(detector2.getAmplifiers()))
497 for amp1, amp2
in zip(detector1.getAmplifiers(), detector2.getAmplifiers()):
498 self.assertAmplifiersEqual(amp1, amp2)
503 """Compare two DetectorCollections.
505 self.assertCountEqual(list(collection1.getNameIter()), list(collection2.getNameIter()))
506 for k
in collection1.getNameIter():
507 self.assertDetectorsEqual(collection1[k], collection2[k], **kwds)
512 """Compare two Camers.
514 self.assertDetectorCollectionsEqual(camera1, camera2, **kwds)
515 self.assertTransformMapsEqual(camera1.getTransformMap(), camera2.getTransformMap())
516 self.assertEqual(camera1.getName(), camera2.getName())
517 self.assertEqual(camera1.getPupilFactoryName(), camera2.getPupilFactoryName())
A mutable Amplifier subclass class that can be used to incrementally construct or modify Amplifiers.
A helper class for creating and modifying cameras.
Describe a detector's orientation in the focal plane.
def makeDetectorConfigs(self, detFile)
def makeTestRepositoryItems(self, isLsstLike=False)
def makeAmpLists(self, ampFile, isLsstLike=False)
def __init__(self, plateScale=20.0, radialDistortion=0.925, isLsstLike=False)
def __init__(self, name="detector 1", id=1, detType=DetectorType.SCIENCE, serial="xkcd722", bbox=None, numAmps=3, pixelSize=(0.02, 0.02), ampExtent=(5, 6), orientation=Orientation(), plateScale=20.0, radialDistortion=0.925, crosstalk=None, modFunc=None, physicalType="CCD", cameraBuilder=None)
daf::base::PropertyList * list
def makeCameraFromAmpLists(cameraConfig, ampListDict, pupilFactoryClass=PupilFactory)
def makePixelToTanPixel(bbox, orientation, focalPlaneToField, pixelSizeMm)
def assertAmplifiersEqual(self, amp1, amp2)
def compare2DFunctions(self, func1, func2, minVal=-10, maxVal=None, nVal=5)
def assertTransformMapsEqual(self, map1, map2, **kwds)
def assertCamerasEqual(self, camera1, camera2, **kwds)
def assertDetectorCollectionsEqual(self, collection1, collection2, **kwds)
def assertDetectorsEqual(self, detector1, detector2, *compareTransforms=True, **kwds)
constexpr double arcsecToRad(double x) noexcept