28
28
"SkyMeasurementConfig" ,
29
29
"SkyMeasurementTask" ,
30
30
"SkyStatsConfig" ,
31
+ "TractBackground" ,
32
+ "TractBackgroundConfig" ,
31
33
]
32
34
33
35
import importlib
@@ -368,16 +370,10 @@ def solveScales(self, scales):
368
370
skySamples = numpy .array (skySamples )
369
371
370
372
def solve (mask ):
371
- < << << << HEAD
372
373
# Make sure we return a float, not an array.
373
374
return afwMath .LeastSquares .fromDesignMatrix (skySamples [mask ].reshape (mask .sum (), 1 ),
374
375
imageSamples [mask ],
375
376
afwMath .LeastSquares .DIRECT_SVD ).getSolution ()[0 ]
376
- == == == =
377
- return afwMath .LeastSquares .fromDesignMatrix (
378
- skySamples [mask ].reshape (mask .sum (), 1 ), imageSamples [mask ], afwMath .LeastSquares .DIRECT_SVD
379
- ).getSolution ()
380
- >> >> >> > 8 cf1e691 (Add full tract background functionality .)
381
377
382
378
mask = numpy .isfinite (imageSamples ) & numpy .isfinite (skySamples )
383
379
for ii in range (self .config .skyIter ):
@@ -860,14 +856,13 @@ class TractBackgroundConfig(Config):
860
856
)
861
857
doSmooth = Field (dtype = bool , default = False , doc = "Do smoothing?" )
862
858
smoothScale = Field (dtype = float , default = 2.0 , doc = "Smoothing scale, as a multiple of the bin size" )
863
- binning = Field (dtype = int , default = 200 , doc = "Binning to use for warp background model (pixels)" )
859
+ binning = Field (dtype = int , default = 64 , doc = "Binning to use for warp background model (pixels)" )
864
860
865
861
866
862
class TractBackground :
867
863
"""
868
864
As FocalPlaneBackground, but works in warped tract coordinates
869
865
"""
870
-
871
866
@classmethod
872
867
def fromSimilar (cls , other ):
873
868
"""Construct from an object that has the same interface.
@@ -883,9 +878,9 @@ def fromSimilar(cls, other):
883
878
background : `TractBackground`
884
879
Something guaranteed to be a `TractBackground`.
885
880
"""
886
- return cls (other .config , other .dims , other .transform , other ._values , other ._numbers )
881
+ return cls (other .config , other .tract , other . dims , other .transform , other ._values , other ._numbers )
887
882
888
- def __init__ (self , config , values = None , numbers = None ):
883
+ def __init__ (self , config , skymap , tract , values = None , numbers = None ):
889
884
"""Constructor
890
885
891
886
Developers should note that changes to the signature of this method
@@ -895,14 +890,24 @@ def __init__(self, config, values=None, numbers=None):
895
890
----------
896
891
config : `TractBackgroundConfig`
897
892
Configuration for measuring tract backgrounds.
893
+ skymap : `lsst.skymap.ringsSkyMap.RingsSkyMap`
894
+ Skymap object
895
+ tract : `int`
896
+ Placeholder Tract ID
897
+ transform : `lsst.afw.geom.TransformPoint2ToPoint2`
898
+ Transformation from tract coordinates to warp coordinates.
898
899
values : `lsst.afw.image.ImageF`
899
900
Measured background values.
900
901
numbers : `lsst.afw.image.ImageF`
901
902
Number of pixels in each background measurement.
902
903
"""
903
904
self .config = config
904
- # TODO: dynamic tract dimensions?
905
- self .dims = geom .Extent2I (36000 / self .config .xBin , 36000 / self .config .yBin )
905
+ self .skymap = skymap
906
+ self .tract = tract
907
+ self .tractInfo = self .skymap .generateTract (tract )
908
+ tractDimX , tractDimY = self .tractInfo .getBBox ().getDimensions ()
909
+ self .dims = geom .Extent2I (tractDimX / self .config .xBin ,
910
+ tractDimY / self .config .yBin )
906
911
907
912
if values is None :
908
913
values = afwImage .ImageF (self .dims )
@@ -920,10 +925,10 @@ def __init__(self, config, values=None, numbers=None):
920
925
self ._numbers = numbers
921
926
922
927
def __reduce__ (self ):
923
- return self .__class__ , (self .config , self ._values , self ._numbers )
928
+ return self .__class__ , (self .config , self .skymap , self . tract , self . _values , self ._numbers )
924
929
925
930
def clone (self ):
926
- return self .__class__ (self .config , self ._values , self ._numbers )
931
+ return self .__class__ (self .config , self .skymap , self . tract , self . _values , self ._numbers )
927
932
928
933
def addWarp (self , warp ):
929
934
"""
@@ -1039,10 +1044,21 @@ def toWarpBackground(self, warp):
1039
1044
1040
1045
# Transform from binned tract plane to tract plane
1041
1046
# Start at the patch corner, not the warp corner overlap region
1047
+ wcs = self .tractInfo .getWcs ()
1048
+ coo = wcs .pixelToSky (1 , 1 )
1049
+ ptch = self .tractInfo .findPatch (coo )
1050
+ ptchDimX , ptchDimY = ptch .getInnerBBox ().getDimensions ()
1051
+ if ptchDimX != ptchDimY :
1052
+ raise ValueError (
1053
+ "Patch dimensions %d,%d are unequal: cannot proceed as written."
1054
+ % (ptchDimX , ptchDimY )
1055
+ )
1056
+ ptchOutDimX , _ = ptch .getOuterBBox ().getDimensions ()
1057
+ overlap = ptchDimX - ptchOutDimX
1042
1058
corner = warp .getBBox ().getMin ()
1043
- if corner [0 ] % 4000 != 0 : # TODO: hard-coded patch dimensions are bad
1044
- corner [0 ] += 100
1045
- corner [1 ] += 100
1059
+ if corner [0 ] % ptchDimX != 0 :
1060
+ corner [0 ] += overlap
1061
+ corner [1 ] += overlap
1046
1062
offset = geom .Extent2D (corner [0 ], corner [1 ])
1047
1063
tractTransform = (
1048
1064
geom .AffineTransform .makeTranslation (geom .Extent2D (- 0.5 , - 0.5 ))
@@ -1064,13 +1080,15 @@ def toWarpBackground(self, warp):
1064
1080
image = afwImage .ImageF (bbox .getDimensions () // self .config .binning )
1065
1081
norm = afwImage .ImageF (image .getBBox ())
1066
1082
1083
+ # Warps full tract model to warp image scale
1067
1084
ctrl = afwMath .WarpingControl ("bilinear" )
1068
1085
afwMath .warpImage (image , tractPlane , toSample .inverted (), ctrl )
1069
1086
afwMath .warpImage (norm , tpNorm , toSample .inverted (), ctrl )
1070
1087
image /= norm
1071
1088
# Convert back to counts so the model can be subtracted w/o conversion
1072
1089
image /= warp .getPhotoCalib ().instFluxToNanojansky (1 )
1073
1090
1091
+ # Only sky background model, so include only null values in mask plane
1074
1092
mask = afwImage .Mask (image .getBBox ())
1075
1093
isBad = numpy .isnan (image .getArray ())
1076
1094
mask .getArray ()[isBad ] = mask .getPlaneBitMask ("BAD" )
@@ -1095,18 +1113,12 @@ def getStatsImage(self):
1095
1113
"""
1096
1114
values = self ._values .clone ()
1097
1115
values /= self ._numbers
1098
- # TODO: this logic smoothes over bad parts of the image, including NaN
1099
- # values. But it doesn't work here because NaN pixels are always found
1100
- # at the image edges. Could ignore it, or devise an alternative.
1101
- # tract have no overlap with the visit?
1102
- # thresh = self.config.minFrac * (self.config.xBin) * (self.config.yBin)
1103
- # isBad = self._numbers.getArray() < thresh
1104
- # if self.config.doSmooth:
1105
- # array = values.getArray()
1106
- # array[:] = smoothArray(array, isBad, self.config.smoothScale)
1107
- # isBad = numpy.isnan(values.array)
1108
- # if numpy.any(isBad):
1109
- # interpolateBadPixels(values.getArray(), isBad, self.config.interpolation)
1116
+ # TODO: filling in bad pixels. Currently BAD mask plane includes both
1117
+ # chip gaps and regions outside FP, so interpolating across chip gaps
1118
+ # also includes extrapolating flux outside the FP, which is
1119
+ # undesirable. But interpolation and extrapolation aren't currently
1120
+ # separable, so for now this step is just not done.
1121
+
1110
1122
return values
1111
1123
1112
1124
0 commit comments