Coverage for tests/test_fakeProcessing.py: 28%
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#
2# LSST Data Management System
3# Copyright 2008-2017 AURA/LSST.
4#
5# This product includes software developed by the
6# LSST Project (http://www.lsst.org/).
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the LSST License Statement and
19# the GNU General Public License along with this program. If not,
20# see <https://www.lsstcorp.org/LegalNotices/>.
22import numpy as np
23import os
24import shutil
25import tempfile
26import unittest
28from collections import namedtuple
30import lsst.utils.tests
31import lsst.geom as geom
33from lsst.pipe.tasks.processCcd import ProcessCcdTask
34from lsst.pipe.tasks.fakes import BaseFakeSourcesConfig, BaseFakeSourcesTask
36obsTestDir = lsst.utils.getPackageDir('obs_test')
37InputDir = os.path.join(obsTestDir, "data", "input")
39OutputName = None # specify a name (as a string) to save the output repository
41positionTuple = namedtuple("positionTuple", "y x")
44class FakeSourcesTestConfig(BaseFakeSourcesConfig):
45 pass
48class FakeSourcesTestTask(BaseFakeSourcesTask):
49 '''
50 A task to insert fake objects into test data to verify the hooks for the
51 fake object pipeline work.
52 '''
54 ConfigClass = FakeSourcesTestConfig
55 _DefaultName = "fakeSourcesTest"
57 # Ground truth position and intensities for the fake sources
58 fakeSources = [(positionTuple(800, 435), 11342),
59 (positionTuple(400, 350), 18235),
60 (positionTuple(1834, 379), 13574),
61 (positionTuple(1234, 642), 12456)]
63 def __init__(self, **kwargs):
64 BaseFakeSourcesTask.__init__(self, **kwargs)
66 def run(self, exposure, background):
67 if not exposure.hasPsf():
68 raise RuntimeError("Exposure object must have a PSF")
69 # Fetch objects from the exposure
70 psf = exposure.getPsf()
71 image = exposure.getMaskedImage().getImage()
72 mask = exposure.getMaskedImage().getMask()
73 variance = exposure.getMaskedImage().getVariance()
75 y0 = image.getY0()
76 x0 = image.getX0()
78 # Bitplane to set corresponding to the FAKE bit
79 fakeMaskValue = 2**mask.getMaskPlaneDict()['FAKE']
81 # At each position create a star with the given intensity and add it
82 # to the image.
83 for pos, intensity in self.fakeSources:
84 objArray, noiseArray = self.makeFakeStar(pos, intensity, psf)
85 psfRad = int((objArray.shape[0]-1)/2.)
86 yslice, xslice = slice(pos.y-psfRad-y0, pos.y+psfRad+y0+1),\
87 slice(pos.x-psfRad-x0, pos.x+psfRad+x0+1)
89 image.getArray()[yslice, xslice] += objArray
90 mask.getArray()[yslice, xslice] += fakeMaskValue
91 variance.getArray()[yslice, xslice] += noiseArray**2
93 # make stars at a given position with a given intensity
94 @staticmethod
95 def makeFakeStar(position, intensity, psf):
96 psfImage = psf.computeImage(geom.Point2D(position.x, position.y)).getArray()
97 psfImage *= intensity
98 noise = np.random.normal(0, np.sqrt(abs(psfImage)))
99 return psfImage + noise, noise
102def getObsTestConfig(TaskClass):
103 """Helper function to get a command-line task config customized by obs_test.
105 This duplicates the config override code in pipe_base's ArgumentParser, but
106 essentially in order to test it.
107 """
108 config = TaskClass.ConfigClass()
109 filename = os.path.join(obsTestDir, "config",
110 TaskClass._DefaultName + ".py")
111 if os.path.exists(filename):
112 config.load(filename)
113 return config
116class FakeProcessingTestCase(lsst.utils.tests.TestCase):
117 def testFakeProcessing(self):
118 # Set the random seed for predictability
119 np.random.seed(500)
121 # Set ouput path and create a dataId
122 outPath = tempfile.mkdtemp() if OutputName is None \
123 else "{}-ProcessCcd".format(OutputName)
124 dataId = dict(visit=1)
125 dataIdStrList = ["{}={}".format(*item) for item in dataId.items()]
126 mask = None
127 maskPlaneName = "FAKE"
129 try:
130 # Set the configurations for running the fake object piepline
131 processCcdConfig = getObsTestConfig(ProcessCcdTask)
132 processCcdConfig.calibrate.doInsertFakes = True
133 processCcdConfig.calibrate.insertFakes.retarget(FakeSourcesTestTask)
135 # Run ProcessCcd
136 pCcdResult = ProcessCcdTask.parseAndRun(
137 args=[InputDir, "--output", outPath,
138 "--clobber-config", "--doraise", "--id"]
139 + dataIdStrList,
140 doReturnResults=True, config=processCcdConfig)
142 # Check the Catalog contains properly measured fake sources
143 sourceCat = pCcdResult.resultList[0].result.calibRes.sourceCat
144 self.checkSourceCatalog(sourceCat)
146 exposure = pCcdResult.resultList[0].result.calibRes.exposure
147 mask = exposure.getMaskedImage().getMask()
148 maskBit = 2**mask.getMaskPlaneDict()[maskPlaneName]
149 fakeMask = np.bitwise_and(mask.getArray(), maskBit)
150 self.assertGreater(fakeMask.sum(), 0)
152 # Clean up temporary directories if needed
153 finally:
154 if mask:
155 # Remove FAKE so as not to contaminate later tests
156 lsst.afw.image.Mask[lsst.afw.image.MaskPixel].removeMaskPlane(maskPlaneName)
157 if OutputName is None:
158 shutil.rmtree(outPath)
159 else:
160 message = "testFakeProcessing.py's output data saved to {}"
161 print(message.format(OutputName))
163 def checkSourceCatalog(self, srcCat, thresh=5):
164 # Find the fake objects in the output source catalog, verify the
165 # measured flux is plausable given ground truth
166 fakeSourceCounter = 0
167 for source in srcCat:
168 srcX = source.getX()
169 srcY = source.getY()
170 for fakePos, fakeIntensity in FakeSourcesTestTask.fakeSources:
171 distSq = (srcX - fakePos.x)**2 + (srcY - fakePos.y)**2
172 if distSq <= thresh**2:
173 fluxDiff = abs(source.getPsfInstFlux() - fakeIntensity)
174 self.assertLessEqual(fluxDiff, 5*np.sqrt(fakeIntensity))
175 fakeSourceCounter += 1
176 # Verify the correct number of fake sources were found
177 self.assertEqual(fakeSourceCounter,
178 len(FakeSourcesTestTask.fakeSources))
181def setup_module(module):
182 lsst.utils.tests.init()
185class MemoryTestCase(lsst.utils.tests.MemoryTestCase):
186 pass
189if __name__ == "__main__": 189 ↛ 190line 189 didn't jump to line 190, because the condition on line 189 was never true
190 lsst.utils.tests.init()
191 unittest.main()