Skip to content

Commit

Permalink
Update to support afw.cameraGeom changes.
Browse files Browse the repository at this point in the history
Use Builder methods when changing amplifier/detector/camera
properties.  Use detector.getAmplifiers() method in place of removed
.getAmpInfoCatalog().  Use lsst.afw.cameraGeom.ReadoutCorner enum.
  • Loading branch information
czwa committed Oct 3, 2019
1 parent d04256d commit a600df4
Show file tree
Hide file tree
Showing 12 changed files with 128 additions and 103 deletions.
8 changes: 4 additions & 4 deletions python/lsst/ip/isr/crosstalk.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,10 @@ def run(self, exposure, crosstalkSources=None, isTrimmed=False):

# Flips required to get the corner to the lower-left
# (an arbitrary choice; flips are relative, so the choice of reference here is not important)
X_FLIP = {lsst.afw.table.LL: False, lsst.afw.table.LR: True,
lsst.afw.table.UL: False, lsst.afw.table.UR: True}
Y_FLIP = {lsst.afw.table.LL: False, lsst.afw.table.LR: False,
lsst.afw.table.UL: True, lsst.afw.table.UR: True}
X_FLIP = {lsst.afw.cameraGeom.ReadoutCorner.LL: False, lsst.afw.cameraGeom.ReadoutCorner.LR: True,
lsst.afw.cameraGeom.ReadoutCorner.UL: False, lsst.afw.cameraGeom.ReadoutCorner.UR: True}
Y_FLIP = {lsst.afw.cameraGeom.ReadoutCorner.LL: False, lsst.afw.cameraGeom.ReadoutCorner.LR: False,
lsst.afw.cameraGeom.ReadoutCorner.UL: True, lsst.afw.cameraGeom.ReadoutCorner.UR: True}


class NullCrosstalkTask(CrosstalkTask):
Expand Down
16 changes: 12 additions & 4 deletions python/lsst/ip/isr/isrMock.py
Original file line number Diff line number Diff line change
Expand Up @@ -542,10 +542,18 @@ def getExposure(self):
metadata.add("MONKEYS", 155, "monkeys per tree")
metadata.add("VAMPIRES", 4, "How scary are vampires.")

for amp in exposure.getDetector():
amp.setLinearityCoeffs((0., 1., 0., 0.))
amp.setLinearityType("Polynomial")
amp.setGain(self.config.gain)
ccd = exposure.getDetector()
newCcd = ccd.rebuild()
newCcd.clear()
for amp in ccd:
newAmp = amp.rebuild()
newAmp.setLinearityCoeffs((0., 1., 0., 0.))
newAmp.setLinearityType("Polynomial")
newAmp.setGain(self.config.gain)
newAmp.setSuspectLevel(25000.0)
newAmp.setSaturation(32000.0)
newCcd.append(newAmp)
exposure.setDetector(newCcd.finish())

exposure.image.array[:] = np.zeros(exposure.getImage().getDimensions()).transpose()
exposure.mask.array[:] = np.zeros(exposure.getMask().getDimensions()).transpose()
Expand Down
10 changes: 5 additions & 5 deletions python/lsst/ip/isr/isrTask.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@
import lsst.geom
import lsst.afw.image as afwImage
import lsst.afw.math as afwMath
import lsst.afw.table as afwTable
import lsst.pex.config as pexConfig
import lsst.pipe.base as pipeBase
import lsst.pipe.base.connectionTypes as cT

from contextlib import contextmanager
from lsstDebug import getDebugFrame

from lsst.afw.cameraGeom import PIXELS, FOCAL_PLANE, NullLinearityType
from lsst.afw.cameraGeom import (PIXELS, FOCAL_PLANE, NullLinearityType,
ReadoutCorner)
from lsst.afw.display import getDisplay
from lsst.afw.geom import Polygon
from lsst.daf.persistence import ButlerDataRef
Expand Down Expand Up @@ -803,7 +803,7 @@ def runQuantum(self, butlerQC, inputRefs, outputRefs):
if self.config.doLinearize is True:
if 'linearizer' not in inputs.keys():
detector = inputs['camera'][inputs['detectorNum']]
linearityName = detector.getAmpInfoCatalog()[0].getLinearityType()
linearityName = detector.getAmplifiers()[0].getLinearityType()
inputs['linearizer'] = linearize.getLinearityTypeByName(linearityName)()

if inputs['defects'] is not None:
Expand Down Expand Up @@ -1746,7 +1746,7 @@ def overscanCorrection(self, ccdExposure, amp):
(ccdExposure.getMetadata().exists(self.config.overscanBiasJumpKeyword) and
ccdExposure.getMetadata().getScalar(self.config.overscanBiasJumpKeyword) in
self.config.overscanBiasJumpDevices)):
if amp.getReadoutCorner() in (afwTable.LL, afwTable.LR):
if amp.getReadoutCorner() in (ReadoutCorner.LL, ReadoutCorner.LR):
yLower = self.config.overscanBiasJumpLocation
yUpper = dataBBox.getHeight() - yLower
else:
Expand Down Expand Up @@ -1923,7 +1923,7 @@ def doLinearize(self, detector):
If True, linearization should be performed.
"""
return self.config.doLinearize and \
detector.getAmpInfoCatalog()[0].getLinearityType() != NullLinearityType
detector.getAmplifiers()[0].getLinearityType() != NullLinearityType

def flatCorrection(self, exposure, flatExposure, invert=False):
"""!Apply flat correction in place.
Expand Down
10 changes: 5 additions & 5 deletions python/lsst/ip/isr/linearize.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def checkLinearityType(self, detector):
@throw RuntimeError if anything doesn't match
"""
ampInfoType = detector.getAmpInfoCatalog()[0].getLinearityType()
ampInfoType = detector.getAmplifiers()[0].getLinearityType()
if self.LinearityType != ampInfoType:
raise RuntimeError("Linearity types don't match: %s != %s" % (self.LinearityType, ampInfoType))

Expand Down Expand Up @@ -133,7 +133,7 @@ def __init__(self, table, detector):
self._detectorName = detector.getName()
self._detectorSerial = detector.getSerial()
self.checkLinearityType(detector)
ampInfoCat = detector.getAmpInfoCatalog()
ampInfoCat = detector.getAmplifiers()
rowIndList = []
colIndOffsetList = []
numTableRows = table.shape[0]
Expand Down Expand Up @@ -168,7 +168,7 @@ def __call__(self, image, detector, log=None):
or number of amplifiers does not match the saved data
"""
self.checkDetector(detector)
ampInfoCat = detector.getAmpInfoCatalog()
ampInfoCat = detector.getAmplifiers()
numOutOfRange = 0
for ampInfo, rowInd, colIndOffset in zip(ampInfoCat, self._rowIndArr, self._colIndOffsetArr):
bbox = ampInfo.getBBox()
Expand Down Expand Up @@ -200,7 +200,7 @@ def checkDetector(self, detector):
raise RuntimeError("Detector serial numbers don't match: %s != %s" %
(self._detectorSerial, detector.getSerial()))

numAmps = len(detector.getAmpInfoCatalog())
numAmps = len(detector.getAmplifiers())
if numAmps != len(self._rowIndArr):
raise RuntimeError("Detector number of amps = %s does not match saved value %s" %
(numAmps, len(self._rowIndArr)))
Expand Down Expand Up @@ -231,7 +231,7 @@ def __call__(self, image, detector, log=None):
@throw RuntimeError if the linearity type is wrong
"""
self.checkLinearityType(detector)
ampInfoCat = detector.getAmpInfoCatalog()
ampInfoCat = detector.getAmplifiers()
numLinearized = 0
for ampInfo in ampInfoCat:
sqCoeff = ampInfo.getLinearityCoeffs()[0]
Expand Down
44 changes: 28 additions & 16 deletions tests/test_crosstalk.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@
import lsst.utils.tests
import lsst.afw.image
import lsst.afw.table
import lsst.afw.cameraGeom
import lsst.afw.cameraGeom as cameraGeom

from lsst.afw.table import LL, LR, UL, UR
from lsst.pipe.base import Struct
from lsst.ip.isr import (IsrTask, subtractCrosstalk,
MeasureCrosstalkTask, writeCrosstalkCoeffs, CrosstalkTask, NullCrosstalkTask)
Expand Down Expand Up @@ -91,28 +90,41 @@ def construct(imageList):
image.getArray()[:] += self.noise
return image

# Construct detector
detName = 'detector'
detId = 1
detSerial = 'serial'
orientation = cameraGeom.Orientation()
pixelSize = lsst.geom.Extent2D(1, 1)
bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
lsst.geom.Extent2I(2*width, 2*height))
crosstalk = np.array(self.crosstalk, dtype=np.float32)

camBuilder = cameraGeom.Camera.Builder("fakeCam")
detBuilder = camBuilder.add(detName, detId)
detBuilder.setSerial(detSerial)
detBuilder.setBBox(bbox)
detBuilder.setOrientation(orientation)
detBuilder.setPixelSize(pixelSize)
detBuilder.setCrosstalk(crosstalk)

# Create amp info
schema = lsst.afw.table.AmpInfoTable.makeMinimalSchema()
amplifiers = lsst.afw.table.AmpInfoCatalog(schema)
for ii, (xx, yy, corner) in enumerate([(0, 0, LL),
(width, 0, LR),
(0, height, UL),
(width, height, UR)]):
amp = amplifiers.addNew()
for ii, (xx, yy, corner) in enumerate([(0, 0, lsst.afw.cameraGeom.ReadoutCorner.LL),
(width, 0, lsst.afw.cameraGeom.ReadoutCorner.LR),
(0, height, lsst.afw.cameraGeom.ReadoutCorner.UL),
(width, height, lsst.afw.cameraGeom.ReadoutCorner.UR)]):

amp = cameraGeom.Amplifier.Builder()
amp.setName("amp %d" % ii)
amp.setBBox(lsst.geom.Box2I(lsst.geom.Point2I(xx, yy),
lsst.geom.Extent2I(width, height)))
amp.setRawDataBBox(lsst.geom.Box2I(lsst.geom.Point2I(xx, yy),
lsst.geom.Extent2I(width, height)))
amp.setReadoutCorner(corner)
detBuilder.append(amp)

# Put everything together
ccd = lsst.afw.cameraGeom.Detector("detector", 123, lsst.afw.cameraGeom.SCIENCE, "serial",
lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
lsst.geom.Extent2I(2*width, 2*height)),
amplifiers, lsst.afw.cameraGeom.Orientation(),
lsst.geom.Extent2D(1, 1), {},
np.array(self.crosstalk, dtype=np.float32))
cam = camBuilder.finish()
ccd = cam.get('detector')

self.exposure = lsst.afw.image.makeExposure(lsst.afw.image.makeMaskedImage(construct(withCrosstalk)))
self.exposure.setDetector(ccd)
Expand Down
34 changes: 22 additions & 12 deletions tests/test_empiricalVariance.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,19 @@
import lsst.utils.tests

from lsst.daf.base import PropertyList
from lsst.afw.cameraGeom import Detector, SCIENCE, Orientation
from lsst.afw.table import AmpInfoCatalog, AmpInfoTable
import lsst.afw.cameraGeom as cameraGeom
from lsst.geom import Point2I, Extent2I, Box2I, Extent2D
from lsst.afw.image import ExposureF, VisitInfo

from lsst.ip.isr.isrTask import IsrTask


def makeAmplifier(catalog, name, bbox, rawImageBox, overscanBox, gain, readNoise, saturation):
amp = catalog.addNew()
def makeAmplifier(name, bbox, rawImageBox, overscanBox, gain, readNoise, saturation):
amp = cameraGeom.Amplifier.Builder()
amp.setName(name)
amp.setBBox(bbox)
amp.setRawDataBBox(rawImageBox)
amp.setRawHorizontalOverscanBBox(overscanBox)
amp.setHasRawInfo(True)
amp.setGain(gain)
amp.setReadNoise(readNoise)
amp.setSaturation(saturation)
Expand Down Expand Up @@ -88,12 +86,20 @@ def setUp(self):
exposure.variance.array[:] = np.nan

# Construct the detectors
amps = AmpInfoCatalog(AmpInfoTable.makeMinimalSchema())
makeAmplifier(amps, "left", target1, image1, overscan1, gain, readNoise, saturation)
makeAmplifier(amps, "right", target2, image2, overscan2, gain, readNoise, saturation)
amp1 = makeAmplifier("left", target1, image1, overscan1, gain, readNoise, saturation)
amp2 = makeAmplifier("right", target2, image2, overscan2, gain, readNoise, saturation)
ccdBox = Box2I(Point2I(0, 0), Extent2I(image1.getWidth() + image2.getWidth(), height))
ccd = Detector("detector", 1, SCIENCE, "det1", ccdBox, amps, Orientation(), Extent2D(1.0, 1.0), {})
exposure.setDetector(ccd)
camBuilder = cameraGeom.Camera.Builder("fakeCam")
detBuilder = camBuilder.add("detector", 1)
detBuilder.setSerial("det1")
detBuilder.setBBox(ccdBox)
detBuilder.setPixelSize(Extent2D(1.0, 1.0))
detBuilder.setOrientation(cameraGeom.Orientation())
detBuilder.append(amp1)
detBuilder.append(amp2)
cam = camBuilder.finish()
exposure.setDetector(cam.get('detector'))

header = PropertyList()
header.add("EXPTIME", 0.0)
exposure.getInfo().setVisitInfo(VisitInfo(header))
Expand All @@ -114,6 +120,9 @@ def setUp(self):
self.config.doCrosstalk = False
self.config.doBrighterFatter = False
self.config.doAttachTransmissionCurve = False
self.config.doAssembleCcd = False
self.config.doNanMasking = False
self.config.doInterpolate = False

# Set the things that match our test setup
self.config.overscanFitType = "CHEB"
Expand All @@ -128,11 +137,12 @@ def tearDown(self):
def testEmpiricalVariance(self):
results = self.task.run(self.exposure)
postIsr = results.exposure

self.assertFloatsEqual(postIsr.mask.array, 0)
# Image is not exactly zero because the noise in the overscan (required to be able to set
# the empirical variance) leads to a slight misestimate in the polynomial fit.
self.assertFloatsAlmostEqual(postIsr.image.array, 0.0, atol=5.0e-2)
self.assertFloatsAlmostEqual(postIsr.variance.array, self.sigma**2, rtol=5.0e-2)
self.assertFloatsAlmostEqual(np.median(postIsr.image.array), 0.0, atol=5.0e-2)
self.assertFloatsAlmostEqual(np.nanmedian(postIsr.variance.array), self.sigma**2, rtol=5.0e-2)


class MemoryTester(lsst.utils.tests.MemoryTestCase):
Expand Down
4 changes: 2 additions & 2 deletions tests/test_flatAndIlluminationCorrection.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,12 @@ def testGainAndReadnoise(self):
readNoise = 1
raw.image.set(level)

amp = detector[0]
amp = detector[0].rebuild()
amp.setReadNoise(readNoise)

for gain in [-1, 0, 0.1, 1]:
amp.setGain(gain)
isrTask.updateVariance(raw, amp)
isrTask.updateVariance(raw, amp.finish())
if gain <= 0:
gain = 1

Expand Down
4 changes: 3 additions & 1 deletion tests/test_isrFunctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,9 @@ def test_gainContext(self):
mi = self.inputExp.getMaskedImage().clone()
with ipIsr.gainContext(self.inputExp, self.inputExp.getImage(), apply=True):
pass
self.assertMaskedImagesEqual(self.inputExp.getMaskedImage(), mi)

self.assertIsNotNone(mi)
self.assertMaskedImagesAlmostEqual(self.inputExp.getMaskedImage(), mi)

def test_addDistortionModel(self):
"""Expect RuntimeError if no model supplied, or incomplete exposure information.
Expand Down
9 changes: 5 additions & 4 deletions tests/test_isrTask.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,12 +190,13 @@ def test_saturationDetection(self):
"""Expect the saturation level detection/masking to scale with
threshold.
"""
self.amp.setSaturation(9000.0)
self.task.saturationDetection(self.inputExp, self.amp)
ampB = self.amp.rebuild()
ampB.setSaturation(9000.0)
self.task.saturationDetection(self.inputExp, ampB.finish())
countBefore = countMaskedPixels(self.mi, "SAT")

self.amp.setSaturation(8250.0)
self.task.saturationDetection(self.inputExp, self.amp)
ampB.setSaturation(8250.0)
self.task.saturationDetection(self.inputExp, ampB.finish())
countAfter = countMaskedPixels(self.mi, "SAT")

self.assertLessEqual(countBefore, countAfter)
Expand Down
40 changes: 18 additions & 22 deletions tests/test_linearizeLookupTable.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import lsst.utils.tests
import lsst.utils
import lsst.afw.image as afwImage
import lsst.afw.table as afwTable
import lsst.afw.cameraGeom as cameraGeom
from lsst.afw.geom.testUtils import BoxGrid
from lsst.afw.image.testUtils import makeRampImage
Expand All @@ -46,7 +45,7 @@ def refLinearize(image, detector, table):
@return the number of pixels whose values were out of range of the lookup table
"""
ampInfoCat = detector.getAmpInfoCatalog()
ampInfoCat = detector.getAmplifiers()
numOutOfRange = 0
for ampInfo in ampInfoCat:
bbox = ampInfo.getBBox()
Expand Down Expand Up @@ -90,7 +89,7 @@ def testBasics(self):
refImage = inImage.Factory(inImage, True)
refNumOutOfRange = refLinearize(image=refImage, detector=self.detector, table=table)

self.assertEqual(linRes.numAmps, len(self.detector.getAmpInfoCatalog()))
self.assertEqual(linRes.numAmps, len(self.detector.getAmplifiers()))
self.assertEqual(linRes.numAmps, linRes.numLinearized)
self.assertEqual(linRes.numOutOfRange, refNumOutOfRange)
self.assertImagesAlmostEqual(refImage, measImage)
Expand Down Expand Up @@ -158,7 +157,7 @@ def castAndReshape(arr):
rowInds = castAndReshape((3, 2, 1, 0)) # avoid the trivial mapping to exercise more of the code
colIndOffsets = castAndReshape((0, 0, 1, 1))
detector = self.makeDetector(bbox=bbox, numAmps=numAmps, rowInds=rowInds, colIndOffsets=colIndOffsets)
ampInfoCat = detector.getAmpInfoCatalog()
ampInfoCat = detector.getAmplifiers()

# note: table rows are reversed relative to amplifier order because rowInds is a descending ramp
table = np.array(((7, 6, 5, 4), (1, 1, 1, 1), (5, 4, 3, 2), (0, 0, 0, 0)), dtype=imArr.dtype)
Expand Down Expand Up @@ -222,32 +221,29 @@ def makeDetector(self, bbox=None, numAmps=None, rowInds=None, colIndOffsets=None
rowInds = rowInds if rowInds is not None else self.rowInds
colIndOffsets = colIndOffsets if colIndOffsets is not None else self.colIndOffsets

schema = afwTable.AmpInfoTable.makeMinimalSchema()
ampInfoCat = afwTable.AmpInfoCatalog(schema)
detId = 1
orientation = cameraGeom.Orientation()
pixelSize = lsst.geom.Extent2D(1, 1)

camBuilder = cameraGeom.Camera.Builder("fakeCam")
detBuilder = camBuilder.add(detName, detId)
detBuilder.setSerial(detSerial)
detBuilder.setBBox(bbox)
detBuilder.setOrientation(orientation)
detBuilder.setPixelSize(pixelSize)

boxArr = BoxGrid(box=bbox, numColRow=numAmps)
for i in range(numAmps[0]):
for j in range(numAmps[1]):
ampInfo = ampInfoCat.addNew()
ampInfo = cameraGeom.Amplifier.Builder()
ampInfo.setName("amp %d_%d" % (i + 1, j + 1))
ampInfo.setBBox(boxArr[i, j])
ampInfo.setLinearityType(linearityType)
# setLinearityCoeffs is picky about getting a mixed int/float list.
ampInfo.setLinearityCoeffs(np.array([rowInds[i, j], colIndOffsets[i, j], 0, 0], dtype=float))
detId = 1
orientation = cameraGeom.Orientation()
pixelSize = lsst.geom.Extent2D(1, 1)
transMap = {}
return cameraGeom.Detector(
detName,
detId,
cameraGeom.SCIENCE,
detSerial,
bbox,
ampInfoCat,
orientation,
pixelSize,
transMap,
)
detBuilder.append(ampInfo)

return detBuilder

def makeTable(self, image, numCols=None, numRows=2500, sigma=55):
"""!Make a 2D lookup table
Expand Down

0 comments on commit a600df4

Please sign in to comment.