CR2RE Pipeline Reference Manual 1.6.2
hdrl_DER_SNR.c
1/*
2 * This file is part of the HDRL
3 * Copyright (C) 2017 European Southern Observatory
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24/*-----------------------------------------------------------------------------
25 Includes
26 -----------------------------------------------------------------------------*/
27#include "hdrl_DER_SNR.h"
28#include "hdrl_image.h"
29#include "hdrl_utils.h"
30
31#include <math.h>
32#include <cpl.h>
33
34/* returns TRUE iff lambdas(i) < lambdas(i + 1) for every i */
35static inline cpl_boolean is_strictly_monotonic(const cpl_array * lambdas);
36
37static inline cpl_boolean
38should_skip(const cpl_binary * msk, cpl_size i1, cpl_size i2, cpl_size i3);
39
40/* DER_SNR estimation if the wavelengths are strictly monotonically increasing*/
41static inline cpl_image *
42estimate_noise_DER_SNR_on_sorted(const hdrl_data_t * flux,
43 const cpl_binary * msk_in, const cpl_size length,
44 const cpl_size half_window);
45
46/* insert the arrays in the table and sorty according to wavelength. The
47 * table is returned*/
48static inline
49cpl_table * conv_to_sorted_table(const hdrl_data_t * flux_in,
50 const cpl_binary * msk_in, const cpl_array * wavelengths,
51 const cpl_size length);
52
53/* DER_SNR estimation if the wavelengths are NOT strictly monotonically
54 * increasing*/
55static inline cpl_image *
56estimate_noise_DER_SNR_on_unsorted(const hdrl_data_t * flux_in,
57 const cpl_binary * msk_in, const cpl_array * wavelengths,
58 const cpl_size length, const cpl_size half_window);
59
64/*-----------------------------------------------------------------------------
65 Functions
66 -----------------------------------------------------------------------------*/
67
68/* ---------------------------------------------------------------------------*/
88/* ---------------------------------------------------------------------------*/
89hdrl_error_t estimate_noise_window(const hdrl_data_t * flux,
90 const cpl_binary * msk, cpl_size start, cpl_size stop,
91 const cpl_size sz){
92
93 const hdrl_data_t factor = CPL_MATH_STD_MAD / sqrt(6.0);
94
95 cpl_ensure(flux != NULL, CPL_ERROR_NULL_INPUT, NAN);
96
97 cpl_ensure(start >= 0, CPL_ERROR_INCOMPATIBLE_INPUT, NAN);
98 cpl_ensure(stop > start, CPL_ERROR_INCOMPATIBLE_INPUT, NAN);
99
100
101 cpl_ensure(stop < sz, CPL_ERROR_INCOMPATIBLE_INPUT, NAN);
102
103 start += 2;
104 stop -= 2;
105
106 const cpl_size max_elems = stop - start + 1;
107
108 cpl_ensure(max_elems > 0 , CPL_ERROR_INCOMPATIBLE_INPUT, NAN);
109
110 cpl_array * data = cpl_array_new(max_elems, HDRL_TYPE_ERROR);
111 cpl_array_fill_window_invalid(data, 0, max_elems - 1);
112
113 for(cpl_size i = start; i <= stop; ++i){
114
115 const cpl_size i_pre = i - 2;
116 const cpl_size i_post = i + 2;
117
118 if(should_skip(msk, i, i_pre, i_post)) continue;
119
120 const hdrl_data_t curr = flux[i];
121 const hdrl_data_t pre = flux[i_pre];
122 const hdrl_data_t next = flux[i_post];
123
124 const hdrl_error_t noise =
125 (hdrl_error_t)fabs(factor * (2.0 * curr - pre - next));
126 cpl_array_set(data, i - start, noise);
127 }
128
129 hdrl_error_t median = NAN;
130
131 /* If no pixels were available for DER_SNR calculation, return NAN */
132 if(cpl_array_count_invalid(data) < max_elems)
133 median = cpl_array_get_median(data);
134
135 cpl_array_delete(data);
136
137 return median;
138
139}
140
141/* ---------------------------------------------------------------------------*/
158/* ---------------------------------------------------------------------------*/
159cpl_image *
160estimate_noise_DER_SNR(const hdrl_data_t * flux_in, const cpl_binary * msk_in,
161 const cpl_array * wavelengths, const cpl_size length,
162 const cpl_size half_window){
163
164 cpl_ensure(half_window >= 2, CPL_ERROR_INCOMPATIBLE_INPUT, NULL);
165 cpl_ensure(flux_in != NULL, CPL_ERROR_NULL_INPUT, NULL);
166 cpl_ensure(wavelengths != NULL, CPL_ERROR_NULL_INPUT, NULL);
167
168 cpl_ensure(length > 4, CPL_ERROR_INCOMPATIBLE_INPUT, NULL);
169
170 /* simple case */
171 if(is_strictly_monotonic(wavelengths))
172 return estimate_noise_DER_SNR_on_sorted
173 (flux_in, msk_in, length, half_window);
174
175 /* complex case, we need to sort copies of the data calculate DER_SNR and
176 * put the correct noise sample in the correct spot, following the positioning
177 * provided as input*/
178 return estimate_noise_DER_SNR_on_unsorted(flux_in, msk_in, wavelengths,
179 length, half_window);
180}
181
182/*-----------------------------------------------------------------------------
183 Private Functions
184 -----------------------------------------------------------------------------*/
185
186static inline cpl_image *
187estimate_noise_DER_SNR_on_sorted(const hdrl_data_t * flux,
188 const cpl_binary * msk_in, const cpl_size length,
189 const cpl_size half_window){
190
191 cpl_image * to_ret = cpl_image_new(length, 1, HDRL_TYPE_ERROR);
192 cpl_mask * msk = cpl_mask_new(length, 1);
193
194 for(cpl_size i = 0; i < length; ++i){
195
196 cpl_boolean rej = msk_in != NULL && msk_in[i];
197
198 double d = NAN;
199 /* skip if bad pixel */
200 if(!rej)
201 {
202 const cpl_size start = CPL_MAX(0, i - half_window);
203 const cpl_size stop = CPL_MIN(length - 1, i + half_window);
204 d = estimate_noise_window(flux, msk_in, start, stop, length);
205 }
206
207 cpl_image_set(to_ret, i + 1, 1, d);
208
209 if(isnan(d)){
210 cpl_mask_set(msk, i + 1, 1, CPL_BINARY_1);
211 }
212 }
213
214 cpl_mask_delete(cpl_image_set_bpm(to_ret, msk));
215 return to_ret;
216}
217
218static inline
219cpl_table * conv_to_sorted_table(const hdrl_data_t * flux_in,
220 const cpl_binary * msk_in, const cpl_array * wavelengths,
221 const cpl_size length){
222
223 cpl_table * tb = cpl_table_new(length);
224 int * map = cpl_calloc(length, sizeof(int));
225 int * pmask = cpl_calloc(length, sizeof(int));
226 hdrl_data_t * flux = cpl_calloc(length, sizeof(hdrl_data_t));
227 double * pwlen = cpl_calloc(length, sizeof(double));
228
229 for(cpl_size i = 0; i < length; ++i){
230 map[i] = i;
231 pwlen[i] = cpl_array_get(wavelengths, i, NULL);
232 pmask[i] = msk_in == NULL ? 0 : (int)msk_in[i];
233 flux[i] = flux_in[i];
234 }
235
236 cpl_table_wrap_int(tb, map, "map");
237 cpl_table_wrap_int(tb, pmask, "bad_pixel_mask");
238 cpl_table_wrap_double(tb, pwlen, "lambda");
239 hdrl_wrap_table(tb, flux, "flux");
240
241 cpl_propertylist * ls = cpl_propertylist_new();
242 cpl_propertylist_append_bool(ls, "lambda", CPL_FALSE);
243 cpl_table_sort(tb, ls);
244 cpl_propertylist_delete(ls);
245 return tb;
246}
247
248static inline cpl_image *
249estimate_noise_DER_SNR_on_unsorted(const hdrl_data_t * flux_in,
250 const cpl_binary * msk_in, const cpl_array * wavelengths,
251 const cpl_size length, const cpl_size half_window){
252
253 cpl_binary * msk_sorted = cpl_calloc(length, sizeof(cpl_binary));
254
255 cpl_table * tb = conv_to_sorted_table(flux_in, msk_in, wavelengths, length);
256
257 /* extract columns */
258 int * map = (int*)cpl_table_unwrap(tb, "map");
259 hdrl_data_t * flux = (hdrl_data_t*)cpl_table_unwrap(tb, "flux");
260 int * pmask = (int*)cpl_table_unwrap(tb, "bad_pixel_mask");
261
262 cpl_table_delete(tb);
263
264 /* convert to a cpl_binary array the sorted masks */
265 for(cpl_size i = 0; i < length; ++i){
266 msk_sorted[i] = pmask[i];
267 }
268 cpl_free(pmask);
269
270 cpl_image * img_sorted = estimate_noise_DER_SNR_on_sorted
271 (flux, msk_sorted, length, half_window);
272
273 cpl_free(flux);
274 cpl_free(msk_sorted);
275
276 cpl_image * to_ret = cpl_image_new(length, 1, HDRL_TYPE_DATA);
277
278 for(cpl_size i = 0; i < length; i++){
279 const cpl_size dest_idx = map[i] + 1;
280 const cpl_size source_idx = i + 1;
281
282 int rej;
283 double data = cpl_image_get(img_sorted, source_idx, 1, &rej);
284 if(rej){
285 cpl_image_reject(to_ret, dest_idx, 1);
286 }
287 else{
288 cpl_image_set(to_ret, dest_idx, 1, data);
289 }
290 }
291
292 cpl_free(map);
293 cpl_image_delete(img_sorted);
294 return to_ret;
295}
296
297static inline cpl_boolean is_strictly_monotonic(const cpl_array * lambdas){
298 for(cpl_size i = 0; i < cpl_array_get_size(lambdas) - 1; ++i){
299 if(cpl_array_get(lambdas, i, NULL) >= cpl_array_get(lambdas, i + 1, NULL)){
300 return CPL_FALSE;
301 }
302 }
303 return CPL_TRUE;
304}
305
306static inline cpl_boolean
307should_skip(const cpl_binary * msk, cpl_size i1, cpl_size i2, cpl_size i3){
308
309 /*no mask means all the pixel are good*/
310 if(!msk) return CPL_FALSE;
311
312 return msk[i1] || msk[i2] || msk[i3];
313}
314
hdrl_error_t estimate_noise_window(const hdrl_data_t *flux, const cpl_binary *msk, cpl_size start, cpl_size stop, const cpl_size sz)
Estimate the noise in the pixels between [start, stop]. The noise calculation is done using the formu...
Definition: hdrl_DER_SNR.c:89
cpl_image * estimate_noise_DER_SNR(const hdrl_data_t *flux_in, const cpl_binary *msk_in, const cpl_array *wavelengths, const cpl_size length, const cpl_size half_window)
For every pixel in position i in img_arg, the function estimates the noise using the pixels in the wi...
Definition: hdrl_DER_SNR.c:160