CR2RE Pipeline Reference Manual 1.6.2
hdrl_cat_background.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20#include "hdrl_cat_background.h"
21
22#include "hdrl_cat_filter.h"
23#include "hdrl_cat_utils_sort.h"
24
25#include "hdrl_image.h"
26
27
28/*** Prototypes ***/
29
30static cpl_image * hdrl_sigclipfilter_image_grid(
31 const cpl_image *ima, cpl_matrix *x, cpl_matrix *y,
32 cpl_size filtersize_x, cpl_size filtersize_y);
33
34static cpl_matrix * matrix_linspace(cpl_size start, cpl_size stop, cpl_size step);
35
36
37/*----------------------------------------------------------------------------*/
45/*----------------------------------------------------------------------------*/
46
49/* ---------------------------------------------------------------------------*/
66/* ---------------------------------------------------------------------------*/
67cpl_error_code hdrl_background(
68 ap_t *ap, cpl_size nbsize, cpl_size bkg_subtr, hdrl_casu_result *res)
69{
70 /* Set up some variables */
71 double *map = ap->indata;
72 cpl_size nx = ap->lsiz;
73 cpl_size ny = ap->csiz;
74 unsigned char *mflag = ap->mflag;
75
76 /* check to see if nbsize is close to exact divisor */
77 nbsize = CPL_MIN(CPL_MIN(nx, ny), nbsize);
78
79 double fracx = (double)nx / (double)nbsize;
80 double fracy = (double)ny / (double)nbsize;
81
82 cpl_size ifracx = (cpl_size)(fracx + 0.1);
83 cpl_size ifracy = (cpl_size)(fracy + 0.1);
84
85 cpl_size nbsizx = nx / ifracx;
86 cpl_size nbsizy = ny / ifracy;
87
88 /* trap for small maps */
89 double a = 0.9 * nbsize;
90 cpl_size aux = (cpl_size)(a + (a < 0 ? -0.5 : 0.5));
91 nbsize = CPL_MAX(aux, CPL_MIN(nbsize, CPL_MIN(nbsizx, nbsizy)));
92 nbsize = CPL_MIN( nx, CPL_MIN(ny, nbsize ));
93
94 /* Divide the map into partitions */
95 cpl_size nbx = nx / nbsize;
96 cpl_size nby = ny / nbsize;
97
98 /* Same for background values array */
99 double **bvals = cpl_malloc(nby * sizeof(double *));
100 for (cpl_size l = 0; l < nby; l++) {
101 bvals[l] = cpl_malloc(nbx * sizeof(double));
102 }
103
104 /* Store some of this away for use later */
105 ap->backmap.nbx = nbx;
106 ap->backmap.nby = nby;
107 ap->backmap.nbsize = nbsize;
108 ap->backmap.bvals = bvals;
109
110 /* create cpl image with mask from raw data */
111 cpl_image *image = cpl_image_wrap_double(nx, ny, map);
112 cpl_mask *image_mask = cpl_image_get_bpm(image);
113 cpl_binary *image_mask_data = cpl_mask_get_data(image_mask);
114
115 /* Add additional bad pixels if present*/
116 for (cpl_size i = 0; i < nx * ny ; i++){
117 if ( mflag[i] == MF_ZEROCONF
118 || mflag[i] == MF_STUPID_VALUE
119 || mflag[i] == MF_SATURATED )
120 {
121 image_mask_data[i] = CPL_BINARY_1;
122 }
123 }
124
125 /* sigclip stepped grid */
126 cpl_size steps_x = nbx;
127 cpl_size steps_y = nby;
128 cpl_size sx = CPL_MAX(nx / steps_x, 1);
129 cpl_size sy = CPL_MAX(ny / steps_y, 1);
130 cpl_matrix *x = matrix_linspace(sx / 2, nx, sx);
131 cpl_matrix *y = matrix_linspace(sy / 2, ny, sy);
132 cpl_size filter_size_x = nbsize / 2;
133 cpl_size filter_size_y = nbsize / 2;
134 cpl_image *imgtmp_mod = hdrl_sigclipfilter_image_grid(image, x, y, filter_size_x, filter_size_y);
135
136 /* interpolate remaining bad pixels */
137 cpl_detector_interpolate_rejected(imgtmp_mod);
138
139 cpl_matrix_delete(x);
140 cpl_matrix_delete(y);
141
142 for (cpl_size l = 0; l < nby; l++) {
143 for (cpl_size j = 0; j < nbx; j++) {
144 int rej;
145 bvals[l][j] = cpl_image_get(imgtmp_mod, j + 1, l + 1, &rej);
146 }
147 }
148
149 cpl_image_delete(imgtmp_mod);
150 cpl_image_unwrap(image);
151
152 /* filter raw background values */
153 hdrl_bfilt(bvals, nbx, nby);
154
155 /* compute average sky level */
156 double *work = cpl_malloc(nbx * nby * sizeof(double));
157
158 cpl_size k = 0;
159 for (cpl_size l = 0; l < nby; l++) {
160 for (cpl_size j = 0; j < nbx; j++) {
161 work[k++] = bvals[l][j];
162 }
163 }
164
165 sort_array((void *)work, k, sizeof(*work), HDRL_SORT_DOUBLE, CPL_SORT_ASCENDING);
166 double avsky = work[(k) / 2];
167 cpl_free(work);
168
169 /* ok now correct map for background variations and put avsky back on */
170 cpl_size nbsizo2 = nbsize / 2;
171 double fnbsize = 1. / (double)nbsize;
172 for (k = 0; k < ny; k++) {
173
174 cpl_size kk = k * nx;
175
176 /* Nearest background pixel vertically */
177 cpl_size iby = (k + 1 + nbsizo2) / nbsize;
178 cpl_size ibyp1 = iby + 1;
179 iby = CPL_MIN(nby, CPL_MAX(1, iby));
180 ibyp1 = CPL_MIN(nby, ibyp1);
181
182 double dely = (k + 1. - nbsize*iby + nbsizo2) * fnbsize;
183
184 for (cpl_size j = 0; j < nx; j++) {
185
186 /* nearest background pixel across */
187 cpl_size ibx = (j + 1 + nbsizo2) / nbsize;
188 cpl_size ibxp1 = ibx + 1;
189 ibx = CPL_MIN(nbx, CPL_MAX(1, ibx));
190 ibxp1 = CPL_MIN(nbx, ibxp1);
191
192 double delx = (j + 1. - nbsize * ibx + nbsizo2) * fnbsize;
193
194 /* bilinear interpolation to find background */
195 double t1 = (1. - dely) * bvals[iby - 1][ibx - 1] + dely * bvals[ibyp1 - 1][ibx - 1];
196 double t2 = (1. - dely) * bvals[iby - 1][ibxp1 - 1] + dely * bvals[ibyp1 - 1][ibxp1 - 1];
197 double dsky = avsky - (1. - delx) * t1 - delx * t2;
198
199 if (bkg_subtr) {
200
201 map[kk+j] += dsky;
202
203 /* Fill the background map */
204 if (res->background) {
205 cpl_image_set(res->background, j + 1, k + 1, avsky - dsky);
206 }
207 }
208 }
209 }
210
211 return CPL_ERROR_NONE;
212}
213
214/* ---------------------------------------------------------------------------*/
229/* ---------------------------------------------------------------------------*/
230cpl_error_code hdrl_backstats(
231 ap_t *ap, double *skymed, double *skysig)
232{
233 /* Get some info from the ap structure */
234 double *map = ap->indata;
235 cpl_size nx = ap->lsiz;
236 cpl_size ny = ap->csiz;
237 unsigned char *mflag = ap->mflag;
238
239 cpl_image *ima_wrp = cpl_image_wrap_double(nx, ny, map);
240 cpl_mask *image_mask = cpl_image_get_bpm(ima_wrp);
241 cpl_binary *image_mask_data = cpl_mask_get_data(image_mask);
242
243 /* Add additional bad pixels if present*/
244 for (cpl_size i = 0; i < nx * ny ; i++){
245 if ( mflag[i] == MF_ZEROCONF
246 || mflag[i] == MF_STUPID_VALUE
247 || mflag[i] == MF_SATURATED )
248 {
249 image_mask_data[i] = CPL_BINARY_1;
250 }
251 }
252
253 /* do kappa-sigma clipping to thresh out basic outliers */
254 cpl_size rej_new;
255
256 cpl_size niter = 30;
257 for (cpl_size i = 0; i < niter; i++ ) {
258
259 double mad;
260 double median = cpl_image_get_mad(ima_wrp, &mad);
261 double stdev = mad * CPL_MATH_STD_MAD;
262
263 double kappa_low = 2.5;
264 double lo_cut = median - kappa_low * stdev;
265
266 double kappa_high = 2.5;
267 double hi_cut = median + kappa_high * stdev;
268
269 cpl_size rej_orig = cpl_image_count_rejected(ima_wrp);
270
271 if (lo_cut < hi_cut) {
272 cpl_mask_threshold_image(image_mask, ima_wrp, lo_cut, hi_cut, CPL_BINARY_0);
273 }
274 rej_new = cpl_image_count_rejected(ima_wrp);
275
276 if (rej_orig == rej_new){
277 break;
278 }
279 }
280
281 /* Set the final answer. Here the normal mean and standard deviation is used
282 * as all outliers should be masked and thus the mean is more accurate */
283 cpl_error_code e;
284 if (rej_new == nx * ny) {
285 *skymed = 0.;
286 *skysig = 0.;
287 e = CPL_ERROR_ILLEGAL_INPUT;
288 } else {
289 *skymed = cpl_image_get_mean( ima_wrp);
290 *skysig = cpl_image_get_stdev(ima_wrp);
291 e = CPL_ERROR_NONE;
292 }
293
294 cpl_image_unwrap(ima_wrp);
295
296 return e;
297}
298
299/* ---------------------------------------------------------------------------*/
314/* ---------------------------------------------------------------------------*/
316 ap_t *ap, double x, double y, double *skylev, double *skyrms)
317{
318 /* Define some local variables */
319 cpl_size nbx = ap->backmap.nbx;
320 cpl_size nby = ap->backmap.nby;
321 cpl_size nbsize = ap->backmap.nbsize;
322 double **bvals = ap->backmap.bvals;
323
324 /* Get closest pixel to the input location */
325 cpl_size i = (cpl_size)(x + (x < 0 ? -0.5 : 0.5));
326 cpl_size j = (cpl_size)(y + (y < 0 ? -0.5 : 0.5));
327
328 /* Now, work out where in the map to do the interpolation */
329 cpl_size nbsizo2 = nbsize / 2;
330
331 cpl_size ibx = (i + nbsizo2) / nbsize;
332 cpl_size ibxp1 = ibx + 1;
333 ibx = CPL_MIN(nbx, CPL_MAX(1, ibx));
334 ibxp1 = CPL_MIN(nbx, ibxp1);
335
336 cpl_size iby = (j + nbsizo2) / nbsize;
337 cpl_size ibyp1 = iby + 1;
338 iby = CPL_MIN(nby, CPL_MAX(1, iby));
339 ibyp1 = CPL_MIN(nby, ibyp1);
340
341 double fnbsize = 1. / (double)nbsize;
342 double delx = (i - nbsize * ibx + nbsizo2) * fnbsize;
343 double dely = (j - nbsize * iby + nbsizo2) * fnbsize;
344
345 /* Now do a linear interpolation to find the background. Calculate MAD of
346 * the four adjacent background cells as an estimate of the RMS */
347
348 double t1 = (1. - dely) * bvals[iby - 1][ibx - 1] + dely * bvals[ibyp1 - 1][ibx - 1];
349 double t2 = (1. - dely) * bvals[iby - 1][ibxp1 - 1] + dely * bvals[ibyp1 - 1][ibxp1 - 1];
350
351 *skylev = (1. - delx) * t1 + delx * t2;
352
353 *skyrms = 0.25 * ( fabs(bvals[iby - 1][ibx - 1] - *skylev)
354 + fabs(bvals[ibyp1 - 1][ibx - 1] - *skylev)
355 + fabs(bvals[iby - 1][ibxp1 - 1] - *skylev)
356 + fabs(bvals[ibyp1 - 1][ibxp1 - 1] - *skylev)
357 );
358}
359
362/* ---------------------------------------------------------------------------*/
375/* ---------------------------------------------------------------------------*/
376static cpl_image * hdrl_sigclipfilter_image_grid(
377 const cpl_image *ima, cpl_matrix *x, cpl_matrix *y,
378 cpl_size filtersize_x, cpl_size filtersize_y)
379{
380 cpl_error_ensure(ima != NULL, CPL_ERROR_NULL_INPUT,
381 return NULL, "NULL input image");
382
383 cpl_error_ensure(filtersize_x > 0 && filtersize_y > 0 , CPL_ERROR_INCOMPATIBLE_INPUT,
384 return NULL, "All function parameters must be greater then Zero");
385
386 const cpl_size nx = cpl_image_get_size_x(ima);
387 const cpl_size ny = cpl_image_get_size_y(ima);
388
389 const cpl_size steps_x = cpl_matrix_get_nrow(x);
390 const cpl_size steps_y = cpl_matrix_get_nrow(y);
391
392 cpl_image *ima_local = cpl_image_new(steps_x, steps_y, CPL_TYPE_DOUBLE);
393 cpl_image_get_bpm(ima_local);
394
395 HDRL_OMP(omp parallel for)
396 for (cpl_size iy = 0; iy < steps_y; iy++) {
397
398 cpl_size middlep_y = cpl_matrix_get(y, iy, 0);
399
400 for (cpl_size ix = 0; ix < steps_x; ix++) {
401
402 cpl_size middlep_x = cpl_matrix_get(x, ix, 0);
403
404 cpl_size lowerlimit_x = CPL_MAX(middlep_x - filtersize_x, 1 );
405 cpl_size lowerlimit_y = CPL_MAX(middlep_y - filtersize_y, 1 );
406 cpl_size upperlimit_x = CPL_MIN(middlep_x + filtersize_x, nx);
407 cpl_size upperlimit_y = CPL_MIN(middlep_y + filtersize_y, ny);
408
409 cpl_image *ima_cut = cpl_image_extract(ima, lowerlimit_x, lowerlimit_y, upperlimit_x, upperlimit_y);
410 hdrl_image *ima_cut_hdrl = hdrl_image_create(ima_cut, NULL);
411 hdrl_value median = hdrl_image_get_sigclip_mean(ima_cut_hdrl, 2.5 ,2.5 ,3);
412
413 cpl_image_set(ima_local, ix + 1, iy + 1, median.data);
414
415 /* reject also if less than one 4th bad pixels as in original catalogue code */
416 if ( isnan(median.data)
417 || cpl_image_count_rejected(ima_cut) >= 0.25 * 2 * (filtersize_x * filtersize_y))
418 {
419 cpl_image_reject(ima_local, ix + 1, iy + 1);
420 }
421
422 cpl_image_delete(ima_cut);
423 hdrl_image_delete(ima_cut_hdrl);
424 }
425 }
426
427 return ima_local;
428}
429
430/* ---------------------------------------------------------------------------*/
440/* ---------------------------------------------------------------------------*/
441static cpl_matrix * matrix_linspace(cpl_size start, cpl_size stop, cpl_size step)
442{
443 cpl_matrix * x = cpl_matrix_new(stop / step, 1);
444 for (intptr_t i = 0; start + i * step < stop && i < stop / step; i++) {
445 cpl_matrix_set(x, i, 0, start + i * step);
446 }
447 return x;
448}
void hdrl_backest(ap_t *ap, double x, double y, double *skylev, double *skyrms)
Work out estimated sky for a pixel position.
cpl_error_code hdrl_background(ap_t *ap, cpl_size nbsize, cpl_size bkg_subtr, hdrl_casu_result *res)
Model and create background map.
cpl_error_code hdrl_backstats(ap_t *ap, double *skymed, double *skysig)
Work out robust background estimate over a whole input image.
cpl_error_code hdrl_bfilt(double **xbuf, cpl_size nx, cpl_size ny)
Do bilinear median and linear filtering on background values.
hdrl_value hdrl_image_get_sigclip_mean(const hdrl_image *self, double kappa_low, double kappa_high, int niter)
computes the sigma-clipped mean and associated error of an image.
hdrl_image * hdrl_image_create(const cpl_image *image, const cpl_image *error)
create a new hdrl_image from to existing images by copying them
Definition: hdrl_image.c:295
void hdrl_image_delete(hdrl_image *himg)
delete hdrl_image
Definition: hdrl_image.c:379
cpl_error_code sort_array(void *a, cpl_size nE, cpl_size sE, hdrl_sort_type type, cpl_sort_direction dir)
sort_array hdrl function for order arrays with know types. Using the type parameter for select the co...