LSST Applications g070148d5b3+33e5256705,g0d53e28543+25c8b88941,g0da5cf3356+2dd1178308,g1081da9e2a+62d12e78cb,g17e5ecfddb+7e422d6136,g1c76d35bf8+ede3a706f7,g295839609d+225697d880,g2e2c1a68ba+cc1f6f037e,g2ffcdf413f+853cd4dcde,g38293774b4+62d12e78cb,g3b44f30a73+d953f1ac34,g48ccf36440+885b902d19,g4b2f1765b6+7dedbde6d2,g5320a0a9f6+0c5d6105b6,g56b687f8c9+ede3a706f7,g5c4744a4d9+ef6ac23297,g5ffd174ac0+0c5d6105b6,g6075d09f38+66af417445,g667d525e37+2ced63db88,g670421136f+2ced63db88,g71f27ac40c+2ced63db88,g774830318a+463cbe8d1f,g7876bc68e5+1d137996f1,g7985c39107+62d12e78cb,g7fdac2220c+0fd8241c05,g96f01af41f+368e6903a7,g9ca82378b8+2ced63db88,g9d27549199+ef6ac23297,gabe93b2c52+e3573e3735,gb065e2a02a+3dfbe639da,gbc3249ced9+0c5d6105b6,gbec6a3398f+0c5d6105b6,gc9534b9d65+35b9f25267,gd01420fc67+0c5d6105b6,geee7ff78d7+a14128c129,gf63283c776+ede3a706f7,gfed783d017+0c5d6105b6,w.2022.47
LSST Data Management Base Package
Loading...
Searching...
No Matches
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.shape = shape
57 self.bit_mask_value = bit_mask_value
58 self.mask_map = mask_map
59 self.no_good_pixels_mask = no_good_pixels_mask
60 self.calc_error_from_input_variance = calc_error_from_input_variance
61 self.compute_n_image = compute_n_image
62
63 # Only track threshold bits that are in the bad bit_mask_value.
65 for bit in mask_threshold_dict:
66 if (self.bit_mask_value & 2**bit) > 0:
67 self.mask_threshold_dict[bit] = mask_threshold_dict[bit]
68
69 # sum_weight holds the sum of weights for each pixel.
70 self.sum_weight = np.zeros(shape, dtype=np.float64)
71 # sum_wdata holds the sum of weight*data for each pixel.
72 self.sum_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_w2var = np.zeros(shape, dtype=np.float64)
77 else:
78 # sum_weight2 holds the sum of weight**2 for each pixel.
79 self.sum_weight2 = np.zeros(shape, dtype=np.float64)
80 # sum_wdata2 holds the sum of weight * data**2 for each pixel.
81 self.sum_wdata2 = np.zeros(shape, dtype=np.float64)
82
83 self.or_mask = np.zeros(shape, dtype=np.int64)
85 for bit in self.mask_threshold_dict:
86 self.rejected_weights_by_bit[bit] = np.zeros(shape, dtype=np.float64)
87
88 self.masked_pixels_mask = np.zeros(shape, dtype=np.int64)
89
90 if self.compute_n_image:
91 self.n_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_value) == 0)
105 & np.isfinite(masked_image.mask.array))
106
107 self.sum_weight[good_pixels] += weight
108 self.sum_wdata[good_pixels] += weight*masked_image.image.array[good_pixels]
109
110 if self.compute_n_image:
111 self.n_image[good_pixels] += 1
112
114 self.sum_w2var[good_pixels] += (weight**2.)*masked_image.variance.array[good_pixels]
115 else:
116 self.sum_weight2[good_pixels] += weight**2.
117 self.sum_wdata2[good_pixels] += weight*(masked_image.image.array[good_pixels]**2.)
118
119 # Mask bits are propagated for good pixels
120 self.or_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_dict:
124 bad_pixels = ((masked_image.mask.array & 2**bit) > 0)
125 self.rejected_weights_by_bit[bit][bad_pixels] += weight
126 self.masked_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_wdata/self.sum_weight
142
144 mean_var = self.sum_w2var/(self.sum_weight**2.)
145 else:
146 # Compute the biased estimator
147 variance = self.sum_wdata2/self.sum_weight - stacked_masked_image.image.array[:, :]**2.
148 # De-bias
149 variance *= (self.sum_weight**2.)/(self.sum_weight**2. - self.sum_weight2)
150
151 # Compute the mean variance
152 mean_var = variance*self.sum_weight2/(self.sum_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_dict:
158 hypothetical_total_weight = self.sum_weight + self.rejected_weights_by_bit[bit]
159 self.rejected_weights_by_bit[bit] /= hypothetical_total_weight
160 propagate = np.where(self.rejected_weights_by_bit[bit] > self.mask_threshold_dict[bit])
161 self.or_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_map:
166 self.or_mask[(self.masked_pixels_mask & mask_tuple[0]) > 0] |= mask_tuple[1]
167
168 stacked_masked_image.mask.array[:, :] = self.or_mask
169
170 if self.no_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_mask
175
176 bad_pixels = (self.sum_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_weight[:, :] += weight
193 self.sum_wdata[:, :] += weight*image.array[:]
194
195 if self.compute_n_image:
196 self.n_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_wdata/self.sum_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:74
Pass parameters to a Statistics object.
Definition: Statistics.h:83
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)