Hide keyboard shortcuts

Hot-keys 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-2015 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/>. 

21# 

22import numpy as np 

23 

24from lsstDebug import getDebugFrame 

25import lsst.afw.table as afwTable 

26import lsst.pex.config as pexConfig 

27import lsst.pipe.base as pipeBase 

28import lsst.daf.base as dafBase 

29import lsst.pipe.base.connectionTypes as cT 

30from lsst.afw.math import BackgroundList 

31from lsst.afw.table import SourceTable, SourceCatalog, IdFactory 

32from lsst.meas.algorithms import SubtractBackgroundTask, SourceDetectionTask, MeasureApCorrTask 

33from lsst.meas.algorithms.installGaussianPsf import InstallGaussianPsfTask 

34from lsst.meas.astrom import RefMatchTask, displayAstrometry 

35from lsst.meas.algorithms import LoadIndexedReferenceObjectsTask 

36from lsst.obs.base import ExposureIdInfo 

37from lsst.meas.base import SingleFrameMeasurementTask, ApplyApCorrTask, CatalogCalculationTask 

38from lsst.meas.deblender import SourceDeblendTask 

39from .measurePsf import MeasurePsfTask 

40from .repair import RepairTask 

41 

42__all__ = ["CharacterizeImageConfig", "CharacterizeImageTask"] 

43 

44 

45class CharacterizeImageConnections(pipeBase.PipelineTaskConnections, 

46 dimensions=("instrument", "visit", "detector")): 

47 exposure = cT.Input( 

48 doc="Input exposure data", 

49 name="postISRCCD", 

50 storageClass="ExposureF", 

51 dimensions=["instrument", "visit", "detector"], 

52 ) 

53 characterized = cT.Output( 

54 doc="Output characterized data.", 

55 name="icExp", 

56 storageClass="ExposureF", 

57 dimensions=["instrument", "visit", "detector"], 

58 ) 

59 sourceCat = cT.Output( 

60 doc="Output source catalog.", 

61 name="icSrc", 

62 storageClass="SourceCatalog", 

63 dimensions=["instrument", "visit", "detector"], 

64 ) 

65 backgroundModel = cT.Output( 

66 doc="Output background model.", 

67 name="icExpBackground", 

68 storageClass="Background", 

69 dimensions=["instrument", "visit", "detector"], 

70 ) 

71 outputSchema = cT.InitOutput( 

72 doc="Schema of the catalog produced by CharacterizeImage", 

73 name="icSrc_schema", 

74 storageClass="SourceCatalog", 

75 ) 

76 

77 

78class CharacterizeImageConfig(pipeBase.PipelineTaskConfig, 

79 pipelineConnections=CharacterizeImageConnections): 

80 

81 """!Config for CharacterizeImageTask""" 

82 doMeasurePsf = pexConfig.Field( 

83 dtype=bool, 

84 default=True, 

85 doc="Measure PSF? If False then for all subsequent operations use either existing PSF " 

86 "model when present, or install simple PSF model when not (see installSimplePsf " 

87 "config options)" 

88 ) 

89 doWrite = pexConfig.Field( 

90 dtype=bool, 

91 default=True, 

92 doc="Persist results?", 

93 ) 

94 doWriteExposure = pexConfig.Field( 

95 dtype=bool, 

96 default=True, 

97 doc="Write icExp and icExpBackground in addition to icSrc? Ignored if doWrite False.", 

98 ) 

99 psfIterations = pexConfig.RangeField( 

100 dtype=int, 

101 default=2, 

102 min=1, 

103 doc="Number of iterations of detect sources, measure sources, " 

104 "estimate PSF. If useSimplePsf is True then 2 should be plenty; " 

105 "otherwise more may be wanted.", 

106 ) 

107 background = pexConfig.ConfigurableField( 

108 target=SubtractBackgroundTask, 

109 doc="Configuration for initial background estimation", 

110 ) 

111 detection = pexConfig.ConfigurableField( 

112 target=SourceDetectionTask, 

113 doc="Detect sources" 

114 ) 

115 doDeblend = pexConfig.Field( 

116 dtype=bool, 

117 default=True, 

118 doc="Run deblender input exposure" 

119 ) 

120 deblend = pexConfig.ConfigurableField( 

121 target=SourceDeblendTask, 

122 doc="Split blended source into their components" 

123 ) 

124 measurement = pexConfig.ConfigurableField( 

125 target=SingleFrameMeasurementTask, 

126 doc="Measure sources" 

127 ) 

128 doApCorr = pexConfig.Field( 

129 dtype=bool, 

130 default=True, 

131 doc="Run subtasks to measure and apply aperture corrections" 

132 ) 

133 measureApCorr = pexConfig.ConfigurableField( 

134 target=MeasureApCorrTask, 

135 doc="Subtask to measure aperture corrections" 

136 ) 

137 applyApCorr = pexConfig.ConfigurableField( 

138 target=ApplyApCorrTask, 

139 doc="Subtask to apply aperture corrections" 

140 ) 

141 # If doApCorr is False, and the exposure does not have apcorrections already applied, the 

142 # active plugins in catalogCalculation almost certainly should not contain the characterization plugin 

143 catalogCalculation = pexConfig.ConfigurableField( 

144 target=CatalogCalculationTask, 

145 doc="Subtask to run catalogCalculation plugins on catalog" 

146 ) 

147 useSimplePsf = pexConfig.Field( 

148 dtype=bool, 

149 default=True, 

150 doc="Replace the existing PSF model with a simplified version that has the same sigma " 

151 "at the start of each PSF determination iteration? Doing so makes PSF determination " 

152 "converge more robustly and quickly.", 

153 ) 

154 installSimplePsf = pexConfig.ConfigurableField( 

155 target=InstallGaussianPsfTask, 

156 doc="Install a simple PSF model", 

157 ) 

158 refObjLoader = pexConfig.ConfigurableField( 

159 target=LoadIndexedReferenceObjectsTask, 

160 doc="reference object loader", 

161 ) 

162 ref_match = pexConfig.ConfigurableField( 

163 target=RefMatchTask, 

164 doc="Task to load and match reference objects. Only used if measurePsf can use matches. " 

165 "Warning: matching will only work well if the initial WCS is accurate enough " 

166 "to give good matches (roughly: good to 3 arcsec across the CCD).", 

167 ) 

168 measurePsf = pexConfig.ConfigurableField( 

169 target=MeasurePsfTask, 

170 doc="Measure PSF", 

171 ) 

172 repair = pexConfig.ConfigurableField( 

173 target=RepairTask, 

174 doc="Remove cosmic rays", 

175 ) 

176 checkUnitsParseStrict = pexConfig.Field( 

177 doc="Strictness of Astropy unit compatibility check, can be 'raise', 'warn' or 'silent'", 

178 dtype=str, 

179 default="raise", 

180 ) 

181 

182 def setDefaults(self): 

183 super().setDefaults() 

184 # just detect bright stars; includeThresholdMultipler=10 seems large, 

185 # but these are the values we have been using 

186 self.detection.thresholdValue = 5.0 

187 self.detection.includeThresholdMultiplier = 10.0 

188 self.detection.doTempLocalBackground = False 

189 # do not deblend, as it makes a mess 

190 self.doDeblend = False 

191 # measure and apply aperture correction; note: measuring and applying aperture 

192 # correction are disabled until the final measurement, after PSF is measured 

193 self.doApCorr = True 

194 # minimal set of measurements needed to determine PSF 

195 self.measurement.plugins.names = [ 

196 "base_PixelFlags", 

197 "base_SdssCentroid", 

198 "base_SdssShape", 

199 "base_GaussianFlux", 

200 "base_PsfFlux", 

201 "base_CircularApertureFlux", 

202 ] 

203 

204 def validate(self): 

205 if self.doApCorr and not self.measurePsf: 

206 raise RuntimeError("Must measure PSF to measure aperture correction, " 

207 "because flags determined by PSF measurement are used to identify " 

208 "sources used to measure aperture correction") 

209 

210## \addtogroup LSST_task_documentation 

211## \{ 

212## \page CharacterizeImageTask 

213## \ref CharacterizeImageTask_ "CharacterizeImageTask" 

214## \copybrief CharacterizeImageTask 

215## \} 

216 

217 

218class CharacterizeImageTask(pipeBase.PipelineTask, pipeBase.CmdLineTask): 

219 r"""!Measure bright sources and use this to estimate background and PSF of an exposure 

220 

221 @anchor CharacterizeImageTask_ 

222 

223 @section pipe_tasks_characterizeImage_Contents Contents 

224 

225 - @ref pipe_tasks_characterizeImage_Purpose 

226 - @ref pipe_tasks_characterizeImage_Initialize 

227 - @ref pipe_tasks_characterizeImage_IO 

228 - @ref pipe_tasks_characterizeImage_Config 

229 - @ref pipe_tasks_characterizeImage_Debug 

230 

231 

232 @section pipe_tasks_characterizeImage_Purpose Description 

233 

234 Given an exposure with defects repaired (masked and interpolated over, e.g. as output by IsrTask): 

235 - detect and measure bright sources 

236 - repair cosmic rays 

237 - measure and subtract background 

238 - measure PSF 

239 

240 @section pipe_tasks_characterizeImage_Initialize Task initialisation 

241 

242 @copydoc \_\_init\_\_ 

243 

244 @section pipe_tasks_characterizeImage_IO Invoking the Task 

245 

246 If you want this task to unpersist inputs or persist outputs, then call 

247 the `runDataRef` method (a thin wrapper around the `run` method). 

248 

249 If you already have the inputs unpersisted and do not want to persist the output 

250 then it is more direct to call the `run` method: 

251 

252 @section pipe_tasks_characterizeImage_Config Configuration parameters 

253 

254 See @ref CharacterizeImageConfig 

255 

256 @section pipe_tasks_characterizeImage_Debug Debug variables 

257 

258 The @link lsst.pipe.base.cmdLineTask.CmdLineTask command line task@endlink interface supports a flag 

259 `--debug` to import `debug.py` from your `$PYTHONPATH`; see @ref baseDebug for more about `debug.py`. 

260 

261 CharacterizeImageTask has a debug dictionary with the following keys: 

262 <dl> 

263 <dt>frame 

264 <dd>int: if specified, the frame of first debug image displayed (defaults to 1) 

265 <dt>repair_iter 

266 <dd>bool; if True display image after each repair in the measure PSF loop 

267 <dt>background_iter 

268 <dd>bool; if True display image after each background subtraction in the measure PSF loop 

269 <dt>measure_iter 

270 <dd>bool; if True display image and sources at the end of each iteration of the measure PSF loop 

271 See @ref lsst.meas.astrom.displayAstrometry for the meaning of the various symbols. 

272 <dt>psf 

273 <dd>bool; if True display image and sources after PSF is measured; 

274 this will be identical to the final image displayed by measure_iter if measure_iter is true 

275 <dt>repair 

276 <dd>bool; if True display image and sources after final repair 

277 <dt>measure 

278 <dd>bool; if True display image and sources after final measurement 

279 </dl> 

280 

281 For example, put something like: 

282 @code{.py} 

283 import lsstDebug 

284 def DebugInfo(name): 

285 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively 

286 if name == "lsst.pipe.tasks.characterizeImage": 

287 di.display = dict( 

288 repair = True, 

289 ) 

290 

291 return di 

292 

293 lsstDebug.Info = DebugInfo 

294 @endcode 

295 into your `debug.py` file and run `calibrateTask.py` with the `--debug` flag. 

296 

297 Some subtasks may have their own debug variables; see individual Task documentation. 

298 """ 

299 

300 # Example description used to live here, removed 2-20-2017 by MSSG 

301 

302 ConfigClass = CharacterizeImageConfig 

303 _DefaultName = "characterizeImage" 

304 RunnerClass = pipeBase.ButlerInitializedTaskRunner 

305 

306 def runQuantum(self, butlerQC, inputRefs, outputRefs): 

307 inputs = butlerQC.get(inputRefs) 

308 if 'exposureIdInfo' not in inputs.keys(): 

309 exposureIdInfo = ExposureIdInfo() 

310 exposureIdInfo.expId, exposureIdInfo.expBits = butlerQC.quantum.dataId.pack("visit_detector", 

311 returnMaxBits=True) 

312 inputs['exposureIdInfo'] = exposureIdInfo 

313 outputs = self.run(**inputs) 

314 butlerQC.put(outputs, outputRefs) 

315 

316 def __init__(self, butler=None, refObjLoader=None, schema=None, **kwargs): 

317 """!Construct a CharacterizeImageTask 

318 

319 @param[in] butler A butler object is passed to the refObjLoader constructor in case 

320 it is needed to load catalogs. May be None if a catalog-based star selector is 

321 not used, if the reference object loader constructor does not require a butler, 

322 or if a reference object loader is passed directly via the refObjLoader argument. 

323 @param[in] refObjLoader An instance of LoadReferenceObjectsTasks that supplies an 

324 external reference catalog to a catalog-based star selector. May be None if a 

325 catalog star selector is not used or the loader can be constructed from the 

326 butler argument. 

327 @param[in,out] schema initial schema (an lsst.afw.table.SourceTable), or None 

328 @param[in,out] kwargs other keyword arguments for lsst.pipe.base.CmdLineTask 

329 """ 

330 super().__init__(**kwargs) 

331 

332 if schema is None: 

333 schema = SourceTable.makeMinimalSchema() 

334 self.schema = schema 

335 self.makeSubtask("background") 

336 self.makeSubtask("installSimplePsf") 

337 self.makeSubtask("repair") 

338 self.makeSubtask("measurePsf", schema=self.schema) 

339 if self.config.doMeasurePsf and self.measurePsf.usesMatches: 

340 if not refObjLoader: 

341 self.makeSubtask('refObjLoader', butler=butler) 

342 refObjLoader = self.refObjLoader 

343 self.makeSubtask("ref_match", refObjLoader=refObjLoader) 

344 self.algMetadata = dafBase.PropertyList() 

345 self.makeSubtask('detection', schema=self.schema) 

346 if self.config.doDeblend: 

347 self.makeSubtask("deblend", schema=self.schema) 

348 self.makeSubtask('measurement', schema=self.schema, algMetadata=self.algMetadata) 

349 if self.config.doApCorr: 

350 self.makeSubtask('measureApCorr', schema=self.schema) 

351 self.makeSubtask('applyApCorr', schema=self.schema) 

352 self.makeSubtask('catalogCalculation', schema=self.schema) 

353 self._initialFrame = getDebugFrame(self._display, "frame") or 1 

354 self._frame = self._initialFrame 

355 self.schema.checkUnits(parse_strict=self.config.checkUnitsParseStrict) 

356 self.outputSchema = afwTable.SourceCatalog(self.schema) 

357 

358 def getInitOutputDatasets(self): 

359 outputCatSchema = afwTable.SourceCatalog(self.schema) 

360 outputCatSchema.getTable().setMetadata(self.algMetadata) 

361 return {'outputSchema': outputCatSchema} 

362 

363 @pipeBase.timeMethod 

364 def runDataRef(self, dataRef, exposure=None, background=None, doUnpersist=True): 

365 """!Characterize a science image and, if wanted, persist the results 

366 

367 This simply unpacks the exposure and passes it to the characterize method to do the work. 

368 

369 @param[in] dataRef: butler data reference for science exposure 

370 @param[in,out] exposure exposure to characterize (an lsst.afw.image.ExposureF or similar). 

371 If None then unpersist from "postISRCCD". 

372 The following changes are made, depending on the config: 

373 - set psf to the measured PSF 

374 - set apCorrMap to the measured aperture correction 

375 - subtract background 

376 - interpolate over cosmic rays 

377 - update detection and cosmic ray mask planes 

378 @param[in,out] background initial model of background already subtracted from exposure 

379 (an lsst.afw.math.BackgroundList). May be None if no background has been subtracted, 

380 which is typical for image characterization. 

381 A refined background model is output. 

382 @param[in] doUnpersist if True the exposure is read from the repository 

383 and the exposure and background arguments must be None; 

384 if False the exposure must be provided. 

385 True is intended for running as a command-line task, False for running as a subtask 

386 

387 @return same data as the characterize method 

388 """ 

389 self._frame = self._initialFrame # reset debug display frame 

390 self.log.info("Processing %s" % (dataRef.dataId)) 

391 

392 if doUnpersist: 

393 if exposure is not None or background is not None: 

394 raise RuntimeError("doUnpersist true; exposure and background must be None") 

395 exposure = dataRef.get("postISRCCD", immediate=True) 

396 elif exposure is None: 

397 raise RuntimeError("doUnpersist false; exposure must be provided") 

398 

399 exposureIdInfo = dataRef.get("expIdInfo") 

400 

401 charRes = self.run( 

402 exposure=exposure, 

403 exposureIdInfo=exposureIdInfo, 

404 background=background, 

405 ) 

406 

407 if self.config.doWrite: 

408 dataRef.put(charRes.sourceCat, "icSrc") 

409 if self.config.doWriteExposure: 

410 dataRef.put(charRes.exposure, "icExp") 

411 dataRef.put(charRes.background, "icExpBackground") 

412 

413 return charRes 

414 

415 @pipeBase.timeMethod 

416 def run(self, exposure, exposureIdInfo=None, background=None): 

417 """!Characterize a science image 

418 

419 Peforms the following operations: 

420 - Iterate the following config.psfIterations times, or once if config.doMeasurePsf false: 

421 - detect and measure sources and estimate PSF (see detectMeasureAndEstimatePsf for details) 

422 - interpolate over cosmic rays 

423 - perform final measurement 

424 

425 @param[in,out] exposure exposure to characterize (an lsst.afw.image.ExposureF or similar). 

426 The following changes are made: 

427 - update or set psf 

428 - set apCorrMap 

429 - update detection and cosmic ray mask planes 

430 - subtract background and interpolate over cosmic rays 

431 @param[in] exposureIdInfo ID info for exposure (an lsst.obs.base.ExposureIdInfo). 

432 If not provided, returned SourceCatalog IDs will not be globally unique. 

433 @param[in,out] background initial model of background already subtracted from exposure 

434 (an lsst.afw.math.BackgroundList). May be None if no background has been subtracted, 

435 which is typical for image characterization. 

436 

437 @return pipe_base Struct containing these fields, all from the final iteration 

438 of detectMeasureAndEstimatePsf: 

439 - exposure: characterized exposure; image is repaired by interpolating over cosmic rays, 

440 mask is updated accordingly, and the PSF model is set 

441 - sourceCat: detected sources (an lsst.afw.table.SourceCatalog) 

442 - background: model of background subtracted from exposure (an lsst.afw.math.BackgroundList) 

443 - psfCellSet: spatial cells of PSF candidates (an lsst.afw.math.SpatialCellSet) 

444 """ 

445 self._frame = self._initialFrame # reset debug display frame 

446 

447 if not self.config.doMeasurePsf and not exposure.hasPsf(): 

448 self.log.warn("Source catalog detected and measured with placeholder or default PSF") 

449 self.installSimplePsf.run(exposure=exposure) 

450 

451 if exposureIdInfo is None: 

452 exposureIdInfo = ExposureIdInfo() 

453 

454 # subtract an initial estimate of background level 

455 background = self.background.run(exposure).background 

456 

457 psfIterations = self.config.psfIterations if self.config.doMeasurePsf else 1 

458 for i in range(psfIterations): 

459 dmeRes = self.detectMeasureAndEstimatePsf( 

460 exposure=exposure, 

461 exposureIdInfo=exposureIdInfo, 

462 background=background, 

463 ) 

464 

465 psf = dmeRes.exposure.getPsf() 

466 psfSigma = psf.computeShape().getDeterminantRadius() 

467 psfDimensions = psf.computeImage().getDimensions() 

468 medBackground = np.median(dmeRes.background.getImage().getArray()) 

469 self.log.info("iter %s; PSF sigma=%0.2f, dimensions=%s; median background=%0.2f" % 

470 (i + 1, psfSigma, psfDimensions, medBackground)) 

471 

472 self.display("psf", exposure=dmeRes.exposure, sourceCat=dmeRes.sourceCat) 

473 

474 # perform final repair with final PSF 

475 self.repair.run(exposure=dmeRes.exposure) 

476 self.display("repair", exposure=dmeRes.exposure, sourceCat=dmeRes.sourceCat) 

477 

478 # perform final measurement with final PSF, including measuring and applying aperture correction, 

479 # if wanted 

480 self.measurement.run(measCat=dmeRes.sourceCat, exposure=dmeRes.exposure, 

481 exposureId=exposureIdInfo.expId) 

482 if self.config.doApCorr: 

483 apCorrMap = self.measureApCorr.run(exposure=dmeRes.exposure, catalog=dmeRes.sourceCat).apCorrMap 

484 dmeRes.exposure.getInfo().setApCorrMap(apCorrMap) 

485 self.applyApCorr.run(catalog=dmeRes.sourceCat, apCorrMap=exposure.getInfo().getApCorrMap()) 

486 self.catalogCalculation.run(dmeRes.sourceCat) 

487 

488 self.display("measure", exposure=dmeRes.exposure, sourceCat=dmeRes.sourceCat) 

489 

490 return pipeBase.Struct( 

491 exposure=dmeRes.exposure, 

492 sourceCat=dmeRes.sourceCat, 

493 background=dmeRes.background, 

494 psfCellSet=dmeRes.psfCellSet, 

495 

496 characterized=dmeRes.exposure, 

497 backgroundModel=dmeRes.background 

498 ) 

499 

500 @pipeBase.timeMethod 

501 def detectMeasureAndEstimatePsf(self, exposure, exposureIdInfo, background): 

502 """!Perform one iteration of detect, measure and estimate PSF 

503 

504 Performs the following operations: 

505 - if config.doMeasurePsf or not exposure.hasPsf(): 

506 - install a simple PSF model (replacing the existing one, if need be) 

507 - interpolate over cosmic rays with keepCRs=True 

508 - estimate background and subtract it from the exposure 

509 - detect, deblend and measure sources, and subtract a refined background model; 

510 - if config.doMeasurePsf: 

511 - measure PSF 

512 

513 @param[in,out] exposure exposure to characterize (an lsst.afw.image.ExposureF or similar) 

514 The following changes are made: 

515 - update or set psf 

516 - update detection and cosmic ray mask planes 

517 - subtract background 

518 @param[in] exposureIdInfo ID info for exposure (an lsst.obs_base.ExposureIdInfo) 

519 @param[in,out] background initial model of background already subtracted from exposure 

520 (an lsst.afw.math.BackgroundList). 

521 

522 @return pipe_base Struct containing these fields, all from the final iteration 

523 of detect sources, measure sources and estimate PSF: 

524 - exposure characterized exposure; image is repaired by interpolating over cosmic rays, 

525 mask is updated accordingly, and the PSF model is set 

526 - sourceCat detected sources (an lsst.afw.table.SourceCatalog) 

527 - background model of background subtracted from exposure (an lsst.afw.math.BackgroundList) 

528 - psfCellSet spatial cells of PSF candidates (an lsst.afw.math.SpatialCellSet) 

529 """ 

530 # install a simple PSF model, if needed or wanted 

531 if not exposure.hasPsf() or (self.config.doMeasurePsf and self.config.useSimplePsf): 

532 self.log.warn("Source catalog detected and measured with placeholder or default PSF") 

533 self.installSimplePsf.run(exposure=exposure) 

534 

535 # run repair, but do not interpolate over cosmic rays (do that elsewhere, with the final PSF model) 

536 self.repair.run(exposure=exposure, keepCRs=True) 

537 self.display("repair_iter", exposure=exposure) 

538 

539 if background is None: 

540 background = BackgroundList() 

541 

542 sourceIdFactory = IdFactory.makeSource(exposureIdInfo.expId, exposureIdInfo.unusedBits) 

543 table = SourceTable.make(self.schema, sourceIdFactory) 

544 table.setMetadata(self.algMetadata) 

545 

546 detRes = self.detection.run(table=table, exposure=exposure, doSmooth=True) 

547 sourceCat = detRes.sources 

548 if detRes.fpSets.background: 

549 for bg in detRes.fpSets.background: 

550 background.append(bg) 

551 

552 if self.config.doDeblend: 

553 self.deblend.run(exposure=exposure, sources=sourceCat) 

554 

555 self.measurement.run(measCat=sourceCat, exposure=exposure, exposureId=exposureIdInfo.expId) 

556 

557 measPsfRes = pipeBase.Struct(cellSet=None) 

558 if self.config.doMeasurePsf: 

559 if self.measurePsf.usesMatches: 

560 matches = self.ref_match.loadAndMatch(exposure=exposure, sourceCat=sourceCat).matches 

561 else: 

562 matches = None 

563 measPsfRes = self.measurePsf.run(exposure=exposure, sources=sourceCat, matches=matches, 

564 expId=exposureIdInfo.expId) 

565 self.display("measure_iter", exposure=exposure, sourceCat=sourceCat) 

566 

567 return pipeBase.Struct( 

568 exposure=exposure, 

569 sourceCat=sourceCat, 

570 background=background, 

571 psfCellSet=measPsfRes.cellSet, 

572 ) 

573 

574 def getSchemaCatalogs(self): 

575 """Return a dict of empty catalogs for each catalog dataset produced by this task. 

576 """ 

577 sourceCat = SourceCatalog(self.schema) 

578 sourceCat.getTable().setMetadata(self.algMetadata) 

579 return {"icSrc": sourceCat} 

580 

581 def display(self, itemName, exposure, sourceCat=None): 

582 """Display exposure and sources on next frame, if display of itemName has been requested 

583 

584 @param[in] itemName name of item in debugInfo 

585 @param[in] exposure exposure to display 

586 @param[in] sourceCat source catalog to display 

587 """ 

588 val = getDebugFrame(self._display, itemName) 

589 if not val: 

590 return 

591 

592 displayAstrometry(exposure=exposure, sourceCat=sourceCat, frame=self._frame, pause=False) 

593 self._frame += 1