27 from .applyLookupTable
import applyLookupTable
29 __all__ = [
"Linearizer",
30 "LinearizeBase",
"LinearizeLookupTable",
"LinearizeSquared",
31 "LinearizeProportional",
"LinearizePolynomial",
"LinearizeNone"]
35 """Parameter set for linearization. 37 These parameters are included in cameraGeom.Amplifier, but 38 should be accessible externally to allow for testing. 42 table : `numpy.array`, optional 43 Lookup table; a 2-dimensional array of floats: 44 - one row for each row index (value of coef[0] in the amplifier) 45 - one column for each image value 46 To avoid copying the table the last index should vary fastest 47 (numpy default "C" order) 48 override : `bool`, optional 49 Override the parameters defined in the detector/amplifier. 50 log : `lsst.log.Log`, optional 51 Logger to handle messages. 56 Raised if the supplied table is not 2D, or if the table has fewer 57 columns than rows (indicating that the indices are swapped). 59 def __init__(self, table=None, detector=None, override=False, log=None):
76 if len(table.shape) != 2:
77 raise RuntimeError(
"table shape = %s; must have two dimensions" % (table.shape,))
78 if table.shape[1] < table.shape[0]:
79 raise RuntimeError(
"table shape = %s; indices are switched" % (table.shape,))
80 self.
tableData = np.array(table, order=
"C")
86 """Apply linearity, setting parameters if necessary. 90 exposure : `lsst.afw.image.Exposure` 95 output : `lsst.pipe.base.Struct` 96 Linearization results: 98 Number of amplifiers considered. 100 Number of amplifiers linearized. 104 """Read linearity parameters from a detector. 108 detector : `lsst.afw.cameraGeom.detector` 109 Input detector with parameters to use. 111 self._detectorName = detector.getName()
112 self._detectorSerial = detector.getSerial()
113 self.populated =
True 116 for amp
in detector.getAmplifiers():
117 ampName = amp.getName()
118 self.linearityCoeffs[ampName] = amp.getLinearityCoeffs()
119 self.linearityType[ampName] = amp.getLinearityType()
120 self.linearityBBox[ampName] = amp.getBBox()
123 """Read linearity parameters from a dict. 128 Dictionary containing detector and amplifier information. 135 for amp
in yamlObject[
'amplifiers']:
136 ampName = amp[
'name']
138 self.
linearityType[ampName] = amp.get(
'linearityType',
'None')
142 """Return linearity parameters as a dict. 150 'hasTable': self._table
is not None,
151 'amplifiers': dict()}
153 outDict[
'amplifiers'][ampName] = {
'linearityType': self.
linearityType[ampName],
158 """Determine the linearity class to use from the type name. 162 linearityTypeName : str 163 String name of the linearity type that is needed. 167 linearityType : `~lsst.ip.isr.linearize.LinearizeBase` 168 The appropriate linearity class to use. If no matching class 169 is found, `None` is returned. 171 for t
in [LinearizeLookupTable,
174 LinearizeProportional,
176 if t.LinearityType == linearityTypeName:
181 """Validate linearity for a detector/amplifier. 185 detector : `lsst.afw.cameraGeom.Detector`, optional 186 Detector to validate, along with its amplifiers. 187 amplifier : `lsst.afw.cameraGeom.Amplifier`, optional 188 Single amplifier to validate. 193 Raised if there is a mismatch in linearity parameters, and 194 the cameraGeom parameters are not being overridden. 196 amplifiersToCheck = []
199 raise RuntimeError(
"Detector names don't match: %s != %s" %
202 raise RuntimeError(
"Detector serial numbers don't match: %s != %s" %
205 raise RuntimeError(
"Detector number of amps = %s does not match saved value %s" %
206 (len(detector.getAmplifiers()),
208 amplifiersToCheck.extend(detector.getAmplifiers())
211 amplifiersToCheck.extend(amplifier)
213 for amp
in amplifiersToCheck:
214 ampName = amp.getName()
216 raise RuntimeError(
"Amplifier %s is not in linearity data" %
220 self.
log.warn(
"Overriding amplifier defined linearityType (%s) for %s",
223 raise RuntimeError(
"Amplifier %s type %s does not match saved value %s" %
224 (ampName, amp.getLinearityType(), self.
linearityType[ampName]))
225 if not np.allclose(amp.getLinearityCoeffs(), self.
linearityCoeffs[ampName], equal_nan=
True):
227 self.
log.warn(
"Overriding amplifier defined linearityCoeffs (%s) for %s",
230 raise RuntimeError(
"Amplifier %s coeffs %s does not match saved value %s" %
234 """Apply the linearity to an image. 236 If the linearity parameters are populated, use those, 237 otherwise use the values from the detector. 241 image : `~lsst.afw.image.image` 243 detector : `~lsst.afw.cameraGeom.detector` 244 Detector to use for linearity parameters if not already 246 log : `~lsst.log.Log`, optional 247 Log object to use for logging. 263 if linearizer
is not None:
265 success, outOfRange = linearizer()(ampView, **{
'coeffs': self.
linearityCoeffs[ampName],
268 numOutOfRange += outOfRange
271 elif log
is not None:
272 log.warn(
"Amplifier %s did not linearize.",
276 numLinearized=numLinearized,
277 numOutOfRange=numOutOfRange
282 """Abstract base class functor for correcting non-linearity. 284 Subclasses must define __call__ and set class variable 285 LinearityType to a string that will be used for linearity type in 286 the cameraGeom.Amplifier.linearityType field. 288 All linearity corrections should be defined in terms of an 289 additive correction, such that: 291 corrected_value = uncorrected_value + f(uncorrected_value) 297 """Correct non-linearity. 301 image : `lsst.afw.image.Image` 302 Image to be corrected 304 Dictionary of parameter keywords: 306 Coefficient vector (`list` or `numpy.array`). 308 Lookup table data (`numpy.array`). 310 Logger to handle messages (`lsst.log.Log`). 315 If true, a correction was applied successfully. 320 Raised if the linearity type listed in the 321 detector does not match the class type. 326 class LinearizeLookupTable(LinearizeBase):
327 """Correct non-linearity with a persisted lookup table. 329 The lookup table consists of entries such that given 330 "coefficients" c0, c1: 332 for each i,j of image: 334 colInd = int(c1 + uncorrImage[i,j]) 335 corrImage[i,j] = uncorrImage[i,j] + table[rowInd, colInd] 337 - c0: row index; used to identify which row of the table to use 338 (typically one per amplifier, though one can have multiple 339 amplifiers use the same table) 340 - c1: column index offset; added to the uncorrected image value 341 before truncation; this supports tables that can handle 342 negative image values; also, if the c1 ends with .5 then 343 the nearest index is used instead of truncating to the 346 LinearityType =
"LookupTable" 349 """Correct for non-linearity. 353 image : `lsst.afw.image.Image` 354 Image to be corrected 356 Dictionary of parameter keywords: 358 Columnation vector (`list` or `numpy.array`). 360 Lookup table data (`numpy.array`). 362 Logger to handle messages (`lsst.log.Log`). 367 If true, a correction was applied successfully. 372 Raised if the requested row index is out of the table 377 rowInd, colIndOffset = kwargs[
'coeffs'][0:2]
378 table = kwargs[
'table']
381 numTableRows = table.shape[0]
383 if rowInd < 0
or rowInd > numTableRows:
384 raise RuntimeError(
"LinearizeLookupTable rowInd=%s not in range[0, %s)" %
385 (rowInd, numTableRows))
386 tableRow = table[rowInd, :]
389 if numOutOfRange > 0
and log
is not None:
390 log.warn(
"%s pixels were out of range of the linearization table",
392 if numOutOfRange < image.getArray().size:
393 return True, numOutOfRange
395 return False, numOutOfRange
399 """Correct non-linearity with a polynomial mode. 401 corrImage = uncorrImage + sum_i c_i uncorrImage^(2 + i) 403 where c_i are the linearity coefficients for each amplifier. 404 Lower order coefficients are not included as they duplicate other 405 calibration parameters: 407 A coefficient multiplied by uncorrImage**0 is equivalent to 408 bias level. Irrelevant for correcting non-linearity. 410 A coefficient multiplied by uncorrImage**1 is proportional 411 to the gain. Not necessary for correcting non-linearity. 413 LinearityType =
"Polynomial" 416 """Correct non-linearity. 420 image : `lsst.afw.image.Image` 421 Image to be corrected 423 Dictionary of parameter keywords: 425 Coefficient vector (`list` or `numpy.array`). 427 Logger to handle messages (`lsst.log.Log`). 432 If true, a correction was applied successfully. 434 if np.any(np.isfinite(kwargs[
'coeffs'])):
436 if not np.any(kwargs[
'coeffs']):
439 ampArray = image.getArray()
440 correction = np.zeroes_like(ampArray)
441 for coeff, order
in enumerate(kwargs[
'coeffs'], start=2):
442 correction += coeff * np.power(ampArray, order)
443 ampArray += correction
449 """Correct non-linearity with a squared model. 451 corrImage = uncorrImage + c0*uncorrImage^2 453 where c0 is linearity coefficient 0 for each amplifier. 455 LinearityType =
"Squared" 458 """Correct for non-linearity. 462 image : `lsst.afw.image.Image` 463 Image to be corrected 465 Dictionary of parameter keywords: 467 Coefficient vector (`list` or `numpy.array`). 469 Logger to handle messages (`lsst.log.Log`). 474 If true, a correction was applied successfully. 477 sqCoeff = kwargs[
'coeffs'][0]
479 ampArr = image.getArray()
480 ampArr *= (1 + sqCoeff*ampArr)
487 """Do not correct non-linearity. 489 LinearityType =
"Proportional" 492 """Do not correct for non-linearity. 496 image : `lsst.afw.image.Image` 497 Image to be corrected 499 Dictionary of parameter keywords: 501 Coefficient vector (`list` or `numpy.array`). 503 Logger to handle messages (`lsst.log.Log`). 508 If true, a correction was applied successfully. 514 """Do not correct non-linearity. 516 LinearityType =
"None" 519 """Do not correct for non-linearity. 523 image : `lsst.afw.image.Image` 524 Image to be corrected 526 Dictionary of parameter keywords: 528 Coefficient vector (`list` or `numpy.array`). 530 Logger to handle messages (`lsst.log.Log`). 535 If true, a correction was applied successfully.
def fromYaml(self, yamlObject)
def __call__(self, exposure)
def __call__(self, image, kwargs)
def getLinearityTypeByName(self, linearityTypeName)
def fromDetector(self, detector)
def __call__(self, image, kwargs)
def __call__(self, image, kwargs)
def __call__(self, image, kwargs)
def applyLinearity(self, image, detector=None, log=None)
def __call__(self, image, kwargs)
def __call__(self, image, kwargs)
def validate(self, detector=None, amplifier=None)
def __init__(self, table=None, detector=None, override=False, log=None)