LSST Applications g0b6bd0c080+a72a5dd7e6,g1182afd7b4+2a019aa3bb,g17e5ecfddb+2b8207f7de,g1d67935e3f+06cf436103,g38293774b4+ac198e9f13,g396055baef+6a2097e274,g3b44f30a73+6611e0205b,g480783c3b1+98f8679e14,g48ccf36440+89c08d0516,g4b93dc025c+98f8679e14,g5c4744a4d9+a302e8c7f0,g613e996a0d+e1c447f2e0,g6c8d09e9e7+25247a063c,g7271f0639c+98f8679e14,g7a9cd813b8+124095ede6,g9d27549199+a302e8c7f0,ga1cf026fa3+ac198e9f13,ga32aa97882+7403ac30ac,ga786bb30fb+7a139211af,gaa63f70f4e+9994eb9896,gabf319e997+ade567573c,gba47b54d5d+94dc90c3ea,gbec6a3398f+06cf436103,gc6308e37c7+07dd123edb,gc655b1545f+ade567573c,gcc9029db3c+ab229f5caf,gd01420fc67+06cf436103,gd877ba84e5+06cf436103,gdb4cecd868+6f279b5b48,ge2d134c3d5+cc4dbb2e3f,ge448b5faa6+86d1ceac1d,gecc7e12556+98f8679e14,gf3ee170dca+25247a063c,gf4ac96e456+ade567573c,gf9f5ea5b4d+ac198e9f13,gff490e6085+8c2580be5c,w.2022.27
LSST Data Management Base Package
accumulator_mean_stack.py
Go to the documentation of this file.
1# This file is part of meas_algorithms.
2#
3# LSST Data Management System
4# This product includes software developed by the
5# LSST Project (http://www.lsst.org/).
6# See COPYRIGHT file at the top of the source tree.
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
24
25__all__ = ['AccumulatorMeanStack']
26
27
29 """Stack masked images.
30
31 Parameters
32 ----------
33 shape : `tuple`
34 Shape of the input and output images.
35 bit_mask_value : `int`
36 Bit mask to flag for "bad" inputs that should not be stacked.
37 mask_threshold_dict : `dict` [`int`: `float`], optional
38 Dictionary of mapping from bit number to threshold for flagging.
39 Only bad bits (in bit_mask_value) which mask fractional weight
40 greater than this threshold will be flagged in the output image.
41 mask_map : `list` [`tuple`], optional
42 Mapping from input image bits to aggregated coadd bits.
43 no_good_pixels_mask : `int`, optional
44 Bit mask to set when there are no good pixels in the stack.
45 If not set then will set coadd masked image 'NO_DATA' bit.
46 calc_error_from_input_variance : `bool`, optional
47 Calculate the error from the input variance?
48 compute_n_image : `bool`, optional
49 Calculate the n_image map as well as stack?
50 """
51 def __init__(self, shape,
52 bit_mask_value, mask_threshold_dict={},
53 mask_map=[], no_good_pixels_mask=None,
54 calc_error_from_input_variance=True,
55 compute_n_image=False):
56 self.shapeshape = shape
57 self.bit_mask_valuebit_mask_value = bit_mask_value
58 self.mask_mapmask_map = mask_map
59 self.no_good_pixels_maskno_good_pixels_mask = no_good_pixels_mask
60 self.calc_error_from_input_variancecalc_error_from_input_variance = calc_error_from_input_variance
61 self.compute_n_imagecompute_n_image = compute_n_image
62
63 # Only track threshold bits that are in the bad bit_mask_value.
64 self.mask_threshold_dictmask_threshold_dict = {}
65 for bit in mask_threshold_dict:
66 if (self.bit_mask_valuebit_mask_value & 2**bit) > 0:
67 self.mask_threshold_dictmask_threshold_dict[bit] = mask_threshold_dict[bit]
68
69 # sum_weight holds the sum of weights for each pixel.
70 self.sum_weightsum_weight = np.zeros(shape, dtype=np.float64)
71 # sum_wdata holds the sum of weight*data for each pixel.
72 self.sum_wdatasum_wdata = np.zeros(shape, dtype=np.float64)
73
74 if calc_error_from_input_variance:
75 # sum_w2var holds the sum of weight**2 * variance for each pixel.
76 self.sum_w2varsum_w2var = np.zeros(shape, dtype=np.float64)
77 else:
78 # sum_weight2 holds the sum of weight**2 for each pixel.
79 self.sum_weight2sum_weight2 = np.zeros(shape, dtype=np.float64)
80 # sum_wdata2 holds the sum of weight * data**2 for each pixel.
81 self.sum_wdata2sum_wdata2 = np.zeros(shape, dtype=np.float64)
82
83 self.or_maskor_mask = np.zeros(shape, dtype=np.int64)
84 self.rejected_weights_by_bitrejected_weights_by_bit = {}
85 for bit in self.mask_threshold_dictmask_threshold_dict:
86 self.rejected_weights_by_bitrejected_weights_by_bit[bit] = np.zeros(shape, dtype=np.float64)
87
88 self.masked_pixels_maskmasked_pixels_mask = np.zeros(shape, dtype=np.int64)
89
90 if self.compute_n_imagecompute_n_image:
91 self.n_imagen_image = np.zeros(shape, dtype=np.int32)
92
93 def add_masked_image(self, masked_image, weight=1.0):
94 """Add a masked image to the stack.
95
96 Parameters
97 ----------
98 masked_image : `lsst.afw.image.MaskedImage`
99 Masked image to add to the stack.
100 weight : `float` or `np.ndarray`, optional
101 Weight to apply for weighted mean. If an array,
102 must be same size and shape as input masked_image.
103 """
104 good_pixels = np.where(((masked_image.mask.array & self.bit_mask_valuebit_mask_value) == 0)
105 & np.isfinite(masked_image.mask.array))
106
107 self.sum_weightsum_weight[good_pixels] += weight
108 self.sum_wdatasum_wdata[good_pixels] += weight*masked_image.image.array[good_pixels]
109
110 if self.compute_n_imagecompute_n_image:
111 self.n_imagen_image[good_pixels] += 1
112
113 if self.calc_error_from_input_variancecalc_error_from_input_variance:
114 self.sum_w2varsum_w2var[good_pixels] += (weight**2.)*masked_image.variance.array[good_pixels]
115 else:
116 self.sum_weight2sum_weight2[good_pixels] += weight**2.
117 self.sum_wdata2sum_wdata2[good_pixels] += weight*(masked_image.image.array[good_pixels]**2.)
118
119 # Mask bits are propagated for good pixels
120 self.or_maskor_mask[good_pixels] |= masked_image.mask.array[good_pixels]
121
122 # Bad pixels are only tracked if they cross a threshold
123 for bit in self.mask_threshold_dictmask_threshold_dict:
124 bad_pixels = ((masked_image.mask.array & 2**bit) > 0)
125 self.rejected_weights_by_bitrejected_weights_by_bit[bit][bad_pixels] += weight
126 self.masked_pixels_maskmasked_pixels_mask[bad_pixels] |= 2**bit
127
128 def fill_stacked_masked_image(self, stacked_masked_image):
129 """Fill the stacked mask image after accumulation.
130
131 Parameters
132 ----------
133 stacked_masked_image : `lsst.afw.image.MaskedImage`
134 Total masked image.
135 """
136 with np.warnings.catch_warnings():
137 # Let the NaNs through and flag bad pixels below
138 np.warnings.simplefilter("ignore")
139
140 # The image plane is sum(weight*data)/sum(weight)
141 stacked_masked_image.image.array[:, :] = self.sum_wdatasum_wdata/self.sum_weightsum_weight
142
143 if self.calc_error_from_input_variancecalc_error_from_input_variance:
144 mean_var = self.sum_w2varsum_w2var/(self.sum_weightsum_weight**2.)
145 else:
146 # Compute the biased estimator
147 variance = self.sum_wdata2sum_wdata2/self.sum_weightsum_weight - stacked_masked_image.image.array[:, :]**2.
148 # De-bias
149 variance *= (self.sum_weightsum_weight**2.)/(self.sum_weightsum_weight**2. - self.sum_weight2sum_weight2)
150
151 # Compute the mean variance
152 mean_var = variance*self.sum_weight2sum_weight2/(self.sum_weightsum_weight**2.)
153
154 stacked_masked_image.variance.array[:, :] = mean_var
155
156 # Propagate bits when they cross the threshold
157 for bit in self.mask_threshold_dictmask_threshold_dict:
158 hypothetical_total_weight = self.sum_weightsum_weight + self.rejected_weights_by_bitrejected_weights_by_bit[bit]
159 self.rejected_weights_by_bitrejected_weights_by_bit[bit] /= hypothetical_total_weight
160 propagate = np.where(self.rejected_weights_by_bitrejected_weights_by_bit[bit] > self.mask_threshold_dictmask_threshold_dict[bit])
161 self.or_maskor_mask[propagate] |= 2**bit
162
163 # Map mask planes to new bits for pixels that had at least one
164 # bad input rejected and are in the mask_map.
165 for mask_tuple in self.mask_mapmask_map:
166 self.or_maskor_mask[(self.masked_pixels_maskmasked_pixels_mask & mask_tuple[0]) > 0] |= mask_tuple[1]
167
168 stacked_masked_image.mask.array[:, :] = self.or_maskor_mask
169
170 if self.no_good_pixels_maskno_good_pixels_mask is None:
171 mask_dict = stacked_masked_image.mask.getMaskPlaneDict()
172 no_good_pixels_mask = 2**(mask_dict['NO_DATA'])
173 else:
174 no_good_pixels_mask = self.no_good_pixels_maskno_good_pixels_mask
175
176 bad_pixels = (self.sum_weightsum_weight <= 0.0)
177 stacked_masked_image.mask.array[bad_pixels] |= no_good_pixels_mask
178
179 def add_image(self, image, weight=1.0):
180 """Add an image to the stack.
181
182 No bit-filtering is performed when adding an image.
183
184 Parameters
185 ----------
186 image : `lsst.afw.image.Image`
187 Image to add to the stack.
188 weight : `float` or `np.ndarray`, optional
189 Weight to apply for weighted mean. If an array,
190 must be same size and shape as input image.
191 """
192 self.sum_weightsum_weight[:, :] += weight
193 self.sum_wdatasum_wdata[:, :] += weight*image.array[:]
194
195 if self.compute_n_imagecompute_n_image:
196 self.n_imagen_image[:, :] += 1
197
198 def fill_stacked_image(self, stacked_image):
199 """Fill the image after accumulation.
200
201 Parameters
202 ----------
203 stacked_image : `lsst.afw.image.Image`
204 Total image.
205 """
206 with np.warnings.catch_warnings():
207 # Let the NaNs through, this should only happen
208 # if we're stacking with no inputs.
209 np.warnings.simplefilter("ignore")
210
211 # The image plane is sum(weight*data)/sum(weight)
212 stacked_image.array[:, :] = self.sum_wdatasum_wdata/self.sum_weightsum_weight
213
214 @staticmethod
216 """Convert stats control to threshold dict.
217
218 Parameters
219 ----------
221
222 Returns
223 -------
224 threshold_dict : `dict`
225 Dict mapping from bit to propagation threshold.
226 """
227 threshold_dict = {}
228 for bit in range(64):
229 threshold_dict[bit] = stats_ctrl.getMaskPropagationThreshold(bit)
230
231 return threshold_dict
A class to represent a 2-dimensional array of pixels.
Definition: Image.h:51
A class to manipulate images, masks, and variance as a single object.
Definition: MaskedImage.h:73
Pass parameters to a Statistics object.
Definition: Statistics.h:92
def __init__(self, shape, bit_mask_value, mask_threshold_dict={}, mask_map=[], no_good_pixels_mask=None, calc_error_from_input_variance=True, compute_n_image=False)