26 "LinearizeLookupTable",
28 "LinearizeProportional",
29 "LinearizePolynomial",
31 "LinearizeDoubleSpline",
37from scipy.interpolate
import Akima1DInterpolator
39from astropy.table
import Table
41from lsst.pipe.base
import Struct
42from lsst.geom import Box2I, Point2I, Extent2I
43from .applyLookupTable
import applyLookupTable
44from .calibType
import IsrCalib
45from .isrFunctions
import isTrimmedImage
49 """Parameter set for linearization.
51 These parameters are included in `lsst.afw.cameraGeom.Amplifier`, but
52 should be accessible externally to allow for testing.
56 table : `numpy.array`, optional
57 Lookup table; a 2-dimensional array of floats:
59 - one row for each row index (value of coef[0] in the amplifier)
60 - one column for each image value
62 To avoid copying the table the last index should vary fastest
63 (numpy default "C" order)
64 detector : `lsst.afw.cameraGeom.Detector`, optional
65 Detector object. Passed to self.fromDetector() on init.
66 log : `logging.Logger`, optional
67 Logger to handle messages.
68 kwargs : `dict`, optional
69 Other keyword arguments to pass to the parent init.
74 Raised if the supplied table is not 2D, or if the table has fewer
75 columns than rows (indicating that the indices are swapped).
79 The linearizer attributes stored are:
82 Whether a linearity correction is defined for this detector.
84 Whether the detector parameters should be overridden.
85 ampNames : `list` [`str`]
86 List of amplifier names to correct.
87 linearityCoeffs : `dict` [`str`, `np.ndarray`]
88 Coefficients to use in correction. Indexed by amplifier
89 names. The format of the array depends on the type of
91 linearityType : `dict` [`str`, `str`]
92 Type of correction to use, indexed by amplifier names.
93 linearityBBox : `dict` [`str`, `lsst.geom.Box2I`]
94 Bounding box the correction is valid over, indexed by
96 fitParams : `dict` [`str`, `np.ndarray`], optional
97 Linearity fit parameters used to construct the correction
98 coefficients, indexed as above.
99 fitParamsErr : `dict` [`str`, `np.ndarray`], optional
100 Uncertainty values of the linearity fit parameters used to
101 construct the correction coefficients, indexed as above.
102 fitChiSq : `dict` [`str`, `float`], optional
103 Chi-squared value of the linearity fit, indexed as above.
104 fitResiduals : `dict` [`str`, `np.ndarray`], optional
105 Residuals of the fit, indexed as above. Used for
106 calculating photodiode corrections
107 fitResidualsSigmaMad : `dict` [`str`, `float`], optional
108 Robust median-absolute-deviation of fit residuals, scaled
110 fitResidualsUnmasked : `dict` [`str`, `np.ndarray`], optional
111 Same as fitResiduals, but all outliers are included and
113 fitResidualsModel : `dict` [`str`, `np.ndarray`], optional
114 The model count level for each of the fitResiduals.
115 linearFit : The linear fit to the low flux region of the curve.
117 tableData : `np.ndarray`, optional
118 Lookup table data for the linearity correction.
119 inputAbscissa : `dict` [`str`, `np.ndarray`], optional
120 Input abscissa used to construct linearizer (usually photodiode
122 inputOrdinate : `dict` [`str`, `np.ndarray`], optional
123 Input ordinate used to construct linearizer (raw mean counts).
124 inputMask : `dict` [`str`, `np.ndarray`], optional
125 Input mask used for the fitting.
126 inputGroupingIndex : `dict` [`str`, `np.ndarray`], optional
127 Input grouping index used for fitting.
128 inputNormalization : `dict` [`str`, `np.ndarray`], optional
129 Input normalization that was applied to the abscissa for
130 each pair from the PTC used for the linearization fit.
131 absoluteReferenceAmplifier : `str`, optional
132 Amplifier used for the reference for absolute linearization
135 Version 1.4 adds ``linearityTurnoff`` and ``linearityMaxSignal``.
136 Version 1.5 adds ``fitResidualsUnmasked``, ``inputAbscissa``,
137 ``inputOrdinate``, ``inputMask``, ``inputGroupingIndex``,
138 ``fitResidualsModel``, and ``inputNormalization``.
139 Version 1.6 adds ``absoluteReferenceAmplifier``.
141 _OBSTYPE =
"LINEARIZER"
142 _SCHEMA =
'Gen3 Linearizer'
170 if table
is not None:
171 if len(table.shape) != 2:
172 raise RuntimeError(
"table shape = %s; must have two dimensions" % (table.shape,))
173 if table.shape[1] < table.shape[0]:
174 raise RuntimeError(
"table shape = %s; indices are switched" % (table.shape,))
175 self.
tableData = np.array(table, order=
"C")
184 'linearityCoeffs',
'linearityType',
'linearityBBox',
185 'fitParams',
'fitParamsErr',
'fitChiSq',
186 'fitResiduals',
'fitResidualsSigmaMad',
'linearFit',
'tableData',
187 'units',
'linearityTurnoff',
'linearityMaxSignal',
188 'fitResidualsUnmasked',
'inputAbscissa',
'inputOrdinate',
189 'inputMask',
'inputGroupingIndex',
'fitResidualsModel',
190 'inputNormalization',
'absoluteReferenceAmplifier'])
193 """Update metadata keywords with new values.
195 This calls the base class's method after ensuring the required
196 calibration keywords will be saved.
200 setDate : `bool`, optional
201 Update the CALIBDATE fields in the metadata to the current
202 time. Defaults to False.
204 Other keyword parameters to set in the metadata.
208 kwargs[
'HAS_TABLE'] = self.
tableData is not None
214 """Read linearity parameters from a detector.
218 detector : `lsst.afw.cameraGeom.detector`
219 Input detector with parameters to use.
223 calib : `lsst.ip.isr.Linearizer`
224 The calibration constructed from the detector.
232 for amp
in detector.getAmplifiers():
233 ampName = amp.getName()
246 """Construct a calibration from a dictionary of properties
251 Dictionary of properties
255 calib : `lsst.ip.isr.Linearity`
256 Constructed calibration.
261 Raised if the supplied dictionary is for a different
267 if calib._OBSTYPE != dictionary[
'metadata'][
'OBSTYPE']:
268 raise RuntimeError(f
"Incorrect linearity supplied. Expected {calib._OBSTYPE}, "
269 f
"found {dictionary['metadata']['OBSTYPE']}")
271 calib.setMetadata(dictionary[
'metadata'])
273 calib.hasLinearity = dictionary.get(
'hasLinearity',
274 dictionary[
'metadata'].get(
'HAS_LINEARITY',
False))
275 calib.override = dictionary.get(
'override',
True)
280 calib.linearityUnits = dictionary.get(
'linearityUnits',
'adu')
282 if calib.hasLinearity:
283 for ampName
in dictionary[
'amplifiers']:
284 amp = dictionary[
'amplifiers'][ampName]
285 calib.ampNames.append(ampName)
286 calib.linearityCoeffs[ampName] = np.array(amp.get(
'linearityCoeffs', [0.0]))
287 calib.linearityType[ampName] = amp.get(
'linearityType',
'None')
288 calib.linearityBBox[ampName] = amp.get(
'linearityBBox',
None)
290 calib.inputAbscissa[ampName] = np.array(amp.get(
'inputAbscissa', [0.0]))
291 calib.inputOrdinate[ampName] = np.array(amp.get(
'inputOrdinate', [0.0]))
292 calib.inputMask[ampName] = np.array(amp.get(
'inputMask', [
False]))
293 calib.inputGroupingIndex[ampName] = np.array(amp.get(
'inputGroupingIndex', [0.0]))
294 calib.inputNormalization[ampName] = np.array(amp.get(
'inputNormalization', [1.0]))
296 calib.fitParams[ampName] = np.array(amp.get(
'fitParams', [0.0]))
297 calib.fitParamsErr[ampName] = np.array(amp.get(
'fitParamsErr', [0.0]))
298 calib.fitChiSq[ampName] = amp.get(
'fitChiSq', np.nan)
299 calib.fitResiduals[ampName] = np.array(amp.get(
'fitResiduals', [0.0]))
300 calib.fitResidualsSigmaMad[ampName] = np.array(amp.get(
'fitResidualsSigmaMad', np.nan))
301 calib.fitResidualsUnmasked[ampName] = np.array(amp.get(
'fitResidualsUnmasked', [0.0]))
302 calib.fitResidualsModel[ampName] = np.array(amp.get(
'fitResidualsModel', [0.0]))
303 calib.linearFit[ampName] = np.array(amp.get(
'linearFit', [0.0]))
305 calib.linearityTurnoff[ampName] = np.array(amp.get(
'linearityTurnoff', np.nan))
306 calib.linearityMaxSignal[ampName] = np.array(amp.get(
'linearityMaxSignal', np.nan))
308 calib.tableData = dictionary.get(
'tableData',
None)
310 calib.tableData = np.array(calib.tableData)
312 calib.absoluteReferenceAmplifier = dictionary.get(
313 'absoluteReferenceAmplifier', calib.absoluteReferenceAmplifier,
319 """Return linearity parameters as a dict.
332 'amplifiers': dict(),
336 outDict[
'amplifiers'][ampName] = {
342 'inputMask': self.
inputMask[ampName].tolist(),
345 'fitParams': self.
fitParams[ampName].tolist(),
352 'linearFit': self.
linearFit[ampName].tolist(),
357 outDict[
'tableData'] = self.
tableData.tolist()
365 """Read linearity from a FITS file.
367 This method uses the `fromDict` method to create the
368 calibration, after constructing an appropriate dictionary from
373 tableList : `list` [`astropy.table.Table`]
374 afwTable read from input file name.
378 linearity : `~lsst.ip.isr.linearize.Linearizer``
379 Linearity parameters.
383 The method reads a FITS file with 1 or 2 extensions. The metadata is
384 read from the header of extension 1, which must exist. Then the table
385 is loaded, and the ['AMPLIFIER_NAME', 'TYPE', 'COEFFS', 'BBOX_X0',
386 'BBOX_Y0', 'BBOX_DX', 'BBOX_DY'] columns are read and used to set each
387 dictionary by looping over rows.
388 Extension 2 is then attempted to read in the try block (which only
389 exists for lookup tables). It has a column named 'LOOKUP_VALUES' that
390 contains a vector of the lookup entries in each row.
392 coeffTable = tableList[0]
394 metadata = coeffTable.meta
396 inDict[
'metadata'] = metadata
397 inDict[
'hasLinearity'] = metadata.get(
'HAS_LINEARITY',
False)
398 inDict[
'amplifiers'] = dict()
399 inDict[
'linearityUnits'] = metadata.get(
'LINEARITY_UNITS',
'adu')
401 for record
in coeffTable:
402 ampName = record[
'AMPLIFIER_NAME']
404 inputAbscissa = record[
'INP_ABSCISSA']
if 'INP_ABSCISSA' in record.columns
else np.array([0.0])
405 inputOrdinate = record[
'INP_ORDINATE']
if 'INP_ORDINATE' in record.columns
else np.array([0.0])
406 inputMask = record[
'INP_MASK']
if 'INP_MASK' in record.columns
else np.array([
False])
407 inputGroupingIndex = record[
'INP_GROUPING_INDEX']
if 'INP_GROUPING_INDEX' in record.columns \
409 inputNormalization = record[
'INP_NORMALIZATION']
if 'INP_NORMALIZATION' in record.columns \
411 fitParams = record[
'FIT_PARAMS']
if 'FIT_PARAMS' in record.columns
else np.array([0.0])
412 fitParamsErr = record[
'FIT_PARAMS_ERR']
if 'FIT_PARAMS_ERR' in record.columns
else np.array([0.0])
413 fitChiSq = record[
'RED_CHI_SQ']
if 'RED_CHI_SQ' in record.columns
else np.nan
414 fitResiduals = record[
'FIT_RES']
if 'FIT_RES' in record.columns
else np.array([0.0])
415 fitResidualsSigmaMad = record[
'FIT_RES_SIGMAD']
if 'FIT_RES_SIGMAD' in record.columns
else np.nan
416 fitResidualsUnmasked = record[
'FIT_RES_UNMASKED'] \
417 if 'FIT_RES_UNMASKED' in record.columns
else np.array([0.0])
418 fitResidualsModel = record[
'FIT_RES_MODEL'] \
419 if 'FIT_RES_MODEL' in record.columns
else np.array([0.0])
420 linearFit = record[
'LIN_FIT']
if 'LIN_FIT' in record.columns
else np.array([0.0])
422 linearityTurnoff = record[
'LINEARITY_TURNOFF']
if 'LINEARITY_TURNOFF' in record.columns \
424 linearityMaxSignal = record[
'LINEARITY_MAX_SIGNAL']
if 'LINEARITY_MAX_SIGNAL' in record.columns \
427 inDict[
'amplifiers'][ampName] = {
428 'linearityType': record[
'TYPE'],
429 'linearityCoeffs': record[
'COEFFS'],
430 'linearityBBox':
Box2I(
Point2I(record[
'BBOX_X0'], record[
'BBOX_Y0']),
431 Extent2I(record[
'BBOX_DX'], record[
'BBOX_DY'])),
432 'inputAbscissa': inputAbscissa,
433 'inputOrdinate': inputOrdinate,
434 'inputMask': inputMask,
435 'inputGroupingIndex': inputGroupingIndex,
436 'inputNormalization': inputNormalization,
437 'fitParams': fitParams,
438 'fitParamsErr': fitParamsErr,
439 'fitChiSq': fitChiSq,
440 'fitResiduals': fitResiduals,
441 'fitResidualsSigmaMad': fitResidualsSigmaMad,
442 'fitResidualsUnmasked': fitResidualsUnmasked,
443 'fitResidualsModel': fitResidualsModel,
444 'linearFit': linearFit,
445 'linearityTurnoff': linearityTurnoff,
446 'linearityMaxSignal': linearityMaxSignal,
449 if len(tableList) > 1:
450 tableData = tableList[1]
451 inDict[
'tableData'] = [record[
'LOOKUP_VALUES']
for record
in tableData]
453 if 'ABS_REF_AMP' in coeffTable.columns:
454 inDict[
'absoluteReferenceAmplifier'] = str(coeffTable[
'ABS_REF_AMP'][0])
456 inDict[
'absoluteReferenceAmplifier'] =
''
461 """Construct a list of tables containing the information in this
464 The list of tables should create an identical calibration
465 after being passed to this class's fromTable method.
469 tableList : `list` [`astropy.table.Table`]
470 List of tables containing the linearity calibration
476 catalog = Table([{
'AMPLIFIER_NAME': ampName,
490 'RED_CHI_SQ': self.
fitChiSq[ampName],
501 tableList.append(catalog)
504 catalog = Table([{
'LOOKUP_VALUES': value}
for value
in self.
tableData])
505 tableList.append(catalog)
509 """Determine the linearity class to use from the type name.
513 linearityTypeName : str
514 String name of the linearity type that is needed.
518 linearityType : `~lsst.ip.isr.linearize.LinearizeBase`
519 The appropriate linearity class to use. If no matching class
520 is found, `None` is returned.
522 for t
in [LinearizeLookupTable,
525 LinearizeProportional,
527 LinearizeDoubleSpline,
529 if t.LinearityType == linearityTypeName:
534 """Validate linearity for a detector/amplifier.
538 detector : `lsst.afw.cameraGeom.Detector`, optional
539 Detector to validate, along with its amplifiers.
540 amplifier : `lsst.afw.cameraGeom.Amplifier`, optional
541 Single amplifier to validate.
546 Raised if there is a mismatch in linearity parameters, and
547 the cameraGeom parameters are not being overridden.
549 amplifiersToCheck = []
552 raise RuntimeError(
"Detector names don't match: %s != %s" %
555 raise RuntimeError(
"Detector IDs don't match: %s != %s" %
564 raise RuntimeError(
"Detector number of amps = %s does not match saved value %s" %
565 (len(detector.getAmplifiers()),
567 amplifiersToCheck.extend(detector.getAmplifiers())
570 amplifiersToCheck.extend(amplifier)
572 for amp
in amplifiersToCheck:
573 ampName = amp.getName()
575 raise RuntimeError(
"Amplifier %s is not in linearity data" %
579 self.
log.debug(
"Overriding amplifier defined linearityType (%s) for %s",
582 raise RuntimeError(
"Amplifier %s type %s does not match saved value %s" %
583 (ampName, amp.getLinearityType(), self.
linearityType[ampName]))
584 if (amp.getLinearityCoeffs().shape != self.
linearityCoeffs[ampName].shape
or not
585 np.allclose(amp.getLinearityCoeffs(), self.
linearityCoeffs[ampName], equal_nan=
True)):
587 self.
log.debug(
"Overriding amplifier defined linearityCoeffs (%s) for %s",
590 raise RuntimeError(
"Amplifier %s coeffs %s does not match saved value %s" %
594 """Apply the linearity to an image.
596 If the linearity parameters are populated, use those,
597 otherwise use the values from the detector.
601 image : `~lsst.afw.image.image`
603 detector : `~lsst.afw.cameraGeom.detector`, optional
604 Detector to use to determine exposure trimmed state. If
605 supplied, but no other linearity information is provided
606 by the calibration, then the static solution stored in the
607 detector will be used.
608 log : `~logging.Logger`, optional
609 Log object to use for logging.
610 gains : `dict` [`str`, `float`], optional
611 Dictionary of amp name to gain. If this is provided then
612 linearity terms will be converted from adu to electrons.
613 Only used for Spline linearity corrections.
635 gainValue = gains[ampName]
639 if linearizer
is not None:
642 bbox = detector[ampName].getBBox()
644 bbox = detector[ampName].getRawBBox()
648 ampView = image.Factory(image, bbox)
649 success, outOfRange = linearizer()(ampView, **{
'coeffs': self.
linearityCoeffs[ampName],
653 numOutOfRange += outOfRange
656 elif log
is not None:
657 log.warning(
"Amplifier %s did not linearize.",
661 numLinearized=numLinearized,
662 numOutOfRange=numOutOfRange
667 """Abstract base class functor for correcting non-linearity.
669 Subclasses must define ``__call__`` and set class variable
670 LinearityType to a string that will be used for linearity type in
671 the cameraGeom.Amplifier.linearityType field.
673 All linearity corrections should be defined in terms of an
674 additive correction, such that:
676 corrected_value = uncorrected_value + f(uncorrected_value)
682 """Correct non-linearity.
686 image : `lsst.afw.image.Image`
687 Image to be corrected
689 Dictionary of parameter keywords:
692 Coefficient vector (`list` or `np.ndarray`).
694 Lookup table data (`np.ndarray`).
696 Logger to handle messages (`logging.Logger`).
701 If `True`, a correction was applied successfully.
706 Raised if the linearity type listed in the
707 detector does not match the class type.
712class LinearizeLookupTable(LinearizeBase):
713 """Correct non-linearity with a persisted lookup table.
715 The lookup table consists of entries such that given
716 "coefficients" c0, c1:
718 for each i,j of image:
720 colInd = int(c1 + uncorrImage[i,j])
721 corrImage[i,j] = uncorrImage[i,j] + table[rowInd, colInd]
723 - c0: row index; used to identify which row of the table to use
724 (typically one per amplifier, though one can have multiple
725 amplifiers use the same table)
726 - c1: column index offset; added to the uncorrected image value
727 before truncation; this supports tables that can handle
728 negative image values; also, if the c1 ends with .5 then
729 the nearest index is used instead of truncating to the
732 LinearityType =
"LookupTable"
735 """Correct for non-linearity.
739 image : `lsst.afw.image.Image`
740 Image to be corrected
742 Dictionary of parameter keywords:
745 Columnation vector (`list` or `np.ndarray`).
747 Lookup table data (`np.ndarray`).
749 Logger to handle messages (`logging.Logger`).
753 output : `tuple` [`bool`, `int`]
754 If true, a correction was applied successfully. The
755 integer indicates the number of pixels that were
756 uncorrectable by being out of range.
761 Raised if the requested row index is out of the table
766 rowInd, colIndOffset = kwargs[
'coeffs'][0:2]
767 table = kwargs[
'table']
770 numTableRows = table.shape[0]
772 if rowInd < 0
or rowInd > numTableRows:
773 raise RuntimeError(
"LinearizeLookupTable rowInd=%s not in range[0, %s)" %
774 (rowInd, numTableRows))
775 tableRow = np.array(table[rowInd, :], dtype=image.getArray().dtype)
779 if numOutOfRange > 0
and log
is not None:
780 log.warning(
"%s pixels were out of range of the linearization table",
782 if numOutOfRange < image.getArray().size:
783 return True, numOutOfRange
785 return False, numOutOfRange
789 """Correct non-linearity with a polynomial mode.
793 corrImage = uncorrImage + sum_i c_i uncorrImage^(2 + i)
795 where ``c_i`` are the linearity coefficients for each amplifier.
796 Lower order coefficients are not included as they duplicate other
797 calibration parameters:
800 A coefficient multiplied by ``uncorrImage**0`` is equivalent to
801 bias level. Irrelevant for correcting non-linearity.
803 A coefficient multiplied by ``uncorrImage**1`` is proportional
804 to the gain. Not necessary for correcting non-linearity.
806 LinearityType =
"Polynomial"
809 """Correct non-linearity.
813 image : `lsst.afw.image.Image`
814 Image to be corrected
816 Dictionary of parameter keywords:
819 Coefficient vector (`list` or `np.ndarray`).
820 If the order of the polynomial is n, this list
821 should have a length of n-1 ("k0" and "k1" are
822 not needed for the correction).
824 Logger to handle messages (`logging.Logger`).
828 output : `tuple` [`bool`, `int`]
829 If true, a correction was applied successfully. The
830 integer indicates the number of pixels that were
831 uncorrectable by being out of range.
833 if not np.any(np.isfinite(kwargs[
'coeffs'])):
835 if not np.any(kwargs[
'coeffs']):
838 ampArray = image.getArray()
839 correction = np.zeros_like(ampArray)
840 for order, coeff
in enumerate(kwargs[
'coeffs'], start=2):
841 correction += coeff * np.power(ampArray, order)
842 ampArray += correction
848 """Correct non-linearity with a squared model.
850 corrImage = uncorrImage + c0*uncorrImage^2
852 where c0 is linearity coefficient 0 for each amplifier.
854 LinearityType =
"Squared"
857 """Correct for non-linearity.
861 image : `lsst.afw.image.Image`
862 Image to be corrected
864 Dictionary of parameter keywords:
867 Coefficient vector (`list` or `np.ndarray`).
869 Logger to handle messages (`logging.Logger`).
873 output : `tuple` [`bool`, `int`]
874 If true, a correction was applied successfully. The
875 integer indicates the number of pixels that were
876 uncorrectable by being out of range.
879 sqCoeff = kwargs[
'coeffs'][0]
881 ampArr = image.getArray()
882 ampArr *= (1 + sqCoeff*ampArr)
889 """Correct non-linearity with a spline model.
891 corrImage = uncorrImage - Spline(coeffs, uncorrImage)
896 The spline fit calculates a correction as a function of the
897 expected linear flux term. Because of this, the correction needs
898 to be subtracted from the observed flux.
901 LinearityType =
"Spline"
904 """Correct for non-linearity.
908 image : `lsst.afw.image.Image`
909 Image to be corrected
911 Dictionary of parameter keywords:
914 Coefficient vector (`list` or `np.ndarray`).
916 Logger to handle messages (`logging.Logger`).
922 output : `tuple` [`bool`, `int`]
923 If true, a correction was applied successfully. The
924 integer indicates the number of pixels that were
925 uncorrectable by being out of range.
927 splineCoeff = kwargs[
'coeffs']
928 gain = kwargs.get(
'gain', 1.0)
929 centers, values = np.split(splineCoeff, 2)
932 centers = gain * centers
933 values = gain * values
940 if centers[0] != 0.0:
941 centers = np.concatenate(([0.0], centers))
942 values = np.concatenate(([0.0], values))
946 if np.any(~np.isfinite(centers))
or np.any(~np.isfinite(values)):
952 interp = Akima1DInterpolator(centers, values, method=
"akima")
954 ampArr -= interp(np.clip(ampArr, centers[0], centers[-1] - 0.1))
960 """Correct non-linearity with a spline model.
962 corrImage1 = uncorrImage - Spline1(coeffs, uncorrImage)
963 corrImage = corrImage1 - Spline2(coeffs, corrImage1)
968 The spline fit calculates a correction as a function of the
969 expected linear flux term. Because of this, the correction needs
970 to be subtracted from the observed flux.
973 LinearityType =
"DoubleSpline"
976 """Correct for non-linearity.
980 image : `lsst.afw.image.Image`
981 Image to be corrected
983 Dictionary of parameter keywords:
986 Coefficient vector (`list` or `np.ndarray`).
988 Logger to handle messages (`logging.Logger`).
994 output : `tuple` [`bool`, `int`]
995 If true, a correction was applied successfully. The
996 integer indicates the number of pixels that were
997 uncorrectable by being out of range.
999 coeffs = kwargs[
'coeffs']
1000 gain = kwargs.get(
'gain', 1.0)
1004 nNodes1 = int(coeffs[0])
1005 nNodes2 = int(coeffs[1])
1007 ampArr = image.array
1010 splineCoeff1 = coeffs[2: 2 + 2*nNodes1]
1011 centers1, values1 = np.split(splineCoeff1, 2)
1013 if np.any(~np.isfinite(centers1))
or np.any(~np.isfinite(values1)):
1019 centers1 = centers1 * gain
1020 values1 = values1 * gain
1022 splineCoeff2 = coeffs[2 + 2*nNodes1: 2 + 2*nNodes1 + 2*nNodes2]
1023 centers2, values2 = np.split(splineCoeff2, 2)
1025 if np.any(~np.isfinite(centers2))
or np.any(~np.isfinite(values2)):
1031 centers2 = centers2 * gain
1032 values2 = values2 * gain
1037 interp1 = Akima1DInterpolator(centers1, values1, method=
"akima")
1039 ampArr -= interp1(np.clip(ampArr, centers1[0], centers1[-1] - 0.01))
1041 interp2 = Akima1DInterpolator(centers2, values2, method=
"akima")
1043 ampArr -= interp2(np.clip(ampArr, centers2[0], centers2[-1] - 0.01))
1049 """Do not correct non-linearity.
1051 LinearityType =
"Proportional"
1054 """Do not correct for non-linearity.
1058 image : `lsst.afw.image.Image`
1059 Image to be corrected
1061 Dictionary of parameter keywords:
1064 Coefficient vector (`list` or `np.ndarray`).
1066 Logger to handle messages (`logging.Logger`).
1070 output : `tuple` [`bool`, `int`]
1071 If true, a correction was applied successfully. The
1072 integer indicates the number of pixels that were
1073 uncorrectable by being out of range.
1079 """Do not correct non-linearity.
1081 LinearityType =
"None"
1084 """Do not correct for non-linearity.
1088 image : `lsst.afw.image.Image`
1089 Image to be corrected
1091 Dictionary of parameter keywords:
1094 Coefficient vector (`list` or `np.ndarray`).
1096 Logger to handle messages (`logging.Logger`).
1100 output : `tuple` [`bool`, `int`]
1101 If true, a correction was applied successfully. The
1102 integer indicates the number of pixels that were
1103 uncorrectable by being out of range.
validate(self, other=None)
fromDetector(self, detector)
updateMetadata(self, camera=None, detector=None, filterName=None, setCalibId=False, setCalibInfo=False, setDate=False, **kwargs)
__call__(self, image, **kwargs)
__call__(self, image, **kwargs)
__call__(self, image, **kwargs)
__call__(self, image, **kwargs)
__call__(self, image, **kwargs)
__call__(self, image, **kwargs)
__call__(self, image, **kwargs)
__call__(self, image, **kwargs)
str absoluteReferenceAmplifier
getLinearityTypeByName(self, linearityTypeName)
fromDetector(self, detector)
fromTable(cls, tableList)
__init__(self, table=None, **kwargs)
updateMetadata(self, setDate=False, **kwargs)
fromDict(cls, dictionary)
applyLinearity(self, image, detector=None, log=None, gains=None)
validate(self, detector=None, amplifier=None)
isTrimmedImage(image, detector)