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 * 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 * 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 * detect_sources_hdrl_catalogue(
49 const cpl_image * image,
50 hdrl_parameter* p);
51
52static cpl_mask * 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
107const static int debug = 1;
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_RAW);
169
170 if (!raw_frames || cpl_frameset_get_size(raw_frames) == 0) {
171 cpl_msg_error(cpl_func, "No science frames found with tag %s", ERIS_NIX_IMG_SUPERSKY_RAW);
172 error = CPL_ERROR_DATA_NOT_FOUND;
173 goto cleanup;
174 }
175 num_frames = cpl_frameset_get_size(raw_frames);
176 cpl_msg_info(cpl_func, "Processing %d science frames", num_frames);
177
178 /* Load bad pixel map */
179 bpm_frame = cpl_frameset_find_const(frameset, ERIS_NIX_IMG_SUPERSKY_BPM);
180 if (!bpm_frame) {
181 cpl_msg_error(cpl_func, "No bad pixel map found with tag %s", ERIS_NIX_IMG_SUPERSKY_BPM);
182 error = CPL_ERROR_DATA_NOT_FOUND;
183 goto cleanup;
184 }
185
186 cpl_image * bpm_image = cpl_image_load(cpl_frame_get_filename(bpm_frame), CPL_TYPE_INT, 0, 0);
187 if (!bpm_image) {
188 error = CPL_ERROR_FILE_NOT_FOUND;
189 goto cleanup;
190 }
191
192 /* Convert BPM image to mask (0=good, >0=bad) */
193 bad_pixel_map = cpl_mask_threshold_image_create(bpm_image, 0.5, DBL_MAX);
194 if(debug) {
195 cpl_mask_save(bad_pixel_map, "bad_pixel_map.fits", NULL, CPL_IO_DEFAULT);
196 }
197 cpl_mask *inp_mask = cpl_mask_threshold_image_create(bpm_image, -0.5, 0.5);
198
199 if (!bad_pixel_map) {
200 error = CPL_ERROR_ILLEGAL_INPUT;
201 goto cleanup;
202 }
203 cpl_boolean same_cubes = CPL_TRUE;
204 cpl_boolean test_cubes = CPL_TRUE;
205 same_cubes = eris_check_format_is_cube_of_same_size(raw_frames);
206 cpl_size num_tot_images = 0;
207 //char* fname;
208 *product_frames = cpl_frameset_new();
209 if(!test_cubes && !same_cubes) {
210
211 /* Load science images as HDRL imagelist */
212 science_images = eris_crea_imagelist_all(raw_frames, bad_pixel_map);
213
214 /* Step 1: Create first-pass super-sky */
215 cpl_msg_info(cpl_func, "Creating first-pass super-sky flat...");
216 sky_flat_0 = create_sky_flat_first(science_images, bad_pixel_map, combine_method, sigma_clip,
217 max_iter, -1);
218
219 /* Step 2: Subtract first-pass sky from each frame */
220 eris_first_sky_sub(science_images, sky_flat_0, &skysub_images_0);
221 if (sky_flat_0) hdrl_image_delete(sky_flat_0);
222
223 eris_print_rec_status(300);
224 /* Step 3: Detect sources in sky-subtracted frames */
225 num_tot_images = hdrl_imagelist_get_size(skysub_images_0);
226 cpl_msg_info(cpl_func,"num_tot_images: %lld",num_tot_images);
227 source_masks = cpl_calloc(num_tot_images, sizeof(cpl_mask *));
228
229 eris_locate_and_mask_sources(skysub_images_0, bpm_image, parlist, RECIPE_NAME, source_masks);
230
231 eris_print_rec_status(400);
232 /* Step 4: Create final super-sky with source masks */
233 cpl_msg_info(cpl_func, "Creating final super-sky flat with source masking...");
234 sky_flat_final = create_sky_flat_final(science_images, source_masks,
235 combine_method, sigma_clip, max_iter, -1);
236 eris_print_rec_status(450);
237
238 eris_print_rec_status(460);
239
240 if (!sky_flat_final) {
241 cpl_msg_error(cpl_func, "Failed to create final sky flat");
242 error = CPL_ERROR_ILLEGAL_OUTPUT;
243 goto cleanup;
244 }
245
246 if(sky_flat_final) {
247 /* Step 5: Save products */
248 eris_print_rec_status(500);
249
250 eris_save_sky_flat_final(raw_frames, sky_flat_final, product_frames);
251 }
252
253 /* Save sky-subtracted frames if requested */
254
255 if (save_skysub) {
256 eris_create_final_skysub_products(science_images, sky_flat_final, product_frames);
257 }
258
259 if (science_images) hdrl_imagelist_delete(science_images);
260 if (source_masks != NULL) {
261 for (int i = 0; i < num_tot_images; i++) {
262 if (source_masks[i]) cpl_mask_delete(source_masks[i]);
263 }
264 cpl_free(source_masks);
265 }
266 } else {
267 cpl_msg_warning(cpl_func,"code to be implemented");
268
269 const cpl_frame * frame = cpl_frameset_get_position_const(raw_frames, 0);
270 const char * filename = cpl_frame_get_filename(frame);
271 cpl_imagelist * data_iml = cpl_imagelist_load(filename, CPL_TYPE_DOUBLE, 1);
272 cpl_size nplanes = cpl_imagelist_get_size(data_iml);
273
274 for(cpl_size k = 0; k < nplanes; k++) {
275 science_images = hdrl_imagelist_new();
276 for(cpl_size kkk = 0; kkk < num_frames; kkk++) {
277 frame = cpl_frameset_get_position_const(raw_frames, kkk);
278 filename = cpl_frame_get_filename(frame);
279 eris_load_and_error_cube_slice(filename, bad_pixel_map, k, kkk, &science_images);
280 }
281
282 /* Step 1: Create first-pass super-sky */
283 cpl_msg_info(cpl_func, "Creating first-pass super-sky flat...");
284 sky_flat_0 = create_sky_flat_first(science_images, bad_pixel_map, combine_method, sigma_clip, max_iter, k);
285
286 /* Step 2: Subtract first-pass sky from each frame */
287 eris_first_sky_sub(science_images, sky_flat_0, &skysub_images_0);
288
289 eris_print_rec_status(300);
290 /* Step 3: Detect sources in sky-subtracted frames */
291 num_tot_images = hdrl_imagelist_get_size(skysub_images_0);
292 cpl_msg_info(cpl_func,"num_tot_images: %lld",num_tot_images);
293 source_masks = cpl_calloc(num_tot_images, sizeof(cpl_mask *));
294
295 eris_locate_and_mask_sources(skysub_images_0, bpm_image, parlist, RECIPE_NAME, source_masks);
296
297 eris_print_rec_status(400);
298 /* Step 4: Create final super-sky with source masks */
299 cpl_msg_info(cpl_func, "Creating final super-sky flat with source masking...");
300
301 sky_flat_final = create_sky_flat_final(science_images, source_masks,
302 combine_method, sigma_clip, max_iter, k);
303
304 if (save_skysub) {
305 eris_create_final_skysub_products(science_images, sky_flat_final, product_frames);
306 }
307
308
309
310 if (sky_flat_0) hdrl_image_delete(sky_flat_0);
311 if (science_images) hdrl_imagelist_delete(science_images);
312 if (source_masks != NULL) {
313 for (int i = 0; i < num_tot_images; i++) {
314 if (source_masks[i]) cpl_mask_delete(source_masks[i]);
315 }
316 cpl_free(source_masks);
317 }
318 }
319 cpl_imagelist_delete(data_iml);
320 }
321
322 eris_print_rec_status(700);
323
324 eris_print_rec_status(800);
325 cpl_msg_info(cpl_func, "Super-sky recipe completed successfully");
326
327cleanup:
328 eris_print_rec_status(600);
329 if (raw_frames) cpl_frameset_delete(raw_frames);
330 if (bad_pixel_map) cpl_mask_delete(bad_pixel_map);
331 if (skysub_images_0) hdrl_imagelist_delete(skysub_images_0);
332
333 if (sky_flat_final) hdrl_image_delete(sky_flat_final);
334 if(inp_mask) cpl_mask_delete(inp_mask);
335 if(bpm_image) cpl_image_delete(bpm_image);
336
337 eris_check_error_code(cpl_func);
338 return error;
339}
340
341static cpl_error_code
342eris_save_sky_flat_final( const cpl_frameset* raw_frames, const hdrl_image* sky_flat_final, cpl_frameset** product_frames)
343{
344
345 /* Step 5: Save products */
346 cpl_size num_frames = cpl_frameset_get_size(raw_frames);
347 eris_print_rec_status(500);
348 //*product_frames = cpl_frameset_new();
349 hdrl_value med_sky_final = hdrl_image_get_median(sky_flat_final);
350
351 /* Save master sky flat */
352 cpl_propertylist* plist = cpl_propertylist_load(cpl_frame_get_filename(
353 cpl_frameset_get_position_const(raw_frames, 0)), 0);
354 cpl_propertylist_update_string(plist, CPL_DFS_PRO_CATG, ERIS_NIX_IMG_SUPERSKY_SKYFLAT);
355 cpl_propertylist_update_double(plist, "ESO QC SKY MEDIAN", med_sky_final.data);
356 cpl_propertylist_update_int(plist, "ESO QC NFRAMES", num_frames);
357 eris_print_rec_status(600);
358 char* fname = cpl_sprintf("sky_flat_2nd.fits");
359
360 cpl_image_save(hdrl_image_get_image_const(sky_flat_final), fname,CPL_TYPE_DOUBLE, plist, CPL_IO_CREATE);
361
362 cpl_frame * product = cpl_frame_new();
363 cpl_frame_set_filename(product, fname);
364 cpl_frame_set_tag(product, ERIS_NIX_IMG_SUPERSKY_SKYFLAT);
365 cpl_frame_set_type(product, CPL_FRAME_TYPE_IMAGE);
366 cpl_frame_set_group(product, CPL_FRAME_GROUP_PRODUCT);
367 cpl_frameset_insert(*product_frames, product);
368
369 cpl_free(fname);
370 cpl_propertylist_delete(plist);
371
372 eris_check_error_code(cpl_func);
373 return cpl_error_get_code();
374
375}
376static hdrl_imagelist*
377eris_crea_imagelist_all(cpl_frameset* raw_frames, cpl_mask* bad_pixel_map)
378{
379
380 /* Load science images as HDRL imagelist */
381 hdrl_imagelist* science_images = hdrl_imagelist_new();
382 cpl_size num_frames = cpl_frameset_get_size(raw_frames);
383 cpl_size next = 0;
384 const char* format = NULL;
385 cpl_propertylist* pheader = NULL;
386 eris_print_rec_status(100);
387 cpl_size kkk = 0;
388 for (cpl_size kk = 0; kk < num_frames; kk++) {
389 const cpl_frame * frame = cpl_frameset_get_position_const(raw_frames, kk);
390 const char * filename = cpl_frame_get_filename(frame);
391 next = cpl_frame_get_nextensions(frame);
392 pheader = cpl_propertylist_load(filename,0);
393 format = eris_pfits_get_frame_format(pheader);
394
395 if (next < 4) {
396 eris_load_data_and_crea_error(filename, kk, bad_pixel_map, &science_images);
397 } else {
398 cpl_msg_info(cpl_func,"sci data with extentions %s", filename);
399 if(!strcmp(format,"single")) {
400 eris_load_data_and_error_simple(filename, kk, bad_pixel_map, &science_images);
401 } else {
402 eris_load_and_error_cube(filename, bad_pixel_map, &kkk, &science_images);
403 }/* end cube format case */
404 } /* end case with extensions */
405 cpl_propertylist_delete(pheader);
406 } /* end loop over input frames */
407
408 cpl_size nx = hdrl_image_get_size_x(hdrl_imagelist_get_const(science_images, 0));
409 cpl_size ny = hdrl_image_get_size_y(hdrl_imagelist_get_const(science_images, 0));
410 cpl_msg_info(cpl_func, "Image dimensions: %lld x %lld", nx, ny);
411
412
413 eris_check_error_code(cpl_func);
414 return science_images;
415}
416
417static cpl_boolean
418eris_check_format_is_cube_of_same_size(const cpl_frameset * raw_frames)
419{
420
421 cpl_boolean result = CPL_TRUE;
422 cpl_size nplanes = 0;
423 cpl_size nplanes_tmp = 0;
424
425 cpl_size num_frames = cpl_frameset_get_size(raw_frames);
426 cpl_size next = 0;
427 for (int kk = 0; kk < num_frames; kk++) {
428 const cpl_frame * frame = cpl_frameset_get_position_const(raw_frames, kk);
429 next = cpl_frame_get_nextensions(frame);
430 if(next > 3) {
431 const char * fname = cpl_frame_get_filename(frame);
432
433 cpl_propertylist* phead = cpl_propertylist_load(fname,0);
434 if(cpl_propertylist_has(phead,"CD3_3")) {
435 cpl_imagelist * data_iml = cpl_imagelist_load(fname, CPL_TYPE_DOUBLE, 1);
436
437 nplanes_tmp = cpl_imagelist_get_size(data_iml);
438 if(kk == 0) {
439 nplanes = nplanes_tmp;
440 } else if (nplanes_tmp != nplanes) {
441 cpl_msg_info(cpl_func,"cubes not of same size");
442 result = CPL_FALSE;
443 }
444 cpl_imagelist_delete(data_iml);
445 } else {
446 cpl_msg_info(cpl_func,"Input Data are simple format (images)");
447 cpl_propertylist_delete(phead);
448 return CPL_FALSE;
449 }
450 cpl_propertylist_delete(phead);
451 } else {
452 cpl_msg_info(cpl_func,"Data with less than 4 extensions");
453 return CPL_FALSE;
454 }
455 }
456 eris_check_error_code(cpl_func);
457 return result;
458}
459
460/*----------------------------------------------------------------------------*/
469/*----------------------------------------------------------------------------*/
470
471static cpl_error_code
472eris_create_final_skysub_products(const hdrl_imagelist* science_iml, const hdrl_image* sky_flat, cpl_frameset** products)
473{
474
475 cpl_msg_info(cpl_func, "Saving sky-subtracted frames...");
476 cpl_size num_tot_images = hdrl_imagelist_get_size(science_iml);
477 hdrl_value med_sky = hdrl_image_get_median(sky_flat);
478
479 for (int i = 0; i < num_tot_images; i++) {
480
481 const hdrl_image * himg = hdrl_imagelist_get_const(science_iml, i);
482 hdrl_value med_img = hdrl_image_get_median(himg);
483 hdrl_value scale;
484 scale.data = med_img.data / med_sky.data;
485
486 hdrl_image * skysub = hdrl_image_duplicate(himg);
487 hdrl_image * scaled_sky = hdrl_image_duplicate(sky_flat);
488 hdrl_image_mul_scalar(scaled_sky, scale);
489 hdrl_image_sub_image(skysub, scaled_sky);
490 hdrl_value med_skyub = hdrl_image_get_median(skysub);
491 /* Create output filename */
492 char outfile[256];
493 snprintf(outfile, 256, "sci_skysub_2nd_%03d.fits", i);
494
495 /* Load and update header */
496 cpl_msg_info(cpl_func,"ESO QC SKY_MED: %g",med_img.data);
497 cpl_msg_info(cpl_func,"ESO QC SKYSUB_MED: %g", med_skyub.data);
498 /*
499 const cpl_frame * frame = cpl_frameset_get_position_const(raw_frames, i);
500 plist = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
501 cpl_propertylist_update_string(plist, CPL_DFS_PRO_CATG,
502 ERIS_NIX_IMG_SUPERSKY_SKYSUB);
503
504 //cpl_propertylist_update_double(plist, "ESO QC SKY_MED", med_img.data);
505 //cpl_propertylist_update_double(plist, "ESO QC SKYSUB_MED", med_skyub.data);
506
507 */
508
509 cpl_image_save(hdrl_image_get_image(skysub), outfile, CPL_TYPE_DOUBLE, NULL, CPL_IO_CREATE);
510
511 cpl_frame * product = cpl_frame_new();
512 cpl_frame_set_filename(product, outfile);
513 cpl_frame_set_tag(product, ERIS_NIX_IMG_SUPERSKY_SKYSUB);
514 cpl_frame_set_type(product, CPL_FRAME_TYPE_IMAGE);
515 cpl_frame_set_group(product, CPL_FRAME_GROUP_PRODUCT);
516 cpl_frameset_insert(*products, product);
517 //cpl_propertylist_delete(plist);
518 hdrl_image_delete(skysub);
519 hdrl_image_delete(scaled_sky);
520 }
521
522 eris_check_error_code(cpl_func);
523 return cpl_error_get_code();
524}
525/*----------------------------------------------------------------------------*/
535/*----------------------------------------------------------------------------*/
536static cpl_error_code
537eris_load_and_error_cube(const char* fname, const cpl_mask* bad_pixel_map, cpl_size* kkk,
538 hdrl_imagelist** science_images){
539
540 eris_print_rec_status(0);
541 cpl_imagelist * data_iml = cpl_imagelist_load(fname, CPL_TYPE_DOUBLE, 1);
542 cpl_imagelist * error_iml = cpl_imagelist_load(fname, CPL_TYPE_DOUBLE, 2);
543 cpl_mask * dqual_msk = cpl_mask_load(fname, 0, 3);
544 cpl_imagelist * confm_iml = cpl_imagelist_load(fname, CPL_TYPE_DOUBLE, 4);
545
546 double* pconf_img = cpl_image_get_data_double(cpl_imagelist_get(confm_iml,0));
547
548 cpl_binary* pbpm = cpl_mask_get_data(dqual_msk);
549 cpl_size sx = cpl_mask_get_size_x(dqual_msk);
550 cpl_size sy = cpl_mask_get_size_y(dqual_msk);
551 cpl_size sz = cpl_imagelist_get_size(data_iml);
552
553 cpl_mask* mask_tot = cpl_mask_duplicate(bad_pixel_map);
554 cpl_mask_or(mask_tot, dqual_msk);
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(bad_pixel_map);
626
627 cpl_mask_or(mask_tot, dqual_msk);
628 double* pdata = NULL;
629 cpl_image * data_img = NULL;
630 cpl_image * error_img = NULL;
631 cpl_size pixel = 0;
632 cpl_size j_sx = 0;
633
634 data_img = cpl_imagelist_get(data_iml,k);
635 error_img = cpl_imagelist_get(error_iml,k);
636 pdata = cpl_image_get_data(cpl_imagelist_get(data_iml,k));
637
638 for (cpl_size j = 0; j < sy; j++) {
639
640 j_sx = j * sx;
641 for (cpl_size i = 0; i < sx; i++) {
642
643 pixel = i + j_sx;
644 if((pconf_img[pixel] == 0) || !isfinite(pdata[pixel])) {
645 pbpm[pixel] = CPL_BINARY_1;
646 }
647 }
648
649 }
650
651 cpl_image_reject_from_mask(data_img, mask_tot);
652 cpl_image_reject_from_mask(error_img, mask_tot);
653 hdrl_image * himg = hdrl_image_create(data_img, error_img);
654 hdrl_imagelist_set(*science_images, himg, kkk);
655
656 cpl_mask_delete(mask_tot);
657 cpl_mask_delete(dqual_msk);
658 cpl_imagelist_delete(data_iml);
659 cpl_imagelist_delete(error_iml);
660 cpl_imagelist_delete(confm_iml);
661
662 eris_check_error_code(cpl_func);
663 return cpl_error_get_code();
664
665}
666
667
668/*----------------------------------------------------------------------------*/
678/*----------------------------------------------------------------------------*/
679static cpl_error_code
680eris_load_data_and_error_simple(const char* fname, const cpl_size kk, const cpl_mask* bad_pixel_map,
681 hdrl_imagelist** science_images)
682{
683
684 cpl_ensure_code(fname, CPL_ERROR_NULL_INPUT);
685 cpl_image * data_img = cpl_image_load(fname, CPL_TYPE_DOUBLE, 0, 1);
686 cpl_image * error_img = cpl_image_load(fname, CPL_TYPE_DOUBLE, 0, 2);
687 cpl_mask* dqual_msk = cpl_mask_load(fname, 0, 3);
688 cpl_image* confm_img = cpl_image_load(fname, CPL_TYPE_DOUBLE, 0, 4);
689 double* pdata = cpl_image_get_data(data_img);
690 double* pconf_img = cpl_image_get_data_double(confm_img);
691 cpl_binary* pbpm = cpl_mask_get_data(dqual_msk);
692 cpl_size sx = cpl_image_get_size_x(confm_img);
693 cpl_size sy = cpl_image_get_size_y(confm_img);
694 cpl_size j_sx = 0;
695 cpl_size pixel = 0;
696 for (cpl_size j = 0; j < sy; j++) {
697
698 j_sx = j * sx;
699 for (cpl_size i = 0; i < sx; i++) {
700
701 pixel = i + j_sx;
702 if((pconf_img[pixel] == 0) || !isfinite(pdata[pixel])) {
703 pbpm[pixel] = CPL_BINARY_1;
704 }
705 }
706
707 }
708
709 cpl_mask* mask_tot = cpl_mask_duplicate(bad_pixel_map);
710 cpl_mask_or(mask_tot, dqual_msk);
711
712 cpl_image_reject_from_mask(data_img, mask_tot);
713 cpl_image_reject_from_mask(error_img, mask_tot);
714
715 //if(cpl_error_get_code != CPL_ERROR_NONE) {
716 // exit(0);
717 //}
718 hdrl_image * himg = hdrl_image_create(data_img, error_img);
719 hdrl_imagelist_set(*science_images, himg, kk);
720
721 cpl_image_delete(data_img);
722 cpl_image_delete(error_img);
723 cpl_mask_delete(mask_tot);
724 cpl_mask_delete(dqual_msk);
725 cpl_image_delete(confm_img);
726
727 // determine statistics of the science images
728 cpl_mask* mask = cpl_image_get_bpm(hdrl_image_get_image(himg));
729 double median=0, mean=0, stdev=0,tmean=0, tstd=0, mad=0, min_val=0, max_val=0;
730 eris_image_stats(
731 himg,
732 2.0, // l_sig: lower sigma clip
733 2.0, // u_sig: upper sigma clip
734 mask, // optional mask
735 0.1, // 10% trim fraction
736 &median, &mean, &stdev, &tmean, &tstd, &mad, &min_val, &max_val
737 );
738
739 cpl_msg_info(cpl_func,"process file: %s", fname);
740 cpl_msg_info(cpl_func, "Median=%.3f, Mean=%.3f, StdDev=%.3f "
741 "Trimmed: Mean=%.3f, StdDev=%.3f, MAD=%.3f "
742 "Range: [%.3f, %.3f]", median, mean, stdev, tmean, tstd, mad, min_val, max_val);
743
744 eris_check_error_code(cpl_func);
745 return cpl_error_get_code();
746}
747
748/*----------------------------------------------------------------------------*/
758/*----------------------------------------------------------------------------*/
759static cpl_error_code
760eris_load_data_and_crea_error(const char* fname, const cpl_size kk, const cpl_mask* bad_pixel_map,
761 hdrl_imagelist** science_images)
762{
763 cpl_ensure_code(fname, CPL_ERROR_NULL_INPUT);
764 cpl_image * data_img = NULL;
765 cpl_image * error_img = NULL;
766 cpl_msg_info(cpl_func,"sci data with less than 4 extensions");
767 data_img = cpl_image_load(fname, CPL_TYPE_DOUBLE, 0, 0);
768 if (!data_img) {
769 cpl_msg_error(cpl_func, "Failed to load frame %s", fname);
770 eris_check_error_code(cpl_func);
771 return cpl_error_get_code();
772 }
773 double* pdata = NULL;
774 cpl_mask * dqual_msk = NULL;
775 pdata = cpl_image_get_data(data_img);
776 dqual_msk = cpl_image_get_bpm(data_img);
777 cpl_size sx = cpl_image_get_size_x(data_img);
778 cpl_size sy = cpl_image_get_size_y(data_img);
779 cpl_binary* pbpm = cpl_mask_get_data(dqual_msk);
780 cpl_size j_sx = 0;
781 cpl_size pixel = 0;
782 for (cpl_size j = 0; j < sy; j++) {
783 j_sx = j * sx;
784 for (cpl_size i = 0; i < sx; i++) {
785 pixel = i + j_sx;
786 if(!isfinite(pdata[pixel])) {
787 pbpm[pixel] = CPL_BINARY_1;
788 }
789 }
790
791 }
792 cpl_mask* mask_tot = cpl_mask_duplicate(bad_pixel_map);
793 cpl_mask_or(mask_tot, dqual_msk);
794 // Apply bad pixel mask
795 cpl_image_reject_from_mask(data_img, mask_tot);
796
797 // Create error image (simple Poisson model for now)
798 error_img = cpl_image_duplicate(data_img);
799 cpl_image_abs(error_img);
800 cpl_image_add_scalar(error_img, 1.0); // Add readnoise^2
801 cpl_image_power(error_img, 0.5);
802
803
804 hdrl_image * himg = hdrl_image_create(data_img, error_img);
805 hdrl_imagelist_set(*science_images, himg, kk);
806
807 cpl_image_delete(data_img);
808 cpl_image_delete(error_img);
809 cpl_mask_delete(mask_tot);
810
811 eris_check_error_code(cpl_func);
812 return cpl_error_get_code();
813}
814
815/*----------------------------------------------------------------------------*/
826/*----------------------------------------------------------------------------*/
827static cpl_error_code
828eris_locate_and_mask_sources(hdrl_imagelist* skysub_images, const cpl_image* bpm_image,
829 const cpl_parameterlist* parlist, const char* RECIPE_NAME, cpl_mask** source_masks)
830{
831 cpl_msg_info(cpl_func, "Detecting sources...");
832 cpl_size num_tot_images = hdrl_imagelist_get_size(skysub_images);
833 //cpl_msg_info(cpl_func,"num_tot_images: %lld",num_tot_images);
834 //source_masks = cpl_calloc(num_tot_images, sizeof(cpl_mask *));
835
836 hdrl_parameter *par_cat = hdrl_catalogue_parameter_parse_parlist(parlist, RECIPE_NAME);
837 const hdrl_catalogue_options opt = HDRL_CATALOGUE_SEGMAP;
839
840 for (int i = 0; i < num_tot_images; i++) {
841
842 hdrl_image * himg = hdrl_imagelist_get(skysub_images, i);
843 cpl_image * data = hdrl_image_get_image(himg);
844
845 cpl_mask *bpm = cpl_mask_threshold_image_create(bpm_image, 0, INT_MAX);
846 cpl_image_reject_from_mask(data, bpm);
847
848 source_masks[i] = detect_sources_hdrl_catalogue(data, par_cat);
849 if(debug) {
850 char* fname = cpl_sprintf("msk_%d.fits",i);
851 cpl_mask_save(source_masks[i], fname, NULL, CPL_IO_DEFAULT);
852 cpl_free(fname);
853 }
854 cpl_mask_delete(bpm);
855
856 /* Convert BPM image to mask (0=good, >0=bad) */
857 cpl_mask* bad_pixel_map = cpl_mask_threshold_image_create(bpm_image, 0.5, DBL_MAX);
858 if(source_masks[i] != NULL && bad_pixel_map != NULL) {
859 int nsources = cpl_mask_count(source_masks[i]) -
860 cpl_mask_count(bad_pixel_map);
861
862 if (nsources > 0) {
863 cpl_msg_info(cpl_func, " Frame %d: detected %d source pixels", i+1, nsources);
864 } else {
865 cpl_msg_warning(cpl_func, " Frame %d: no sources detected", i+1);
866 }
867 }
868 cpl_mask_delete(bad_pixel_map);
869 }
870 if(par_cat) hdrl_parameter_delete(par_cat);
871 eris_check_error_code(cpl_func);
872 return cpl_error_get_code();
873}
874/*----------------------------------------------------------------------------*/
882/*----------------------------------------------------------------------------*/
883static cpl_error_code
884eris_first_sky_sub(const hdrl_imagelist* science_images, const hdrl_image* sky_flat_0,
885 hdrl_imagelist** skysub_images)
886{
887
888 cpl_msg_info(cpl_func, "Subtracting first-pass sky flat...");
889 *skysub_images = hdrl_imagelist_new();
890 hdrl_value med_sky_0 = hdrl_image_get_median(sky_flat_0);
891 char* fname = NULL;
892 hdrl_value scale = {0, 0};
893 cpl_size num_tot_images = hdrl_imagelist_get_size(science_images);
894 for (int i = 0; i < num_tot_images; i++) {
895 const hdrl_image * himg = hdrl_imagelist_get_const(science_images, i);
896 hdrl_value med_img = hdrl_image_get_median(himg);;
897
898 scale.data = med_img.data / med_sky_0.data;
899 hdrl_image * skysub = hdrl_image_duplicate(himg);
900 hdrl_image * scaled_sky = hdrl_image_duplicate(sky_flat_0);
901 hdrl_image_mul_scalar(scaled_sky, scale);
902
903 if(debug) {
904 fname = cpl_sprintf("sci_%d.fits",i);
905 cpl_image_save(hdrl_image_get_image_const(himg),fname, CPL_TYPE_DOUBLE, NULL, CPL_IO_DEFAULT);
906 cpl_free(fname);
907 fname = cpl_sprintf("scaled_sky_0_%d.fits",i);
908 cpl_image_save(hdrl_image_get_image(scaled_sky),fname, CPL_TYPE_DOUBLE, NULL, CPL_IO_DEFAULT);
909 cpl_free(fname);
910 }
911
912 hdrl_image_sub_image(skysub, scaled_sky);
913
914 if(debug) {
915 fname = cpl_sprintf("sci_skysub_1st_%03d.fits",i);
916 cpl_image_save(hdrl_image_get_image(skysub), fname, CPL_TYPE_DOUBLE, NULL, CPL_IO_DEFAULT);
917 cpl_free(fname);
918 }
919 hdrl_imagelist_set(*skysub_images, skysub, i);
920 hdrl_image_delete(scaled_sky);
921 }
922
923 eris_check_error_code(cpl_func);
924 return cpl_error_get_code();
925}
926
927#define HDRL_USE_PRIVATE YES
928/*----------------------------------------------------------------------------*/
938/*----------------------------------------------------------------------------*/
939static hdrl_image * create_sky_flat_first(
940 hdrl_imagelist * image_list,
941 cpl_mask * mask,
942 const char * method,
943 const double kappa,
944 const int niter,
945 const cpl_size plane_id)
946{
947 hdrl_image * result = NULL;
948
949 hdrl_imagelist * masked_list = NULL;
950 hdrl_parameter * collapse_par = NULL;
951 cpl_image* contrib;
952
953 cpl_ensure(image_list, CPL_ERROR_NULL_INPUT, NULL);
954 cpl_ensure(method, CPL_ERROR_NULL_INPUT, NULL);
955
956 int num_images = hdrl_imagelist_get_size(image_list);
957 cpl_ensure(num_images > 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
958 cpl_msg_warning(cpl_func,"kappa: %g niter: %d", kappa, niter);
959 /* Apply masks if provided */
960 if (mask) {
961 masked_list = hdrl_imagelist_new();
962
963 for (int i = 0; i < num_images; i++) {
964
965 hdrl_image * himg = hdrl_image_duplicate(
966 hdrl_imagelist_get_const(image_list, i));
967
968 cpl_image * data = hdrl_image_get_image(himg);
969 cpl_image_reject_from_mask(data, mask);
970 hdrl_imagelist_set(masked_list, himg, i);
971
972 }
973 } else {
974 masked_list = image_list;
975 }
976
977 /* Create collapse parameters */
978 if (strcmp(method, "median") == 0) {
980 } else {
981 cpl_msg_info(cpl_func,"kappa: %g niter: %d",kappa, niter);
982 collapse_par = hdrl_collapse_sigclip_parameter_create(kappa, kappa, niter);
983 }
984
985 /* Collapse image list */
986 hdrl_imagelist_collapse(image_list, collapse_par, &result, &contrib);
987 if(debug) {
988 char* fname;
989 if(plane_id < 0) {
990 fname = cpl_sprintf("sky_flat_1st.fits");
991 } else {
992 fname = cpl_sprintf("sky_flat_1st_plane_%3.3lld.fits",plane_id);
993 }
994 cpl_image_save(hdrl_image_get_image(result), fname,CPL_TYPE_DOUBLE,NULL,CPL_IO_DEFAULT);
995 cpl_free(fname);
996 }
997
998 /* Cleanup */
999 cpl_image_delete(contrib);
1000 hdrl_parameter_delete(collapse_par);
1001 if (mask && masked_list) hdrl_imagelist_delete(masked_list);
1002 eris_check_error_code(cpl_func);
1003 return result;
1004}
1005
1015/*----------------------------------------------------------------------------*/
1016static hdrl_image * create_sky_flat_final(
1017 hdrl_imagelist * image_list,
1018 cpl_mask ** masks,
1019 const char * method,
1020 const double kappa,
1021 const int niter,
1022 const cpl_size plane_id)
1023{
1024 hdrl_image * result = NULL;
1025 hdrl_imagelist * masked_list = NULL;
1026 hdrl_parameter * collapse_par = NULL;
1027 cpl_image* contrib = NULL;
1028
1029 cpl_ensure(image_list, CPL_ERROR_NULL_INPUT, NULL);
1030 cpl_ensure(method, CPL_ERROR_NULL_INPUT, NULL);
1031
1032 int num_images = hdrl_imagelist_get_size(image_list);
1033 cpl_ensure(num_images > 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
1034
1035 /* Apply masks if provided */
1036 if (masks) {
1037 masked_list = hdrl_imagelist_new();
1038
1039 for (int i = 0; i < num_images; i++) {
1040
1041 hdrl_image * himg = hdrl_image_duplicate(
1042 hdrl_imagelist_get_const(image_list, i));
1043
1044 cpl_image * data = hdrl_image_get_image(himg);
1045
1046 //AMO: TODO THE FOLLOWING LINE IS SOURCE OF TROUBLES
1047 cpl_image_reject_from_mask(data, masks[i]);
1048 if(debug) {
1049 char* fname = NULL;
1050 if(plane_id < 0) {
1051 fname = cpl_sprintf("mask_%d.fits",i);
1052 } else {
1053 fname = cpl_sprintf("mask_%d_%3.3lld.fits",i,plane_id);
1054 }
1055 //cpl_msg_warning(cpl_func,"mask[%d]: %p",i, masks[i]);
1056 cpl_mask_save(masks[i], fname, NULL, CPL_IO_DEFAULT);
1057 cpl_free(fname);
1058 }
1059
1060 hdrl_imagelist_set(masked_list, himg, i);
1061
1062 }
1063 } else {
1064 masked_list = image_list;
1065 }
1066
1067 /* Create collapse parameters */
1068 if (strcmp(method, "median") == 0) {
1070 } else {
1071 collapse_par = hdrl_collapse_sigclip_parameter_create(kappa, kappa, niter);
1072 }
1073
1074
1075 /* Collapse image list */
1076 hdrl_imagelist_collapse(masked_list, collapse_par,&result, &contrib);
1077 if(debug) {
1078 char* fname;
1079 if(plane_id < 0) {
1080 fname = cpl_sprintf("sky_flat_2nd.fits");
1081 } else {
1082 fname = cpl_sprintf("sky_flat_2nd_plane_%3.3lld.fits",plane_id);
1083 }
1084 cpl_image_save(hdrl_image_get_image(result), fname,CPL_TYPE_DOUBLE,NULL,CPL_IO_DEFAULT);
1085 cpl_free(fname);
1086 }
1087
1088 /* Cleanup */
1089 if (contrib != NULL) {
1090 cpl_image_delete(contrib);
1091 }
1092
1093 hdrl_parameter_delete(collapse_par);
1094
1095 if (masks && masked_list) {
1096 hdrl_imagelist_delete(masked_list);
1097 }
1098
1099 eris_check_error_code(cpl_func);
1100 return result;
1101}
1102
1103static cpl_mask * detect_sources_hdrl_catalogue(
1104 const cpl_image * image,
1105 hdrl_parameter* p)
1106{
1107
1108 cpl_mask * mask_res = NULL;
1109
1110 hdrl_catalogue_result *res = hdrl_catalogue_compute(image, NULL, NULL, p);
1111 if(debug) {
1112 char* fname = cpl_sprintf("smap_%d.fits",0);
1113 cpl_image_save(res->segmentation_map, fname, CPL_TYPE_DOUBLE, NULL, CPL_IO_DEFAULT);
1114 cpl_free(fname);
1115 }
1116 double max = cpl_image_get_max(res->segmentation_map);
1117 mask_res = cpl_mask_threshold_image_create(res->segmentation_map, 0.9, max + 0.1);
1119 eris_check_error_code(cpl_func);
1120 return mask_res;
1121}
1122
1123/*----------------------------------------------------------------------------*/
1130/*----------------------------------------------------------------------------*/
1131static cpl_mask * dilate_mask(
1132 const cpl_mask * input_mask,
1133 int radius)
1134{
1135 cpl_mask * result = NULL;
1136 cpl_mask * temp = NULL;
1137 cpl_size nx, ny;
1138
1139 cpl_ensure(input_mask, CPL_ERROR_NULL_INPUT, NULL);
1140 cpl_ensure(radius > 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
1141
1142 nx = cpl_mask_get_size_x(input_mask);
1143 ny = cpl_mask_get_size_y(input_mask);
1144
1145 result = cpl_mask_duplicate(input_mask);
1146
1147 /* Simple box dilation */
1148 for (int iter = 0; iter < radius; iter++) {
1149 temp = cpl_mask_duplicate(result);
1150
1151 for (cpl_size j = 2; j < ny - 1; j++) {
1152 for (cpl_size i = 2; i < nx - 1; i++) {
1153 if (cpl_mask_get(temp, i, j) ||
1154 cpl_mask_get(temp, i-1, j) ||
1155 cpl_mask_get(temp, i+1, j) ||
1156 cpl_mask_get(temp, i, j-1) ||
1157 cpl_mask_get(temp, i, j+1)) {
1158 cpl_mask_set(result, i, j, CPL_BINARY_1);
1159 }
1160 }
1161 }
1162 cpl_mask_delete(temp);
1163 }
1164 eris_check_error_code(cpl_func);
1165 return result;
1166}
1167
1168/*----------------------------------------------------------------------------*/
1198/*----------------------------------------------------------------------------*/
1199cpl_error_code
1200eris_image_stats(const hdrl_image *in_image,
1201 double l_sig,
1202 double u_sig,
1203 const cpl_mask *mask,
1204 double trim_fraction,
1205 double *median,
1206 double *mean,
1207 double *stdev,
1208 double *tmean,
1209 double *tstd,
1210 double *mad,
1211 double *min_val,
1212 double *max_val)
1213{
1214 cpl_ensure_code(in_image, CPL_ERROR_NULL_INPUT);
1215 cpl_ensure_code(median, CPL_ERROR_NULL_INPUT);
1216 cpl_ensure_code(mean, CPL_ERROR_NULL_INPUT);
1217 cpl_ensure_code(stdev, CPL_ERROR_NULL_INPUT);
1218 cpl_ensure_code(tmean, CPL_ERROR_NULL_INPUT);
1219 cpl_ensure_code(tstd, CPL_ERROR_NULL_INPUT);
1220 cpl_ensure_code(mad, CPL_ERROR_NULL_INPUT);
1221 cpl_ensure_code(min_val, CPL_ERROR_NULL_INPUT);
1222 cpl_ensure_code(max_val, CPL_ERROR_NULL_INPUT);
1223
1224 const cpl_image *img = hdrl_image_get_image_const(in_image);
1225 cpl_image *work_img = NULL;
1226 cpl_vector *data_vec = NULL;
1227
1228 /* Create working copy of image */
1229 work_img = cpl_image_duplicate(img);
1230 if (!work_img) {
1231 return cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_OUTPUT,
1232 "Failed to duplicate image");
1233 }
1234
1235 /* Apply mask if provided */
1236 if (mask != NULL) {
1237 cpl_image_reject_from_mask(work_img, mask);
1238 }
1239
1240 /* Step 1: Compute initial statistics (NaN-aware) */
1241 hdrl_value median0 = hdrl_image_get_median(in_image);
1242 hdrl_value mean0 = hdrl_image_get_mean(in_image);
1243 double stdev0 = hdrl_image_get_stdev(in_image);
1244
1245 *median = median0.data;
1246 *mean = mean0.data;
1247 *stdev = stdev0;
1248
1249 /* Step 2: Convert image to vector for trimmed statistics */
1250 cpl_size nx = cpl_image_get_size_x(work_img);
1251 cpl_size ny = cpl_image_get_size_y(work_img);
1252 cpl_size npix = nx * ny;
1253
1254 /* Count valid (non-rejected) pixels */
1255 const cpl_mask *rej_mask = cpl_image_get_bpm_const(work_img);
1256 cpl_size nvalid = npix;
1257 if (rej_mask) {
1258 nvalid = npix - cpl_mask_count(rej_mask);
1259 }
1260
1261 if (nvalid == 0) {
1262 cpl_image_delete(work_img);
1263 return cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
1264 "No valid pixels in image");
1265 }
1266
1267 /* Extract valid pixel values to vector */
1268 data_vec = cpl_vector_new(nvalid);
1269 cpl_size idx = 0;
1270 for (cpl_size j = 1; j <= ny; j++) {
1271 for (cpl_size i = 1; i <= nx; i++) {
1272 int is_rejected = 0;
1273 double val = cpl_image_get(work_img, i, j, &is_rejected);
1274 if (!is_rejected && isfinite(val)) {
1275 cpl_vector_set(data_vec, idx, val);
1276 idx++;
1277 }
1278 }
1279 }
1280
1281 /* Adjust vector size if some NaNs were found */
1282 if (idx < nvalid) {
1283 cpl_vector *tmp = cpl_vector_extract(data_vec, 0, idx - 1, 1);
1284 cpl_vector_delete(data_vec);
1285 data_vec = tmp;
1286 nvalid = idx;
1287 }
1288
1289 /* Step 3: Compute trimmed mean (trim_fraction from each end) */
1290 cpl_vector_sort(data_vec, CPL_SORT_ASCENDING);
1291 cpl_size trim_count = (cpl_size)(trim_fraction * nvalid);
1292 if (trim_count * 2 >= nvalid) {
1293 trim_count = 0; /* Not enough data for trimming */
1294 }
1295
1296 double tmean0_10 = 0.0;
1297 if (trim_count > 0) {
1298 /* Compute mean of middle (1 - 2*trim_fraction) of data */
1299 cpl_vector *trimmed = cpl_vector_extract(data_vec, trim_count,
1300 nvalid - trim_count - 1, 1);
1301 tmean0_10 = cpl_vector_get_mean(trimmed);
1302 cpl_vector_delete(trimmed);
1303 } else {
1304 tmean0_10 = cpl_vector_get_mean(data_vec);
1305 }
1306
1307 /* Step 4: Define clipping range based on trimmed mean */
1308 double rmin = tmean0_10 - l_sig * (*stdev);
1309 double rmax = tmean0_10 + u_sig * (*stdev);
1310
1311 /* Step 5: Compute trimmed statistics within clipping range */
1312 cpl_size nclipped = 0;
1313 double sum_clipped = 0.0;
1314 double sum_sq_clipped = 0.0;
1315
1316 for (cpl_size i = 0; i < nvalid; i++) {
1317 double val = cpl_vector_get(data_vec, i);
1318 if (val >= rmin && val <= rmax) {
1319 sum_clipped += val;
1320 sum_sq_clipped += val * val;
1321 nclipped++;
1322 }
1323 }
1324
1325 if (nclipped > 0) {
1326 *tmean = sum_clipped / nclipped;
1327
1328 if (nclipped > 1) {
1329 double variance = (sum_sq_clipped - sum_clipped * sum_clipped / nclipped)
1330 / (nclipped - 1);
1331 *tstd = (variance > 0.0) ? sqrt(variance) : 0.0;
1332 } else {
1333 *tstd = 0.0;
1334 }
1335 } else {
1336 /* No data within clipping range - use unclipped statistics */
1337 *tmean = *mean;
1338 *tstd = *stdev;
1339 }
1340
1341 /* Step 6: Compute MAD (Median Absolute Deviation) */
1342 /* MAD = median(|data - median|) */
1343 cpl_vector *abs_dev = cpl_vector_duplicate(data_vec);
1344 cpl_vector_subtract_scalar(abs_dev, *median);
1345 for (cpl_size i = 0; i < nvalid; i++) {
1346 double val = cpl_vector_get(abs_dev, i);
1347 cpl_vector_set(abs_dev, i, fabs(val));
1348 }
1349 cpl_vector_sort(abs_dev, CPL_SORT_ASCENDING);
1350
1351 /*AMO Check MAD computation versus HDRL
1352 double sigma;
1353 cpl_image* ima = cpl_image_wrap_double(nvalid, 1, cpl_vector_get_data(abs_dev));
1354
1355 *mad=cpl_image_get_mad_window(ima, 1, 1, nvalid, 1, &sigma);
1356
1357 cpl_msg_warning(cpl_func,"HDRL mad: %g", *mad);
1358 */
1359
1360 *mad = cpl_vector_get_median(abs_dev);
1361 //cpl_msg_warning(cpl_func,"OTHER mad: %g", *mad);
1362 cpl_vector_delete(abs_dev);
1363
1364 /* Step 7: Get min and max */
1365 *min_val = cpl_vector_get_min(data_vec);
1366 *max_val = cpl_vector_get_max(data_vec);
1367
1368 /* Cleanup */
1369 cpl_vector_delete(data_vec);
1370 cpl_image_delete(work_img);
1371 eris_check_error_code(cpl_func);
1372 return CPL_ERROR_NONE;
1373}
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