27 from .applyLookupTable
import applyLookupTable
29 __all__ = [
"LinearizeBase",
"LinearizeLookupTable",
"LinearizeSquared"]
33 """Determine the linearity class to use if the type is known. 37 linearityTypeName : str 38 String name of the linearity type that is needed. 42 linearityType : `~lsst.ip.isr.linearize.LinearizeSquared` 43 The appropriate linearity class to use. If no matching class 44 is found, `None` is returned. 46 for t
in [LinearizeLookupTable, LinearizeSquared]:
47 if t.LinearityType == linearityTypeName:
53 """Abstract base class functor for correcting non-linearity 55 Subclasses must define __call__ and set class variable LinearityType to a string 56 that will be used for linearity type in AmpInfoCatalog 62 """Correct non-linearity 64 @param[in] image image to be corrected (an lsst.afw.image.Image) 65 @param[in] detector detector information (an instance of lsst::afw::cameraGeom::Detector) 66 @param[in] log logger (an lsst.log.Log), or None to disable logging; 67 a warning is logged if amplifiers are skipped or other worrisome events occur 69 @return an lsst.pipe.base.Struct containing at least the following fields: 70 - numAmps number of amplifiers found 71 - numLinearized number of amplifiers linearized 73 @throw RuntimeError if the linearity type is wrong 74 @throw a subclass of Exception if linearization fails for any other reason 79 """Verify that the linearity type is correct for this detector 81 @warning only checks the first record of the amp info catalog 83 @param[in] detector detector information (an instance of lsst::afw::cameraGeom::Detector) 85 @throw RuntimeError if anything doesn't match 87 ampInfoType = detector.getAmpInfoCatalog()[0].getLinearityType()
88 if self.LinearityType != ampInfoType:
89 raise RuntimeError(
"Linearity types don't match: %s != %s" % (self.LinearityType, ampInfoType))
93 """Correct non-linearity with a persisted lookup table 95 for each i,j of image: 97 colInd = int(c1 + uncorrImage[i,j]) 98 corrImage[i,j] = uncorrImage[i,j] + table[rowInd, colInd] 100 where c0, c1 are collimation coefficients from the AmpInfoTable of the detector: 101 - c0: row index; used to identify which row of the table to use (typically one per amplifier, 102 though one can have multiple amplifiers use the same table) 103 - c1: column index offset; added to the uncorrected image value before truncation; 104 this supports tables that can handle negative image values; also, if the c1 ends with .5 105 then the nearest index is used instead of truncating to the next smaller index 107 In order to keep related data together, the coefficients are persisted along with the table. 109 LinearityType =
"LookupTable" 112 """Construct a LinearizeLookupTable 114 @param[in] table lookup table; a 2-dimensional array of floats: 115 - one row for each row index (value of coef[0] in the amp info catalog) 116 - one column for each image value 117 To avoid copying the table the last index should vary fastest (numpy default "C" order) 118 @param[in] detector detector information (an instance of lsst::afw::cameraGeom::Detector); 119 the name, serial, and amplifier linearization type and coefficients are saved 121 @throw RuntimeError if table is not 2-dimensional, 122 table has fewer columns than rows (indicating that the indices are swapped), 123 or if any row index (linearity coefficient 0) is out of range 125 LinearizeBase.__init__(self)
127 self.
_table = np.array(table, order=
"C")
128 if len(table.shape) != 2:
129 raise RuntimeError(
"table shape = %s; must have two dimensions" % (table.shape,))
130 if table.shape[1] < table.shape[0]:
131 raise RuntimeError(
"table shape = %s; indices are switched" % (table.shape,))
136 ampInfoCat = detector.getAmpInfoCatalog()
138 colIndOffsetList = []
139 numTableRows = table.shape[0]
140 for ampInfo
in ampInfoCat:
141 rowInd, colIndOffset = ampInfo.getLinearityCoeffs()[0:2]
143 if rowInd < 0
or rowInd >= numTableRows:
144 raise RuntimeError(
"Amplifier %s has rowInd=%s not in range[0, %s)" %
145 (ampInfo.getName(), rowInd, numTableRows))
146 rowIndList.append(
int(rowInd))
147 colIndOffsetList.append(colIndOffset)
148 self.
_rowIndArr = np.array(rowIndList, dtype=int)
152 """Correct for non-linearity 154 @param[in] image image to be corrected (an lsst.afw.image.Image) 155 @param[in] detector detector info about image (an lsst.afw.cameraGeom.Detector); 156 the name, serial and number of amplifiers must match persisted data; 157 the bbox from each amplifier is read; 158 the linearization coefficients are ignored in favor of the persisted values 159 @param[in] log logger (an lsst.log.Log), or None to disable logging; 160 a warning is logged if any pixels are out of range of their lookup table 162 @return an lsst.pipe.base.Struct containing: 163 - numAmps number of amplifiers found 164 - numLinearized number of amplifiers linearized (always equal to numAmps for this linearizer) 165 - numOutOfRange number of pixels out of range of their lookup table (summed across all amps) 167 @throw RuntimeError if the linearity type is wrong or if the detector name, serial 168 or number of amplifiers does not match the saved data 171 ampInfoCat = detector.getAmpInfoCatalog()
174 bbox = ampInfo.getBBox()
175 ampView = image.Factory(image, bbox)
176 tableRow = self.
_table[rowInd, :]
179 if numOutOfRange > 0
and log
is not None:
180 log.warn(
"%s pixels of detector \"%s\" were out of range of the linearization table",
181 numOutOfRange, detector.getName())
182 numAmps = len(ampInfoCat)
185 numLinearized=numAmps,
186 numOutOfRange=numOutOfRange,
190 """Check detector name and serial number, ampInfo table length and linearity type 192 @param[in] detector detector info about image (an lsst.afw.cameraGeom.Detector); 194 @throw RuntimeError if anything doesn't match 197 raise RuntimeError(
"Detector names don't match: %s != %s" %
200 raise RuntimeError(
"Detector serial numbers don't match: %s != %s" %
203 numAmps = len(detector.getAmpInfoCatalog())
205 raise RuntimeError(
"Detector number of amps = %s does not match saved value %s" %
211 """Correct non-linearity with a squared model 213 corrImage = uncorrImage + c0*uncorrImage^2 215 where c0 is linearity coefficient 0 in the AmpInfoCatalog of the detector 217 LinearityType =
"Squared" 220 """Correct for non-linearity 222 @param[in] image image to be corrected (an lsst.afw.image.Image) 223 @param[in] detector detector info about image (an lsst.afw.cameraGeom.Detector) 224 @param[in] log logger (an lsst.log.Log), or None to disable logging; 225 a warning is logged if any amplifiers are skipped because the square coefficient is 0 227 @return an lsst.pipe.base.Struct containing at least the following fields: 228 - nAmps number of amplifiers found 229 - nLinearized number of amplifiers linearized 231 @throw RuntimeError if the linearity type is wrong 234 ampInfoCat = detector.getAmpInfoCatalog()
236 for ampInfo
in ampInfoCat:
237 sqCoeff = ampInfo.getLinearityCoeffs()[0]
239 bbox = ampInfo.getBBox()
240 ampArr = image.Factory(image, bbox).getArray()
241 ampArr *= (1 + sqCoeff*ampArr)
244 numAmps = len(ampInfoCat)
245 if numAmps > numLinearized
and log
is not None:
246 log.warn(
"%s of %s amps in detector \"%s\" were not linearized (coefficient = 0)",
247 numAmps - numLinearized, numAmps, detector.getName())
250 numLinearized=numLinearized,
def __call__(self, image, detector, log=None)
def getLinearityTypeByName(linearityTypeName)
def __call__(self, image, detector, log=None)
def checkLinearityType(self, detector)
def checkDetector(self, detector)
def __call__(self, image, detector, log=None)
def __init__(self, table, detector)