23
23
24
24
import lsstDebug
25
25
import numpy as np
26
- from lsst .afw .image import LOCAL , PARENT , Mask , MaskedImageF
26
+ from lsst .afw .image import LOCAL , PARENT , ImageF , Mask , MaskedImageF
27
27
from lsst .afw .math import (
28
28
MEAN ,
29
29
MEANCLIP ,
@@ -250,12 +250,11 @@ def run(self, warps):
250
250
raise TaskError ("No exposures to match" )
251
251
252
252
# Define a reference warp; 'warps' is modified in-place to exclude it
253
- refWarp = self ._defineWarps (warps = warps , refWarpVisit = self .config .refWarpVisit )
254
- breakpoint ()
253
+ refWarp , refInd = self ._defineWarps (warps = warps , refWarpVisit = self .config .refWarpVisit )
255
254
256
255
# Images must be scaled to a common ZP
257
256
# Converting everything to nJy to accomplish this
258
- refExposure = refMatchedWarp .get ()
257
+ refExposure = refWarp .get ()
259
258
refMI = self ._fluxScale (refExposure ) # Also modifies refExposure
260
259
261
260
# TODO: figure out what this was creating and why
@@ -270,7 +269,7 @@ def run(self, warps):
270
269
271
270
# TODO: refactor below to construct blank bg model
272
271
im = refExposure .getMaskedImage ()
273
- blankIm = im .Factory (im , True ) # Don't do this
272
+ blankIm = im .Factory (im , True )
274
273
blankIm .image .array *= 0
275
274
276
275
width = blankIm .getWidth ()
@@ -286,53 +285,43 @@ def run(self, warps):
286
285
bctrl .setUndersampleStyle (self .config .undersampleStyle )
287
286
288
287
bkgd = makeBackground (blankIm , bctrl )
288
+ blank = BackgroundList (
289
+ (
290
+ bkgd ,
291
+ stringToInterpStyle (self .config .interpStyle ),
292
+ stringToUndersampleStyle (self .config .undersampleStyle ),
293
+ ApproximateControl .UNKNOWN ,
294
+ 0 ,
295
+ 0 ,
296
+ False ,
297
+ )
298
+ )
289
299
290
300
backgroundInfoList = []
291
301
for ind , exp in enumerate (warps ):
292
- if ind in refIndSet :
293
- backgroundInfoStruct = BackgroundList (
294
- (
295
- bkgd ,
296
- stringToInterpStyle (self .config .interpStyle ),
297
- stringToUndersampleStyle (self .config .undersampleStyle ),
298
- ApproximateControl .UNKNOWN ,
299
- 0 ,
300
- 0 ,
301
- False ,
302
- )
302
+ # TODO: simplify this maybe, using only visit IDs?
303
+ self .log .info ("Matching background of %s to %s" , exp .dataId , refWarp .dataId )
304
+ toMatchExposure = exp .get ()
305
+ try :
306
+ # TODO: adjust logic to avoid creating spurious MIs like this
307
+ toMatchMI = self ._fluxScale (toMatchExposure )
308
+
309
+ # store a string specifying the visit to label debug plot
310
+ # self.debugDataIdString = ''.join([str(toMatchRef.dataId[vk]) for vk in debugIdKeyList])
311
+
312
+ backgroundInfoStruct = self .matchBackgrounds (
313
+ refExposure = refExposure ,
314
+ sciExposure = toMatchExposure ,
303
315
)
304
- else :
305
- # TODO: simplify this maybe, using only visit IDs?
306
- self .log .info ("Matching background of %s to %s" , exp .dataId , refMatchedWarp .dataId )
307
- toMatchExposure = exp .get ()
308
- try :
309
- # Seems to require exposure, not masked exposure.
310
- toMatchMI = self ._fluxScale (toMatchExposure )
311
-
312
- # store a string specifying the visit to label debug plot
313
- # self.debugDataIdString = ''.join([str(toMatchRef.dataId[vk]) for vk in debugIdKeyList])
314
-
315
- backgroundInfoStruct = self .matchBackgrounds (
316
- refExposure = refExposure ,
317
- sciExposure = toMatchExposure ,
318
- )
319
- backgroundInfoStruct .isReference = False
320
- except Exception as e :
321
- self .log .warning ("Failed to fit background %s: %s" , exp .dataId , e )
322
- backgroundInfoStruct = BackgroundList (
323
- (
324
- bkgd ,
325
- stringToInterpStyle (self .config .interpStyle ),
326
- stringToUndersampleStyle (self .config .undersampleStyle ),
327
- ApproximateControl .UNKNOWN ,
328
- 0 ,
329
- 0 ,
330
- False ,
331
- )
332
- )
316
+ backgroundInfoStruct .isReference = False
317
+ except Exception as e :
318
+ self .log .warning ("Failed to fit background %s: %s" , exp .dataId , e )
319
+ backgroundInfoStruct = blank
333
320
334
321
backgroundInfoList .append (backgroundInfoStruct )
335
322
323
+ # TODO: more elegant solution than inserting blank model at ref ind?
324
+ backgroundInfoList .insert (refInd , blank )
336
325
return Struct (backgroundInfoList = backgroundInfoList )
337
326
338
327
@timeMethod
@@ -377,15 +366,38 @@ def _defineWarps(self, warps, refWarpVisit=None):
377
366
warpNPoints = []
378
367
for warpDDH in warps :
379
368
warp = warpDDH .get ()
369
+
370
+ # First check if any image edge is all NaN
371
+ # If so, don't use
372
+ leftBool = np .all (np .isnan (warp .image .array [:, 0 ]))
373
+ rightBool = np .all (np .isnan (warp .image .array [:, warp .image .getHeight () - 1 ]))
374
+ bottomBool = np .all (np .isnan (warp .image .array [0 , :]))
375
+ topBool = np .all (np .isnan (warp .image .array [warp .image .getWidth () - 1 , :]))
376
+ if np .any ([leftBool , rightBool , bottomBool , topBool ]):
377
+ continue
378
+
380
379
warp .image .array *= warp .getPhotoCalib ().instFluxToNanojansky (1 ) # Convert image plane to nJy
381
- warpStats = makeStatistics (warp .image , warp .mask , MEAN | VARIANCE | NPOINT , self .statsCtrl )
380
+
381
+ # Select reference based on BG of plane sky-subtracted images
382
+ bkgd , __ , __ , __ = self ._setupBackground (warp )
383
+
384
+ weightByInverseVariance = self .config .approxWeighting
385
+ actrl = ApproximateControl (ApproximateControl .CHEBYSHEV , 1 , 1 , weightByInverseVariance )
386
+ undersampleStyle = stringToUndersampleStyle (self .config .undersampleStyle )
387
+ approx = bkgd .getApproximate (actrl , undersampleStyle )
388
+ bgSubImage = ImageF (warp .image .array - approx .getImage ().array )
389
+
390
+ warpStats = makeStatistics (bgSubImage , warp .mask , MEAN | VARIANCE | NPOINT , self .statsCtrl )
382
391
warpMean , _ = warpStats .getResult (MEAN )
383
392
warpVar , _ = warpStats .getResult (VARIANCE )
384
393
warpNPoint , _ = warpStats .getResult (NPOINT )
385
394
warpMeans .append (warpMean )
386
395
warpVars .append (warpVar )
387
396
warpNPoints .append (int (warpNPoint ))
388
397
398
+ if len (warpNPoints ) == 0 :
399
+ raise TaskError ("No suitable reference visit found in list of warps." )
400
+
389
401
# Normalize mean/var/npoints to range from 0 to 1
390
402
warpMeansFrac = np .array (warpMeans ) / np .nanmax (warpMeans )
391
403
warpVarsFrac = np .array (warpVars ) / np .nanmax (warpVars )
@@ -396,21 +408,22 @@ def _defineWarps(self, warps, refWarpVisit=None):
396
408
costFunctionVals += self .config .bestRefWeightVariance * warpVarsFrac
397
409
costFunctionVals += self .config .bestRefWeightCoverage * warpNPointsFrac
398
410
399
- refWarp = warps .pop (np .nanargmin (costFunctionVals ))
411
+ ind = np .nanargmin (costFunctionVals )
412
+ refWarp = warps .pop (ind )
400
413
self .log .info ("Using best reference visit %d" , refWarp .dataId ["visit" ])
401
- return refWarp
414
+ return refWarp , ind
402
415
403
416
def _fluxScale (self , exposure ):
404
417
"""Scales image to nJy flux using photometric calibration.
405
418
406
419
Parameters
407
420
----------
408
- exposure: ``
421
+ exposure: `lsst.afw.image._exposure.ExposureF `
409
422
Exposure to scale.
410
423
411
424
Returns
412
425
-------
413
- maskedImage: ``
426
+ maskedImage: `lsst.afw.image._maskedImage.MaskedImageF `
414
427
Flux-scaled masked exposure.
415
428
"""
416
429
maskedImage = exposure .getMaskedImage ()
@@ -419,6 +432,56 @@ def _fluxScale(self, exposure):
419
432
420
433
return maskedImage
421
434
435
+ def _setupBackground (self , warp ):
436
+ """Set up and return a background model container and associated images
437
+ and controllers.
438
+
439
+ Uses the following config parameters:
440
+ - ``gridStatistic``
441
+ - ``numSigmaClip``
442
+ - ``numIter``
443
+ - ``binSize``
444
+ - ``undersampleStyle``
445
+
446
+ Parameters
447
+ ----------
448
+ warp: `lsst.afw.image._exposure.ExposureF`
449
+ Warped exposure or difference image for which to estimate
450
+ background.
451
+
452
+ Returns
453
+ -------
454
+ bkgd: `lsst.afw.math.BackgroundMI`
455
+ Background model container.
456
+ bctrl: `lsst.afw.math.BackgroundControl`
457
+ Background model control
458
+ warpMI: `lsst.afw.image._maskedImage.MaskedImageF`
459
+ Masked image associated with warp
460
+ statsFlag: `lsst.afw.math.Property`
461
+ Flag for grid statistic property (self.config.gridStatistic)
462
+ """
463
+ statsFlag = stringToStatisticsProperty (self .config .gridStatistic )
464
+ self .statsCtrl .setNumSigmaClip (self .config .numSigmaClip )
465
+ self .statsCtrl .setNumIter (self .config .numIter )
466
+
467
+ warpMI = warp .getMaskedImage ()
468
+
469
+ width = warpMI .getWidth ()
470
+ height = warpMI .getHeight ()
471
+ nx = width // self .config .binSize
472
+ if width % self .config .binSize != 0 :
473
+ nx += 1
474
+ ny = height // self .config .binSize
475
+ if height % self .config .binSize != 0 :
476
+ ny += 1
477
+
478
+ bctrl = BackgroundControl (nx , ny , self .statsCtrl , statsFlag )
479
+ bctrl .setUndersampleStyle (self .config .undersampleStyle )
480
+
481
+ bkgd = makeBackground (warpMI , bctrl )
482
+
483
+ return bkgd , bctrl , warpMI , statsFlag
484
+
422
485
@timeMethod
423
486
def matchBackgrounds (self , refExposure , sciExposure ):
424
487
"""Match science exposure's background level to that of reference
@@ -489,27 +552,7 @@ def matchBackgrounds(self, refExposure, sciExposure):
489
552
"Exposures are different dimensions. sci:(%i, %i) vs. ref:(%i, %i)" % (wSci , hSci , wRef , hRef )
490
553
)
491
554
492
- statsFlag = stringToStatisticsProperty (self .config .gridStatistic )
493
- self .statsCtrl .setNumSigmaClip (self .config .numSigmaClip )
494
- self .statsCtrl .setNumIter (self .config .numIter )
495
-
496
- im = refExposure .getMaskedImage ()
497
- diffMI = im .Factory (im , True )
498
- diffMI -= sciExposure .getMaskedImage ()
499
-
500
- width = diffMI .getWidth ()
501
- height = diffMI .getHeight ()
502
- nx = width // self .config .binSize
503
- if width % self .config .binSize != 0 :
504
- nx += 1
505
- ny = height // self .config .binSize
506
- if height % self .config .binSize != 0 :
507
- ny += 1
508
-
509
- bctrl = BackgroundControl (nx , ny , self .statsCtrl , statsFlag )
510
- bctrl .setUndersampleStyle (self .config .undersampleStyle )
511
-
512
- bkgd = makeBackground (diffMI , bctrl )
555
+ bkgd , bctrl , diffMI , statsFlag = self ._setupBackground (refExposure )
513
556
514
557
# Some config and input checks if config.usePolynomial:
515
558
# 1) Check that order/bin size make sense:
0 commit comments