def testMeasureCentroid(self): """Test that we can use our silly centroid through the usual Tasks""" algorithms.AlgorithmRegistry.register("centroid.silly", testLib.SillyCentroidControl) x, y = 10, 20 im = afwImage.MaskedImageF(afwGeom.ExtentI(512, 512)) im.set(0) arr = im.getImage().getArray() arr[y, x] = 1 exp = afwImage.makeExposure(im) schema = afwTable.SourceTable.makeMinimalSchema() detConfig = algorithms.SourceDetectionConfig() detConfig.thresholdValue = 0.5 detConfig.thresholdType = "value" measConfig = algorithms.SourceMeasurementConfig() measConfig.algorithms.names.add("centroid.silly") measConfig.slots.centroid = "centroid.silly" measConfig.algorithms["centroid.silly"].param = 5 measConfig.doReplaceWithNoise = False det = algorithms.SourceDetectionTask(schema=schema, config=detConfig) meas = algorithms.SourceMeasurementTask(schema, config=measConfig) table = afwTable.SourceTable.make(schema) sources = det.makeSourceCatalog(table, exp, doSmooth=False, sigma=1.0).sources self.assertEqual(len(sources), 1) meas.run(exp, sources) self.assertEqual(len(sources), 1) self.assertEqual(sources[0].getY(), y + 5)
def createDipole(w, h, xc, yc, scaling=100.0, fracOffset=1.2): # Make random noise image: set image plane to normal distribution image = afwImage.MaskedImageF(w, h) image.set(0) array = image.getImage().getArray() array[:, :] = np.random.randn(w, h) # Set variance to 1.0 var = image.getVariance() var.set(1.0) if display: ds9.mtv(image, frame=1, title="Original image") ds9.mtv(image.getVariance(), frame=2, title="Original variance") # Create Psf for dipole creation and measurement psfSize = 17 psf = measAlg.DoubleGaussianPsf(psfSize, psfSize, 2.0, 3.5, 0.1) psfFwhmPix = sigma2fwhm * psf.computeShape().getDeterminantRadius() psfim = psf.computeImage().convertF() psfim *= scaling / psf.computePeak() psfw, psfh = psfim.getDimensions() psfSum = np.sum(psfim.getArray()) # Create the dipole, offset by fracOffset of the Psf FWHM (pixels) offset = fracOffset * psfFwhmPix // 2 array = image.getImage().getArray() xp, yp = xc - psfw // 2 + offset, yc - psfh // 2 + offset array[yp:yp + psfh, xp:xp + psfw] += psfim.getArray() xn, yn = xc - psfw // 2 - offset, yc - psfh // 2 - offset array[yn:yn + psfh, xn:xn + psfw] -= psfim.getArray() if display: ds9.mtv(image, frame=3, title="With dipole") # Create an exposure, detect positive and negative peaks separately exp = afwImage.makeExposure(image) exp.setPsf(psf) config = measAlg.SourceDetectionConfig() config.thresholdPolarity = "both" config.reEstimateBackground = False schema = afwTable.SourceTable.makeMinimalSchema() task = measAlg.SourceDetectionTask(schema, config=config) table = afwTable.SourceTable.make(schema) results = task.makeSourceCatalog(table, exp) if display: ds9.mtv(image, frame=4, title="Detection plane") # Merge them together assert (len(results.sources) == 2) fpSet = results.fpSets.positive fpSet.merge(results.fpSets.negative, 0, 0, False) sources = afwTable.SourceCatalog(table) fpSet.makeSources(sources) assert (len(sources) == 1) s = sources[0] assert (len(s.getFootprint().getPeaks()) == 2) return psf, psfSum, exp, s
def test1(self): #exposure = afwImage.ExposureF('mini-v85408556-fr-R23-S11.fits') #exposure = afwImage.ExposureF('../afwdata/ImSim/calexp/v85408556-fr/R23/S11.fits') #bb = afwGeom.Box2I(afwGeom.Point2I(0,0), afwGeom.Point2I(511,511)) #exposure = afwImage.ExposureF('data/goodSeeingCoadd/r/3/113,0/coadd-r-3-113,0.fits', 0, bb) #exposure.writeFits('mini-r-3-113,0.fits') fn = os.path.join(os.path.dirname(__file__), 'data', 'mini-r-3-113,0.fits.gz') print 'Reading image', fn exposure = afwImage.ExposureF(fn) exposure.setPsf(afwDetection.GaussianPsf(15, 15, 3)) schema = afwTable.SourceTable.makeMinimalSchema() idFactory = afwTable.IdFactory.makeSimple() dconf = measAlg.SourceDetectionConfig() dconf.reEstimateBackground = False dconf.includeThresholdMultiplier = 5. mconf = SingleFrameMeasurementConfig() aconf = ANetAstrometryConfig() aconf.forceKnownWcs = True det = measAlg.SourceDetectionTask(schema=schema, config=dconf) meas = SingleFrameMeasurementTask(schema, config=mconf) astrom = ANetAstrometryTask(schema, config=aconf, name='astrom') astrom.log.setThreshold(pexLog.Log.DEBUG) inwcs = exposure.getWcs() print 'inwcs:', inwcs instr = inwcs.getFitsMetadata().toString() print 'inwcs:', instr table = afwTable.SourceTable.make(schema, idFactory) sources = det.makeSourceCatalog(table, exposure, sigma=1).sources meas.measure(exposure, sources) for dosip in [False, True]: aconf.solver.calculateSip = dosip ast = astrom.run(sourceCat=sources, exposure=exposure) outwcs = exposure.getWcs() outstr = outwcs.getFitsMetadata().toString() if dosip is False: self.assertEqual(inwcs, outwcs) self.assertEqual(instr, outstr) print 'inwcs:', instr print 'outwcs:', outstr print len(ast.matches), 'matches' self.assertTrue(len(ast.matches) > 10)
def test1(self): fn = os.path.join(os.path.dirname(__file__), 'data', 'mini-r-3-113,0.fits.gz') print('Reading image', fn) exposure = afwImage.ExposureF(fn) exposure.setPsf(afwDetection.GaussianPsf(15, 15, 3)) schema = afwTable.SourceTable.makeMinimalSchema() idFactory = afwTable.IdFactory.makeSimple() dconf = measAlg.SourceDetectionConfig() dconf.reEstimateBackground = False dconf.includeThresholdMultiplier = 5. mconf = SingleFrameMeasurementConfig() aconf = ANetAstrometryConfig() aconf.forceKnownWcs = True det = measAlg.SourceDetectionTask(schema=schema, config=dconf) meas = SingleFrameMeasurementTask(schema, config=mconf) astrom = ANetAstrometryTask(schema, config=aconf, name='astrom') astrom.log.setLevel(astrom.log.TRACE) inwcs = exposure.getWcs() print('inwcs:', inwcs) instr = inwcs.getFitsMetadata().toString() print('inwcs:', instr) table = afwTable.SourceTable.make(schema, idFactory) sources = det.makeSourceCatalog(table, exposure, sigma=1).sources meas.measure(sources, exposure) for dosip in [False, True]: aconf.solver.calculateSip = dosip ast = astrom.run(sourceCat=sources, exposure=exposure) outwcs = exposure.getWcs() outstr = outwcs.getFitsMetadata().toString() if not dosip: self.assertEqual(inwcs, outwcs) self.assertEqual(instr, outstr) print('inwcs:', instr) print('outwcs:', outstr) print(len(ast.matches), 'matches') self.assertGreater(len(ast.matches), 10)
def detectDipoleSources(self, doMerge=True, diffim=None, detectSigma=5.5, grow=3): """!Utility function for detecting dipoles. Detect pos/neg sources in the diffim, then merge them. A bigger "grow" parameter leads to a larger footprint which helps with dipole measurement for faint dipoles. """ if diffim is None: diffim = self.diffim # Start with a minimal schema - only the fields all SourceCatalogs need schema = afwTable.SourceTable.makeMinimalSchema() # Customize the detection task a bit (optional) detectConfig = measAlg.SourceDetectionConfig() detectConfig.returnOriginalFootprints = False # should be the default psfSigma = diffim.getPsf().computeShape().getDeterminantRadius() # code from imageDifference.py: detectConfig.thresholdPolarity = "both" detectConfig.thresholdValue = detectSigma # detectConfig.nSigmaToGrow = psfSigma detectConfig.reEstimateBackground = True # if False, will fail often for faint sources on gradients? detectConfig.thresholdType = "pixel_stdev" # Create the detection task. We pass the schema so the task can declare a few flag fields detectTask = measAlg.SourceDetectionTask(schema, config=detectConfig) table = afwTable.SourceTable.make(schema) catalog = detectTask.makeSourceCatalog(table, diffim, sigma=psfSigma) # Now do the merge. if doMerge: fpSet = catalog.fpSets.positive fpSet.merge(catalog.fpSets.negative, grow, grow, False) sources = afwTable.SourceCatalog(table) fpSet.makeSources(sources) return sources else: return detectTask, schema
def setUp(self): self.x0, self.y0 = 0, 0 self.nx, self.ny = 512, 512 #2048, 4096 self.sky = 100.0 self.nObj = 100 # make a detector with distortion self.detector = DetectorWrapper( bbox = afwGeom.Box2I(afwGeom.Point2I(0,0), afwGeom.Extent2I(self.nx, self.ny)), orientation = cameraGeom.Orientation(afwGeom.Point2D(255.0, 255.0)), radialDistortion = 0.925, ).detector # make a detector with no distortion self.flatDetector = DetectorWrapper( bbox = afwGeom.Box2I(afwGeom.Point2I(0,0), afwGeom.Extent2I(self.nx, self.ny)), orientation = cameraGeom.Orientation(afwGeom.Point2D(255.0, 255.0)), radialDistortion = 0.0, ).detector # detection policies detConfig = measAlg.SourceDetectionConfig() # Cannot use default background approximation order (6) for such a small image. detConfig.background.approxOrderX = 4 # measurement policies measConfig = measBase.SingleFrameMeasurementConfig() measConfig.algorithms.names = [ "base_SdssCentroid", "base_SdssShape", "base_GaussianFlux", "base_PsfFlux", ] measConfig.slots.centroid = "base_SdssCentroid" measConfig.slots.shape = "base_SdssShape" measConfig.slots.psfFlux = "base_PsfFlux" measConfig.slots.apFlux = None measConfig.slots.modelFlux = None measConfig.slots.instFlux = None measConfig.slots.calibFlux = None self.schema = afwTable.SourceTable.makeMinimalSchema() detConfig.validate() measConfig.validate() self.detTask = measAlg.SourceDetectionTask(config=detConfig, schema=self.schema) self.measTask = measBase.SingleFrameMeasurementTask(config=measConfig, schema=self.schema) # psf star selector starSelectorConfig = measAlg.SecondMomentStarSelectorTask.ConfigClass() starSelectorConfig.fluxLim = 5000.0 starSelectorConfig.histSize = 32 starSelectorConfig.clumpNSigma = 1.0 starSelectorConfig.badFlags = [] self.starSelector = measAlg.SecondMomentStarSelectorTask( config=starSelectorConfig, schema=self.schema ) # psf determiner psfDeterminerFactory = measAlg.psfDeterminerRegistry["pca"] psfDeterminerConfig = psfDeterminerFactory.ConfigClass() width, height = self.nx, self.ny nEigenComponents = 3 psfDeterminerConfig.sizeCellX = width//3 psfDeterminerConfig.sizeCellY = height//3 psfDeterminerConfig.nEigenComponents = nEigenComponents psfDeterminerConfig.spatialOrder = 1 psfDeterminerConfig.kernelSizeMin = 31 psfDeterminerConfig.nStarPerCell = 0 psfDeterminerConfig.nStarPerCellSpatialFit = 0 # unlimited self.psfDeterminer = psfDeterminerFactory(psfDeterminerConfig)
def setUp(self): self.x0, self.y0 = 0, 0 self.nx, self.ny = 512, 512 #2048, 4096 self.sky = 100.0 self.nObj = 100 # make a distorter # This is a lot of distortion ... from circle r=1, to ellipse with a=1.3 (ie. 30%) # For suprimecam, we expect only about 5% self.distCoeffs = [0.0, 1.0, 2.0e-04, 3.0e-8] lanczosOrder = 3 coefficientsDistort = True self.distorter = cameraGeom.RadialPolyDistortion( self.distCoeffs, coefficientsDistort, lanczosOrder) # make a detector self.detector = cameraUtils.makeDefaultCcd( afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(self.nx, self.ny))) self.detector.setDistortion(self.distorter) self.detector.setCenter(cameraGeom.FpPoint( 255.5, 255.5)) # move boresight from center to 0,0 if False: for x, y in [(0, 0), (0, 511), (511, 0), (511, 511)]: p = afwGeom.Point2D(x, y) iqq = self.distorter.distort(p, geomEllip.Quadrupole(), self.detector) print x, y, geomEllip.Axes(iqq) print self.detector.getPositionFromPixel(p).getMm() print "Max distortion on this detector: ", self.distorter.computeMaxShear( self.detector) # detection policies self.detConfig = measAlg.SourceDetectionConfig() # measurement policies self.measSrcConfig = measAlg.SourceMeasurementConfig() # psf star selector starSelectorFactory = measAlg.starSelectorRegistry["secondMoment"] starSelectorConfig = starSelectorFactory.ConfigClass() starSelectorConfig.fluxLim = 5000.0 starSelectorConfig.histSize = 32 starSelectorConfig.clumpNSigma = 1.0 starSelectorConfig.badFlags = [] self.starSelector = starSelectorFactory(starSelectorConfig) # psf determiner psfDeterminerFactory = measAlg.psfDeterminerRegistry["pca"] psfDeterminerConfig = psfDeterminerFactory.ConfigClass() width, height = self.nx, self.ny nEigenComponents = 3 psfDeterminerConfig.sizeCellX = width // 3 psfDeterminerConfig.sizeCellY = height // 3 psfDeterminerConfig.nEigenComponents = nEigenComponents psfDeterminerConfig.spatialOrder = 1 psfDeterminerConfig.kernelSizeMin = 31 psfDeterminerConfig.nStarPerCell = 0 psfDeterminerConfig.nStarPerCellSpatialFit = 0 # unlimited self.psfDeterminer = psfDeterminerFactory(psfDeterminerConfig)
def detectDipoleSources(self, doMerge=True, diffim=None, detectSigma=5.5, grow=3, minBinSize=32): """Utility function for detecting dipoles. Detect pos/neg sources in the diffim, then merge them. A bigger "grow" parameter leads to a larger footprint which helps with dipole measurement for faint dipoles. Parameters ---------- doMerge : `bool` Whether to merge the positive and negagive detections into a single source table. diffim : `lsst.afw.image.exposure.exposure.ExposureF` Difference image on which to perform detection. detectSigma : `float` Threshold for object detection. grow : `int` Number of pixels to grow the footprints before merging. minBinSize : `int` Minimum bin size for the background (re)estimation (only applies if the default leads to min(nBinX, nBinY) < fit order so the default config parameter needs to be decreased, but not to a value smaller than ``minBinSize``, in which case the fitting algorithm will take over and decrease the fit order appropriately.) Returns ------- sources : `lsst.afw.table.SourceCatalog` If doMerge=True, the merged source catalog is returned OR detectTask : `lsst.meas.algorithms.SourceDetectionTask` schema : `lsst.afw.table.Schema` If doMerge=False, the source detection task and its schema are returned. """ if diffim is None: diffim = self.diffim # Start with a minimal schema - only the fields all SourceCatalogs need schema = afwTable.SourceTable.makeMinimalSchema() # Customize the detection task a bit (optional) detectConfig = measAlg.SourceDetectionConfig() detectConfig.returnOriginalFootprints = False # should be the default psfSigma = diffim.getPsf().computeShape().getDeterminantRadius() # code from imageDifference.py: detectConfig.thresholdPolarity = "both" detectConfig.thresholdValue = detectSigma # detectConfig.nSigmaToGrow = psfSigma detectConfig.reEstimateBackground = True # if False, will fail often for faint sources on gradients? detectConfig.thresholdType = "pixel_stdev" # Test images are often quite small, so may need to adjust background binSize while ((min(diffim.getWidth(), diffim.getHeight()))//detectConfig.background.binSize < detectConfig.background.approxOrderX and detectConfig.background.binSize > minBinSize): detectConfig.background.binSize = max(minBinSize, detectConfig.background.binSize//2) # Create the detection task. We pass the schema so the task can declare a few flag fields detectTask = measAlg.SourceDetectionTask(schema, config=detectConfig) table = afwTable.SourceTable.make(schema) catalog = detectTask.run(table, diffim, sigma=psfSigma) # Now do the merge. if doMerge: fpSet = catalog.fpSets.positive fpSet.merge(catalog.fpSets.negative, grow, grow, False) sources = afwTable.SourceCatalog(table) fpSet.makeSources(sources) return sources else: return detectTask, schema
def runone(self, kk, rand): psf = self.getpsf() im = afwImage.ImageF(120, 200) skystd = 100 afwMath.randomGaussianImage(im, rand) im *= skystd # The SDSS adaptive moments code seems sometimes to latch onto # an incorrect answer (maybe from a noise spike or something). # None of the flags seem to be set. The result are variance # measurements a bit bigger than the PSF. With different # noise draws the source values here will show this effect # (hence the loop in "test1" to try "runone" will different # noise draws). # The real point of this test case, though, is to show that # replacing other detections by noise results in better # measurements. We do this by constructing a fake image # containing six rows. In the top three rows, we have a # galaxy flanked by two stars that are far enough away that # they don't confuse the SDSS adaptive moments code. In the # bottom three rows, they're close enough that the detections # don't merge, but the stars cause the variance of the galaxy # to be mis-estimated. We want to show that with the # "doReplaceWithNoise" option, the measurements on the # bottom three improve. # If you love ASCII art (and who doesn't, really), the # synthetic image is going to look like this: # # * GGG * # * GGG * # * GGG * # * GGG * # * GGG * # * GGG * # We have three of each to work around the instability # mentioned above. x = 60 y0 = 16 ystep = 33 for i in range(6): dx = [28, 29, 30, 35, 36, 37][i] y = y0 + i * ystep # x y sx sy flux addGaussian(im, x, y, 10, 3, 2e5) addPsf(im, psf, x + dx, y, 1000) addPsf(im, psf, x - dx, y, 1000) #im.writeFits('im.fits') mi = afwImage.MaskedImageF(im) var = mi.getVariance() var.set(skystd**2) exposure = afwImage.makeExposure(mi) exposure.setPsf(psf) detconf = measAlg.SourceDetectionConfig() detconf.returnOriginalFootprints = True detconf.reEstimateBackground = False measconf = measAlg.SourceMeasurementConfig() measconf.doReplaceWithNoise = False #newalgs = [ 'shape.hsm.ksb', 'shape.hsm.bj', 'shape.hsm.linear' ] #measconf.algorithms = list(measconf.algorithms.names) + newalgs schema = afwTable.SourceTable.makeMinimalSchema() detect = measAlg.SourceDetectionTask(config=detconf, schema=schema) measure = measAlg.SourceMeasurementTask(config=measconf, schema=schema) print 'Running detection...' table = afwTable.SourceTable.make(schema) detected = detect.makeSourceCatalog(table, exposure) sources = detected.sources # We don't want the sources to be close enough that their # detection masks touch. self.assertEqual(len(sources), 18) # Run measurement with and without "doReplaceWithNoise"... for jj in range(2): print 'Running measurement...' measure.run(exposure, sources) #fields = schema.getNames() #print 'Fields:', fields fields = [ 'centroid.sdss', 'shape.sdss', #'shape.hsm.bj.moments', #'shape.hsm.ksb.moments', #'shape.hsm.linear.moments', #'shape.sdss.flags.maxiter', 'shape.sdss.flags.shift', #'shape.sdss.flags.unweighted', 'shape.sdss.flags.unweightedbad' ] keys = [schema.find(f).key for f in fields] xx, yy, vx, vy = [], [], [], [] for source in sources: #print ' ', source #for f,k in zip(fields, keys): # val = source.get(k) # print ' ', f, val xx.append(source.getX()) yy.append(source.getY()) vx.append(source.getIxx()) vy.append(source.getIyy()) if plots: plotSources(im, sources, schema) plt.savefig('%i%s.png' % (kk, chr(ord('a') + jj))) # Now we want to find the galaxy variance measurements... # Sort, first vertically then horizontally # iy ~ row number iy = [int(round((y - y0) / float(ystep))) for y in yy] iy = np.array(iy) xx = np.array(xx) vx = np.array(vx) vy = np.array(vy) I = np.argsort(iy * 1000 + xx) vx = vx[I] vy = vy[I] # The "left" stars will be indices 0, 3, 6, ... # The galaxies will be 1, 4, 7, ... vx = vx[slice(1, 18, 3)] # Bottom three galaxies may be contaminated by the stars bad = vx[:3] # Top three should be clean good = vx[3:] # When SdssShape fails, we get variance ~ 11 I = np.flatnonzero(bad > 50.) # Hope that we got at least one valid measurement self.assertTrue(len(I) > 0) bad = bad[I] I = np.flatnonzero(good > 50.) self.assertTrue(len(I) > 0) good = good[I] print 'bad:', bad print 'good:', good # Typical: # bad: [ 209.78476672 192.35271583 176.76274525] # good: [ 99.40557099 110.5701382 ] oklo, okhi = 80, 120 self.assertTrue(all((good > oklo) * (good < okhi))) if jj == 0: # Without "doReplaceWithNoise", we expect to find the variances # overestimated. self.assertTrue(all(bad > okhi)) else: # With "doReplaceWithNoise", no problem! self.assertTrue(all((bad > oklo) * (bad < okhi))) # Set "doReplaceWithNoise" for the second time through the loop... measconf.doReplaceWithNoise = True
def test2(self): # Check that doReplaceWithNoise works with deblended source # hierarchies. seed = 42 rand = afwMath.Random(afwMath.Random.MT19937, seed) psf = self.getpsf() im = afwImage.ImageF(200, 50) skystd = 100 afwMath.randomGaussianImage(im, rand) im *= skystd imorig = afwImage.ImageF(im, True) noiseim = imorig mi = afwImage.MaskedImageF(im) mi.getVariance().set(skystd**2) exposure = afwImage.makeExposure(mi) exposure.setPsf(psf) detconf = measAlg.SourceDetectionConfig() detconf.returnOriginalFootprints = True detconf.reEstimateBackground = False measconf = measAlg.SourceMeasurementConfig() measconf.doReplaceWithNoise = True measconf.replaceWithNoise.noiseSeed = 42 schema = afwTable.SourceTable.makeMinimalSchema() detect = measAlg.SourceDetectionTask(config=detconf, schema=schema) measure = MySourceMeasurementTask(config=measconf, schema=schema, doplot=plots) table = afwTable.SourceTable.make(schema) table.preallocate(10) # We're going to fake up a perfect deblend hierarchy here, by # creating individual images containing single sources and # measuring them, and then creating a deblend hierarchy where # the children have the correct HeavyFootprints. We want to # find that the measurements on the deblend hierarchy and the # blended image are equal to the individual images. # # Note that in the normal setup we don't expect the # measurements to be *identical* because of the faint wings of # the objects; when measuring a deblended child, we pick up # the wings of the other objects. # # In order to get exactly equal measurements, we'll fake some # sources that have no wings -- we'll copy just the source # pixels within the footprint. This means that all the # footprints are the same, and the pixels inside the footprint # are the same. fullim = None sources = None # "normal" measurements xx0, yy0, vx0, vy0 = [], [], [], [] # "no-wing" measurements xx1, yy1, vx1, vy1 = [], [], [], [] y = 25 for i in range(5): # no-noise source image sim = afwImage.ImageF(imorig.getWidth(), imorig.getHeight()) # Put all four sources in the parent (i==0), and one # source in each child (i=[1 to 4]) if i in [0, 1]: addPsf(sim, psf, 20, y, 1000) if i in [0, 2]: addGaussian(sim, 40, y, 10, 3, 2e5) if i in [0, 3]: addGaussian(sim, 75, y, 10, 3, 2e5) if i in [0, 4]: addPsf(sim, psf, 95, y, 1000) imcopy = afwImage.ImageF(imorig, True) imcopy += sim # copy the pixels into the exposure object im <<= imcopy if i == 0: detected = detect.makeSourceCatalog(table, exposure) sources = detected.sources print 'detected', len(sources), 'sources' self.assertEqual(len(sources), 1) else: fpSets = detect.detectFootprints(exposure) print 'detected', fpSets.numPos, 'sources' fpSets.positive.makeSources(sources) self.assertEqual(fpSets.numPos, 1) print len(sources), 'sources total' measure.plotpat = 'single-%i.png' % i measure.run(exposure, sources[-1:]) s = sources[-1] fp = s.getFootprint() if i == 0: # This is the blended image fullim = imcopy else: print 'Creating heavy footprint...' heavy = afwDet.makeHeavyFootprint(fp, mi) s.setFootprint(heavy) # Record the single-source measurements. xx0.append(s.getX()) yy0.append(s.getY()) vx0.append(s.getIxx()) vy0.append(s.getIyy()) # "no-wings": add just the source pixels within the footprint im <<= sim h = afwDet.makeHeavyFootprint(fp, mi) sim2 = afwImage.ImageF(imorig.getWidth(), imorig.getHeight()) h.insert(sim2) imcopy = afwImage.ImageF(imorig, True) imcopy += sim2 im <<= imcopy measure.plotpat = 'single2-%i.png' % i measure.run(exposure, sources[i:i + 1], noiseImage=noiseim) s = sources[i] xx1.append(s.getX()) yy1.append(s.getY()) vx1.append(s.getIxx()) vy1.append(s.getIyy()) if i == 0: fullim2 = imcopy # Now we'll build the fake deblended hierarchy. parent = sources[0] kids = sources[1:] # Ensure that the parent footprint contains all the child footprints pfp = parent.getFootprint() for s in kids: for span in s.getFootprint().getSpans(): pfp.addSpan(span) pfp.normalize() #parent.setFootprint(pfp) # The parent-child relationship is established through the IDs parentid = parent.getId() for s in kids: s.setParent(parentid) # Reset all the measurements shkey = sources.getTable().getShapeKey() ckey = sources.getTable().getCentroidKey() for s in sources: sh = s.get(shkey) sh.setIxx(np.nan) sh.setIyy(np.nan) sh.setIxy(np.nan) s.set(shkey, sh) c = s.get(ckey) c.setX(np.nan) c.setY(np.nan) s.set(ckey, c) # Measure the "deblended" normal sources im <<= fullim measure.plotpat = 'joint-%(sourcenum)i.png' measure.run(exposure, sources) xx2, yy2, vx2, vy2 = [], [], [], [] for s in sources: xx2.append(s.getX()) yy2.append(s.getY()) vx2.append(s.getIxx()) vy2.append(s.getIyy()) # Measure the "deblended" no-wings sources im <<= fullim2 measure.plotpat = 'joint2-%(sourcenum)i.png' measure.run(exposure, sources, noiseImage=noiseim) xx3, yy3, vx3, vy3 = [], [], [], [] for s in sources: xx3.append(s.getX()) yy3.append(s.getY()) vx3.append(s.getIxx()) vy3.append(s.getIyy()) print 'Normal:' print 'xx ', xx0 print ' vs', xx2 print 'yy ', yy0 print ' vs', yy2 print 'vx ', vx0 print ' vs', vx2 print 'vy ', vy0 print ' vs', vy2 print 'No wings:' print 'xx ', xx1 print ' vs', xx3 print 'yy ', yy1 print ' vs', yy3 print 'vx ', vx1 print ' vs', vx3 print 'vy ', vy1 print ' vs', vy3 # These "normal" tests are not very stringent. # 0.1-pixel centroids self.assertTrue(all([abs(v1 - v2) < 0.1 for v1, v2 in zip(xx0, xx2)])) self.assertTrue(all([abs(v1 - v2) < 0.1 for v1, v2 in zip(yy0, yy2)])) # 10% variances self.assertTrue( all([ abs(v1 - v2) / ((v1 + v2) / 2.) < 0.1 for v1, v2 in zip(vx0, vx2) ])) self.assertTrue( all([ abs(v1 - v2) / ((v1 + v2) / 2.) < 0.1 for v1, v2 in zip(vy0, vy2) ])) # The "no-wings" tests should be exact. self.assertTrue(xx1 == xx3) self.assertTrue(yy1 == yy3) self.assertTrue(vx1 == vx3) self.assertTrue(vy1 == vy3) # Reset sources for s in sources: sh = s.get(shkey) sh.setIxx(np.nan) sh.setIyy(np.nan) sh.setIxy(np.nan) s.set(shkey, sh) c = s.get(ckey) c.setX(np.nan) c.setY(np.nan) s.set(ckey, c) # Test that the parent/child order is unimportant. im <<= fullim2 measure.doplot = False sources2 = sources.copy() perm = [2, 1, 0, 3, 4] for i, j in enumerate(perm): sources2[i] = sources[j] # I'm not convinced that HeavyFootprints get copied correctly... sources2[i].setFootprint(sources[j].getFootprint()) measure.run(exposure, sources2, noiseImage=noiseim) # "measure.run" reorders the sources! xx3, yy3, vx3, vy3 = [], [], [], [] for s in sources: xx3.append(s.getX()) yy3.append(s.getY()) vx3.append(s.getIxx()) vy3.append(s.getIyy()) self.assertTrue(xx1 == xx3) self.assertTrue(yy1 == yy3) self.assertTrue(vx1 == vx3) self.assertTrue(vy1 == vy3) # Reset sources for s in sources: sh = s.get(shkey) sh.setIxx(np.nan) sh.setIyy(np.nan) sh.setIxy(np.nan) s.set(shkey, sh) c = s.get(ckey) c.setX(np.nan) c.setY(np.nan) s.set(ckey, c) # Test that it still works when the parent ID falls in the middle of # the child IDs. im <<= fullim2 measure.doplot = False sources2 = sources.copy() parentid = 3 ids = [parentid, 1, 2, 4, 5] for i, s in enumerate(sources2): s.setId(ids[i]) if i != 0: s.setParent(parentid) s.setFootprint(sources[i].getFootprint()) measure.run(exposure, sources2, noiseImage=noiseim) # The sources get reordered! xx3, yy3, vx3, vy3 = [], [], [], [] xx3, yy3, vx3, vy3 = [0] * 5, [0] * 5, [0] * 5, [0] * 5 for i, j in enumerate(ids): xx3[i] = sources2[j - 1].getX() yy3[i] = sources2[j - 1].getY() vx3[i] = sources2[j - 1].getIxx() vy3[i] = sources2[j - 1].getIyy() self.assertTrue(xx1 == xx3) self.assertTrue(yy1 == yy3) self.assertTrue(vx1 == vx3) self.assertTrue(vy1 == vy3)
import lsst.pex.logging as pexLog import lsst.afw.math as afwMath import lsst.afw.table as afwTable import lsst.afw.image as afwImg import lsst.afw.detection as afwDetect import lsst.meas.algorithms as measAlg statFlags = (afwMath.NPOINT | afwMath.MEAN | afwMath.STDEV | afwMath.MAX | afwMath.MIN | afwMath.ERRORS) print "The statistics flags are set to %s." % bin(statFlags) print "Errors will be calculated.\n" # Configure the detection and measurement algorithms schema = afwTable.SourceTable.makeMinimalSchema() detectSourcesConfig = measAlg.SourceDetectionConfig(thresholdType='value') measureSourcesConfig = measAlg.SourceMeasurementConfig() # Setup the detection and measurement tasks detect = measAlg.SourceDetectionTask(config=detectSourcesConfig, schema=schema) measure = measAlg.SourceMeasurementTask(config=measureSourcesConfig, schema=schema) # Choose algorithms to look at the output of. fields = [ #'centroid.naive', #'centroid.naive.err', 'centroid.naive.flags', #'centroid.gaussian', #'centroid.gaussian.err', 'centroid.sdss', 'centroid.sdss.flags', 'shape.sdss',