22 """Brighter Fatter Kernel calibration definition."""
25 __all__ = [
'BrighterFatterKernel']
29 from astropy.table
import Table
31 from .
import IsrCalib
35 """Calibration of brighter-fatter kernels for an instrument.
37 ampKernels are the kernels for each amplifier in a detector, as
38 generated by having level == 'AMP'
40 detectorKernel is the kernel generated for a detector as a
41 whole, as generated by having level == 'DETECTOR'
43 makeDetectorKernelFromAmpwiseKernels is a method to generate the
44 kernel for a detector, constructed by averaging together the
45 ampwise kernels in the detector. The existing application code is
46 only defined for kernels with level == 'DETECTOR', so this method
47 is used if the supplied kernel was built with level == 'AMP'.
52 Level the kernels will be generated for.
53 log : `logging.Logger`, optional
54 Log to write messages to.
56 Parameters to pass to parent constructor.
60 _SCHEMA =
'Brighter-fatter kernel'
63 def __init__(self, camera=None, level=None, **kwargs):
86 self.
initFromCamerainitFromCamera(camera, detectorId=kwargs.get(
'detectorId',
None))
88 self.requiredAttributes.update([
'level',
'means',
'variances',
'rawXcorrs',
89 'badAmps',
'gain',
'noise',
'meanXcorrs',
'valid',
90 'ampKernels',
'detKernels'])
93 """Update calibration metadata.
95 This calls the base class's method after ensuring the required
96 calibration keywords will be saved.
100 setDate : `bool`, optional
101 Update the CALIBDATE fields in the metadata to the current
102 time. Defaults to False.
104 Other keyword parameters to set in the metadata.
106 kwargs[
'LEVEL'] = self.
levellevel
107 kwargs[
'KERNEL_DX'] = self.
shapeshape[0]
108 kwargs[
'KERNEL_DY'] = self.
shapeshape[1]
113 """Initialize kernel structure from camera.
117 camera : `lsst.afw.cameraGeom.Camera`
118 Camera to use to define geometry.
119 detectorId : `int`, optional
120 Index of the detector to generate.
124 calib : `lsst.ip.isr.BrighterFatterKernel`
125 The initialized calibration.
130 Raised if no detectorId is supplied for a calibration with
135 if detectorId
is not None:
136 detector = camera[detectorId]
141 if self.
levellevel ==
'AMP':
142 if detectorId
is None:
143 raise RuntimeError(
"A detectorId must be supplied if level='AMP'.")
148 ampName = amp.getName()
149 self.
meansmeans[ampName] = []
152 self.
gaingain[ampName] = amp.getGain()
153 self.
noisenoise[ampName] = amp.getReadNoise()
156 self.
validvalid[ampName] = []
157 elif self.
levellevel ==
'DETECTOR':
158 if detectorId
is None:
160 detName = det.getName()
168 """Return the set of lengths needed for reshaping components.
173 Product of the elements of self.shape.
175 Size of an untiled covariance.
177 Number of observation pairs used in the kernel.
179 kernelLength = self.
shapeshape[0] * self.
shapeshape[1]
180 smallLength = int((self.
shapeshape[0] - 1)*(self.
shapeshape[1] - 1)/4)
181 if self.
levellevel ==
'AMP':
182 nObservations =
set([len(self.
meansmeans[amp])
for amp
in self.
meansmeans])
183 if len(nObservations) != 1:
184 raise RuntimeError(
"Inconsistent number of observations found.")
185 nObs = nObservations.pop()
189 return (kernelLength, smallLength, nObs)
193 """Construct a calibration from a dictionary of properties.
198 Dictionary of properties.
202 calib : `lsst.ip.isr.BrighterFatterKernel
203 Constructed calibration.
208 Raised if the supplied dictionary is for a different
213 if calib._OBSTYPE != (found := dictionary[
'metadata'][
'OBSTYPE']):
214 raise RuntimeError(f
"Incorrect brighter-fatter kernel supplied. Expected {calib._OBSTYPE}, "
217 calib.setMetadata(dictionary[
'metadata'])
218 calib.calibInfoFromDict(dictionary)
220 calib.level = dictionary[
'metadata'].get(
'LEVEL',
'AMP')
221 calib.shape = (dictionary[
'metadata'].get(
'KERNEL_DX', 0),
222 dictionary[
'metadata'].get(
'KERNEL_DY', 0))
224 calib.means = {amp: np.array(dictionary[
'means'][amp])
for amp
in dictionary[
'means']}
225 calib.variances = {amp: np.array(dictionary[
'variances'][amp])
for amp
in dictionary[
'variances']}
228 _, smallLength, nObs = calib.getLengths()
229 smallShapeSide = int(np.sqrt(smallLength))
231 calib.rawXcorrs = {amp: np.array(dictionary[
'rawXcorrs'][amp]).reshape((nObs,
234 for amp
in dictionary[
'rawXcorrs']}
236 calib.gain = dictionary[
'gain']
237 calib.noise = dictionary[
'noise']
239 calib.meanXcorrs = {amp: np.array(dictionary[
'meanXcorrs'][amp]).reshape(calib.shape)
240 for amp
in dictionary[
'rawXcorrs']}
241 calib.ampKernels = {amp: np.array(dictionary[
'ampKernels'][amp]).reshape(calib.shape)
242 for amp
in dictionary[
'ampKernels']}
243 calib.valid = {amp: bool(value)
for amp, value
in dictionary[
'valid'].
items()}
244 calib.badAmps = [amp
for amp, valid
in dictionary[
'valid'].
items()
if valid
is False]
246 calib.detKernels = {det: np.array(dictionary[
'detKernels'][det]).reshape(calib.shape)
247 for det
in dictionary[
'detKernels']}
249 calib.updateMetadata()
253 """Return a dictionary containing the calibration properties.
255 The dictionary should be able to be round-tripped through
261 Dictionary of properties.
266 metadata = self.getMetadata()
267 outDict[
'metadata'] = metadata
270 kernelLength, smallLength, nObs = self.
getLengthsgetLengths()
272 outDict[
'means'] = {amp: np.array(self.
meansmeans[amp]).tolist()
for amp
in self.
meansmeans}
273 outDict[
'variances'] = {amp: np.array(self.
variancesvariances[amp]).tolist()
for amp
in self.
variancesvariances}
274 outDict[
'rawXcorrs'] = {amp: np.array(self.
rawXcorrsrawXcorrs[amp]).reshape(nObs*smallLength).tolist()
276 outDict[
'badAmps'] = self.
badAmpsbadAmps
277 outDict[
'gain'] = self.
gaingain
278 outDict[
'noise'] = self.
noisenoise
280 outDict[
'meanXcorrs'] = {amp: self.
meanXcorrsmeanXcorrs[amp].reshape(kernelLength).tolist()
282 outDict[
'ampKernels'] = {amp: self.
ampKernelsampKernels[amp].reshape(kernelLength).tolist()
284 outDict[
'valid'] = self.
validvalid
286 outDict[
'detKernels'] = {det: self.
detKernelsdetKernels[det].reshape(kernelLength).tolist()
292 """Construct calibration from a list of tables.
294 This method uses the `fromDict` method to create the
295 calibration, after constructing an appropriate dictionary from
300 tableList : `list` [`astropy.table.Table`]
301 List of tables to use to construct the brighter-fatter
306 calib : `lsst.ip.isr.BrighterFatterKernel`
307 The calibration defined in the tables.
309 ampTable = tableList[0]
311 metadata = ampTable.meta
313 inDict[
'metadata'] = metadata
315 amps = ampTable[
'AMPLIFIER']
317 meanList = ampTable[
'MEANS']
318 varianceList = ampTable[
'VARIANCES']
320 rawXcorrs = ampTable[
'RAW_XCORRS']
321 gainList = ampTable[
'GAIN']
322 noiseList = ampTable[
'NOISE']
324 meanXcorrs = ampTable[
'MEAN_XCORRS']
325 ampKernels = ampTable[
'KERNEL']
326 validList = ampTable[
'VALID']
328 inDict[
'means'] = {amp: mean
for amp, mean
in zip(amps, meanList)}
329 inDict[
'variances'] = {amp: var
for amp, var
in zip(amps, varianceList)}
330 inDict[
'rawXcorrs'] = {amp: kernel
for amp, kernel
in zip(amps, rawXcorrs)}
331 inDict[
'gain'] = {amp: gain
for amp, gain
in zip(amps, gainList)}
332 inDict[
'noise'] = {amp: noise
for amp, noise
in zip(amps, noiseList)}
333 inDict[
'meanXcorrs'] = {amp: kernel
for amp, kernel
in zip(amps, meanXcorrs)}
334 inDict[
'ampKernels'] = {amp: kernel
for amp, kernel
in zip(amps, ampKernels)}
335 inDict[
'valid'] = {amp: bool(valid)
for amp, valid
in zip(amps, validList)}
337 inDict[
'badAmps'] = [amp
for amp, valid
in inDict[
'valid'].
items()
if valid
is False]
339 if len(tableList) > 1:
340 detTable = tableList[1]
341 inDict[
'detKernels'] = {det: kernel
for det, kernel
342 in zip(detTable[
'DETECTOR'], detTable[
'KERNEL'])}
344 inDict[
'detKernels'] = {}
349 """Construct a list of tables containing the information in this calibration.
351 The list of tables should create an identical calibration
352 after being passed to this class's fromTable method.
356 tableList : `list` [`lsst.afw.table.Table`]
357 List of tables containing the crosstalk calibration
365 kernelLength, smallLength, nObs = self.
getLengthsgetLengths()
378 if self.
levellevel ==
'AMP':
381 meanList.append(self.
meansmeans[amp])
382 varianceList.append(self.
variancesvariances[amp])
383 rawXcorrs.append(np.array(self.
rawXcorrsrawXcorrs[amp]).reshape(nObs*smallLength).tolist())
384 gainList.append(self.
gaingain[amp])
385 noiseList.append(self.
noisenoise[amp])
387 meanXcorrsList.append(self.
meanXcorrsmeanXcorrs[amp].reshape(kernelLength).tolist())
388 kernelList.append(self.
ampKernelsampKernels[amp].reshape(kernelLength).tolist())
389 validList.append(int(self.
validvalid[amp]
and not (amp
in self.
badAmpsbadAmps)))
391 ampTable = Table({
'AMPLIFIER': ampList,
393 'VARIANCES': varianceList,
394 'RAW_XCORRS': rawXcorrs,
397 'MEAN_XCORRS': meanXcorrsList,
398 'KERNEL': kernelList,
402 ampTable.meta = self.getMetadata().
toDict()
403 tableList.append(ampTable)
410 kernelList.append(self.
detKernelsdetKernels[det].reshape(kernelLength).tolist())
412 detTable = Table({
'DETECTOR': detList,
413 'KERNEL': kernelList})
414 detTable.meta = self.getMetadata().
toDict()
415 tableList.append(detTable)
421 """Average the amplifier level kernels to create a detector level kernel.
423 inKernels = np.array([self.
ampKernelsampKernels[amp]
for amp
in
424 self.
ampKernelsampKernels
if amp
not in ampsToExclude])
425 averagingList = np.transpose(inKernels)
426 avgKernel = np.zeros_like(inKernels[0])
428 sctrl.setNumSigmaClip(5.0)
429 for i
in range(np.shape(avgKernel)[0]):
430 for j
in range(np.shape(avgKernel)[1]):
432 afwMath.MEANCLIP, sctrl).getValue()
434 self.
detKernelsdetKernels[detectorName] = avgKernel
437 self.detKernel[detectorName] = self.ampKernel[ampName]
std::vector< SchemaItem< Flag > > * items
Pass parameters to a Statistics object.
def __init__(self, camera=None, level=None, **kwargs)
def fromTable(cls, tableList)
def initFromCamera(self, camera, detectorId=None)
def updateMetadata(self, setDate=False, **kwargs)
def replaceDetectorKernelWithAmpKernel(self, ampName, detectorName)
def fromDict(cls, dictionary)
def makeDetectorKernelFromAmpwiseKernels(self, detectorName, ampsToExclude=[])
daf::base::PropertyList * list
daf::base::PropertySet * set
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)