def test_auto_convolveScience(self): """Test that auto mode gives the same result as convolveScience when the science psf is the smaller. """ noiseLevel = 1. science, sources = makeTestImage(psfSize=2.0, noiseLevel=noiseLevel, noiseSeed=6) template, _ = makeTestImage(psfSize=3.0, noiseLevel=noiseLevel, noiseSeed=7, templateBorderSize=20, doApplyCalibration=True) config = subtractImages.AlardLuptonSubtractTask.ConfigClass() config.doSubtractBackground = False config.mode = "convolveScience" task = subtractImages.AlardLuptonSubtractTask(config=config) output = task.run(template.clone(), science.clone(), sources) config.mode = "auto" task = subtractImages.AlardLuptonSubtractTask(config=config) outputAuto = task.run(template, science, sources) self.assertMaskedImagesEqual(output.difference.maskedImage, outputAuto.difference.maskedImage)
def test_order_equal_images(self): """Verify that the result is the same regardless of convolution mode if the images are equivalent. """ noiseLevel = .1 seed1 = 6 seed2 = 7 science1, sources1 = makeTestImage(psfSize=2.0, noiseLevel=noiseLevel, noiseSeed=seed1) template1, _ = makeTestImage(psfSize=2.0, noiseLevel=noiseLevel, noiseSeed=seed2, templateBorderSize=0, doApplyCalibration=True) config1 = subtractImages.AlardLuptonSubtractTask.ConfigClass() config1.mode = "convolveTemplate" config1.doSubtractBackground = False task1 = subtractImages.AlardLuptonSubtractTask(config=config1) results_convolveTemplate = task1.run(template1, science1, sources1) science2, sources2 = makeTestImage(psfSize=2.0, noiseLevel=noiseLevel, noiseSeed=seed1) template2, _ = makeTestImage(psfSize=2.0, noiseLevel=noiseLevel, noiseSeed=seed2, templateBorderSize=0, doApplyCalibration=True) config2 = subtractImages.AlardLuptonSubtractTask.ConfigClass() config2.mode = "convolveScience" config2.doSubtractBackground = False task2 = subtractImages.AlardLuptonSubtractTask(config=config2) results_convolveScience = task2.run(template2, science2, sources2) diff1 = science1.maskedImage.clone() diff1 -= template1.maskedImage diff2 = science2.maskedImage.clone() diff2 -= template2.maskedImage self.assertFloatsAlmostEqual( results_convolveTemplate.difference.image.array, diff1.image.array, atol=noiseLevel * 5.) self.assertFloatsAlmostEqual( results_convolveScience.difference.image.array, diff2.image.array, atol=noiseLevel * 5.) diffErr = noiseLevel * 2 self.assertMaskedImagesAlmostEqual( results_convolveTemplate.difference.maskedImage, results_convolveScience.difference.maskedImage, atol=diffErr * 5.)
def _run_and_check_images(doDecorrelation): """Check that the metadata is correct with or without decorrelation. """ config.doDecorrelation = doDecorrelation task = subtractImages.AlardLuptonSubtractTask(config=config) output = task.run(template.clone(), science.clone(), sources) if doDecorrelation: # Decorrelation requires recalculating the PSF, # so it will not be the same as the input psfOutSize = getPsfFwhm(template.psf) self.assertFloatsAlmostEqual(psfSize, psfOutSize) else: psfOut = output.difference.psf psfAvgPos = psfOut.getAveragePosition() psfOutImg = psfOut.computeKernelImage(psfAvgPos) self.assertImagesAlmostEqual(psfImg, psfOutImg) # check PSF, WCS, bbox, filterLabel, photoCalib, aperture correction self._compare_apCorrMaps(apCorrMap, output.difference.info.getApCorrMap()) self.assertWcsAlmostEqualOverBBox(science.wcs, output.difference.wcs, science.getBBox()) self.assertEqual(science.filter, output.difference.filter) self.assertEqual(science.photoCalib, output.difference.photoCalib)
def _run_and_check_images(config, statsCtrl, mode): """Check that the fit background matches the input model. """ config.mode = mode task = subtractImages.AlardLuptonSubtractTask(config=config) output = task.run(template.clone(), science.clone(), sources) # We should be fitting the same number of parameters as were in the input model self.assertEqual(output.backgroundModel.getNParameters(), background_model.getNParameters()) # The parameters of the background fit should be close to the input model self.assertFloatsAlmostEqual(np.array( output.backgroundModel.getParameters()), np.array(params), rtol=0.3) # stddev of difference image should be close to expected value. # This will fail if we have mis-subtracted the background. stdVal = _computeRobustStatistics(output.difference.image, output.difference.mask, statsCtrl, statistic=afwMath.STDEV) self.assertFloatsAlmostEqual(stdVal, np.sqrt(2) * noiseLevel, rtol=0.1)
def test_equal_images(self): """Test that running with enough sources produces reasonable output, with the same size psf in the template and science. """ noiseLevel = 1. science, sources = makeTestImage(psfSize=2.4, noiseLevel=noiseLevel, noiseSeed=6) template, _ = makeTestImage(psfSize=2.4, noiseLevel=noiseLevel, noiseSeed=7, templateBorderSize=20, doApplyCalibration=True) config = subtractImages.AlardLuptonSubtractTask.ConfigClass() config.doSubtractBackground = False task = subtractImages.AlardLuptonSubtractTask(config=config) output = task.run(template, science, sources) # There shoud be no NaN values in the difference image self.assertTrue(np.all(np.isfinite(output.difference.image.array))) # Mean of difference image should be close to zero. meanError = noiseLevel / np.sqrt(output.difference.image.array.size) # Make sure to include pixels with the DETECTED mask bit set. statsCtrl = _makeStats(badMaskPlanes=("EDGE", "BAD", "NO_DATA")) differenceMean = _computeRobustStatistics(output.difference.image, output.difference.mask, statsCtrl) self.assertFloatsAlmostEqual(differenceMean, 0, atol=5 * meanError) # stddev of difference image should be close to expected value. differenceStd = _computeRobustStatistics(output.difference.image, output.difference.mask, _makeStats(), statistic=afwMath.STDEV) self.assertFloatsAlmostEqual(differenceStd, np.sqrt(2) * noiseLevel, rtol=0.1)
def _run_and_check_images(science, template, sources, statsCtrl, doDecorrelation, doScaleVariance, scaleFactor=1.): """Check that the variance plane matches the expected value for different configurations of ``doDecorrelation`` and ``doScaleVariance``. """ config = subtractImages.AlardLuptonSubtractTask.ConfigClass() config.doSubtractBackground = False config.doDecorrelation = doDecorrelation config.doScaleVariance = doScaleVariance task = subtractImages.AlardLuptonSubtractTask(config=config) output = task.run(template.clone(), science.clone(), sources) if doScaleVariance: self.assertFloatsAlmostEqual( task.metadata["scaleTemplateVarianceFactor"], scaleFactor, atol=0.05) self.assertFloatsAlmostEqual( task.metadata["scaleScienceVarianceFactor"], scaleFactor, atol=0.05) templateNoise = _computeRobustStatistics(template.variance, template.mask, statsCtrl) if doDecorrelation: scienceNoise = _computeRobustStatistics( science.variance, science.mask, statsCtrl) else: scienceNoise = _computeRobustStatistics( output.matchedScience.variance, output.matchedScience.mask, statsCtrl) if doScaleVariance: templateNoise *= scaleFactor scienceNoise *= scaleFactor varMean = _computeRobustStatistics(output.difference.variance, output.difference.mask, statsCtrl) self.assertFloatsAlmostEqual(varMean, scienceNoise + templateNoise, rtol=0.1)
def test_symmetry(self): """Test that convolving the science and convolving the template are symmetric: if the psfs are switched between them, the difference image should be nearly the same. """ noiseLevel = 1. # Don't include a border for the template, in order to make the results # comparable when we swap which image is treated as the "science" image. science, sources = makeTestImage(psfSize=2.0, noiseLevel=noiseLevel, noiseSeed=6, templateBorderSize=0) template, _ = makeTestImage(psfSize=3.0, noiseLevel=noiseLevel, noiseSeed=7, templateBorderSize=0, doApplyCalibration=True) config = subtractImages.AlardLuptonSubtractTask.ConfigClass() config.mode = 'auto' config.doSubtractBackground = False task = subtractImages.AlardLuptonSubtractTask(config=config) # The science image will be modified in place, so use a copy for the second run. science_better = task.run(template.clone(), science.clone(), sources) template_better = task.run(science, template, sources) delta = template_better.difference.clone() delta.image -= science_better.difference.image delta.variance -= science_better.difference.variance delta.mask.array -= science_better.difference.mask.array statsCtrl = _makeStats() # Mean of delta should be very close to zero. nGoodPix = np.sum(np.isfinite(delta.image.array)) meanError = 2 * noiseLevel / np.sqrt(nGoodPix) deltaMean = _computeRobustStatistics(delta.image, delta.mask, statsCtrl) deltaStd = _computeRobustStatistics(delta.image, delta.mask, statsCtrl, statistic=afwMath.STDEV) self.assertFloatsAlmostEqual(deltaMean, 0, atol=5 * meanError) # stddev of difference image should be close to expected value self.assertFloatsAlmostEqual(deltaStd, 2 * np.sqrt(2) * noiseLevel, rtol=.1)
def test_mismatched_template(self): """Test that an error is raised if the template does not fully contain the science image. """ xSize = 200 ySize = 200 science, sources = makeTestImage(psfSize=2.4, xSize=xSize + 20, ySize=ySize + 20) template, _ = makeTestImage(psfSize=2.4, xSize=xSize, ySize=ySize, doApplyCalibration=True) config = subtractImages.AlardLuptonSubtractTask.ConfigClass() task = subtractImages.AlardLuptonSubtractTask(config=config) with self.assertRaises(AssertionError): task.run(template, science, sources)
def test_few_sources(self): """Test with only 1 source, to check that we get a useful error. """ xSize = 256 ySize = 256 science, sources = makeTestImage(psfSize=2.4, nSrc=1, xSize=xSize, ySize=ySize) template, _ = makeTestImage(psfSize=2.0, nSrc=1, xSize=xSize, ySize=ySize, doApplyCalibration=True) config = subtractImages.AlardLuptonSubtractTask.ConfigClass() task = subtractImages.AlardLuptonSubtractTask(config=config) with self.assertRaisesRegex( lsst.pex.exceptions.Exception, 'Unable to determine kernel sum; 0 candidates'): task.run(template, science, sources)
def _run_and_check_images(statsCtrl, statsCtrlDetect, scienceNoiseLevel, templateNoiseLevel): science, sources = makeTestImage(psfSize=3.0, noiseLevel=scienceNoiseLevel, noiseSeed=6) template, _ = makeTestImage(psfSize=2.0, noiseLevel=templateNoiseLevel, noiseSeed=7, templateBorderSize=20, doApplyCalibration=True) config = subtractImages.AlardLuptonSubtractTask.ConfigClass() config.doSubtractBackground = False task = subtractImages.AlardLuptonSubtractTask(config=config) output = task.run(template, science, sources) self.assertFloatsAlmostEqual( task.metadata["scaleTemplateVarianceFactor"], 1., atol=.05) self.assertFloatsAlmostEqual( task.metadata["scaleScienceVarianceFactor"], 1., atol=.05) # There should be no NaNs in the image if we convolve the template with a buffer self.assertTrue(np.all(np.isfinite(output.difference.image.array))) # Mean of difference image should be close to zero. meanError = (scienceNoiseLevel + templateNoiseLevel) / np.sqrt( output.difference.image.array.size) diffimMean = _computeRobustStatistics(output.difference.image, output.difference.mask, statsCtrlDetect) self.assertFloatsAlmostEqual(diffimMean, 0, atol=5 * meanError) # stddev of difference image should be close to expected value. noiseLevel = np.sqrt(scienceNoiseLevel**2 + templateNoiseLevel**2) varianceMean = _computeRobustStatistics(output.difference.variance, output.difference.mask, statsCtrl) diffimStd = _computeRobustStatistics(output.difference.image, output.difference.mask, statsCtrl, statistic=afwMath.STDEV) self.assertFloatsAlmostEqual(varianceMean, noiseLevel**2, rtol=0.1) self.assertFloatsAlmostEqual(diffimStd, noiseLevel, rtol=0.1)