def setUp(self): im = afwImage.ImageF(self.monetFile("small.fits")) self.mi = afwImage.MaskedImageF(im, afwImage.MaskU(im.getDimensions()), afwImage.ImageF(im.getDimensions())); self.ds = afwDetection.FootprintSet(self.mi, afwDetection.Threshold(100)) if display: ds9.mtv(self.mi.getImage()) ds9.erase() for foot in self.ds.getFootprints(): bbox = foot.getBBox() x0, y0 = bbox.getMinX(), bbox.getMinY() x1, y1 = bbox.getMaxX(), bbox.getMaxY() xc = (x0 + x1)/2.0 yc = (y0 + y1)/2.0 if display: ds9.dot("+", xc, yc, ctype=ds9.BLUE) if False: x0 -= 0.5; y0 -= 0.5 x1 += 0.5; y1 += 0.5 ds9.line([(x0, y0), (x1, y0), (x1, y1), (x0, y1), (x0, y0)], ctype=ds9.RED) self.control = algorithms.GaussianCentroidControl() schema = afwTable.SourceTable.makeMinimalSchema() self.centroider = algorithms.MeasureSourcesBuilder().addAlgorithm(self.control).build(schema) self.ssMeasured = afwTable.SourceCatalog(schema) self.ssMeasured.table.defineCentroid(self.control.name) self.ssTruth = afwTable.SourceCatalog(schema) self.readTruth(self.monetFile("positions.dat-original"))
def OLDprepareFrames(mos, frame0=1, R=23, medianN=23, onlyVisits=[], force=False): """Prepare frames to have their radial profiles measured. Subtract the first quartile Set a radius R circle about the centre of Arcturus to NaN Set a chip with a bad QE value to NaN Median filter with a medianN x medianN filter The result is set as mos[-visit] If onlyVisits is specified, only process those chips [n.b. frame0 is still obeyed] """ visits = sorted(position.keys()) width, height = mos[visits[0]].getDimensions() X, Y = np.meshgrid(np.arange(width), np.arange(height)) frame = frame0 - 1 for v in visits: frame += 1 if onlyVisits and v not in onlyVisits: continue print("Processing %d" % v) im = mos[v].clone() im[2121:2230, 590:830] = np.nan # QE for this chip is bad ima = im.getArray() im[:] -= np.percentile(ima, 25) xc, yc = position[v] xc -= im.getX0() yc -= im.getY0() ima[np.hypot(X - xc, Y - yc) < R] = np.nan if force or -v not in mos: im = utils.medianFilterImage(im, medianN) mos[-v] = im else: im = mos[-v] if True: ds9.mtv(im, title=v, frame=frame) else: ds9.erase(frame=frame) ds9.dot("o", xc, yc, size=R, frame=frame, ctype=ds9.GREEN if yc < im.getHeight() else ds9.RED)
def makeAndMeasure(self, measureKron, a, b, theta, dx=0.0, dy=0.0, nsigma=6, kfac=2, nIterForRadius=1, xcen=None, ycen=None, makeImage=True, apCorrValue=None, # if a numeric value, use as the constant value of aperture correction ): """Make and measure an elliptical Gaussian""" if xcen is None: xcen = 0.5*self.width + dx if ycen is None: ycen = 0.5*self.height + dy # # Make the object # if a < b: a, b = b, a theta += 90 if self.objImg is None: makeImage = True if makeImage: self.objImg = makeGalaxy(self.width, self.height, self.flux, a, b, theta, dx, dy, afwGeom.Point2I(10, 10), xcen=xcen, ycen=ycen) if display: ds9.mtv(self.objImg, frame=ds9Frame, title="%g %g" % (a, b)) doApplyApCorr = "noButWarn" if apCorrValue != None: addApCorrMap(self.objImg, apCorrValue) doApplyApCorr = "yes" if display: if not makeImage: ds9.erase(frame=ds9Frame) ds9.dot("+", xcen - self.objImg.getX0(), ycen - self.objImg.getY0(), size=1, ctype=ds9.RED, frame=ds9Frame) ds9.pan(xcen - self.objImg.getX0(), ycen - self.objImg.getY0(), frame=ds9Frame) c, s = math.cos(math.radians(theta)), math.sin(math.radians(theta)) # N.b. add 1/12 in quadrature to allow for pixellisation ds9.dot("@:%f,%f,%f" % (nsigma**2*((a**2 + 1/12.0)*c**2 + (b**2 + 1/12.0)*s**2), nsigma**2*(a**2 - b**2)*c*s, nsigma**2*((a**2 + 1/12.0)*s**2 + (b**2 + 1/12.0)*c**2)), xcen - self.objImg.getX0(), ycen - self.objImg.getY0(), size=1, ctype=ds9.RED, frame=ds9Frame, silent=True) # # Do the measuring # FWHM = 5 ksize = 25 # size of desired kernel self.objImg.setPsf(measAlg.DoubleGaussianPsf(ksize, ksize, FWHM/(2*math.sqrt(2*math.log(2))), 1, 0.1)) return measureKron(self.objImg, xcen, ycen, nsigma, kfac, nIterForRadius, doApplyApCorr)
def testText(self): """Test drawing text""" ds9.erase() exp = afwImage.ExposureF(300, 350) ds9.mtv(exp, title="parent") # tells display0 about the image's xy0 with ds9.Buffering(): ds9.dot('hello', 200, 200) ds9.dot('hello', 200, 210, size=1.25) ds9.dot('hello', 200, 220, size=3, fontFamily="times") ds9.dot('hello', 200, 230, fontFamily="helvetica bold italic")
def testDrawing(self): """Test drawing lines and glyphs""" ds9.erase() exp = afwImage.ExposureF(300, 350) ds9.mtv(exp, title="parent") # tells display0 about the image's xy0 with ds9.Buffering(): ds9.dot('o', 200, 220) vertices = [(200, 220), (210, 230), (224, 230), (214, 220), (200, 220)] ds9.line(vertices, ctype=ds9.CYAN) ds9.line(vertices[:-1], symbs="+x+x", size=3)
def testTwoDisplays(self): """Test that we can do things with two frames""" exp = afwImage.ExposureF(300, 350) for frame in (0, 1): ds9.setMaskTransparency(50, frame=frame) if frame == 1: ds9.setMaskPlaneColor("CROSSTALK", "ignore", frame=frame) ds9.mtv(exp, title="parent", frame=frame) ds9.erase(frame=frame) ds9.dot('o', 205, 180, size=6, ctype=ds9.RED, frame=frame)
def setUp(self): im = afwImage.ImageF(self.monetFile("small.fits")) self.mi = afwImage.MaskedImageF(im, afwImage.Mask(im.getDimensions()), afwImage.ImageF(im.getDimensions())) self.ds = afwDetection.FootprintSet(self.mi, afwDetection.Threshold(100)) if display: ds9.mtv(self.mi.getImage()) ds9.erase() for foot in self.ds.getFootprints(): bbox = foot.getBBox() x0, y0 = bbox.getMinX(), bbox.getMinY() x1, y1 = bbox.getMaxX(), bbox.getMaxY() xc = (x0 + x1) / 2.0 yc = (y0 + y1) / 2.0 if display: ds9.dot("+", xc, yc, ctype=ds9.BLUE) if False: x0 -= 0.5 y0 -= 0.5 x1 += 0.5 y1 += 0.5 ds9.line([(x0, y0), (x1, y0), (x1, y1), (x0, y1), (x0, y0)], ctype=ds9.RED) msConfig = measBase.SingleFrameMeasurementConfig() msConfig.algorithms.names = ["base_GaussianCentroid"] msConfig.plugins["base_GaussianCentroid"].doFootprintCheck = False msConfig.slots.centroid = "base_GaussianCentroid" msConfig.slots.shape = None msConfig.slots.apFlux = None msConfig.slots.modelFlux = None msConfig.slots.psfFlux = None msConfig.slots.instFlux = None msConfig.slots.calibFlux = None schema = afwTable.SourceTable.makeMinimalSchema() self.task = measBase.SingleFrameMeasurementTask(schema, config=msConfig) self.ssMeasured = afwTable.SourceCatalog(schema) self.ssMeasured.table.defineCentroid("base_GaussianCentroid") self.ssTruth = afwTable.SourceCatalog(schema) self.ssTruth.table.defineCentroid("base_GaussianCentroid") self.readTruth(self.monetFile("positions.dat-original"))
def OLDprepareFrames(mos, frame0=1, R=23, medianN=23, onlyVisits=[], force=False): """Prepare frames to have their radial profiles measured. Subtract the first quartile Set a radius R circle about the centre of Arcturus to NaN Set a chip with a bad QE value to NaN Median filter with a medianN x medianN filter The result is set as mos[-visit] If onlyVisits is specified, only process those chips [n.b. frame0 is still obeyed] """ visits = sorted(position.keys()) width, height = mos[visits[0]].getDimensions() X, Y = np.meshgrid(np.arange(width), np.arange(height)) frame = frame0 - 1 for v in visits: frame += 1 if onlyVisits and v not in onlyVisits: continue print "Processing %d" % v im = mos[v].clone() im[2121:2230, 590:830] = np.nan # QE for this chip is bad ima = im.getArray() im[:] -= np.percentile(ima, 25) xc, yc = position[v] xc -= im.getX0() yc -= im.getY0() ima[np.hypot(X - xc, Y - yc) < R] = np.nan if force or not -v in mos: im = utils.medianFilterImage(im, medianN) mos[-v] = im else: im = mos[-v] if True: ds9.mtv(im, title=v, frame=frame) else: ds9.erase(frame=frame) ds9.dot("o", xc, yc, size=R, frame=frame, ctype=ds9.GREEN if yc < im.getHeight() else ds9.RED)
def makeAndMeasure(self, measureKron, a, b, theta, dx=0.0, dy=0.0, nsigma=6, kfac=2, nIterForRadius=1, xcen=None, ycen=None, makeImage=True): """Make and measure an elliptical Gaussian""" if xcen is None: xcen = 0.5*self.width + dx if ycen is None: ycen = 0.5*self.height + dy # # Make the object # if a < b: a, b = b, a theta += 90 if self.objImg is None: makeImage = True if makeImage: self.objImg = makeGalaxy(self.width, self.height, self.flux, a, b, theta, dx, dy, afwGeom.Point2I(10, 10), xcen=xcen, ycen=ycen) if display: ds9.mtv(self.objImg, frame=ds9Frame, title="%g %g" % (a, b)) if display: if not makeImage: ds9.erase(frame=ds9Frame) ds9.dot("+", xcen - self.objImg.getX0(), ycen - self.objImg.getY0(), size=1, ctype=ds9.RED, frame=ds9Frame) ds9.pan(xcen - self.objImg.getX0(), ycen - self.objImg.getY0(), frame=ds9Frame) c, s = math.cos(math.radians(theta)), math.sin(math.radians(theta)) # N.b. add 1/12 in quadrature to allow for pixellisation ds9.dot("@:%f,%f,%f" % (nsigma**2*((a**2 + 1/12.0)*c**2 + (b**2 + 1/12.0)*s**2), nsigma**2*(a**2 - b**2)*c*s, nsigma**2*((a**2 + 1/12.0)*s**2 + (b**2 + 1/12.0)*c**2)), xcen - self.objImg.getX0(), ycen - self.objImg.getY0(), size=1, ctype=ds9.RED, frame=ds9Frame, silent=True) # # Do the measuring # FWHM = 5 ksize = 25 # size of desired kernel self.objImg.setPsf(measAlg.DoubleGaussianPsf(ksize, ksize, FWHM/(2*math.sqrt(2*math.log(2))), 1, 0.1)) return measureKron(self.objImg, xcen, ycen, nsigma, kfac, nIterForRadius)
def setUp(self): im = afwImage.ImageF(self.monetFile("small.fits")) self.mi = afwImage.MaskedImageF(im, afwImage.MaskU(im.getDimensions()), afwImage.ImageF(im.getDimensions())); self.ds = afwDetection.FootprintSet(self.mi, afwDetection.Threshold(100)) if display: ds9.mtv(self.mi.getImage()) ds9.erase() for foot in self.ds.getFootprints(): bbox = foot.getBBox() x0, y0 = bbox.getMinX(), bbox.getMinY() x1, y1 = bbox.getMaxX(), bbox.getMaxY() xc = (x0 + x1)/2.0 yc = (y0 + y1)/2.0 if display: ds9.dot("+", xc, yc, ctype=ds9.BLUE) if False: x0 -= 0.5; y0 -= 0.5 x1 += 0.5; y1 += 0.5 ds9.line([(x0, y0), (x1, y0), (x1, y1), (x0, y1), (x0, y0)], ctype=ds9.RED) msConfig = measBase.SingleFrameMeasurementConfig() msConfig.algorithms.names = ["base_GaussianCentroid"] msConfig.slots.centroid = "base_GaussianCentroid" msConfig.slots.shape = None msConfig.slots.apFlux = None msConfig.slots.modelFlux = None msConfig.slots.psfFlux = None msConfig.slots.instFlux = None msConfig.slots.calibFlux = None schema = afwTable.SourceTable.makeMinimalSchema() self.task = measBase.SingleFrameMeasurementTask(schema, config=msConfig) self.ssMeasured = afwTable.SourceCatalog(schema) self.ssMeasured.table.defineCentroid("base_GaussianCentroid") self.ssTruth = afwTable.SourceCatalog(schema) self.ssTruth.table.defineCentroid("base_GaussianCentroid") self.readTruth(self.monetFile("positions.dat-original"))
def setUp(self): im = afwImage.ImageF(self.monetFile("small.fits")) self.mi = afwImage.MaskedImageF(im, afwImage.MaskU(im.getDimensions()), afwImage.ImageF(im.getDimensions())) self.ds = afwDetection.FootprintSet(self.mi, afwDetection.Threshold(100)) if display: ds9.mtv(self.mi.getImage()) ds9.erase() for foot in self.ds.getFootprints(): bbox = foot.getBBox() x0, y0 = bbox.getMinX(), bbox.getMinY() x1, y1 = bbox.getMaxX(), bbox.getMaxY() xc = (x0 + x1) / 2.0 yc = (y0 + y1) / 2.0 if display: ds9.dot("+", xc, yc, ctype=ds9.BLUE) if False: x0 -= 0.5 y0 -= 0.5 x1 += 0.5 y1 += 0.5 ds9.line([(x0, y0), (x1, y0), (x1, y1), (x0, y1), (x0, y0)], ctype=ds9.RED) self.control = algorithms.GaussianCentroidControl() schema = afwTable.SourceTable.makeMinimalSchema() self.centroider = algorithms.MeasureSourcesBuilder().addAlgorithm( self.control).build(schema) self.ssMeasured = afwTable.SourceCatalog(schema) self.ssMeasured.table.defineCentroid(self.control.name) self.ssTruth = afwTable.SourceCatalog(schema) self.readTruth(self.monetFile("positions.dat-original"))
def determinePsf(self, exposure, psfCandidateList, metadata=None, flagKey=None): """!Determine a PCA PSF model for an exposure given a list of PSF candidates \param[in] exposure exposure containing the psf candidates (lsst.afw.image.Exposure) \param[in] psfCandidateList a sequence of PSF candidates (each an lsst.meas.algorithms.PsfCandidate); typically obtained by detecting sources and then running them through a star selector \param[in,out] metadata a home for interesting tidbits of information \param[in] flagKey schema key used to mark sources actually used in PSF determination \return a list of - psf: the measured PSF, an lsst.meas.algorithms.PcaPsf - cellSet: an lsst.afw.math.SpatialCellSet containing the PSF candidates """ import lsstDebug display = lsstDebug.Info(__name__).display displayExposure = lsstDebug.Info( __name__).displayExposure # display the Exposure + spatialCells displayPsfCandidates = lsstDebug.Info( __name__).displayPsfCandidates # show the viable candidates displayIterations = lsstDebug.Info( __name__).displayIterations # display on each PSF iteration displayPsfComponents = lsstDebug.Info( __name__).displayPsfComponents # show the PCA components displayResiduals = lsstDebug.Info( __name__).displayResiduals # show residuals displayPsfMosaic = lsstDebug.Info( __name__).displayPsfMosaic # show mosaic of reconstructed PSF(x,y) # match Kernel amplitudes for spatial plots matchKernelAmplitudes = lsstDebug.Info(__name__).matchKernelAmplitudes # Keep matplotlib alive post mortem keepMatplotlibPlots = lsstDebug.Info(__name__).keepMatplotlibPlots displayPsfSpatialModel = lsstDebug.Info( __name__).displayPsfSpatialModel # Plot spatial model? showBadCandidates = lsstDebug.Info( __name__).showBadCandidates # Include bad candidates # Normalize residuals by object amplitude normalizeResiduals = lsstDebug.Info(__name__).normalizeResiduals pause = lsstDebug.Info( __name__).pause # Prompt user after each iteration? if display > 1: pause = True mi = exposure.getMaskedImage() if len(psfCandidateList) == 0: raise RuntimeError("No PSF candidates supplied.") # construct and populate a spatial cell set bbox = mi.getBBox() psfCellSet = afwMath.SpatialCellSet(bbox, self.config.sizeCellX, self.config.sizeCellY) sizes = [] for i, psfCandidate in enumerate(psfCandidateList): if psfCandidate.getSource().getPsfFluxFlag(): # bad measurement continue try: psfCellSet.insertCandidate(psfCandidate) except Exception as e: self.log.debug("Skipping PSF candidate %d of %d: %s", i, len(psfCandidateList), e) continue source = psfCandidate.getSource() quad = afwEll.Quadrupole(source.getIxx(), source.getIyy(), source.getIxy()) axes = afwEll.Axes(quad) sizes.append(axes.getA()) if len(sizes) == 0: raise RuntimeError("No usable PSF candidates supplied") nEigenComponents = self.config.nEigenComponents # initial version if self.config.kernelSize >= 15: self.log.warn( "WARNING: NOT scaling kernelSize by stellar quadrupole moment " + "because config.kernelSize=%s >= 15; using config.kernelSize as as the width, instead", self.config.kernelSize) actualKernelSize = int(self.config.kernelSize) else: medSize = numpy.median(sizes) actualKernelSize = 2 * int(self.config.kernelSize * math.sqrt(medSize) + 0.5) + 1 if actualKernelSize < self.config.kernelSizeMin: actualKernelSize = self.config.kernelSizeMin if actualKernelSize > self.config.kernelSizeMax: actualKernelSize = self.config.kernelSizeMax if display: print("Median size=%s" % (medSize, )) self.log.trace("Kernel size=%s", actualKernelSize) # Set size of image returned around candidate psfCandidateList[0].setHeight(actualKernelSize) psfCandidateList[0].setWidth(actualKernelSize) if self.config.doRejectBlends: # Remove blended candidates completely blendedCandidates = [ ] # Candidates to remove; can't do it while iterating for cell, cand in candidatesIter(psfCellSet, False): if len(cand.getSource().getFootprint().getPeaks()) > 1: blendedCandidates.append((cell, cand)) continue if display: print("Removing %d blended Psf candidates" % len(blendedCandidates)) for cell, cand in blendedCandidates: cell.removeCandidate(cand) if sum(1 for cand in candidatesIter(psfCellSet, False)) == 0: raise RuntimeError("All PSF candidates removed as blends") if display: frame = 0 if displayExposure: ds9.mtv(exposure, frame=frame, title="psf determination") maUtils.showPsfSpatialCells(exposure, psfCellSet, self.config.nStarPerCell, symb="o", ctype=ds9.CYAN, ctypeUnused=ds9.YELLOW, size=4, frame=frame) # # Do a PCA decomposition of those PSF candidates # reply = "y" # used in interactive mode for iterNum in range(self.config.nIterForPsf): if display and displayPsfCandidates: # Show a mosaic of usable PSF candidates # import lsst.afw.display.utils as displayUtils stamps = [] for cell in psfCellSet.getCellList(): for cand in cell.begin(not showBadCandidates ): # maybe include bad candidates try: im = cand.getMaskedImage() chi2 = cand.getChi2() if chi2 > 1e100: chi2 = numpy.nan stamps.append( (im, "%d%s" % (maUtils.splitId(cand.getSource().getId(), True)["objId"], chi2), cand.getStatus())) except Exception as e: continue if len(stamps) == 0: print( "WARNING: No PSF candidates to show; try setting showBadCandidates=True" ) else: mos = displayUtils.Mosaic() for im, label, status in stamps: im = type(im)(im, True) try: im /= afwMath.makeStatistics( im, afwMath.MAX).getValue() except NotImplementedError: pass mos.append( im, label, ds9.GREEN if status == afwMath.SpatialCellCandidate.GOOD else ds9.YELLOW if status == afwMath.SpatialCellCandidate.UNKNOWN else ds9.RED) mos.makeMosaic(frame=8, title="Psf Candidates") # Re-fit until we don't have any candidates with naughty chi^2 values influencing the fit cleanChi2 = False # Any naughty (negative/NAN) chi^2 values? while not cleanChi2: cleanChi2 = True # # First, estimate the PSF # psf, eigenValues, nEigenComponents, fitChi2 = \ self._fitPsf(exposure, psfCellSet, actualKernelSize, nEigenComponents) # # In clipping, allow all candidates to be innocent until proven guilty on this iteration. # Throw out any prima facie guilty candidates (naughty chi^2 values) # for cell in psfCellSet.getCellList(): awfulCandidates = [] for cand in cell.begin(False): # include bad candidates cand.setStatus(afwMath.SpatialCellCandidate.UNKNOWN ) # until proven guilty rchi2 = cand.getChi2() if not numpy.isfinite(rchi2) or rchi2 <= 0: # Guilty prima facie awfulCandidates.append(cand) cleanChi2 = False self.log.debug("chi^2=%s; id=%s", cand.getChi2(), cand.getSource().getId()) for cand in awfulCandidates: if display: print("Removing bad candidate: id=%d, chi^2=%f" % \ (cand.getSource().getId(), cand.getChi2())) cell.removeCandidate(cand) # # Clip out bad fits based on reduced chi^2 # badCandidates = list() for cell in psfCellSet.getCellList(): for cand in cell.begin(False): # include bad candidates rchi2 = cand.getChi2( ) # reduced chi^2 when fitting PSF to candidate assert rchi2 > 0 if rchi2 > self.config.reducedChi2ForPsfCandidates: badCandidates.append(cand) badCandidates.sort(key=lambda x: x.getChi2(), reverse=True) numBad = numCandidatesToReject(len(badCandidates), iterNum, self.config.nIterForPsf) for i, c in zip(range(numBad), badCandidates): if display: chi2 = c.getChi2() if chi2 > 1e100: chi2 = numpy.nan print("Chi^2 clipping %-4d %.2g" % (c.getSource().getId(), chi2)) c.setStatus(afwMath.SpatialCellCandidate.BAD) # # Clip out bad fits based on spatial fitting. # # This appears to be better at getting rid of sources that have a single dominant kernel component # (other than the zeroth; e.g., a nearby contaminant) because the surrounding sources (which help # set the spatial model) don't contain that kernel component, and so the spatial modeling # downweights the component. # residuals = list() candidates = list() kernel = psf.getKernel() noSpatialKernel = psf.getKernel() for cell in psfCellSet.getCellList(): for cand in cell.begin(False): candCenter = afwGeom.PointD(cand.getXCenter(), cand.getYCenter()) try: im = cand.getMaskedImage(kernel.getWidth(), kernel.getHeight()) except Exception as e: continue fit = fitKernelParamsToImage(noSpatialKernel, im, candCenter) params = fit[0] kernels = fit[1] amp = 0.0 for p, k in zip(params, kernels): amp += p * k.getSum() predict = [ kernel.getSpatialFunction(k)(candCenter.getX(), candCenter.getY()) for k in range(kernel.getNKernelParameters()) ] #print cand.getSource().getId(), [a / amp for a in params], predict residuals.append( [a / amp - p for a, p in zip(params, predict)]) candidates.append(cand) residuals = numpy.array(residuals) for k in range(kernel.getNKernelParameters()): if False: # Straight standard deviation mean = residuals[:, k].mean() rms = residuals[:, k].std() elif False: # Using interquartile range sr = numpy.sort(residuals[:, k]) mean = sr[int(0.5*len(sr))] if len(sr) % 2 else \ 0.5 * (sr[int(0.5*len(sr))] + sr[int(0.5*len(sr))+1]) rms = 0.74 * (sr[int(0.75 * len(sr))] - sr[int(0.25 * len(sr))]) else: stats = afwMath.makeStatistics( residuals[:, k], afwMath.MEANCLIP | afwMath.STDEVCLIP) mean = stats.getValue(afwMath.MEANCLIP) rms = stats.getValue(afwMath.STDEVCLIP) rms = max( 1.0e-4, rms) # Don't trust RMS below this due to numerical issues if display: print("Mean for component %d is %f" % (k, mean)) print("RMS for component %d is %f" % (k, rms)) badCandidates = list() for i, cand in enumerate(candidates): if numpy.fabs(residuals[i, k] - mean) > self.config.spatialReject * rms: badCandidates.append(i) badCandidates.sort( key=lambda x: numpy.fabs(residuals[x, k] - mean), reverse=True) numBad = numCandidatesToReject(len(badCandidates), iterNum, self.config.nIterForPsf) for i, c in zip(range(min(len(badCandidates), numBad)), badCandidates): cand = candidates[c] if display: print("Spatial clipping %d (%f,%f) based on %d: %f vs %f" % \ (cand.getSource().getId(), cand.getXCenter(), cand.getYCenter(), k, residuals[badCandidates[i], k], self.config.spatialReject * rms)) cand.setStatus(afwMath.SpatialCellCandidate.BAD) # # Display results # if display and displayIterations: if displayExposure: if iterNum > 0: ds9.erase(frame=frame) maUtils.showPsfSpatialCells(exposure, psfCellSet, self.config.nStarPerCell, showChi2=True, symb="o", size=8, frame=frame, ctype=ds9.YELLOW, ctypeBad=ds9.RED, ctypeUnused=ds9.MAGENTA) if self.config.nStarPerCellSpatialFit != self.config.nStarPerCell: maUtils.showPsfSpatialCells( exposure, psfCellSet, self.config.nStarPerCellSpatialFit, symb="o", size=10, frame=frame, ctype=ds9.YELLOW, ctypeBad=ds9.RED) if displayResiduals: while True: try: maUtils.showPsfCandidates( exposure, psfCellSet, psf=psf, frame=4, normalize=normalizeResiduals, showBadCandidates=showBadCandidates) maUtils.showPsfCandidates( exposure, psfCellSet, psf=psf, frame=5, normalize=normalizeResiduals, showBadCandidates=showBadCandidates, variance=True) except: if not showBadCandidates: showBadCandidates = True continue break if displayPsfComponents: maUtils.showPsf(psf, eigenValues, frame=6) if displayPsfMosaic: maUtils.showPsfMosaic(exposure, psf, frame=7, showFwhm=True) ds9.scale('linear', 0, 1, frame=7) if displayPsfSpatialModel: maUtils.plotPsfSpatialModel( exposure, psf, psfCellSet, showBadCandidates=True, matchKernelAmplitudes=matchKernelAmplitudes, keepPlots=keepMatplotlibPlots) if pause: while True: try: reply = input( "Next iteration? [ynchpqQs] ").strip() except EOFError: reply = "n" reply = reply.split() if reply: reply, args = reply[0], reply[1:] else: reply = "" if reply in ("", "c", "h", "n", "p", "q", "Q", "s", "y"): if reply == "c": pause = False elif reply == "h": print("c[ontinue without prompting] h[elp] n[o] p[db] q[uit displaying] " \ "s[ave fileName] y[es]") continue elif reply == "p": import pdb pdb.set_trace() elif reply == "q": display = False elif reply == "Q": sys.exit(1) elif reply == "s": fileName = args.pop(0) if not fileName: print("Please provide a filename") continue print("Saving to %s" % fileName) maUtils.saveSpatialCellSet(psfCellSet, fileName=fileName) continue break else: print("Unrecognised response: %s" % reply, file=sys.stderr) if reply == "n": break # One last time, to take advantage of the last iteration psf, eigenValues, nEigenComponents, fitChi2 = \ self._fitPsf(exposure, psfCellSet, actualKernelSize, nEigenComponents) # # Display code for debugging # if display and reply != "n": if displayExposure: maUtils.showPsfSpatialCells(exposure, psfCellSet, self.config.nStarPerCell, showChi2=True, symb="o", ctype=ds9.YELLOW, ctypeBad=ds9.RED, size=8, frame=frame) if self.config.nStarPerCellSpatialFit != self.config.nStarPerCell: maUtils.showPsfSpatialCells( exposure, psfCellSet, self.config.nStarPerCellSpatialFit, symb="o", ctype=ds9.YELLOW, ctypeBad=ds9.RED, size=10, frame=frame) if displayResiduals: maUtils.showPsfCandidates( exposure, psfCellSet, psf=psf, frame=4, normalize=normalizeResiduals, showBadCandidates=showBadCandidates) if displayPsfComponents: maUtils.showPsf(psf, eigenValues, frame=6) if displayPsfMosaic: maUtils.showPsfMosaic(exposure, psf, frame=7, showFwhm=True) ds9.scale("linear", 0, 1, frame=7) if displayPsfSpatialModel: maUtils.plotPsfSpatialModel( exposure, psf, psfCellSet, showBadCandidates=True, matchKernelAmplitudes=matchKernelAmplitudes, keepPlots=keepMatplotlibPlots) # # Generate some QA information # # Count PSF stars # numGoodStars = 0 numAvailStars = 0 avgX = 0.0 avgY = 0.0 for cell in psfCellSet.getCellList(): for cand in cell.begin(False): # don't ignore BAD stars numAvailStars += 1 for cand in cell.begin(True): # do ignore BAD stars src = cand.getSource() if flagKey is not None: src.set(flagKey, True) avgX += src.getX() avgY += src.getY() numGoodStars += 1 avgX /= numGoodStars avgY /= numGoodStars if metadata is not None: metadata.set("spatialFitChi2", fitChi2) metadata.set("numGoodStars", numGoodStars) metadata.set("numAvailStars", numAvailStars) metadata.set("avgX", avgX) metadata.set("avgY", avgY) psf = PcaPsf(psf.getKernel(), afwGeom.Point2D(avgX, avgY)) return psf, psfCellSet
def prepareFrames(mos, frame0=1, R=100, medianN=23, onlyVisits=[], nJob=1, force=False): """Prepare frames to have their radial profiles measured. Subtract the first quartile Set a radius R circle about the centre of Arcturus to NaN Set a chip with a bad QE value to NaN Median filter with a medianN x medianN filter The results are set as mos[-visit] If onlyVisits is specified, only process those chips [n.b. frame0 is still obeyed]; otherwise process every visit in the positions dict """ visits = sorted(position.keys()) visits0 = visits[:] # needed to keep frame numbers aligned if not force: visits = [v for v in visits if -v not in mos] # don't process ones we've already seen if onlyVisits: visits = [v for v in visits if v in onlyVisits] width, height = mos[visits[0]].getDimensions() X, Y = np.meshgrid(np.arange(width), np.arange(height)) for v in visits: im = mos[v].clone() mos[-v] = im im[2121:2230, 590:830] = np.nan # QE for this chip is bad ima = im.getArray() im[:] -= np.percentile(ima, 25) xc, yc = position[v] xc -= im.getX0() yc -= im.getY0() ima[np.hypot(X - xc, Y - yc) < R] = np.nan # # multiprocessing # worker = MedianFilterImageWorker(mos, medianN) if nJob > 1: pool = multiprocessing.Pool(max([len(visits), nJob])) # We use map_async(...).get(9999) instead of map(...) to workaround a python bug # in handling ^C in subprocesses (http://bugs.python.org/issue8296) for v, im in pool.map_async(worker, visits).get(9999): mos[-v] = im pool.close() pool.join() else: for v in visits: mos[-v] = worker(v) # # Display # frame = frame0 - 1 for v in visits0: frame += 1 if v not in visits: continue im = mos[-v] if True: ds9.mtv(im, title=v, frame=frame) else: ds9.erase(frame=frame) xc, yc = position[v] xc -= im.getX0() yc -= im.getY0() ds9.dot("o", xc, yc, size=R, frame=frame, ctype=ds9.GREEN if yc < im.getHeight() else ds9.RED)
for i, c in zip(range(min(len(badCandidates), numBad)), badCandidates): cand = candidates[c] if display: print "Spatial clipping %d (%f,%f) based on %d: %f vs %f" % \ (cand.getSource().getId(), cand.getXCenter(), cand.getYCenter(), k, residuals[badCandidates[i],k], self.config.spatialReject * rms) cand.setStatus(afwMath.SpatialCellCandidate.BAD) # # Display results # if display and displayIterations: if displayExposure: if iter > 0: ds9.erase(frame=frame) maUtils.showPsfSpatialCells(exposure, psfCellSet, self.config.nStarPerCell, showChi2=True, symb="o", size=8, frame=frame, ctype=ds9.YELLOW, ctypeBad=ds9.RED, ctypeUnused=ds9.MAGENTA) if self.config.nStarPerCellSpatialFit != self.config.nStarPerCell: maUtils.showPsfSpatialCells( exposure, psfCellSet, self.config.nStarPerCellSpatialFit,
def imagePca(mosaics, visits=None, nComponent=3, log=False, rng=30, showEigen=True, showResiduals=False, showOriginal=True, showRecon=False, normalizeEimages=True, scale=False, frame0=0, verbose=False): if showOriginal and showRecon: raise RuntimeError( "You may not specify both showOriginal and showRecon") try: rng[0] except TypeError: rng = [rng, rng] if not visits: visits = sorted(mosaics.keys()) mosaics = mosaics.copy() for v in visits: mosaics[v] = mosaics[v].clone() pca = afwImage.ImagePcaF() mask = None for v in visits: im = mosaics[v] if not mask: mask = im.Factory(im.getDimensions()) maska = mask.getArray() X, Y = np.meshgrid(np.arange(mask.getWidth()), np.arange(mask.getHeight())) maska[np.where(np.hypot(X - 571, Y - 552) > 531)] = np.nan del maska mask[168:184, 701:838] = np.nan mask[667:733, 420:556] = np.nan mask[653:677, 548:570] = np.nan mask[1031:1047, 274:414] = np.nan if False: mask[866:931, 697:828] = np.nan mask[267:334, 145:276] = np.nan im += mask pca.addImage(im, afwMath.makeStatistics(im, afwMath.SUM).getValue()) pca.analyze() eValues = np.array(pca.getEigenValues()) eValues /= eValues[0] eImages = pca.getEigenImages() # # Fiddle eigen images (we don't care about orthogonality) # if False: f10 = 0.1 f20 = -0.3 f30 = 0.55 eImages[1].getArray()[:] += f10 * eImages[0].getArray() eImages[2].getArray()[:] += f20 * eImages[0].getArray() eImages[3].getArray()[:] += f30 * eImages[0].getArray() if nComponent: eImages = eImages[:nComponent] else: nComponent = len(eImages) # # Normalize # good = np.where(np.isfinite(eImages[0].getArray())) if normalizeEimages: for eim in eImages: eima = eim.getArray() eima /= 1e-3 * np.sqrt(np.sum(eima[good]**2)) # # Plot/display eigen values/images # frame = frame0 if showEigen: for i in range(len(eImages)): ds9.mtv(eImages[i], frame=frame, title="%d %.2g" % (i, eValues[i])) ds9.ds9Cmd("scale linear; scale mode zscale") frame += 1 pyplot.clf() if log: eValues = np.log10(eValues) pyplot.plot(eValues) pyplot.plot(eValues, marker='o') pyplot.xlim(-0.5, len(eValues)) pyplot.ylim(max(-5, pyplot.ylim()[0]), 0.05 if log else 1.05) pyplot.xlabel("n") pyplot.ylabel(r"$lg(\lambda)$" if log else r"$\lambda$") pyplot.show() if showOriginal or showRecon or showResiduals: # # Expand input images in the (modified) eImages # A = np.empty(nComponent * nComponent).reshape((nComponent, nComponent)) b = np.empty(nComponent) for v in visits: im = mosaics[v] if scale: im /= afwMath.makeStatistics(im, afwMath.MEANCLIP).getValue() for i in range(nComponent): b[i] = np.dot(eImages[i].getArray()[good], im.getArray()[good]) for j in range(i, nComponent): A[i, j] = np.dot(eImages[i].getArray()[good], eImages[j].getArray()[good]) A[j, i] = A[i, j] x = np.linalg.solve(A, b) #print v, A, b, x print "%d [%s] %s" % (v, ", ".join(["%9.2e" % _ for _ in x / x[0] ]), labels.get(v, "")) recon = eImages[0].clone() recon *= x[0] recona = recon.getArray() for i in range(1, nComponent): recona += x[i] * eImages[i].getArray() mtv = True if showOriginal or showRecon: if mtv: ds9.mtv(im if showOriginal else recon, frame=frame, title=v) else: ds9.erase(frame=frame) s0 = afwMath.makeStatistics(recon, afwMath.MEDIAN).getValue() s0 -= 0.5 * rng[0] ds9.ds9Cmd("scale linear; scale limits %g %g" % (s0, s0 + rng[0]), frame=frame) ds9.dot("%s %d" % ("orig" if showOriginal else "resid", v), int(0.5 * im.getWidth()), int(0.15 * im.getHeight()), frame=frame, ctype=ds9.RED) if labels.has_key(v): ds9.dot(labels[v], int(0.5 * im.getWidth()), int(0.85 * im.getHeight()), frame=frame, ctype=ds9.RED) frame += 1 if showResiduals: recon -= im if mtv: ds9.mtv(recon, frame=frame) else: ds9.erase(frame=frame) s0 = 0 s0 -= 0.5 * rng[1] ds9.ds9Cmd("scale linear; scale limits %g %g" % (s0, s0 + rng[1]), frame=frame) frame += 1 return eImages
def diffs(mosaics, visits, refVisit=None, scale=True, raw=None, rng=20, IRatioMax=1.0, frame0=0, verbose=False): """Display a series of differences and/or scaled differences (scale = True, False, (True, False), ...) """ visits = list(visits) # in case it's an iterator, and to get a copy if refVisit is None: refVisit = visits[0] ref = mosaics[refVisit] nameRef = str(refVisit) refMedian = float(np.median(ref.getArray())) visits = [v for v in visits if v != refVisit] try: scale[0] except TypeError: scale = (scale, ) frame = frame0 goodVisits = [refVisit] for v2 in visits: for s in scale: nameA, nameB = str(v2), nameRef diff = mosaics[v2].clone() IRatio = np.median(mosaics[v2].getArray()) / refMedian if IRatioMax and IRatio > IRatioMax and IRatio < 1 / IRatioMax: if verbose: print "Skipping %d [ratio = %.2f]" % (v2, IRatio) break if s == scale[0]: goodVisits.append(v2) if s: diff /= IRatio nameA = "%s/%.2f" % (nameA, IRatio) diffa = diff.getArray() good = np.where(np.isfinite(diffa)) v2Median = np.mean(diffa[good]) if raw: title = nameA else: diff -= ref if IRatio < 1: diff *= -1 nameA, nameB = nameB, nameA if verbose: print "%s %5.1f%% %-s" % (v2, 100 * np.mean(diffa[good]) / v2Median, labels.get(v2, "")) title = "%s - %s" % (nameA, nameB) if not s: title += " [rat=%.2f]" % (IRatio) if True: ds9.mtv(diff, title=title, frame=frame) else: ds9.erase(frame=frame) ds9.dot(title, int(0.5 * diff.getWidth()), int(0.05 * diff.getHeight()), frame=frame, ctype=ds9.RED) if labels.has_key(v2): ds9.dot(labels[v2], int(0.5 * diff.getWidth()), int(0.95 * diff.getHeight()), frame=frame, ctype=ds9.RED) s0 = 0 if (s and not raw) else afwMath.makeStatistics( diff, afwMath.MEDIAN).getValue() s0 -= 0.5 * rng ds9.ds9Cmd("scale linear; scale limits %g %g" % (s0, s0 + rng), frame=frame) frame += 1 return list(sorted(goodVisits))
for i, c in zip(range(min(len(badCandidates), numBad)), badCandidates): cand = candidates[c] if display: print "Spatial clipping %d (%f,%f) based on %d: %f vs %f" % \ (cand.getSource().getId(), cand.getXCenter(), cand.getYCenter(), k, residuals[badCandidates[i],k], self.config.spatialReject * rms) cand.setStatus(afwMath.SpatialCellCandidate.BAD) # # Display results # if display and displayIterations: if displayExposure: if iter > 0: ds9.erase(frame=frame) maUtils.showPsfSpatialCells(exposure, psfCellSet, self.config.nStarPerCell, showChi2=True, symb="o", size=8, frame=frame, ctype=ds9.YELLOW, ctypeBad=ds9.RED, ctypeUnused=ds9.MAGENTA) if self.config.nStarPerCellSpatialFit != self.config.nStarPerCell: maUtils.showPsfSpatialCells(exposure, psfCellSet, self.config.nStarPerCellSpatialFit, symb="o", size=10, frame=frame, ctype=ds9.YELLOW, ctypeBad=ds9.RED) if displayResiduals: while True: try: maUtils.showPsfCandidates(exposure, psfCellSet, psf=psf, frame=4, normalize=normalizeResiduals, showBadCandidates=showBadCandidates) maUtils.showPsfCandidates(exposure, psfCellSet, psf=psf, frame=5, normalize=normalizeResiduals,
def imagePca(mosaics, visits=None, nComponent=3, log=False, rng=30, showEigen=True, showResiduals=False, showOriginal=True, showRecon=False, normalizeEimages=True, scale=False, frame0=0, verbose=False): if showOriginal and showRecon: raise RuntimeError("You may not specify both showOriginal and showRecon") try: rng[0] except TypeError: rng = [rng, rng] if not visits: visits = sorted(mosaics.keys()) mosaics = mosaics.copy() for v in visits: mosaics[v] = mosaics[v].clone() pca = afwImage.ImagePcaF() mask = None for v in visits: im = mosaics[v] if not mask: mask = im.Factory(im.getDimensions()) maska = mask.getArray() X, Y = np.meshgrid(np.arange(mask.getWidth()), np.arange(mask.getHeight())) maska[np.where(np.hypot(X - 571, Y - 552) > 531)] = np.nan del maska mask[ 168: 184, 701:838] = np.nan mask[ 667: 733, 420:556] = np.nan mask[ 653: 677, 548:570] = np.nan mask[1031:1047, 274:414] = np.nan if False: mask[866:931, 697:828] = np.nan mask[267:334, 145:276] = np.nan im += mask pca.addImage(im, afwMath.makeStatistics(im, afwMath.SUM).getValue()) pca.analyze() eValues = np.array(pca.getEigenValues()) eValues /= eValues[0] eImages = pca.getEigenImages() # # Fiddle eigen images (we don't care about orthogonality) # if False: f10 = 0.1 f20 = -0.3 f30 = 0.55 eImages[1].getArray()[:] += f10*eImages[0].getArray() eImages[2].getArray()[:] += f20*eImages[0].getArray() eImages[3].getArray()[:] += f30*eImages[0].getArray() if nComponent: eImages = eImages[:nComponent] else: nComponent = len(eImages) # # Normalize # good = np.where(np.isfinite(eImages[0].getArray())) if normalizeEimages: for eim in eImages: eima = eim.getArray() eima /= 1e-3*np.sqrt(np.sum(eima[good]**2)) # # Plot/display eigen values/images # frame = frame0 if showEigen: for i in range(len(eImages)): ds9.mtv(eImages[i], frame=frame, title="%d %.2g" % (i, eValues[i])) ds9.ds9Cmd("scale linear; scale mode zscale") frame += 1 pyplot.clf() if log: eValues = np.log10(eValues) pyplot.plot(eValues) pyplot.plot(eValues, marker='o') pyplot.xlim(-0.5, len(eValues)) pyplot.ylim(max(-5, pyplot.ylim()[0]), 0.05 if log else 1.05) pyplot.xlabel("n") pyplot.ylabel(r"$lg(\lambda)$" if log else r"$\lambda$") pyplot.show() if showOriginal or showRecon or showResiduals: # # Expand input images in the (modified) eImages # A = np.empty(nComponent*nComponent).reshape((nComponent, nComponent)) b = np.empty(nComponent) for v in visits: im = mosaics[v] if scale: im /= afwMath.makeStatistics(im, afwMath.MEANCLIP).getValue() for i in range(nComponent): b[i] = np.dot(eImages[i].getArray()[good], im.getArray()[good]) for j in range(i, nComponent): A[i, j] = np.dot(eImages[i].getArray()[good], eImages[j].getArray()[good]) A[j, i] = A[i, j] x = np.linalg.solve(A, b) #print v, A, b, x print "%d [%s] %s" % (v, ", ".join(["%9.2e" % _ for _ in x/x[0]]), labels.get(v, "")) recon = eImages[0].clone(); recon *= x[0] recona = recon.getArray() for i in range(1, nComponent): recona += x[i]*eImages[i].getArray() mtv = True if showOriginal or showRecon: if mtv: ds9.mtv(im if showOriginal else recon, frame=frame, title=v) else: ds9.erase(frame=frame) s0 = afwMath.makeStatistics(recon, afwMath.MEDIAN).getValue() s0 -= 0.5*rng[0] ds9.ds9Cmd("scale linear; scale limits %g %g" % (s0, s0 + rng[0]), frame=frame) ds9.dot("%s %d" % ("orig" if showOriginal else "resid", v), int(0.5*im.getWidth()), int(0.15*im.getHeight()), frame=frame, ctype=ds9.RED) if labels.has_key(v): ds9.dot(labels[v], int(0.5*im.getWidth()), int(0.85*im.getHeight()), frame=frame, ctype=ds9.RED) frame += 1 if showResiduals: recon -= im if mtv: ds9.mtv(recon, frame=frame) else: ds9.erase(frame=frame) s0 = 0 s0 -= 0.5*rng[1] ds9.ds9Cmd("scale linear; scale limits %g %g" % (s0, s0 + rng[1]), frame=frame) frame += 1 return eImages
def diffs(mosaics, visits, refVisit=None, scale=True, raw=None, rng=20, IRatioMax=1.0, frame0=0, verbose=False): """Display a series of differences and/or scaled differences (scale = True, False, (True, False), ...) """ visits = list(visits) # in case it's an iterator, and to get a copy if refVisit is None: refVisit = visits[0] ref = mosaics[refVisit] nameRef = str(refVisit) refMedian = float(np.median(ref.getArray())) visits = [v for v in visits if v != refVisit] try: scale[0] except TypeError: scale = (scale,) frame = frame0 goodVisits = [refVisit] for v2 in visits: for s in scale: nameA, nameB = str(v2), nameRef diff = mosaics[v2].clone() IRatio = np.median(mosaics[v2].getArray())/refMedian if IRatioMax and IRatio > IRatioMax and IRatio < 1/IRatioMax: if verbose: print "Skipping %d [ratio = %.2f]" % (v2, IRatio) break if s == scale[0]: goodVisits.append(v2) if s: diff /= IRatio nameA = "%s/%.2f" % (nameA, IRatio) diffa = diff.getArray() good = np.where(np.isfinite(diffa)) v2Median = np.mean(diffa[good]) if raw: title = nameA else: diff -= ref if IRatio < 1: diff *= -1 nameA, nameB = nameB, nameA if verbose: print "%s %5.1f%% %-s" % (v2, 100*np.mean(diffa[good])/v2Median, labels.get(v2, "")) title = "%s - %s" % (nameA, nameB) if not s: title += " [rat=%.2f]" % (IRatio) if True: ds9.mtv(diff, title=title, frame=frame) else: ds9.erase(frame=frame) ds9.dot(title, int(0.5*diff.getWidth()), int(0.05*diff.getHeight()), frame=frame, ctype=ds9.RED) if labels.has_key(v2): ds9.dot(labels[v2], int(0.5*diff.getWidth()), int(0.95*diff.getHeight()), frame=frame, ctype=ds9.RED) s0 = 0 if (s and not raw) else afwMath.makeStatistics(diff, afwMath.MEDIAN).getValue() s0 -= 0.5*rng ds9.ds9Cmd("scale linear; scale limits %g %g" % (s0, s0 + rng), frame=frame) frame += 1 return list(sorted(goodVisits))
def prepareFrames(mos, frame0=1, R=100, medianN=23, onlyVisits=[], nJob=1, force=False): """Prepare frames to have their radial profiles measured. Subtract the first quartile Set a radius R circle about the centre of Arcturus to NaN Set a chip with a bad QE value to NaN Median filter with a medianN x medianN filter The results are set as mos[-visit] If onlyVisits is specified, only process those chips [n.b. frame0 is still obeyed]; otherwise process every visit in the positions dict """ visits = sorted(position.keys()) visits0 = visits[:] # needed to keep frame numbers aligned if not force: visits = [v for v in visits if not -v in mos] # don't process ones we've already seen if onlyVisits: visits = [v for v in visits if v in onlyVisits] width, height = mos[visits[0]].getDimensions() X, Y = np.meshgrid(np.arange(width), np.arange(height)) for v in visits: im = mos[v].clone() mos[-v] = im im[2121:2230, 590:830] = np.nan # QE for this chip is bad ima = im.getArray() im[:] -= np.percentile(ima, 25) xc, yc = position[v] xc -= im.getX0() yc -= im.getY0() ima[np.hypot(X - xc, Y - yc) < R] = np.nan # # multiprocessing # worker = MedianFilterImageWorker(mos, medianN) if nJob > 1: pool = multiprocessing.Pool(max([len(visits), nJob])) # We use map_async(...).get(9999) instead of map(...) to workaround a python bug # in handling ^C in subprocesses (http://bugs.python.org/issue8296) for v, im in pool.map_async(worker, visits).get(9999): mos[-v] = im pool.close() pool.join() else: for v in visits: mos[-v] = worker(v) # # Display # frame = frame0 - 1 for v in visits0: frame += 1 if v not in visits: continue im = mos[-v] if True: ds9.mtv(im, title=v, frame=frame) else: ds9.erase(frame=frame) xc, yc = position[v] xc -= im.getX0() yc -= im.getY0() ds9.dot("o", xc, yc, size=R, frame=frame, ctype=ds9.GREEN if yc < im.getHeight() else ds9.RED)