22 __all__ = [
"DetectorWrapper",
"CameraWrapper"]
33 from .cameraGeomLib
import PIXELS, TAN_PIXELS, FIELD_ANGLE, FOCAL_PLANE, SCIENCE, ACTUAL_PIXELS, \
34 CameraSys, Detector, Orientation
35 from .cameraConfig
import DetectorConfig, CameraConfig
36 from .cameraFactory
import makeCameraFromCatalogs
37 from .makePixelToTanPixel
import makePixelToTanPixel
38 from .transformConfig
import TransformMapConfig
42 """A Detector and the data used to construct it 44 Intended for use with unit tests, thus saves a copy of all input parameters. 45 Does not support setting details of amplifiers. 49 name : `str` (optional) 53 detType : `lsst.afw.cameraGeom.DetectorType` (optional) 55 serial : `str` (optional) 57 bbox : `lsst.geom.Box2I` (optional) 58 Bounding box; defaults to (0, 0), (1024x1024). 59 numAmps : `int` (optional) 61 pixelSize : `lsst.geom.Point2D` (optional) 63 ampExtent : `lsst.geom.Extent2I` (optional) 64 Dimensions of amplifier image bbox. 65 orientation : `lsst.afw.cameraGeom.Orientation` (optional) 66 Orientation of CCC in focal plane. 67 plateScale : `float` (optional) 68 Plate scale in arcsec/mm; 20.0 is for LSST. 69 radialDistortion : `float` (optional) 70 Radial distortion, in mm/rad^2. 71 The r^3 coefficient of the radial distortion polynomial 72 that converts FIELD_ANGLE in radians to FOCAL_PLANE in mm; 73 0.925 is the value Dave Monet measured for lsstSim data 74 crosstalk : `iterable` (optional) 75 Crosstalk coefficient matrix. If None, then no crosstalk correction 77 modFunc : `callable` (optional) 78 A function that can modify attributes just before constructing the 79 detector; modFunc receives one argument: a DetectorWrapper with all 80 attributes except detector set. 81 physicalType : `str` (optional) 82 The physical type of the device, e.g. CCD, E2V, HgCdTe 92 pixelSize=(0.02, 0.02),
96 radialDistortion=0.925,
114 schema = afwTable.AmpInfoTable.makeMinimalSchema()
116 for i
in range(numAmps):
118 ampName =
"amp %d" % (i + 1,)
119 record.setName(ampName)
121 record.setGain(1.71234e3)
122 record.setReadNoise(0.521237e2)
123 record.setReadoutCorner(afwTable.LL)
124 record.setHasRawInfo(
False)
129 radialDistortCoeffs = [0.0, 1.0/pScaleRad,
135 focalPlaneToField=focalPlaneToField,
144 if crosstalk
is None:
145 crosstalk = [[0.0
for _
in range(numAmps)]
for _
in range(numAmps)]
160 np.array(self.
crosstalk, dtype=np.float32),
166 """A simple Camera and the data used to construct it 168 Intended for use with unit tests, thus saves some interesting information. 173 Plate scale in arcsec/mm; 20.0 is for LSST. 174 radialDistortion : `float` 175 Radial distortion, in mm/rad^2. 176 The r^3 coefficient of the radial distortion polynomial 177 that converts FIELD_ANGLE in radians to FOCAL_PLANE in mm; 178 0.925 is the value Dave Monet measured for lsstSim data. 180 Make repository products with one raw image per amplifier (True) 181 or with one raw image per detector (False). 184 def __init__(self, plateScale=20.0, radialDistortion=0.925, isLsstLike=False):
187 "cameraGeom",
"testData")
203 """Return the number of detectors""" 207 """Construct a list of DetectorConfig, one per detector 212 with open(detFile)
as fh:
213 names = fh.readline().rstrip().lstrip(
"#").split(
"|")
215 els = l.rstrip().split(
"|")
216 detectorProps = dict([(name, el)
217 for name, el
in zip(names, els)])
218 detectors.append(detectorProps)
220 for i, detector
in enumerate(detectors):
221 detectorId = (i + 1) * 10
222 detectorName = detector[
'name']
224 detConfig.name = detectorName
225 detConfig.id = detectorId
226 detConfig.bbox_x0 = 0
227 detConfig.bbox_y0 = 0
228 detConfig.bbox_x1 =
int(detector[
'npix_x']) - 1
229 detConfig.bbox_y1 =
int(detector[
'npix_y']) - 1
230 detConfig.serial =
str(detector[
'serial'])
231 detConfig.detectorType =
int(detector[
'detectorType'])
232 detConfig.offset_x =
float(detector[
'x'])
233 detConfig.offset_y =
float(detector[
'y'])
234 detConfig.refpos_x =
float(detector[
'refPixPos_x'])
235 detConfig.refpos_y =
float(detector[
'refPixPos_y'])
236 detConfig.yawDeg =
float(detector[
'yaw'])
237 detConfig.pitchDeg =
float(detector[
'pitch'])
238 detConfig.rollDeg =
float(detector[
'roll'])
239 detConfig.pixelSize_x =
float(detector[
'pixelSize'])
240 detConfig.pixelSize_y =
float(detector[
'pixelSize'])
241 detConfig.transposeDetector =
False 242 detConfig.transformDict.nativeSys = PIXELS.getSysName()
243 detectorConfigs.append(detConfig)
246 return detectorConfigs
249 """Construct a dict of AmpInfoCatalog, one per detector. 254 Path to amplifier data file. 256 If True then there is one raw image per amplifier; 257 if False then there is one raw image per detector. 260 'LL': afwTable.ReadoutCorner.LL,
261 'LR': afwTable.ReadoutCorner.LR,
262 'UR': afwTable.ReadoutCorner.UR, 263 'UL': afwTable.ReadoutCorner.UL,
266 with open(ampFile)
as fh:
267 names = fh.readline().rstrip().lstrip(
"#").split(
"|")
269 els = l.rstrip().split(
"|")
270 ampProps = dict([(name, el)
for name, el
in zip(names, els)])
271 amps.append(ampProps)
273 schema = afwTable.AmpInfoTable.makeMinimalSchema()
274 linThreshKey = schema.addField(
'linearityThreshold', type=np.float64)
275 linMaxKey = schema.addField(
'linearityMaximum', type=np.float64)
276 linUnitsKey = schema.addField(
'linearityUnits', type=str, size=9)
279 if amp[
'ccd_name']
in ampTablesDict:
280 ampCatalog = ampTablesDict[amp[
'ccd_name']]
284 ampTablesDict[amp[
'ccd_name']] = ampCatalog
285 self.
ampInfoDict[amp[
'ccd_name']] = {
'namps': 1,
'linInfo': {}}
286 record = ampCatalog.addNew()
288 int(amp[
'trimmed_ymin'])),
290 int(amp[
'trimmed_ymax'])))
292 int(amp[
'raw_ymin'])),
294 int(amp[
'raw_ymax'])))
297 int(amp[
'raw_data_ymin'])),
299 int(amp[
'raw_data_ymax'])))
302 int(amp[
'hoscan_ymin'])),
304 int(amp[
'hoscan_ymax'])))
307 int(amp[
'voscan_ymin'])),
309 int(amp[
'voscan_ymax'])))
312 int(amp[
'pscan_ymin'])),
314 int(amp[
'pscan_ymax'])))
315 xoffset =
int(amp[
'x_offset'])
316 yoffset =
int(amp[
'y_offset'])
317 flipx = bool(
int(amp[
'flipx']))
318 flipy = bool(
int(amp[
'flipy']))
323 xExt = rawBbox.getDimensions().getX()
325 rawDataBbox.flipLR(xExt)
326 rawHOverscanBbox.flipLR(xExt)
327 rawVOverscanBbox.flipLR(xExt)
328 rawPrescanBbox.flipLR(xExt)
330 yExt = rawBbox.getDimensions().getY()
332 rawDataBbox.flipTB(yExt)
333 rawHOverscanBbox.flipTB(yExt)
334 rawVOverscanBbox.flipTB(yExt)
335 rawPrescanBbox.flipTB(yExt)
336 if not flipx
and not flipy:
338 elif flipx
and not flipy:
340 elif flipx
and flipy:
342 elif not flipx
and flipy:
345 raise RuntimeError(
"Couldn't find read corner")
349 rawBbox.shift(offext)
350 rawDataBbox.shift(offext)
351 rawHOverscanBbox.shift(offext)
352 rawVOverscanBbox.shift(offext)
353 rawPrescanBbox.shift(offext)
358 record.setRawXYOffset(offset)
359 record.setName(
str(amp[
'name']))
360 record.setReadoutCorner(readoutMap[readcorner])
361 record.setGain(
float(amp[
'gain']))
362 record.setReadNoise(
float(amp[
'readnoise']))
363 record.setLinearityCoeffs([
float(amp[
'lin_coeffs']), ])
364 record.setLinearityType(
str(amp[
'lin_type']))
365 record.setHasRawInfo(
True)
366 record.setRawFlipX(flipx)
367 record.setRawFlipY(flipy)
368 record.setRawBBox(rawBbox)
369 record.setRawDataBBox(rawDataBbox)
370 record.setRawHorizontalOverscanBBox(rawHOverscanBbox)
371 record.setRawVerticalOverscanBBox(rawVOverscanBbox)
372 record.setRawPrescanBBox(rawPrescanBbox)
373 record.set(linThreshKey,
float(amp[
'lin_thresh']))
374 record.set(linMaxKey,
float(amp[
'lin_max']))
375 record.set(linUnitsKey,
str(amp[
'lin_units']))
377 saveCoeffs = (
float(amp[
'lin_coeffs']),)
378 saveCoeffs += (np.nan, np.nan, np.nan)
379 self.
ampInfoDict[amp[
'ccd_name']][
'linInfo'][amp[
'name']] = \
380 {
'lincoeffs': saveCoeffs,
'lintype':
str(amp[
'lin_type']),
381 'linthresh':
float(amp[
'lin_thresh']),
'linmax':
float(amp[
'lin_max']),
382 'linunits':
str(amp[
'lin_units'])}
386 """Make camera config and amp catalog dictionary, using default 387 detector and amp files. 392 If True then there is one raw image per amplifier; 393 if False then there is one raw image per detector. 395 detFile = os.path.join(self.
_afwTestDataDir,
"testCameraDetectors.dat")
400 camConfig.name =
"testCamera%s"%(
'LSST' if isLsstLike
else 'SC')
401 camConfig.detectorList = dict((i, detConfig)
402 for i, detConfig
in enumerate(detectorConfigs))
405 radialDistortCoeffs = [0.0, 1.0/pScaleRad,
408 tConfig.transform.name =
'inverted' 409 radialClass = afwGeom.transformRegistry[
'radial']
410 tConfig.transform.active.transform.retarget(radialClass)
411 tConfig.transform.active.transform.coeffs = radialDistortCoeffs
413 tmc.nativeSys = FOCAL_PLANE.getSysName()
414 tmc.transforms = {FIELD_ANGLE.getSysName(): tConfig}
415 camConfig.transformDict = tmc
416 return camConfig, ampCatalogDict
421 """Compare two Point2D(Point2D) functions by evaluating them over a 426 dVal = (maxVal - minVal) / (nVal - 1)
427 for xInd
in range(nVal):
428 x = minVal + (xInd * dVal)
429 for yInd
in range(nVal):
430 y = minVal + (yInd * dVal)
432 res1 = func1(fromPoint)
433 res2 = func2(fromPoint)
434 self.assertPairsAlmostEqual(res1, res2)
439 """Compare two TransformMaps. 441 self.assertEqual(
list(map1),
list(map2))
444 with self.subTest(sysFrom=sysFrom, sysTo=sysTo):
445 transform1 = map1.getTransform(sysFrom, sysTo)
446 transform2 = map2.getTransform(sysFrom, sysTo)
447 self.compare2DFunctions(transform1.applyForward, transform2.applyForward, **kwds)
448 self.compare2DFunctions(transform1.applyInverse, transform2.applyInverse, **kwds)
453 """Compare two Detectors. 455 self.assertEqual(detector1.getName(), detector2.getName())
456 self.assertEqual(detector1.getId(), detector2.getId())
457 self.assertEqual(detector1.getSerial(), detector2.getSerial())
458 self.assertEqual(detector1.getPhysicalType(), detector2.getPhysicalType())
459 self.assertEqual(detector1.getBBox(), detector2.getBBox())
460 self.assertEqual(detector1.getPixelSize(), detector2.getPixelSize())
461 orientationIn = detector1.getOrientation()
462 orientationOut = detector2.getOrientation()
463 self.assertEqual(orientationIn.getFpPosition(), orientationOut.getFpPosition())
464 self.assertEqual(orientationIn.getReferencePoint(), orientationOut.getReferencePoint())
465 self.assertEqual(orientationIn.getYaw(), orientationOut.getYaw())
466 self.assertEqual(orientationIn.getPitch(), orientationOut.getPitch())
467 self.assertEqual(orientationIn.getRoll(), orientationOut.getRoll())
468 self.assertFloatsEqual(detector1.getCrosstalk(), detector2.getCrosstalk())
469 self.assertTransformMapsEqual(detector1.getTransformMap(), detector2.getTransformMap(), **kwds)
474 """Compare two DetectorCollections. 476 self.assertCountEqual(
list(collection1.getNameIter()),
list(collection2.getNameIter()))
477 for k
in collection1.getNameIter():
478 self.assertDetectorsEqual(collection1[k], collection2[k], **kwds)
483 """Compare two Camers. 485 self.assertDetectorCollectionsEqual(camera1, camera2, **kwds)
486 self.assertTransformMapsEqual(camera1.getTransformMap(), camera2.getTransformMap())
487 self.assertEqual(camera1.getName(), camera2.getName())
488 self.assertEqual(camera1.getPupilFactoryName(), camera2.getPupilFactoryName())
def assertDetectorCollectionsEqual(self, collection1, collection2, kwds)
Camera coordinate system; used as a key in in TransformMap.
def makeTestRepositoryItems(self, isLsstLike=False)
def makeCameraFromCatalogs(cameraConfig, ampInfoCatDict, pupilFactoryClass=PupilFactory)
def assertDetectorsEqual(self, detector1, detector2, kwds)
def __init__(self, name="detector 1", id=1, detType=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")
def assertTransformMapsEqual(self, map1, map2, kwds)
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
Describe a detector's orientation in the focal plane.
std::string getPackageDir(std::string const &packageName)
return the root directory of a setup package
def assertCamerasEqual(self, camera1, camera2, kwds)
def makeAmpCatalogs(self, ampFile, isLsstLike=False)
std::shared_ptr< TransformPoint2ToPoint2 > makeRadialTransform(std::vector< double > const &forwardCoeffs, std::vector< double > const &inverseCoeffs)
A purely radial polynomial distortion.
def compare2DFunctions(self, func1, func2, minVal=-10, maxVal=None, nVal=5)
Information about a CCD or other imaging detector.
def makePixelToTanPixel(bbox, orientation, focalPlaneToField, pixelSizeMm)
constexpr double arcsecToRad(double x) noexcept
An integer coordinate rectangle.
daf::base::PropertyList * list
def makeDetectorConfigs(self, detFile)
def __init__(self, plateScale=20.0, radialDistortion=0.925, isLsstLike=False)