ERIS Pipeline Reference Manual 1.9.1
eris_nix_img_supersky_static.c
1/* $Id$
2 *
3 * This file is part of the ERIS/NIX Pipeline
4 * Copyright (C) 2025 European Southern Observatory
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12#ifdef HAVE_CONFIG_H
13#include <config.h>
14#endif
15
16/*-----------------------------------------------------------------------------
17 Includes
18 -----------------------------------------------------------------------------*/
19#include <cpl.h>
20#include <hdrl.h>
21#include <math.h>
22#include <string.h>
23
24#include "eris_nix_img_supersky.h"
25#include "eris_nix_utils.h"
26#include "eris_nix_dfs.h"
27#include "eris_pfits.h"
28#include "eris_utils.h"
29/*-----------------------------------------------------------------------------
30 Private function prototypes
31 -----------------------------------------------------------------------------*/
32static hdrl_image * eris_create_sky_flat_first(
33 hdrl_imagelist * image_list,
34 cpl_mask * masks,
35 const char * method,
36 const double kappa,
37 const int niter,
38 const cpl_size plane_id);
39
40static hdrl_image * eris_create_sky_flat_final(
41 hdrl_imagelist * image_list,
42 cpl_mask ** masks,
43 const char * method,
44 const double kappa,
45 const int niter,
46 const cpl_size plane_id);
47
48static cpl_mask * eris_detect_sources_hdrl_catalogue(
49 const cpl_image * image,
50 hdrl_parameter* p);
51
52static cpl_mask * eris_dilate_mask(
53 const cpl_mask * input_mask,
54 int radius);
55
56static cpl_error_code
57eris_image_stats(const hdrl_image *in_image,
58 double l_sig,
59 double u_sig,
60 const cpl_mask *mask,
61 double trim_fraction,
62 double *median,
63 double *mean,
64 double *stdev,
65 double *tmean,
66 double *tstd,
67 double *mad,
68 double *min_val,
69 double *max_val);
70
71static cpl_error_code
72eris_first_sky_sub(const hdrl_imagelist* science_images, const hdrl_image* sky_flat_0,
73 hdrl_imagelist** skysub_images);
74
75static cpl_error_code
76eris_locate_and_mask_sources(hdrl_imagelist* skysub_images, const cpl_image* bpm_image,
77 const cpl_parameterlist* parlist, const char* RECIPE_NAME, cpl_mask** source_masks);
78
79static cpl_error_code
80eris_load_data_and_crea_error(const char* fname, const cpl_size kk, const cpl_mask* bad_pixel_map,
81 hdrl_imagelist** science_images);
82
83static cpl_error_code
84eris_load_data_and_error_simple(const char* fname, const cpl_size kk, const cpl_mask* bad_pixel_map,
85 hdrl_imagelist** science_images);
86
87static cpl_error_code
88eris_load_and_error_cube(const char* fname, const cpl_mask* bad_pixel_map, cpl_size* kkk,
89 hdrl_imagelist** science_images);
90
91
92static cpl_error_code
93eris_create_final_skysub_products(const hdrl_imagelist* science_iml, const hdrl_image* sky_flat, cpl_frameset** products);
94
95static hdrl_imagelist*
96eris_crea_imagelist_all(cpl_frameset* raw_frames, cpl_mask* bad_pixel_map);
97
98static cpl_boolean
99eris_check_format_is_cube_of_same_size(const cpl_frameset * raw_frames);
100
101static cpl_error_code
102eris_load_and_error_cube_slice(const char* fname, const cpl_mask* bad_pixel_map, const cpl_size k,
103 const cpl_size kkk, hdrl_imagelist** science_images);
104static cpl_error_code
105eris_save_sky_flat_final( const cpl_frameset* raw_frames, const hdrl_image* sky_flat_final, cpl_frameset** product_frames);
106
107static const int debug = 0;
108/*-----------------------------------------------------------------------------
109 Functions
110 -----------------------------------------------------------------------------*/
111
112/*----------------------------------------------------------------------------*/
128/*----------------------------------------------------------------------------*/
129cpl_error_code eris_nix_img_supersky_run(
130 cpl_frameset * frameset,
131 const cpl_parameterlist * parlist,
132 const char* RECIPE_NAME,
133 cpl_frameset ** product_frames)
134{
135 cpl_frameset * raw_frames = NULL;
136 const cpl_frame * bpm_frame = NULL;
137 cpl_mask * bad_pixel_map = NULL;
138 hdrl_imagelist * science_images = NULL;
139 hdrl_imagelist * skysub_images_0 = NULL;
140 cpl_mask ** source_masks = NULL;
141 hdrl_image * sky_flat_0 = NULL;
142 hdrl_image * sky_flat_final = NULL;
143 //cpl_propertylist * plist = NULL;
144 //cpl_propertylist * qc_list = NULL;
145
146 int num_frames = 0;
147 const char * combine_method;
148 double sigma_clip;
149 int max_iter;
150 cpl_boolean save_skysub;
151 cpl_error_code error = CPL_ERROR_NONE;
152
153 cpl_ensure_code(frameset, CPL_ERROR_NULL_INPUT);
154 cpl_ensure_code(parlist, CPL_ERROR_NULL_INPUT);
155 cpl_ensure_code(product_frames, CPL_ERROR_NULL_INPUT);
156
157 /* Extract parameters */
158 combine_method = cpl_parameter_get_string(
159 cpl_parameterlist_find_const(parlist, "eris.eris_nix_img_supersky.combine_method"));
160 sigma_clip = cpl_parameter_get_double(
161 cpl_parameterlist_find_const(parlist, "eris.eris_nix_img_supersky.sigma_clip"));
162 max_iter = cpl_parameter_get_int(
163 cpl_parameterlist_find_const(parlist, "eris.eris_nix_img_supersky.max_iter"));
164 save_skysub = cpl_parameter_get_bool(
165 cpl_parameterlist_find_const(parlist, "eris.eris_nix_img_supersky.save_skysub"));
166
167 /* Extract science frames */
168 raw_frames = eris_dfs_extract_frames_with_tag (frameset, ERIS_NIX_IMG_SUPERSKY_OBJ_RAW);
169 if (!raw_frames || cpl_frameset_get_size(raw_frames) == 0) {
170 raw_frames = eris_dfs_extract_frames_with_tag (frameset, ERIS_NIX_IMG_SUPERSKY_STD_RAW);
171 }
172 if (!raw_frames || cpl_frameset_get_size(raw_frames) == 0) {
173 cpl_msg_error(cpl_func, "No science frames found with tag %s or %s",
174 ERIS_NIX_IMG_SUPERSKY_OBJ_RAW, ERIS_NIX_IMG_SUPERSKY_STD_RAW);
175 error = CPL_ERROR_DATA_NOT_FOUND;
176 goto cleanup;
177 }
178 num_frames = cpl_frameset_get_size(raw_frames);
179 cpl_msg_info(cpl_func, "Processing %d science frames", num_frames);
180
181 /* Load bad pixel map */
182 bpm_frame = cpl_frameset_find_const(frameset, ERIS_NIX_IMG_SUPERSKY_BPM);
183 if (!bpm_frame) {
184 cpl_msg_warning(cpl_func, "No bad pixel map found with tag %s", ERIS_NIX_IMG_SUPERSKY_BPM);
185 }
186 cpl_image * bpm_image = NULL;
187 //cpl_mask *inp_mask = NULL;
188 if(bpm_frame) {
189 bpm_image = cpl_image_load(cpl_frame_get_filename(bpm_frame), CPL_TYPE_INT, 0, 0);
190
191 /* Convert BPM image to mask (0=good, >0=bad) */
192 bad_pixel_map = cpl_mask_threshold_image_create(bpm_image, 0.5, DBL_MAX);
193 if(debug) {
194 cpl_mask_save(bad_pixel_map, "bad_pixel_map.fits", NULL, CPL_IO_DEFAULT);
195 }
196 //inp_mask = cpl_mask_threshold_image_create(bpm_image, -0.5, 0.5);
197 }
198 cpl_boolean format_is_cube_of_same_size = CPL_TRUE;
199 cpl_boolean test_cubes_slices = CPL_TRUE;
200 format_is_cube_of_same_size = eris_check_format_is_cube_of_same_size(raw_frames);
201 cpl_size num_tot_images = 0;
202 //char* fname;
203 *product_frames = cpl_frameset_new();
204 if(!format_is_cube_of_same_size) {
205
206 /* Load science images as HDRL imagelist */
207 science_images = eris_crea_imagelist_all(raw_frames, bad_pixel_map);
208
209 /* Step 1: Create first-pass super-sky */
210 cpl_msg_info(cpl_func, "Creating first-pass super-sky flat...");
211 sky_flat_0 = eris_create_sky_flat_first(science_images, bad_pixel_map, combine_method, sigma_clip,
212 max_iter, -1);
213
214 /* Step 2: Subtract first-pass sky from each frame */
215 eris_first_sky_sub(science_images, sky_flat_0, &skysub_images_0);
216 if (sky_flat_0) hdrl_image_delete(sky_flat_0);
217
218 eris_print_rec_status(300);
219 /* Step 3: Detect sources in sky-subtracted frames */
220 num_tot_images = hdrl_imagelist_get_size(skysub_images_0);
221 cpl_msg_info(cpl_func,"num_tot_images: %lld",num_tot_images);
222 source_masks = cpl_calloc(num_tot_images, sizeof(cpl_mask *));
223
224 eris_locate_and_mask_sources(skysub_images_0, bpm_image, parlist, RECIPE_NAME, source_masks);
225
226 eris_print_rec_status(400);
227 /* Step 4: Create final super-sky with source masks */
228 cpl_msg_info(cpl_func, "Creating final super-sky flat with source masking...");
229 sky_flat_final = eris_create_sky_flat_final(science_images, source_masks,
230 combine_method, sigma_clip, max_iter, -1);
231 eris_print_rec_status(450);
232
233 eris_print_rec_status(460);
234
235 if (!sky_flat_final) {
236 cpl_msg_error(cpl_func, "Failed to create final sky flat");
237 error = CPL_ERROR_ILLEGAL_OUTPUT;
238 goto cleanup;
239 }
240
241 if(sky_flat_final) {
242 /* Step 5: Save products */
243 eris_print_rec_status(500);
244
245 eris_save_sky_flat_final(raw_frames, sky_flat_final, product_frames);
246 }
247
248 /* Save sky-subtracted frames if requested */
249
250 if (save_skysub) {
251 eris_create_final_skysub_products(science_images, sky_flat_final, product_frames);
252 }
253
254 if (science_images) hdrl_imagelist_delete(science_images);
255 if (sky_flat_final) hdrl_image_delete(sky_flat_final);
256 if (skysub_images_0) hdrl_imagelist_delete(skysub_images_0);
257 if (source_masks != NULL) {
258 for (int i = 0; i < num_tot_images; i++) {
259 if (source_masks[i]) cpl_mask_delete(source_masks[i]);
260 }
261 cpl_free(source_masks);
262 }
263 } else {
264 cpl_msg_warning(cpl_func,"cube format, process slice by slice");
265
266 const cpl_frame * frame = cpl_frameset_get_position_const(raw_frames, 0);
267 const char * filename = cpl_frame_get_filename(frame);
268 cpl_imagelist * data_iml = cpl_imagelist_load(filename, CPL_TYPE_DOUBLE, 1);
269 cpl_size nplanes = cpl_imagelist_get_size(data_iml);
270
271 for(cpl_size k = 0; k < nplanes; k++) {
272 science_images = hdrl_imagelist_new();
273 for(cpl_size kkk = 0; kkk < num_frames; kkk++) {
274 frame = cpl_frameset_get_position_const(raw_frames, kkk);
275 filename = cpl_frame_get_filename(frame);
276 eris_load_and_error_cube_slice(filename, bad_pixel_map, k, kkk, &science_images);
277 }
278
279 /* Step 1: Create first-pass super-sky */
280 cpl_msg_info(cpl_func, "Creating first-pass super-sky flat...");
281 sky_flat_0 = eris_create_sky_flat_first(science_images, bad_pixel_map, combine_method, sigma_clip, max_iter, k);
282
283 /* Step 2: Subtract first-pass sky from each frame */
284 eris_first_sky_sub(science_images, sky_flat_0, &skysub_images_0);
285
286 eris_print_rec_status(300);
287 /* Step 3: Detect sources in sky-subtracted frames */
288 num_tot_images = hdrl_imagelist_get_size(skysub_images_0);
289 cpl_msg_info(cpl_func,"num_tot_images: %lld",num_tot_images);
290 source_masks = cpl_calloc(num_tot_images, sizeof(cpl_mask *));
291
292 eris_locate_and_mask_sources(skysub_images_0, bpm_image, parlist, RECIPE_NAME, source_masks);
293
294 eris_print_rec_status(400);
295 /* Step 4: Create final super-sky with source masks */
296 cpl_msg_info(cpl_func, "Creating final super-sky flat with source masking...");
297
298 sky_flat_final = eris_create_sky_flat_final(science_images, source_masks,
299 combine_method, sigma_clip, max_iter, k);
300
301 if (save_skysub) {
302 eris_create_final_skysub_products(science_images, sky_flat_final, product_frames);
303 }
304
305
306
307 if (sky_flat_0) hdrl_image_delete(sky_flat_0);
308 if (science_images) hdrl_imagelist_delete(science_images);
309 if (sky_flat_final) hdrl_image_delete(sky_flat_final);
310 if (skysub_images_0) hdrl_imagelist_delete(skysub_images_0);
311 if (source_masks != NULL) {
312 for (int i = 0; i < num_tot_images; i++) {
313 if (source_masks[i]) cpl_mask_delete(source_masks[i]);
314 }
315 cpl_free(source_masks);
316 }
317 }
318 cpl_imagelist_delete(data_iml);
319 }
320
321 eris_print_rec_status(700);
322
323 eris_print_rec_status(800);
324 cpl_msg_info(cpl_func, "Super-sky recipe completed successfully");
325
326cleanup:
327 eris_print_rec_status(600);
328 if (raw_frames) cpl_frameset_delete(raw_frames);
329 if (bad_pixel_map) cpl_mask_delete(bad_pixel_map);
330
331 //if(inp_mask) cpl_mask_delete(inp_mask);
332 if(bpm_image !=NULL) cpl_image_delete(bpm_image);
333
334 eris_check_error_code(cpl_func);
335 return error;
336}
337
338static cpl_error_code
339eris_save_sky_flat_final( const cpl_frameset* raw_frames, const hdrl_image* sky_flat_final, cpl_frameset** product_frames)
340{
341
342 /* Step 5: Save products */
343 cpl_size num_frames = cpl_frameset_get_size(raw_frames);
344 eris_print_rec_status(500);
345 //*product_frames = cpl_frameset_new();
346 hdrl_value med_sky_final = hdrl_image_get_median(sky_flat_final);
347
348 /* Save master sky flat */
349 cpl_propertylist* plist = cpl_propertylist_load(cpl_frame_get_filename(
350 cpl_frameset_get_position_const(raw_frames, 0)), 0);
351 cpl_propertylist_update_string(plist, CPL_DFS_PRO_CATG, ERIS_NIX_IMG_SUPERSKY_SKYFLAT);
352 cpl_propertylist_update_double(plist, "ESO QC SKY MEDIAN", med_sky_final.data);
353 cpl_propertylist_update_int(plist, "ESO QC NFRAMES", num_frames);
354 eris_print_rec_status(600);
355 char* fname = cpl_sprintf("sky_flat_2nd.fits");
356
357 cpl_image_save(hdrl_image_get_image_const(sky_flat_final), fname,CPL_TYPE_DOUBLE, plist, CPL_IO_CREATE);
358
359 cpl_frame * product = cpl_frame_new();
360 cpl_frame_set_filename(product, fname);
361 cpl_frame_set_tag(product, ERIS_NIX_IMG_SUPERSKY_SKYFLAT);
362 cpl_frame_set_type(product, CPL_FRAME_TYPE_IMAGE);
363 cpl_frame_set_group(product, CPL_FRAME_GROUP_PRODUCT);
364 cpl_frameset_insert(*product_frames, product);
365
366 cpl_free(fname);
367 cpl_propertylist_delete(plist);
368
369 eris_check_error_code(cpl_func);
370 return cpl_error_get_code();
371
372}
373static hdrl_imagelist*
374eris_crea_imagelist_all(cpl_frameset* raw_frames, cpl_mask* bad_pixel_map)
375{
376
377 /* Load science images as HDRL imagelist */
378 hdrl_imagelist* science_images = hdrl_imagelist_new();
379 cpl_size num_frames = cpl_frameset_get_size(raw_frames);
380 cpl_size next = 0;
381 const char* format = NULL;
382 cpl_propertylist* pheader = NULL;
383 eris_print_rec_status(100);
384 cpl_size kkk = 0;
385 for (cpl_size kk = 0; kk < num_frames; kk++) {
386 const cpl_frame * frame = cpl_frameset_get_position_const(raw_frames, kk);
387 const char * filename = cpl_frame_get_filename(frame);
388 next = cpl_frame_get_nextensions(frame);
389 pheader = cpl_propertylist_load(filename,0);
390 format = eris_pfits_get_frame_format(pheader);
391
392 if (next < 4) {
393 eris_load_data_and_crea_error(filename, kk, bad_pixel_map, &science_images);
394 } else {
395 cpl_msg_info(cpl_func,"sci data with extentions %s", filename);
396 if(!strcmp(format,"single")) {
397 eris_load_data_and_error_simple(filename, kk, bad_pixel_map, &science_images);
398 } else {
399 eris_load_and_error_cube(filename, bad_pixel_map, &kkk, &science_images);
400 }/* end cube format case */
401 } /* end case with extensions */
402 cpl_propertylist_delete(pheader);
403 } /* end loop over input frames */
404
405 cpl_size nx = hdrl_image_get_size_x(hdrl_imagelist_get_const(science_images, 0));
406 cpl_size ny = hdrl_image_get_size_y(hdrl_imagelist_get_const(science_images, 0));
407 cpl_msg_info(cpl_func, "Image dimensions: %lld x %lld", nx, ny);
408
409
410 eris_check_error_code(cpl_func);
411 return science_images;
412}
413
414static cpl_boolean
415eris_check_format_is_cube_of_same_size(const cpl_frameset * raw_frames)
416{
417
418 cpl_boolean result = CPL_TRUE;
419 cpl_size nplanes = 0;
420 cpl_size nplanes_tmp = 0;
421
422 cpl_size num_frames = cpl_frameset_get_size(raw_frames);
423 cpl_size next = 0;
424 for (int kk = 0; kk < num_frames; kk++) {
425 const cpl_frame * frame = cpl_frameset_get_position_const(raw_frames, kk);
426 next = cpl_frame_get_nextensions(frame);
427
428 if(next > 3) {
429 const char * fname = cpl_frame_get_filename(frame);
430
431 cpl_propertylist* xhead = cpl_propertylist_load(fname,1);
432 if(cpl_propertylist_has(xhead,"CD3_3")) {
433 cpl_imagelist * data_iml = cpl_imagelist_load(fname, CPL_TYPE_DOUBLE, 1);
434
435 nplanes_tmp = cpl_imagelist_get_size(data_iml);
436 if(kk == 0) {
437 nplanes = nplanes_tmp;
438 } else if (nplanes_tmp != nplanes) {
439 cpl_msg_info(cpl_func,"cubes not of same size");
440 result = CPL_FALSE;
441 }
442 cpl_imagelist_delete(data_iml);
443 } else {
444 cpl_msg_info(cpl_func,"Input Data are simple format (images)");
445 cpl_propertylist_delete(xhead);
446 return CPL_FALSE;
447 }
448 cpl_propertylist_delete(xhead);
449 } else {
450 cpl_msg_info(cpl_func,"Data with less than 4 extensions");
451 return CPL_FALSE;
452 }
453 }
454 eris_check_error_code(cpl_func);
455 return result;
456}
457
458/*----------------------------------------------------------------------------*/
467/*----------------------------------------------------------------------------*/
468
469static cpl_error_code
470eris_create_final_skysub_products(const hdrl_imagelist* science_iml, const hdrl_image* sky_flat, cpl_frameset** products)
471{
472
473 cpl_msg_info(cpl_func, "Saving sky-subtracted frames...");
474 cpl_size num_tot_images = hdrl_imagelist_get_size(science_iml);
475 hdrl_value med_sky = hdrl_image_get_median(sky_flat);
476
477 for (int i = 0; i < num_tot_images; i++) {
478
479 const hdrl_image * himg = hdrl_imagelist_get_const(science_iml, i);
480 hdrl_value med_img = hdrl_image_get_median(himg);
481 hdrl_value scale;
482 scale.data = med_img.data / med_sky.data;
483
484 hdrl_image * skysub = hdrl_image_duplicate(himg);
485 hdrl_image * scaled_sky = hdrl_image_duplicate(sky_flat);
486 hdrl_image_mul_scalar(scaled_sky, scale);
487 hdrl_image_sub_image(skysub, scaled_sky);
488 hdrl_value med_skyub = hdrl_image_get_median(skysub);
489 /* Create output filename */
490 char outfile[256];
491 snprintf(outfile, 256, "sci_skysub_2nd_%03d.fits", i);
492
493 /* Load and update header */
494 cpl_msg_info(cpl_func,"ESO QC SKY_MED: %g",med_img.data);
495 cpl_msg_info(cpl_func,"ESO QC SKYSUB_MED: %g", med_skyub.data);
496 /*
497 const cpl_frame * frame = cpl_frameset_get_position_const(raw_frames, i);
498 plist = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
499 cpl_propertylist_update_string(plist, CPL_DFS_PRO_CATG,
500 ERIS_NIX_IMG_SUPERSKY_SKYSUB);
501
502 //cpl_propertylist_update_double(plist, "ESO QC SKY_MED", med_img.data);
503 //cpl_propertylist_update_double(plist, "ESO QC SKYSUB_MED", med_skyub.data);
504
505 */
506
507 cpl_image_save(hdrl_image_get_image(skysub), outfile, CPL_TYPE_DOUBLE, NULL, CPL_IO_CREATE);
508
509 cpl_frame * product = cpl_frame_new();
510 cpl_frame_set_filename(product, outfile);
511 cpl_frame_set_tag(product, ERIS_NIX_IMG_SUPERSKY_SKYSUB);
512 cpl_frame_set_type(product, CPL_FRAME_TYPE_IMAGE);
513 cpl_frame_set_group(product, CPL_FRAME_GROUP_PRODUCT);
514 cpl_frameset_insert(*products, product);
515 //cpl_propertylist_delete(plist);
516 hdrl_image_delete(skysub);
517 hdrl_image_delete(scaled_sky);
518 }
519
520 eris_check_error_code(cpl_func);
521 return cpl_error_get_code();
522}
523/*----------------------------------------------------------------------------*/
533/*----------------------------------------------------------------------------*/
534static cpl_error_code
535eris_load_and_error_cube(const char* fname, const cpl_mask* bad_pixel_map, cpl_size* kkk,
536 hdrl_imagelist** science_images){
537
538 eris_print_rec_status(0);
539 cpl_imagelist * data_iml = cpl_imagelist_load(fname, CPL_TYPE_DOUBLE, 1);
540 cpl_imagelist * error_iml = cpl_imagelist_load(fname, CPL_TYPE_DOUBLE, 2);
541 cpl_mask * dqual_msk = cpl_mask_load(fname, 0, 3);
542 cpl_imagelist * confm_iml = cpl_imagelist_load(fname, CPL_TYPE_DOUBLE, 4);
543
544 double* pconf_img = cpl_image_get_data_double(cpl_imagelist_get(confm_iml,0));
545
546 cpl_binary* pbpm = cpl_mask_get_data(dqual_msk);
547 cpl_size sx = cpl_mask_get_size_x(dqual_msk);
548 cpl_size sy = cpl_mask_get_size_y(dqual_msk);
549 cpl_size sz = cpl_imagelist_get_size(data_iml);
550
551 cpl_mask* mask_tot = cpl_mask_duplicate(dqual_msk);
552 if(bad_pixel_map) {
553 cpl_mask_or(mask_tot, bad_pixel_map);
554 }
555 double* pdata = NULL;
556 cpl_image * data_img = NULL;
557 cpl_image * error_img = NULL;
558 cpl_size pixel = 0;
559 cpl_size j_sx = 0;
560 for (cpl_size k = 0; k < sz; k++) {
561 data_img = cpl_imagelist_get(data_iml,k);
562 error_img = cpl_imagelist_get(error_iml,k);
563 pdata = cpl_image_get_data(cpl_imagelist_get(data_iml,k));
564 for (cpl_size j = 0; j < sy; j++) {
565
566 j_sx = j * sx;
567 for (cpl_size i = 0; i < sx; i++) {
568
569 pixel = i + j_sx;
570 if((pconf_img[pixel] == 0) || !isfinite(pdata[pixel])) {
571 pbpm[pixel] = CPL_BINARY_1;
572 }
573 }
574
575 }
576
577 cpl_image_reject_from_mask(data_img, mask_tot);
578 cpl_image_reject_from_mask(error_img, mask_tot);
579 hdrl_image * himg = hdrl_image_create(data_img, error_img);
580
581 hdrl_imagelist_set(*science_images, himg, *kkk);
582 (*kkk)++;
583 }
584
585 cpl_mask_delete(mask_tot);
586 cpl_mask_delete(dqual_msk);
587 cpl_imagelist_delete(data_iml);
588 cpl_imagelist_delete(error_iml);
589 cpl_imagelist_delete(confm_iml);
590
591 eris_check_error_code(cpl_func);
592 return cpl_error_get_code();
593
594}
595
596
597
598/*----------------------------------------------------------------------------*/
608/*----------------------------------------------------------------------------*/
609static cpl_error_code
610eris_load_and_error_cube_slice(const char* fname, const cpl_mask* bad_pixel_map, const cpl_size k,
611 const cpl_size kkk, hdrl_imagelist** science_images){
612
613
614 cpl_imagelist * data_iml = cpl_imagelist_load(fname, CPL_TYPE_DOUBLE, 1);
615 cpl_imagelist * error_iml = cpl_imagelist_load(fname, CPL_TYPE_DOUBLE, 2);
616 cpl_mask * dqual_msk = cpl_mask_load(fname, 0, 3);
617 cpl_imagelist * confm_iml = cpl_imagelist_load(fname, CPL_TYPE_DOUBLE, 4);
618
619 double* pconf_img = cpl_image_get_data_double(cpl_imagelist_get(confm_iml,0));
620
621 cpl_binary* pbpm = cpl_mask_get_data(dqual_msk);
622 cpl_size sx = cpl_mask_get_size_x(dqual_msk);
623 cpl_size sy = cpl_mask_get_size_y(dqual_msk);
624
625 cpl_mask* mask_tot = cpl_mask_duplicate(dqual_msk);
626 if(bad_pixel_map) {
627 cpl_mask_or(mask_tot, bad_pixel_map);
628 }
629 double* pdata = NULL;
630 cpl_image * data_img = NULL;
631 cpl_image * error_img = NULL;
632 cpl_size pixel = 0;
633 cpl_size j_sx = 0;
634
635 data_img = cpl_imagelist_get(data_iml,k);
636 error_img = cpl_imagelist_get(error_iml,k);
637 pdata = cpl_image_get_data(cpl_imagelist_get(data_iml,k));
638
639 for (cpl_size j = 0; j < sy; j++) {
640
641 j_sx = j * sx;
642 for (cpl_size i = 0; i < sx; i++) {
643
644 pixel = i + j_sx;
645 if((pconf_img[pixel] == 0) || !isfinite(pdata[pixel])) {
646 pbpm[pixel] = CPL_BINARY_1;
647 }
648 }
649
650 }
651
652 cpl_image_reject_from_mask(data_img, mask_tot);
653 cpl_image_reject_from_mask(error_img, mask_tot);
654 hdrl_image * himg = hdrl_image_create(data_img, error_img);
655 hdrl_imagelist_set(*science_images, himg, kkk);
656
657 cpl_mask_delete(mask_tot);
658 cpl_mask_delete(dqual_msk);
659 cpl_imagelist_delete(data_iml);
660 cpl_imagelist_delete(error_iml);
661 cpl_imagelist_delete(confm_iml);
662
663 eris_check_error_code(cpl_func);
664 return cpl_error_get_code();
665
666}
667
668
669/*----------------------------------------------------------------------------*/
679/*----------------------------------------------------------------------------*/
680static cpl_error_code
681eris_load_data_and_error_simple(const char* fname, const cpl_size kk, const cpl_mask* bad_pixel_map,
682 hdrl_imagelist** science_images)
683{
684
685 cpl_ensure_code(fname, CPL_ERROR_NULL_INPUT);
686 cpl_image * data_img = cpl_image_load(fname, CPL_TYPE_DOUBLE, 0, 1);
687 cpl_image * error_img = cpl_image_load(fname, CPL_TYPE_DOUBLE, 0, 2);
688 cpl_mask* dqual_msk = cpl_mask_load(fname, 0, 3);
689 cpl_image* confm_img = cpl_image_load(fname, CPL_TYPE_DOUBLE, 0, 4);
690 double* pdata = cpl_image_get_data(data_img);
691 double* pconf_img = cpl_image_get_data_double(confm_img);
692 cpl_binary* pbpm = cpl_mask_get_data(dqual_msk);
693 cpl_size sx = cpl_image_get_size_x(confm_img);
694 cpl_size sy = cpl_image_get_size_y(confm_img);
695 cpl_size j_sx = 0;
696 cpl_size pixel = 0;
697 for (cpl_size j = 0; j < sy; j++) {
698
699 j_sx = j * sx;
700 for (cpl_size i = 0; i < sx; i++) {
701
702 pixel = i + j_sx;
703 if((pconf_img[pixel] == 0) || !isfinite(pdata[pixel])) {
704 pbpm[pixel] = CPL_BINARY_1;
705 }
706 }
707
708 }
709
710 cpl_mask* mask_tot = cpl_mask_duplicate(dqual_msk);
711 if(bad_pixel_map) {
712 cpl_mask_or(mask_tot, bad_pixel_map);
713 }
714 cpl_image_reject_from_mask(data_img, mask_tot);
715 cpl_image_reject_from_mask(error_img, mask_tot);
716
717 //if(cpl_error_get_code != CPL_ERROR_NONE) {
718 // exit(0);
719 //}
720 hdrl_image * himg = hdrl_image_create(data_img, error_img);
721 hdrl_imagelist_set(*science_images, himg, kk);
722
723 cpl_image_delete(data_img);
724 cpl_image_delete(error_img);
725 cpl_mask_delete(mask_tot);
726 cpl_mask_delete(dqual_msk);
727 cpl_image_delete(confm_img);
728
729 // determine statistics of the science images
730 cpl_mask* mask = cpl_image_get_bpm(hdrl_image_get_image(himg));
731 double median=0, mean=0, stdev=0,tmean=0, tstd=0, mad=0, min_val=0, max_val=0;
732 eris_image_stats(
733 himg,
734 2.0, // l_sig: lower sigma clip
735 2.0, // u_sig: upper sigma clip
736 mask, // optional mask
737 0.1, // 10% trim fraction
738 &median, &mean, &stdev, &tmean, &tstd, &mad, &min_val, &max_val
739 );
740
741 cpl_msg_info(cpl_func,"process file: %s", fname);
742 cpl_msg_info(cpl_func, "Median=%.3f, Mean=%.3f, StdDev=%.3f "
743 "Trimmed: Mean=%.3f, StdDev=%.3f, MAD=%.3f "
744 "Range: [%.3f, %.3f]", median, mean, stdev, tmean, tstd, mad, min_val, max_val);
745
746 eris_check_error_code(cpl_func);
747 return cpl_error_get_code();
748}
749
750/*----------------------------------------------------------------------------*/
760/*----------------------------------------------------------------------------*/
761static cpl_error_code
762eris_load_data_and_crea_error(const char* fname, const cpl_size kk, const cpl_mask* bad_pixel_map,
763 hdrl_imagelist** science_images)
764{
765 cpl_ensure_code(fname, CPL_ERROR_NULL_INPUT);
766 cpl_image * data_img = NULL;
767 cpl_image * error_img = NULL;
768 cpl_msg_info(cpl_func,"sci data with less than 4 extensions");
769 data_img = cpl_image_load(fname, CPL_TYPE_DOUBLE, 0, 0);
770 if (!data_img) {
771 cpl_msg_error(cpl_func, "Failed to load frame %s", fname);
772 eris_check_error_code(cpl_func);
773 return cpl_error_get_code();
774 }
775 double* pdata = NULL;
776 cpl_mask * dqual_msk = NULL;
777 pdata = cpl_image_get_data(data_img);
778 dqual_msk = cpl_image_get_bpm(data_img);
779 cpl_size sx = cpl_image_get_size_x(data_img);
780 cpl_size sy = cpl_image_get_size_y(data_img);
781 cpl_binary* pbpm = cpl_mask_get_data(dqual_msk);
782 cpl_size j_sx = 0;
783 cpl_size pixel = 0;
784 for (cpl_size j = 0; j < sy; j++) {
785 j_sx = j * sx;
786 for (cpl_size i = 0; i < sx; i++) {
787 pixel = i + j_sx;
788 if(!isfinite(pdata[pixel])) {
789 pbpm[pixel] = CPL_BINARY_1;
790 }
791 }
792
793 }
794 cpl_mask* mask_tot = cpl_mask_duplicate(dqual_msk);
795 if(bad_pixel_map) {
796 cpl_mask_or(mask_tot, bad_pixel_map);
797 }
798 // Apply bad pixel mask
799 cpl_image_reject_from_mask(data_img, mask_tot);
800
801 // Create error image (simple Poisson model for now)
802 error_img = cpl_image_duplicate(data_img);
803 cpl_image_abs(error_img);
804 cpl_image_add_scalar(error_img, 1.0); // Add readnoise^2
805 cpl_image_power(error_img, 0.5);
806
807
808 hdrl_image * himg = hdrl_image_create(data_img, error_img);
809 hdrl_imagelist_set(*science_images, himg, kk);
810
811 cpl_image_delete(data_img);
812 cpl_image_delete(error_img);
813 cpl_mask_delete(mask_tot);
814
815 eris_check_error_code(cpl_func);
816 return cpl_error_get_code();
817}
818
819/*----------------------------------------------------------------------------*/
830/*----------------------------------------------------------------------------*/
831static cpl_error_code
832eris_locate_and_mask_sources(hdrl_imagelist* skysub_images, const cpl_image* bpm_image,
833 const cpl_parameterlist* parlist, const char* RECIPE_NAME, cpl_mask** source_masks)
834{
835 cpl_msg_info(cpl_func, "Detecting sources...");
836 cpl_size num_tot_images = hdrl_imagelist_get_size(skysub_images);
837 //cpl_msg_info(cpl_func,"num_tot_images: %lld",num_tot_images);
838 //source_masks = cpl_calloc(num_tot_images, sizeof(cpl_mask *));
839
840 hdrl_parameter *par_cat = hdrl_catalogue_parameter_parse_parlist(parlist, RECIPE_NAME);
841 const hdrl_catalogue_options opt = HDRL_CATALOGUE_SEGMAP;
843
844 if(bpm_image) {
845 if(debug) {
846 for (int i = 0; i < num_tot_images; i++) {
847 hdrl_image * himg = hdrl_imagelist_get(skysub_images, i);
848 cpl_image * data = hdrl_image_get_image(himg);
849
850 cpl_mask *bpm = cpl_mask_threshold_image_create(bpm_image, 0, INT_MAX);
851 cpl_image_reject_from_mask(data, bpm);
852 cpl_mask_delete(bpm);
853
854 source_masks[i] = eris_detect_sources_hdrl_catalogue(data, par_cat);
855
856 char* fname = cpl_sprintf("msk_%d.fits",i);
857 cpl_mask_save(source_masks[i], fname, NULL, CPL_IO_DEFAULT);
858 cpl_free(fname);
859
860 /* Convert BPM image to mask (0=good, >0=bad) */
861 cpl_mask* bad_pixel_map = cpl_mask_threshold_image_create(bpm_image, 0.5, DBL_MAX);
862
863 int nsources = cpl_mask_count(source_masks[i]) - cpl_mask_count(bad_pixel_map);
864 cpl_msg_info(cpl_func, " Frame %d: detected %d source pixels", i+1, nsources);
865
866 cpl_mask_delete(bad_pixel_map);
867 }
868 } else {
869 for (int i = 0; i < num_tot_images; i++) {
870 hdrl_image * himg = hdrl_imagelist_get(skysub_images, i);
871 cpl_image * data = hdrl_image_get_image(himg);
872
873 cpl_mask *bpm = cpl_mask_threshold_image_create(bpm_image, 0, INT_MAX);
874 cpl_image_reject_from_mask(data, bpm);
875 cpl_mask_delete(bpm);
876
877 source_masks[i] = eris_detect_sources_hdrl_catalogue(data, par_cat);
878
879 /* Convert BPM image to mask (0=good, >0=bad) */
880 cpl_mask* bad_pixel_map = cpl_mask_threshold_image_create(bpm_image, 0.5, DBL_MAX);
881
882 int nsources = cpl_mask_count(source_masks[i]) - cpl_mask_count(bad_pixel_map);
883 cpl_msg_info(cpl_func, " Frame %d: detected %d source pixels", i+1, nsources);
884
885 cpl_mask_delete(bad_pixel_map);
886 }
887 }
888 } else {
889 if(debug) {
890 for (int i = 0; i < num_tot_images; i++) {
891 hdrl_image * himg = hdrl_imagelist_get(skysub_images, i);
892 cpl_image * data = hdrl_image_get_image(himg);
893
894 source_masks[i] = eris_detect_sources_hdrl_catalogue(data, par_cat);
895 char* fname = cpl_sprintf("msk_%d.fits",i);
896 cpl_mask_save(source_masks[i], fname, NULL, CPL_IO_DEFAULT);
897 cpl_free(fname);
898
899 int nsources = cpl_mask_count(source_masks[i]);
900 cpl_msg_info(cpl_func, " Frame %d: detected %d source pixels", i+1, nsources);
901 }
902 } else {
903 for (int i = 0; i < num_tot_images; i++) {
904 hdrl_image * himg = hdrl_imagelist_get(skysub_images, i);
905 cpl_image * data = hdrl_image_get_image(himg);
906
907 source_masks[i] = eris_detect_sources_hdrl_catalogue(data, par_cat);
908
909 int nsources = cpl_mask_count(source_masks[i]);
910 cpl_msg_info(cpl_func, " Frame %d: detected %d source pixels", i+1, nsources);
911 }
912 }
913 }
914
915
916 if(par_cat) hdrl_parameter_delete(par_cat);
917 eris_check_error_code(cpl_func);
918 return cpl_error_get_code();
919}
920/*----------------------------------------------------------------------------*/
928/*----------------------------------------------------------------------------*/
929static cpl_error_code
930eris_first_sky_sub(const hdrl_imagelist* science_images, const hdrl_image* sky_flat_0,
931 hdrl_imagelist** skysub_images)
932{
933
934 cpl_msg_info(cpl_func, "Subtracting first-pass sky flat...");
935 *skysub_images = hdrl_imagelist_new();
936 hdrl_value med_sky_0 = hdrl_image_get_median(sky_flat_0);
937 char* fname = NULL;
938 hdrl_value scale = {0, 0};
939 cpl_size num_tot_images = hdrl_imagelist_get_size(science_images);
940 for (int i = 0; i < num_tot_images; i++) {
941 const hdrl_image * himg = hdrl_imagelist_get_const(science_images, i);
942 hdrl_value med_img = hdrl_image_get_median(himg);;
943
944 scale.data = med_img.data / med_sky_0.data;
945 hdrl_image * skysub = hdrl_image_duplicate(himg);
946 hdrl_image * scaled_sky = hdrl_image_duplicate(sky_flat_0);
947 hdrl_image_mul_scalar(scaled_sky, scale);
948
949 if(debug) {
950 fname = cpl_sprintf("sci_%d.fits",i);
951 cpl_image_save(hdrl_image_get_image_const(himg),fname, CPL_TYPE_DOUBLE, NULL, CPL_IO_DEFAULT);
952 cpl_free(fname);
953 fname = cpl_sprintf("scaled_sky_0_%d.fits",i);
954 cpl_image_save(hdrl_image_get_image(scaled_sky),fname, CPL_TYPE_DOUBLE, NULL, CPL_IO_DEFAULT);
955 cpl_free(fname);
956 }
957
958 hdrl_image_sub_image(skysub, scaled_sky);
959
960 if(debug) {
961 fname = cpl_sprintf("sci_skysub_1st_%03d.fits",i);
962 cpl_image_save(hdrl_image_get_image(skysub), fname, CPL_TYPE_DOUBLE, NULL, CPL_IO_DEFAULT);
963 cpl_free(fname);
964 }
965 hdrl_imagelist_set(*skysub_images, skysub, i);
966 hdrl_image_delete(scaled_sky);
967 }
968
969 eris_check_error_code(cpl_func);
970 return cpl_error_get_code();
971}
972
973#define HDRL_USE_PRIVATE YES
974/*----------------------------------------------------------------------------*/
984/*----------------------------------------------------------------------------*/
985static hdrl_image * eris_create_sky_flat_first(
986 hdrl_imagelist * image_list,
987 cpl_mask * mask,
988 const char * method,
989 const double kappa,
990 const int niter,
991 const cpl_size plane_id)
992{
993 hdrl_image * result = NULL;
994
995 hdrl_imagelist * masked_list = NULL;
996 hdrl_parameter * collapse_par = NULL;
997 cpl_image* contrib;
998
999 cpl_ensure(image_list, CPL_ERROR_NULL_INPUT, NULL);
1000 cpl_ensure(method, CPL_ERROR_NULL_INPUT, NULL);
1001
1002 int num_images = hdrl_imagelist_get_size(image_list);
1003 cpl_ensure(num_images > 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
1004 cpl_msg_warning(cpl_func,"kappa: %g niter: %d", kappa, niter);
1005 /* Apply masks if provided */
1006 if (mask) {
1007 masked_list = hdrl_imagelist_new();
1008
1009 for (int i = 0; i < num_images; i++) {
1010
1011 hdrl_image * himg = hdrl_image_duplicate(
1012 hdrl_imagelist_get_const(image_list, i));
1013
1014 cpl_image * data = hdrl_image_get_image(himg);
1015 cpl_image_reject_from_mask(data, mask);
1016 hdrl_imagelist_set(masked_list, himg, i);
1017
1018 }
1019 } else {
1020 masked_list = image_list;
1021 }
1022
1023 /* Create collapse parameters */
1024 if (strcmp(method, "median") == 0) {
1026 } else {
1027 cpl_msg_info(cpl_func,"kappa: %g niter: %d",kappa, niter);
1028 collapse_par = hdrl_collapse_sigclip_parameter_create(kappa, kappa, niter);
1029 }
1030
1031 /* Collapse image list */
1032 hdrl_imagelist_collapse(image_list, collapse_par, &result, &contrib);
1033 if(debug) {
1034 char* fname;
1035 if(plane_id < 0) {
1036 fname = cpl_sprintf("sky_flat_1st.fits");
1037 } else {
1038 fname = cpl_sprintf("sky_flat_1st_plane_%3.3lld.fits",plane_id);
1039 }
1040 cpl_image_save(hdrl_image_get_image(result), fname,CPL_TYPE_DOUBLE,NULL,CPL_IO_DEFAULT);
1041 cpl_free(fname);
1042 }
1043
1044 /* Cleanup */
1045 cpl_image_delete(contrib);
1046 hdrl_parameter_delete(collapse_par);
1047 if (mask && masked_list) hdrl_imagelist_delete(masked_list);
1048 eris_check_error_code(cpl_func);
1049 return result;
1050}
1051
1061/*----------------------------------------------------------------------------*/
1062static hdrl_image * eris_create_sky_flat_final(
1063 hdrl_imagelist * image_list,
1064 cpl_mask ** masks,
1065 const char * method,
1066 const double kappa,
1067 const int niter,
1068 const cpl_size plane_id)
1069{
1070 hdrl_image * result = NULL;
1071 hdrl_imagelist * masked_list = NULL;
1072 hdrl_parameter * collapse_par = NULL;
1073 cpl_image* contrib = NULL;
1074
1075 cpl_ensure(image_list, CPL_ERROR_NULL_INPUT, NULL);
1076 cpl_ensure(method, CPL_ERROR_NULL_INPUT, NULL);
1077
1078 int num_images = hdrl_imagelist_get_size(image_list);
1079 cpl_ensure(num_images > 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
1080
1081 /* Apply masks if provided */
1082 if (masks) {
1083 masked_list = hdrl_imagelist_new();
1084
1085 for (int i = 0; i < num_images; i++) {
1086
1087 hdrl_image * himg = hdrl_image_duplicate(
1088 hdrl_imagelist_get_const(image_list, i));
1089
1090 cpl_image * data = hdrl_image_get_image(himg);
1091
1092 //AMO: TODO THE FOLLOWING LINE IS SOURCE OF TROUBLES
1093 cpl_image_reject_from_mask(data, masks[i]);
1094 if(debug) {
1095 char* fname = NULL;
1096 if(plane_id < 0) {
1097 fname = cpl_sprintf("mask_%d.fits",i);
1098 } else {
1099 fname = cpl_sprintf("mask_%d_%3.3lld.fits",i,plane_id);
1100 }
1101 //cpl_msg_warning(cpl_func,"mask[%d]: %p",i, masks[i]);
1102 cpl_mask_save(masks[i], fname, NULL, CPL_IO_DEFAULT);
1103 cpl_free(fname);
1104 }
1105
1106 hdrl_imagelist_set(masked_list, himg, i);
1107
1108 }
1109 } else {
1110 masked_list = image_list;
1111 }
1112
1113 /* Create collapse parameters */
1114 if (strcmp(method, "median") == 0) {
1116 } else {
1117 collapse_par = hdrl_collapse_sigclip_parameter_create(kappa, kappa, niter);
1118 }
1119
1120
1121 /* Collapse image list */
1122 hdrl_imagelist_collapse(masked_list, collapse_par,&result, &contrib);
1123 if(debug) {
1124 char* fname;
1125 if(plane_id < 0) {
1126 fname = cpl_sprintf("sky_flat_2nd.fits");
1127 } else {
1128 fname = cpl_sprintf("sky_flat_2nd_plane_%3.3lld.fits",plane_id);
1129 }
1130 cpl_image_save(hdrl_image_get_image(result), fname,CPL_TYPE_DOUBLE,NULL,CPL_IO_DEFAULT);
1131 cpl_free(fname);
1132 }
1133
1134 /* Cleanup */
1135 if (contrib != NULL) {
1136 cpl_image_delete(contrib);
1137 }
1138
1139 hdrl_parameter_delete(collapse_par);
1140
1141 if (masks && masked_list) {
1142 hdrl_imagelist_delete(masked_list);
1143 }
1144
1145 eris_check_error_code(cpl_func);
1146 return result;
1147}
1148
1149static cpl_mask * eris_detect_sources_hdrl_catalogue(
1150 const cpl_image * image,
1151 hdrl_parameter* p)
1152{
1153
1154 cpl_mask * mask_res = NULL;
1155
1156 hdrl_catalogue_result *res = hdrl_catalogue_compute(image, NULL, NULL, p);
1157 /*
1158 if(debug) {
1159 char* fname = cpl_sprintf("smap_%d.fits",0);
1160 cpl_image_save(res->segmentation_map, fname, CPL_TYPE_DOUBLE, NULL, CPL_IO_DEFAULT);
1161 cpl_free(fname);
1162 }
1163 */
1164 double max = cpl_image_get_max(res->segmentation_map);
1165 mask_res = cpl_mask_threshold_image_create(res->segmentation_map, 0.9, max + 0.1);
1167 eris_check_error_code(cpl_func);
1168 return mask_res;
1169}
1170
1171/*----------------------------------------------------------------------------*/
1178/*----------------------------------------------------------------------------*/
1179static cpl_mask * eris_dilate_mask(
1180 const cpl_mask * input_mask,
1181 int radius)
1182{
1183 cpl_mask * result = NULL;
1184 cpl_mask * temp = NULL;
1185 cpl_size nx, ny;
1186
1187 cpl_ensure(input_mask, CPL_ERROR_NULL_INPUT, NULL);
1188 cpl_ensure(radius > 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
1189
1190 nx = cpl_mask_get_size_x(input_mask);
1191 ny = cpl_mask_get_size_y(input_mask);
1192
1193 result = cpl_mask_duplicate(input_mask);
1194
1195 /* Simple box dilation */
1196 for (int iter = 0; iter < radius; iter++) {
1197 temp = cpl_mask_duplicate(result);
1198
1199 for (cpl_size j = 2; j < ny - 1; j++) {
1200 for (cpl_size i = 2; i < nx - 1; i++) {
1201 if (cpl_mask_get(temp, i, j) ||
1202 cpl_mask_get(temp, i-1, j) ||
1203 cpl_mask_get(temp, i+1, j) ||
1204 cpl_mask_get(temp, i, j-1) ||
1205 cpl_mask_get(temp, i, j+1)) {
1206 cpl_mask_set(result, i, j, CPL_BINARY_1);
1207 }
1208 }
1209 }
1210 cpl_mask_delete(temp);
1211 }
1212 eris_check_error_code(cpl_func);
1213 return result;
1214}
1215
1216/*----------------------------------------------------------------------------*/
1246/*----------------------------------------------------------------------------*/
1247cpl_error_code
1248eris_image_stats(const hdrl_image *in_image,
1249 double l_sig,
1250 double u_sig,
1251 const cpl_mask *mask,
1252 double trim_fraction,
1253 double *median,
1254 double *mean,
1255 double *stdev,
1256 double *tmean,
1257 double *tstd,
1258 double *mad,
1259 double *min_val,
1260 double *max_val)
1261{
1262 cpl_ensure_code(in_image, CPL_ERROR_NULL_INPUT);
1263 cpl_ensure_code(median, CPL_ERROR_NULL_INPUT);
1264 cpl_ensure_code(mean, CPL_ERROR_NULL_INPUT);
1265 cpl_ensure_code(stdev, CPL_ERROR_NULL_INPUT);
1266 cpl_ensure_code(tmean, CPL_ERROR_NULL_INPUT);
1267 cpl_ensure_code(tstd, CPL_ERROR_NULL_INPUT);
1268 cpl_ensure_code(mad, CPL_ERROR_NULL_INPUT);
1269 cpl_ensure_code(min_val, CPL_ERROR_NULL_INPUT);
1270 cpl_ensure_code(max_val, CPL_ERROR_NULL_INPUT);
1271
1272 const cpl_image *img = hdrl_image_get_image_const(in_image);
1273 cpl_image *work_img = NULL;
1274 cpl_vector *data_vec = NULL;
1275
1276 /* Create working copy of image */
1277 work_img = cpl_image_duplicate(img);
1278 if (!work_img) {
1279 return cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_OUTPUT,
1280 "Failed to duplicate image");
1281 }
1282
1283 /* Apply mask if provided */
1284 if (mask != NULL) {
1285 cpl_image_reject_from_mask(work_img, mask);
1286 }
1287
1288 /* Step 1: Compute initial statistics (NaN-aware) */
1289 hdrl_value median0 = hdrl_image_get_median(in_image);
1290 hdrl_value mean0 = hdrl_image_get_mean(in_image);
1291 double stdev0 = hdrl_image_get_stdev(in_image);
1292
1293 *median = median0.data;
1294 *mean = mean0.data;
1295 *stdev = stdev0;
1296
1297 /* Step 2: Convert image to vector for trimmed statistics */
1298 cpl_size nx = cpl_image_get_size_x(work_img);
1299 cpl_size ny = cpl_image_get_size_y(work_img);
1300 cpl_size npix = nx * ny;
1301
1302 /* Count valid (non-rejected) pixels */
1303 const cpl_mask *rej_mask = cpl_image_get_bpm_const(work_img);
1304 cpl_size nvalid = npix;
1305 if (rej_mask) {
1306 nvalid = npix - cpl_mask_count(rej_mask);
1307 }
1308
1309 if (nvalid == 0) {
1310 cpl_image_delete(work_img);
1311 return cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
1312 "No valid pixels in image");
1313 }
1314
1315 /* Extract valid pixel values to vector */
1316 data_vec = cpl_vector_new(nvalid);
1317 cpl_size idx = 0;
1318 for (cpl_size j = 1; j <= ny; j++) {
1319 for (cpl_size i = 1; i <= nx; i++) {
1320 int is_rejected = 0;
1321 double val = cpl_image_get(work_img, i, j, &is_rejected);
1322 if (!is_rejected && isfinite(val)) {
1323 cpl_vector_set(data_vec, idx, val);
1324 idx++;
1325 }
1326 }
1327 }
1328
1329 /* Adjust vector size if some NaNs were found */
1330 if (idx < nvalid) {
1331 cpl_vector *tmp = cpl_vector_extract(data_vec, 0, idx - 1, 1);
1332 cpl_vector_delete(data_vec);
1333 data_vec = tmp;
1334 nvalid = idx;
1335 }
1336
1337 /* Step 3: Compute trimmed mean (trim_fraction from each end) */
1338 cpl_vector_sort(data_vec, CPL_SORT_ASCENDING);
1339 cpl_size trim_count = (cpl_size)(trim_fraction * nvalid);
1340 if (trim_count * 2 >= nvalid) {
1341 trim_count = 0; /* Not enough data for trimming */
1342 }
1343
1344 double tmean0_10 = 0.0;
1345 if (trim_count > 0) {
1346 /* Compute mean of middle (1 - 2*trim_fraction) of data */
1347 cpl_vector *trimmed = cpl_vector_extract(data_vec, trim_count,
1348 nvalid - trim_count - 1, 1);
1349 tmean0_10 = cpl_vector_get_mean(trimmed);
1350 cpl_vector_delete(trimmed);
1351 } else {
1352 tmean0_10 = cpl_vector_get_mean(data_vec);
1353 }
1354
1355 /* Step 4: Define clipping range based on trimmed mean */
1356 double rmin = tmean0_10 - l_sig * (*stdev);
1357 double rmax = tmean0_10 + u_sig * (*stdev);
1358
1359 /* Step 5: Compute trimmed statistics within clipping range */
1360 cpl_size nclipped = 0;
1361 double sum_clipped = 0.0;
1362 double sum_sq_clipped = 0.0;
1363
1364 for (cpl_size i = 0; i < nvalid; i++) {
1365 double val = cpl_vector_get(data_vec, i);
1366 if (val >= rmin && val <= rmax) {
1367 sum_clipped += val;
1368 sum_sq_clipped += val * val;
1369 nclipped++;
1370 }
1371 }
1372
1373 if (nclipped > 0) {
1374 *tmean = sum_clipped / nclipped;
1375
1376 if (nclipped > 1) {
1377 double variance = (sum_sq_clipped - sum_clipped * sum_clipped / nclipped)
1378 / (nclipped - 1);
1379 *tstd = (variance > 0.0) ? sqrt(variance) : 0.0;
1380 } else {
1381 *tstd = 0.0;
1382 }
1383 } else {
1384 /* No data within clipping range - use unclipped statistics */
1385 *tmean = *mean;
1386 *tstd = *stdev;
1387 }
1388
1389 /* Step 6: Compute MAD (Median Absolute Deviation) */
1390 /* MAD = median(|data - median|) */
1391 cpl_vector *abs_dev = cpl_vector_duplicate(data_vec);
1392 cpl_vector_subtract_scalar(abs_dev, *median);
1393 for (cpl_size i = 0; i < nvalid; i++) {
1394 double val = cpl_vector_get(abs_dev, i);
1395 cpl_vector_set(abs_dev, i, fabs(val));
1396 }
1397 cpl_vector_sort(abs_dev, CPL_SORT_ASCENDING);
1398
1399 /*AMO Check MAD computation versus HDRL
1400 double sigma;
1401 cpl_image* ima = cpl_image_wrap_double(nvalid, 1, cpl_vector_get_data(abs_dev));
1402
1403 *mad=cpl_image_get_mad_window(ima, 1, 1, nvalid, 1, &sigma);
1404
1405 cpl_msg_warning(cpl_func,"HDRL mad: %g", *mad);
1406 */
1407
1408 *mad = cpl_vector_get_median(abs_dev);
1409 //cpl_msg_warning(cpl_func,"OTHER mad: %g", *mad);
1410 cpl_vector_delete(abs_dev);
1411
1412 /* Step 7: Get min and max */
1413 *min_val = cpl_vector_get_min(data_vec);
1414 *max_val = cpl_vector_get_max(data_vec);
1415
1416 /* Cleanup */
1417 cpl_vector_delete(data_vec);
1418 cpl_image_delete(work_img);
1419 eris_check_error_code(cpl_func);
1420 return CPL_ERROR_NONE;
1421}
const char * eris_pfits_get_frame_format(const cpl_propertylist *plist)
find out the frame format (DET FRAM FORMAT) value
Definition: eris_pfits.c:889
cpl_error_code eris_check_error_code(const char *func_id)
handle CPL errors
Definition: eris_utils.c:56
cpl_frameset * eris_dfs_extract_frames_with_tag(cpl_frameset *input, const char *rtag)
Extract frames of user given tag.
Definition: eris_utils.c:227
hdrl_catalogue_result * hdrl_catalogue_compute(const cpl_image *image_, const cpl_image *confidence_map, const cpl_wcs *wcs, hdrl_parameter *param_)
build object catalog
void hdrl_catalogue_result_delete(hdrl_catalogue_result *result)
delete hdrl parameter result object
hdrl_parameter * hdrl_catalogue_parameter_parse_parlist(const cpl_parameterlist *parlist, const char *prefix)
Parse parameter list to create input parameters for the catalogue.
cpl_error_code hdrl_catalogue_parameter_set_option(hdrl_parameter *par, hdrl_catalogue_options opt)
set result option of catalogue parameter
hdrl_parameter * hdrl_collapse_sigclip_parameter_create(double kappa_low, double kappa_high, int niter)
create a parameter object for sigclipped mean
hdrl_parameter * hdrl_collapse_median_parameter_create(void)
create a parameter object for median
cpl_error_code hdrl_image_sub_image(hdrl_image *self, const hdrl_image *other)
Subtract two images, store the result in the first image.
hdrl_value hdrl_image_get_median(const hdrl_image *self)
computes the median and associated error of an image.
cpl_error_code hdrl_image_mul_scalar(hdrl_image *self, hdrl_value value)
Elementwise multiplication of an image with a scalar.
hdrl_image * hdrl_image_duplicate(const hdrl_image *himg)
copy hdrl_image
Definition: hdrl_image.c:391
double hdrl_image_get_stdev(const hdrl_image *self)
computes the standard deviation of the data of an image
hdrl_value hdrl_image_get_mean(const hdrl_image *self)
computes mean pixel value and associated error of an image.
cpl_size hdrl_image_get_size_y(const hdrl_image *self)
return size of Y dimension of image
Definition: hdrl_image.c:540
cpl_size hdrl_image_get_size_x(const hdrl_image *self)
return size of X dimension of image
Definition: hdrl_image.c:525
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
cpl_image * hdrl_image_get_image(hdrl_image *himg)
get data as cpl image
Definition: hdrl_image.c:105
const cpl_image * hdrl_image_get_image_const(const hdrl_image *himg)
get data as cpl image
Definition: hdrl_image.c:118
void hdrl_image_delete(hdrl_image *himg)
delete hdrl_image
Definition: hdrl_image.c:379
cpl_error_code hdrl_imagelist_set(hdrl_imagelist *himlist, hdrl_image *himg, cpl_size pos)
Insert an image into an imagelist.
void hdrl_imagelist_delete(hdrl_imagelist *himlist)
Free all memory used by a hdrl_imagelist object including the images.
const hdrl_image * hdrl_imagelist_get_const(const hdrl_imagelist *himlist, cpl_size inum)
Get an image from a list of images.
cpl_size hdrl_imagelist_get_size(const hdrl_imagelist *himlist)
Get the number of images in the imagelist.
cpl_error_code hdrl_imagelist_collapse(const hdrl_imagelist *himlist, const hdrl_parameter *param, hdrl_image **out, cpl_image **contrib)
collapsing of image list
hdrl_imagelist * hdrl_imagelist_new(void)
Create an empty imagelist.
hdrl_image * hdrl_imagelist_get(const hdrl_imagelist *himlist, cpl_size inum)
Get an image from a list of images.
void hdrl_parameter_delete(hdrl_parameter *obj)
shallow delete of a parameter