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 inputGain : `dict` [`str`, `float`]
88 Dictionary keyed by amplifirer name containing the input
89 gain from the PTC used to construct this linearizer.
90 linearityCoeffs : `dict` [`str`, `np.ndarray`]
91 Coefficients to use in correction. Indexed by amplifier
92 names. The format of the array depends on the type of
94 linearityType : `dict` [`str`, `str`]
95 Type of correction to use, indexed by amplifier names.
96 linearityBBox : `dict` [`str`, `lsst.geom.Box2I`]
97 Bounding box the correction is valid over, indexed by
99 fitParams : `dict` [`str`, `np.ndarray`], optional
100 Linearity fit parameters used to construct the correction
101 coefficients, indexed as above.
102 fitParamsErr : `dict` [`str`, `np.ndarray`], optional
103 Uncertainty values of the linearity fit parameters used to
104 construct the correction coefficients, indexed as above.
105 fitChiSq : `dict` [`str`, `float`], optional
106 Chi-squared value of the linearity fit, indexed as above.
107 fitResiduals : `dict` [`str`, `np.ndarray`], optional
108 Residuals of the fit, indexed as above. Used for
109 calculating photodiode corrections
110 fitResidualsSigmaMad : `dict` [`str`, `float`], optional
111 Robust median-absolute-deviation of fit residuals, scaled
113 fitResidualsUnmasked : `dict` [`str`, `np.ndarray`], optional
114 Same as fitResiduals, but all outliers are included and
116 fitResidualsModel : `dict` [`str`, `np.ndarray`], optional
117 The model count level for each of the fitResiduals.
118 linearFit : The linear fit to the low flux region of the curve.
120 tableData : `np.ndarray`, optional
121 Lookup table data for the linearity correction.
122 inputAbscissa : `dict` [`str`, `np.ndarray`], optional
123 Input abscissa used to construct linearizer (usually photodiode
125 inputOrdinate : `dict` [`str`, `np.ndarray`], optional
126 Input ordinate used to construct linearizer (raw mean counts).
127 inputMask : `dict` [`str`, `np.ndarray`], optional
128 Input mask used for the fitting.
129 inputGroupingIndex : `dict` [`str`, `np.ndarray`], optional
130 Input grouping index used for fitting.
131 inputNormalization : `dict` [`str`, `np.ndarray`], optional
132 Input normalization that was applied to the abscissa for
133 each pair from the PTC used for the linearization fit.
134 absoluteReferenceAmplifier : `str`, optional
135 Amplifier used for the reference for absolute linearization
138 Version 1.4 adds ``linearityTurnoff`` and ``linearityMaxSignal``.
139 Version 1.5 adds ``fitResidualsUnmasked``, ``inputAbscissa``,
140 ``inputOrdinate``, ``inputMask``, ``inputGroupingIndex``,
141 ``fitResidualsModel``, and ``inputNormalization``.
142 Version 1.6 adds ``absoluteReferenceAmplifier``.
143 Version 1.7 adds ``inputGain``.
145 _OBSTYPE =
"LINEARIZER"
146 _SCHEMA =
'Gen3 Linearizer'
175 if table
is not None:
176 if len(table.shape) != 2:
177 raise RuntimeError(
"table shape = %s; must have two dimensions" % (table.shape,))
178 if table.shape[1] < table.shape[0]:
179 raise RuntimeError(
"table shape = %s; indices are switched" % (table.shape,))
180 self.
tableData = np.array(table, order=
"C")
188 'ampNames',
'inputGain',
189 'linearityCoeffs',
'linearityType',
'linearityBBox',
190 'fitParams',
'fitParamsErr',
'fitChiSq',
191 'fitResiduals',
'fitResidualsSigmaMad',
'linearFit',
'tableData',
192 'units',
'linearityTurnoff',
'linearityMaxSignal',
193 'fitResidualsUnmasked',
'inputAbscissa',
'inputOrdinate',
194 'inputMask',
'inputGroupingIndex',
'fitResidualsModel',
195 'inputNormalization',
'absoluteReferenceAmplifier'])
198 """Update metadata keywords with new values.
200 This calls the base class's method after ensuring the required
201 calibration keywords will be saved.
205 setDate : `bool`, optional
206 Update the CALIBDATE fields in the metadata to the current
207 time. Defaults to False.
209 Other keyword parameters to set in the metadata.
213 kwargs[
'HAS_TABLE'] = self.
tableData is not None
219 """Read linearity parameters from a detector.
223 detector : `lsst.afw.cameraGeom.detector`
224 Input detector with parameters to use.
228 calib : `lsst.ip.isr.Linearizer`
229 The calibration constructed from the detector.
237 for amp
in detector.getAmplifiers():
238 ampName = amp.getName()
251 """Construct a calibration from a dictionary of properties
256 Dictionary of properties
260 calib : `lsst.ip.isr.Linearity`
261 Constructed calibration.
266 Raised if the supplied dictionary is for a different
272 if calib._OBSTYPE != dictionary[
'metadata'][
'OBSTYPE']:
273 raise RuntimeError(f
"Incorrect linearity supplied. Expected {calib._OBSTYPE}, "
274 f
"found {dictionary['metadata']['OBSTYPE']}")
276 calib.setMetadata(dictionary[
'metadata'])
278 calib.hasLinearity = dictionary.get(
'hasLinearity',
279 dictionary[
'metadata'].get(
'HAS_LINEARITY',
False))
280 calib.override = dictionary.get(
'override',
True)
285 calib.linearityUnits = dictionary.get(
'linearityUnits',
'adu')
287 if calib.hasLinearity:
288 for ampName
in dictionary[
'amplifiers']:
289 amp = dictionary[
'amplifiers'][ampName]
290 calib.ampNames.append(ampName)
294 calib.inputGain[ampName] = amp.get(
'inputGain', np.nan)
295 calib.linearityCoeffs[ampName] = np.array(amp.get(
'linearityCoeffs', [0.0]))
296 calib.linearityType[ampName] = amp.get(
'linearityType',
'None')
297 calib.linearityBBox[ampName] = amp.get(
'linearityBBox',
None)
299 calib.inputAbscissa[ampName] = np.array(amp.get(
'inputAbscissa', [0.0]))
300 calib.inputOrdinate[ampName] = np.array(amp.get(
'inputOrdinate', [0.0]))
301 calib.inputMask[ampName] = np.array(amp.get(
'inputMask', [
False]))
302 calib.inputGroupingIndex[ampName] = np.array(amp.get(
'inputGroupingIndex', [0.0]))
303 calib.inputNormalization[ampName] = np.array(amp.get(
'inputNormalization', [1.0]))
305 calib.fitParams[ampName] = np.array(amp.get(
'fitParams', [0.0]))
306 calib.fitParamsErr[ampName] = np.array(amp.get(
'fitParamsErr', [0.0]))
307 calib.fitChiSq[ampName] = amp.get(
'fitChiSq', np.nan)
308 calib.fitResiduals[ampName] = np.array(amp.get(
'fitResiduals', [0.0]))
309 calib.fitResidualsSigmaMad[ampName] = np.array(amp.get(
'fitResidualsSigmaMad', np.nan))
310 calib.fitResidualsUnmasked[ampName] = np.array(amp.get(
'fitResidualsUnmasked', [0.0]))
311 calib.fitResidualsModel[ampName] = np.array(amp.get(
'fitResidualsModel', [0.0]))
312 calib.linearFit[ampName] = np.array(amp.get(
'linearFit', [0.0]))
314 calib.linearityTurnoff[ampName] = np.array(amp.get(
'linearityTurnoff', np.nan))
315 calib.linearityMaxSignal[ampName] = np.array(amp.get(
'linearityMaxSignal', np.nan))
317 calib.tableData = dictionary.get(
'tableData',
None)
319 calib.tableData = np.array(calib.tableData)
321 calib.absoluteReferenceAmplifier = dictionary.get(
322 'absoluteReferenceAmplifier', calib.absoluteReferenceAmplifier,
328 """Return linearity parameters as a dict.
341 'amplifiers': dict(),
345 outDict[
'amplifiers'][ampName] = {
352 'inputMask': self.
inputMask[ampName].tolist(),
355 'fitParams': self.
fitParams[ampName].tolist(),
362 'linearFit': self.
linearFit[ampName].tolist(),
367 outDict[
'tableData'] = self.
tableData.tolist()
375 """Read linearity from a FITS file.
377 This method uses the `fromDict` method to create the
378 calibration, after constructing an appropriate dictionary from
383 tableList : `list` [`astropy.table.Table`]
384 afwTable read from input file name.
388 linearity : `~lsst.ip.isr.linearize.Linearizer``
389 Linearity parameters.
393 The method reads a FITS file with 1 or 2 extensions. The metadata is
394 read from the header of extension 1, which must exist. Then the table
395 is loaded, and the ['AMPLIFIER_NAME', 'TYPE', 'COEFFS', 'BBOX_X0',
396 'BBOX_Y0', 'BBOX_DX', 'BBOX_DY'] columns are read and used to set each
397 dictionary by looping over rows.
398 Extension 2 is then attempted to read in the try block (which only
399 exists for lookup tables). It has a column named 'LOOKUP_VALUES' that
400 contains a vector of the lookup entries in each row.
402 coeffTable = tableList[0]
404 metadata = coeffTable.meta
406 inDict[
'metadata'] = metadata
407 inDict[
'hasLinearity'] = metadata.get(
'HAS_LINEARITY',
False)
408 inDict[
'amplifiers'] = dict()
409 inDict[
'linearityUnits'] = metadata.get(
'LINEARITY_UNITS',
'adu')
411 for record
in coeffTable:
412 ampName = record[
'AMPLIFIER_NAME']
413 inputGain = record[
'INPUT_GAIN']
if 'INPUT_GAIN' in record.columns
else np.nan
414 inputAbscissa = record[
'INP_ABSCISSA']
if 'INP_ABSCISSA' in record.columns
else np.array([0.0])
415 inputOrdinate = record[
'INP_ORDINATE']
if 'INP_ORDINATE' in record.columns
else np.array([0.0])
416 inputMask = record[
'INP_MASK']
if 'INP_MASK' in record.columns
else np.array([
False])
417 inputGroupingIndex = record[
'INP_GROUPING_INDEX']
if 'INP_GROUPING_INDEX' in record.columns \
419 inputNormalization = record[
'INP_NORMALIZATION']
if 'INP_NORMALIZATION' in record.columns \
421 fitParams = record[
'FIT_PARAMS']
if 'FIT_PARAMS' in record.columns
else np.array([0.0])
422 fitParamsErr = record[
'FIT_PARAMS_ERR']
if 'FIT_PARAMS_ERR' in record.columns
else np.array([0.0])
423 fitChiSq = record[
'RED_CHI_SQ']
if 'RED_CHI_SQ' in record.columns
else np.nan
424 fitResiduals = record[
'FIT_RES']
if 'FIT_RES' in record.columns
else np.array([0.0])
425 fitResidualsSigmaMad = record[
'FIT_RES_SIGMAD']
if 'FIT_RES_SIGMAD' in record.columns
else np.nan
426 fitResidualsUnmasked = record[
'FIT_RES_UNMASKED'] \
427 if 'FIT_RES_UNMASKED' in record.columns
else np.array([0.0])
428 fitResidualsModel = record[
'FIT_RES_MODEL'] \
429 if 'FIT_RES_MODEL' in record.columns
else np.array([0.0])
430 linearFit = record[
'LIN_FIT']
if 'LIN_FIT' in record.columns
else np.array([0.0])
432 linearityTurnoff = record[
'LINEARITY_TURNOFF']
if 'LINEARITY_TURNOFF' in record.columns \
434 linearityMaxSignal = record[
'LINEARITY_MAX_SIGNAL']
if 'LINEARITY_MAX_SIGNAL' in record.columns \
437 inDict[
'amplifiers'][ampName] = {
438 'inputGain': inputGain,
439 'linearityType': record[
'TYPE'],
440 'linearityCoeffs': record[
'COEFFS'],
441 'linearityBBox':
Box2I(
Point2I(record[
'BBOX_X0'], record[
'BBOX_Y0']),
442 Extent2I(record[
'BBOX_DX'], record[
'BBOX_DY'])),
443 'inputAbscissa': inputAbscissa,
444 'inputOrdinate': inputOrdinate,
445 'inputMask': inputMask,
446 'inputGroupingIndex': inputGroupingIndex,
447 'inputNormalization': inputNormalization,
448 'fitParams': fitParams,
449 'fitParamsErr': fitParamsErr,
450 'fitChiSq': fitChiSq,
451 'fitResiduals': fitResiduals,
452 'fitResidualsSigmaMad': fitResidualsSigmaMad,
453 'fitResidualsUnmasked': fitResidualsUnmasked,
454 'fitResidualsModel': fitResidualsModel,
455 'linearFit': linearFit,
456 'linearityTurnoff': linearityTurnoff,
457 'linearityMaxSignal': linearityMaxSignal,
460 if len(tableList) > 1:
461 tableData = tableList[1]
462 inDict[
'tableData'] = [record[
'LOOKUP_VALUES']
for record
in tableData]
464 if 'ABS_REF_AMP' in coeffTable.columns:
465 inDict[
'absoluteReferenceAmplifier'] = str(coeffTable[
'ABS_REF_AMP'][0])
467 inDict[
'absoluteReferenceAmplifier'] =
''
472 """Construct a list of tables containing the information in this
475 The list of tables should create an identical calibration
476 after being passed to this class's fromTable method.
480 tableList : `list` [`astropy.table.Table`]
481 List of tables containing the linearity calibration
487 catalog = Table([{
'AMPLIFIER_NAME': ampName,
502 'RED_CHI_SQ': self.
fitChiSq[ampName],
513 tableList.append(catalog)
516 catalog = Table([{
'LOOKUP_VALUES': value}
for value
in self.
tableData])
517 tableList.append(catalog)
521 """Determine the linearity class to use from the type name.
525 linearityTypeName : str
526 String name of the linearity type that is needed.
530 linearityType : `~lsst.ip.isr.linearize.LinearizeBase`
531 The appropriate linearity class to use. If no matching class
532 is found, `None` is returned.
534 for t
in [LinearizeLookupTable,
537 LinearizeProportional,
539 LinearizeDoubleSpline,
541 if t.LinearityType == linearityTypeName:
546 """Validate linearity for a detector/amplifier.
550 detector : `lsst.afw.cameraGeom.Detector`, optional
551 Detector to validate, along with its amplifiers.
552 amplifier : `lsst.afw.cameraGeom.Amplifier`, optional
553 Single amplifier to validate.
558 Raised if there is a mismatch in linearity parameters, and
559 the cameraGeom parameters are not being overridden.
561 amplifiersToCheck = []
564 raise RuntimeError(
"Detector names don't match: %s != %s" %
567 raise RuntimeError(
"Detector IDs don't match: %s != %s" %
576 raise RuntimeError(
"Detector number of amps = %s does not match saved value %s" %
577 (len(detector.getAmplifiers()),
579 amplifiersToCheck.extend(detector.getAmplifiers())
582 amplifiersToCheck.extend(amplifier)
584 for amp
in amplifiersToCheck:
585 ampName = amp.getName()
587 raise RuntimeError(
"Amplifier %s is not in linearity data" %
591 self.
log.debug(
"Overriding amplifier defined linearityType (%s) for %s",
594 raise RuntimeError(
"Amplifier %s type %s does not match saved value %s" %
595 (ampName, amp.getLinearityType(), self.
linearityType[ampName]))
596 if (amp.getLinearityCoeffs().shape != self.
linearityCoeffs[ampName].shape
or not
597 np.allclose(amp.getLinearityCoeffs(), self.
linearityCoeffs[ampName], equal_nan=
True)):
599 self.
log.debug(
"Overriding amplifier defined linearityCoeffs (%s) for %s",
602 raise RuntimeError(
"Amplifier %s coeffs %s does not match saved value %s" %
606 """Apply the linearity to an image.
608 If the linearity parameters are populated, use those,
609 otherwise use the values from the detector.
613 image : `~lsst.afw.image.image`
615 detector : `~lsst.afw.cameraGeom.detector`, optional
616 Detector to use to determine exposure trimmed state. If
617 supplied, but no other linearity information is provided
618 by the calibration, then the static solution stored in the
619 detector will be used.
620 log : `~logging.Logger`, optional
621 Log object to use for logging.
622 gains : `dict` [`str`, `float`], optional
623 Dictionary of amp name to gain. If this is provided then
624 linearity terms will be converted from adu to electrons.
625 Only used for Spline linearity corrections.
647 gainValue = gains[ampName]
652 if np.isnan(gainValue):
653 log.warning(
"Gain value for amp %s is NaN, skipping "
654 "linearization.", ampName)
657 if linearizer
is not None:
660 bbox = detector[ampName].getBBox()
662 bbox = detector[ampName].getRawBBox()
666 ampView = image.Factory(image, bbox)
667 success, outOfRange = linearizer()(ampView, **{
'coeffs': self.
linearityCoeffs[ampName],
671 numOutOfRange += outOfRange
674 elif log
is not None:
675 log.warning(
"Amplifier %s did not linearize.",
679 numLinearized=numLinearized,
680 numOutOfRange=numOutOfRange
685 """Abstract base class functor for correcting non-linearity.
687 Subclasses must define ``__call__`` and set class variable
688 LinearityType to a string that will be used for linearity type in
689 the cameraGeom.Amplifier.linearityType field.
691 All linearity corrections should be defined in terms of an
692 additive correction, such that:
694 corrected_value = uncorrected_value + f(uncorrected_value)
700 """Correct non-linearity.
704 image : `lsst.afw.image.Image`
705 Image to be corrected
707 Dictionary of parameter keywords:
710 Coefficient vector (`list` or `np.ndarray`).
712 Lookup table data (`np.ndarray`).
714 Logger to handle messages (`logging.Logger`).
719 If `True`, a correction was applied successfully.
724 Raised if the linearity type listed in the
725 detector does not match the class type.
730class LinearizeLookupTable(LinearizeBase):
731 """Correct non-linearity with a persisted lookup table.
733 The lookup table consists of entries such that given
734 "coefficients" c0, c1:
736 for each i,j of image:
738 colInd = int(c1 + uncorrImage[i,j])
739 corrImage[i,j] = uncorrImage[i,j] + table[rowInd, colInd]
741 - c0: row index; used to identify which row of the table to use
742 (typically one per amplifier, though one can have multiple
743 amplifiers use the same table)
744 - c1: column index offset; added to the uncorrected image value
745 before truncation; this supports tables that can handle
746 negative image values; also, if the c1 ends with .5 then
747 the nearest index is used instead of truncating to the
750 LinearityType =
"LookupTable"
753 """Correct for non-linearity.
757 image : `lsst.afw.image.Image`
758 Image to be corrected
760 Dictionary of parameter keywords:
763 Columnation vector (`list` or `np.ndarray`).
765 Lookup table data (`np.ndarray`).
767 Logger to handle messages (`logging.Logger`).
771 output : `tuple` [`bool`, `int`]
772 If true, a correction was applied successfully. The
773 integer indicates the number of pixels that were
774 uncorrectable by being out of range.
779 Raised if the requested row index is out of the table
784 rowInd, colIndOffset = kwargs[
'coeffs'][0:2]
785 table = kwargs[
'table']
788 numTableRows = table.shape[0]
790 if rowInd < 0
or rowInd > numTableRows:
791 raise RuntimeError(
"LinearizeLookupTable rowInd=%s not in range[0, %s)" %
792 (rowInd, numTableRows))
793 tableRow = np.array(table[rowInd, :], dtype=image.getArray().dtype)
797 if numOutOfRange > 0
and log
is not None:
798 log.warning(
"%s pixels were out of range of the linearization table",
800 if numOutOfRange < image.getArray().size:
801 return True, numOutOfRange
803 return False, numOutOfRange
807 """Correct non-linearity with a polynomial mode.
811 corrImage = uncorrImage + sum_i c_i uncorrImage^(2 + i)
813 where ``c_i`` are the linearity coefficients for each amplifier.
814 Lower order coefficients are not included as they duplicate other
815 calibration parameters:
818 A coefficient multiplied by ``uncorrImage**0`` is equivalent to
819 bias level. Irrelevant for correcting non-linearity.
821 A coefficient multiplied by ``uncorrImage**1`` is proportional
822 to the gain. Not necessary for correcting non-linearity.
824 LinearityType =
"Polynomial"
827 """Correct non-linearity.
831 image : `lsst.afw.image.Image`
832 Image to be corrected
834 Dictionary of parameter keywords:
837 Coefficient vector (`list` or `np.ndarray`).
838 If the order of the polynomial is n, this list
839 should have a length of n-1 ("k0" and "k1" are
840 not needed for the correction).
842 Logger to handle messages (`logging.Logger`).
846 output : `tuple` [`bool`, `int`]
847 If true, a correction was applied successfully. The
848 integer indicates the number of pixels that were
849 uncorrectable by being out of range.
851 if not np.any(np.isfinite(kwargs[
'coeffs'])):
853 if not np.any(kwargs[
'coeffs']):
856 ampArray = image.getArray()
857 correction = np.zeros_like(ampArray)
858 for order, coeff
in enumerate(kwargs[
'coeffs'], start=2):
859 correction += coeff * np.power(ampArray, order)
860 ampArray += correction
866 """Correct non-linearity with a squared model.
868 corrImage = uncorrImage + c0*uncorrImage^2
870 where c0 is linearity coefficient 0 for each amplifier.
872 LinearityType =
"Squared"
875 """Correct for non-linearity.
879 image : `lsst.afw.image.Image`
880 Image to be corrected
882 Dictionary of parameter keywords:
885 Coefficient vector (`list` or `np.ndarray`).
887 Logger to handle messages (`logging.Logger`).
891 output : `tuple` [`bool`, `int`]
892 If true, a correction was applied successfully. The
893 integer indicates the number of pixels that were
894 uncorrectable by being out of range.
897 sqCoeff = kwargs[
'coeffs'][0]
899 ampArr = image.getArray()
900 ampArr *= (1 + sqCoeff*ampArr)
907 """Correct non-linearity with a spline model.
909 corrImage = uncorrImage - Spline(coeffs, uncorrImage)
914 The spline fit calculates a correction as a function of the
915 expected linear flux term. Because of this, the correction needs
916 to be subtracted from the observed flux.
919 LinearityType =
"Spline"
922 """Correct for non-linearity.
926 image : `lsst.afw.image.Image`
927 Image to be corrected
929 Dictionary of parameter keywords:
932 Coefficient vector (`list` or `np.ndarray`).
934 Logger to handle messages (`logging.Logger`).
940 output : `tuple` [`bool`, `int`]
941 If true, a correction was applied successfully. The
942 integer indicates the number of pixels that were
943 uncorrectable by being out of range.
945 splineCoeff = kwargs[
'coeffs']
946 gain = kwargs.get(
'gain', 1.0)
947 centers, values = np.split(splineCoeff, 2)
950 centers = gain * centers
951 values = gain * values
958 if centers[0] != 0.0:
959 centers = np.concatenate(([0.0], centers))
960 values = np.concatenate(([0.0], values))
964 if np.any(~np.isfinite(centers))
or np.any(~np.isfinite(values)):
970 interp = Akima1DInterpolator(centers, values, method=
"akima")
972 ampArr -= interp(np.clip(ampArr, centers[0], centers[-1] - 0.1))
978 """Correct non-linearity with a spline model.
980 corrImage1 = uncorrImage - Spline1(coeffs, uncorrImage)
981 corrImage = corrImage1 - Spline2(coeffs, corrImage1)
986 The spline fit calculates a correction as a function of the
987 expected linear flux term. Because of this, the correction needs
988 to be subtracted from the observed flux.
991 LinearityType =
"DoubleSpline"
994 """Correct for non-linearity.
998 image : `lsst.afw.image.Image`
999 Image to be corrected
1001 Dictionary of parameter keywords:
1004 Coefficient vector (`list` or `np.ndarray`).
1006 Logger to handle messages (`logging.Logger`).
1008 Gain value to apply.
1012 output : `tuple` [`bool`, `int`]
1013 If true, a correction was applied successfully. The
1014 integer indicates the number of pixels that were
1015 uncorrectable by being out of range.
1017 coeffs = kwargs[
'coeffs']
1018 gain = kwargs.get(
'gain', 1.0)
1022 nNodes1 = int(coeffs[0])
1023 nNodes2 = int(coeffs[1])
1025 ampArr = image.array
1028 splineCoeff1 = coeffs[2: 2 + 2*nNodes1]
1029 centers1, values1 = np.split(splineCoeff1, 2)
1031 if np.any(~np.isfinite(centers1))
or np.any(~np.isfinite(values1)):
1037 centers1 = centers1 * gain
1038 values1 = values1 * gain
1040 splineCoeff2 = coeffs[2 + 2*nNodes1: 2 + 2*nNodes1 + 2*nNodes2]
1041 centers2, values2 = np.split(splineCoeff2, 2)
1043 if np.any(~np.isfinite(centers2))
or np.any(~np.isfinite(values2)):
1049 centers2 = centers2 * gain
1050 values2 = values2 * gain
1055 interp1 = Akima1DInterpolator(centers1, values1, method=
"akima")
1057 ampArr -= interp1(np.clip(ampArr, centers1[0], centers1[-1] - 0.01))
1059 interp2 = Akima1DInterpolator(centers2, values2, method=
"akima")
1061 ampArr -= interp2(np.clip(ampArr, centers2[0], centers2[-1] - 0.01))
1067 """Do not correct non-linearity.
1069 LinearityType =
"Proportional"
1072 """Do not correct for non-linearity.
1076 image : `lsst.afw.image.Image`
1077 Image to be corrected
1079 Dictionary of parameter keywords:
1082 Coefficient vector (`list` or `np.ndarray`).
1084 Logger to handle messages (`logging.Logger`).
1088 output : `tuple` [`bool`, `int`]
1089 If true, a correction was applied successfully. The
1090 integer indicates the number of pixels that were
1091 uncorrectable by being out of range.
1097 """Do not correct non-linearity.
1099 LinearityType =
"None"
1102 """Do not correct for non-linearity.
1106 image : `lsst.afw.image.Image`
1107 Image to be corrected
1109 Dictionary of parameter keywords:
1112 Coefficient vector (`list` or `np.ndarray`).
1114 Logger to handle messages (`logging.Logger`).
1118 output : `tuple` [`bool`, `int`]
1119 If true, a correction was applied successfully. The
1120 integer indicates the number of pixels that were
1121 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)