38 #include "muse_combine.h"
40 #include "muse_basicproc.h"
41 #include "muse_cplwrappers.h"
42 #include "muse_pfits.h"
43 #include "muse_quality.h"
44 #include "muse_utils.h"
51 const char *kCombinationStrings[] = {
76 #define MUSE_COMBINE_DATA_DELETE \
81 #define MUSE_COMBINE_DATA_NEW \
83 int nx = cpl_image_get_size_x(muse_imagelist_get(aImages, 0)->data), \
84 ny = cpl_image_get_size_y(muse_imagelist_get(aImages, 0)->data); \
87 muse_image *combined = muse_image_new(); \
88 combined->data = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); \
89 combined->dq = cpl_image_new(nx, ny, CPL_TYPE_INT); \
90 combined->stat = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); \
91 combined->header = cpl_propertylist_new(); \
92 if (!combined || !combined->data || !combined->dq || !combined->stat) { \
93 cpl_msg_error(__func__, "Could not allocate all parts of output image"); \
94 muse_image_delete(combined); \
99 float *pixdata = cpl_image_get_data_float(combined->data), \
100 *pixstat = cpl_image_get_data_float(combined->stat); \
101 unsigned int *pixdq = (unsigned int *)cpl_image_get_data_int(combined->dq); \
104 float **indata = (float **)cpl_malloc(n * sizeof(float *)), \
105 **instat = (float **)cpl_malloc(n * sizeof(float *)); \
106 unsigned int **indq = (unsigned int **)cpl_malloc(n * sizeof(unsigned int *));\
107 cpl_errorstate state = cpl_errorstate_get(); \
109 for (k = 0; k < n; k++) { \
110 indata[k] = cpl_image_get_data_float(muse_imagelist_get(aImages, k)->data);\
111 indq[k] = (unsigned int *)cpl_image_get_data_int(muse_imagelist_get(aImages, k)->dq);\
112 instat[k] = cpl_image_get_data_float(muse_imagelist_get(aImages, k)->stat);\
114 if (!cpl_errorstate_is_equal(state)) { \
117 cpl_errorstate_set(state); \
118 muse_image_delete(combined); \
119 MUSE_COMBINE_DATA_DELETE \
120 cpl_error_set_message(__func__, CPL_ERROR_INCOMPATIBLE_INPUT, \
121 "An image component in the input list was missing"); \
156 cpl_ensure(aImages, CPL_ERROR_NULL_INPUT, NULL);
159 MUSE_COMBINE_DATA_NEW
163 for (i = 0; i < nx; i++) {
165 for (j = 0; j < ny; j++) {
166 unsigned int ngood = 0, outdq = EURO3D_GOODPIXEL;
167 double sumdata = 0., sumstat = 0.;
170 for (k = 0; k < n; k++) {
171 if (indq[k][i + j*nx] != EURO3D_GOODPIXEL) {
177 sumdata += indata[k][i + j*nx];
178 sumstat += instat[k][i + j*nx];
184 unsigned int kbest = 0;
185 for (k = 0; k < n; k++) {
186 if (indq[k][i + j*nx] < outdq) {
188 outdq = indq[k][i + j*nx];
191 sumdata = indata[kbest][i + j*nx];
192 sumstat = instat[kbest][i + j*nx];
198 pixdata[i + j*nx] = sumdata * n / ngood;
199 pixdq[i + j*nx] = outdq;
201 pixstat[i + j*nx] = sumstat * n * n / ngood / ngood;
205 MUSE_COMBINE_DATA_DELETE
237 cpl_ensure(aImages, CPL_ERROR_NULL_INPUT, NULL);
240 MUSE_COMBINE_DATA_NEW
244 for (i = 0; i < nx; i++) {
246 for (j = 0; j < ny; j++) {
247 unsigned int ngood = 0, outdq = EURO3D_GOODPIXEL;
248 double sumdata = 0., sumstat = 0.;
251 for (k = 0; k < n; k++) {
252 if (indq[k][i + j*nx] != EURO3D_GOODPIXEL) {
258 sumdata += indata[k][i + j*nx];
259 sumstat += instat[k][i + j*nx];
265 unsigned int kbest = 0;
266 for (k = 0; k < n; k++) {
267 if (indq[k][i + j*nx] < outdq) {
269 outdq = indq[k][i + j*nx];
272 sumdata = indata[kbest][i + j*nx];
273 sumstat = instat[kbest][i + j*nx];
278 pixdata[i + j*nx] = sumdata / ngood;
279 pixdq[i + j*nx] = outdq;
282 pixstat[i + j*nx] = sumstat / ngood / ngood;
286 MUSE_COMBINE_DATA_DELETE
319 cpl_ensure(aImages, CPL_ERROR_NULL_INPUT, NULL);
322 MUSE_COMBINE_DATA_NEW
326 for (i = 0; i < nx; i++) {
328 for (j = 0; j < ny; j++) {
329 unsigned int ngood = 0;
333 cpl_matrix *values = cpl_matrix_new(n, 2);
334 for (k = 0; k < n; k++) {
335 if (indq[k][i + j*nx] != EURO3D_GOODPIXEL) {
338 cpl_matrix_set(values, ngood, 0, indata[k][i + j*nx]);
339 cpl_matrix_set(values, ngood, 1, instat[k][i + j*nx]);
344 cpl_matrix_set_size(values, ngood, 2);
345 cpl_matrix_sort_rows(values, 1);
350 pixdata[i + j*nx] = cpl_matrix_get(values, ngood/2, 0);
352 pixstat[i + j*nx] = cpl_matrix_get(values, ngood/2, 1);
355 pixdata[i + j*nx] = 0.5 * (cpl_matrix_get(values, ngood/2, 0)
356 + cpl_matrix_get(values, ngood/2-1, 0));
358 pixstat[i + j*nx] = cpl_matrix_get(values, ngood/2, 1)
359 + cpl_matrix_get(values, ngood/2-1, 1);
361 pixdq[i + j*nx] = EURO3D_GOODPIXEL;
365 unsigned int outdq = 1 << 31,
367 for (k = 0; k < n; k++) {
368 if (indq[k][i + j*nx] < outdq) {
370 outdq = indq[k][i + j*nx];
373 pixdata[i + j*nx] = indata[kbest][i + j*nx];
374 pixdq[i + j*nx] = outdq;
375 pixstat[i + j*nx] = instat[kbest][i + j*nx];
379 cpl_matrix_delete(values);
383 MUSE_COMBINE_DATA_DELETE
430 cpl_ensure(aImages, CPL_ERROR_NULL_INPUT, NULL);
435 if ((nMax-aMin) < aKeep || nMax <= 0) {
436 cpl_msg_error(__func__,
"Not enough images left after minmax rejection: %d "
437 "input images, min=%d, max=%d, keep=%d", n, aMin, aMax, aKeep);
438 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT);
442 MUSE_COMBINE_DATA_NEW
446 for (i = 0; i < nx; i++) {
448 for (j = 0; j < ny; j++) {
449 unsigned int ngood = 0;
453 cpl_matrix *values = cpl_matrix_new(n, 2);
454 for (k = 0; k < n; k++) {
455 if (indq[k][i + j*nx] != EURO3D_GOODPIXEL) {
458 cpl_matrix_set(values, ngood, 0, indata[k][i + j*nx]);
459 cpl_matrix_set(values, ngood, 1, instat[k][i + j*nx]);
463 int npix = ngood - aMax - aMin;
465 unsigned int outdq = EURO3D_GOODPIXEL;
466 if (npix > 0 && npix < aKeep) {
468 for (k = 0; k < n && npix < aKeep; k++) {
469 if (indq[k][i + j*nx] != EURO3D_GOODPIXEL) {
470 cpl_matrix_set(values, ngood, 0, indata[k][i + j*nx]);
471 cpl_matrix_set(values, ngood, 1, instat[k][i + j*nx]);
472 outdq |= indq[k][i + j*nx];
479 cpl_matrix_set_size(values, ngood, 2);
480 cpl_matrix_sort_rows(values, 1);
484 cpl_matrix_erase_rows(values, ngood - aMin, aMin);
487 cpl_matrix_erase_rows(values, 0, aMax);
491 double sumdata = 0., sumstat = 0.;
492 for (k = 0; (int)k < npix; k++) {
493 sumdata += cpl_matrix_get(values, k, 0);
494 sumstat += cpl_matrix_get(values, k, 1);
496 pixdata[i + j*nx] = sumdata / npix;
497 pixstat[i + j*nx] = sumstat / npix / npix;
498 pixdq[i + j*nx] = outdq;
502 unsigned int outdq = 1 << 31,
504 for (k = 0; k < n; k++) {
505 if (indq[k][i + j*nx] < outdq) {
507 outdq = indq[k][i + j*nx];
510 pixdata[i + j*nx] = indata[kbest][i + j*nx];
511 pixdq[i + j*nx] = outdq;
512 pixstat[i + j*nx] = instat[kbest][i + j*nx];
515 cpl_matrix_delete(values);
519 MUSE_COMBINE_DATA_DELETE
560 cpl_ensure(aImages, CPL_ERROR_NULL_INPUT, NULL);
564 cpl_msg_error(__func__,
"Sigma clipping requires at least 3 images!");
565 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT);
568 MUSE_COMBINE_DATA_NEW
571 double *pdata = cpl_malloc(n *
sizeof(
double)),
572 *pdtmp = cpl_malloc(n *
sizeof(
double)),
573 *pstat = cpl_malloc(n *
sizeof(
double));
574 unsigned int *idx = cpl_malloc(n *
sizeof(
unsigned int));
578 for (i = 0; i < nx; i++) {
580 for (j = 0; j < ny; j++) {
581 unsigned int ngood = 0;
585 for (k = 0; k < n; k++) {
586 if (indq[k][i + j*nx] != EURO3D_GOODPIXEL) {
589 pdata[ngood] = indata[k][i + j*nx];
590 pstat[ngood] = instat[k][i + j*nx];
591 pdtmp[ngood] = pdata[ngood];
597 cpl_vector *vtmp = cpl_vector_wrap(ngood, pdtmp);
599 lo = median - aLow * mdev,
600 hi = median + aHigh * mdev;
601 cpl_vector_unwrap(vtmp);
606 unsigned int ntest = ngood;
608 for (k = 0; k < ntest; k++) {
609 if (pdata[k] >= lo && pdata[k] <= hi) {
614 for (k = 0; k < ngood; k++) {
620 double sumdata = 0., sumstat = 0.;
621 for (k = 0; k < ngood; k++) {
622 sumdata += pdata[idx[k]];
623 sumstat += pstat[idx[k]];
625 pixdata[i + j*nx] = sumdata / ngood;
626 pixstat[i + j*nx] = sumstat / ngood / ngood;
627 pixdq[i + j*nx] = EURO3D_GOODPIXEL;
631 unsigned int outdq = 1 << 31,
633 for (k = 0; k < n; k++) {
634 if (indq[k][i + j*nx] < outdq) {
636 outdq = indq[k][i + j*nx];
639 pixdata[i + j*nx] = indata[kbest][i + j*nx];
640 pixdq[i + j*nx] = outdq;
641 pixstat[i + j*nx] = instat[kbest][i + j*nx];
650 MUSE_COMBINE_DATA_DELETE
675 cpl_ensure(aParameters && aPrefix, CPL_ERROR_NULL_INPUT, NULL);
676 muse_combinepar *cpars = cpl_calloc(1,
sizeof(muse_combinepar));
677 cpars->combine = MUSE_COMBINE_UNKNOWN;
679 cpl_parameter *param;
682 const char *method = param ? cpl_parameter_get_string(param) :
"median";
684 for (i = 0; i < MUSE_COMBINE_UNKNOWN; i++) {
685 if (!strcmp(kCombinationStrings[i], method)) {
691 cpars->nLow = param ? cpl_parameter_get_int(param) : 1;
693 cpars->nHigh = param ? cpl_parameter_get_int(param) : 1;
695 cpars->nKeep = param ? cpl_parameter_get_int(param) : 1;
698 cpars->lSigma = param ? cpl_parameter_get_double(param) : 3.0;
700 cpars->hSigma = param ? cpl_parameter_get_double(param) : 3.0;
703 cpars->scale = param ? cpl_parameter_get_bool(param) : FALSE;
745 cpl_msg_error(__func__,
"Image list missing!");
746 cpl_error_set(__func__, CPL_ERROR_NULL_INPUT);
750 cpl_msg_error(__func__,
"Parameters missing!");
751 cpl_error_set(__func__, CPL_ERROR_NULL_INPUT);
756 cpl_msg_debug(__func__,
"Only one image in list, duplicate instead of combine...");
765 switch (aCPars->combine) {
766 case MUSE_COMBINE_SUM:
767 cpl_msg_info(__func__,
"Combination method: sum (without rejection)");
771 case MUSE_COMBINE_AVERAGE:
772 cpl_msg_info(__func__,
"Combination method: average (without rejection)");
776 case MUSE_COMBINE_MEDIAN:
777 cpl_msg_info(__func__,
"Combination method: median (without rejection)");
781 case MUSE_COMBINE_MINMAX:
782 cpl_msg_info(__func__,
"Combination method: average with minmax rejection "
783 "(%d/%d/%d)", aCPars->nLow, aCPars->nHigh, aCPars->nKeep);
788 case MUSE_COMBINE_SIGCLIP:
789 cpl_msg_info(__func__,
"Combination method: average with sigma clipping "
790 "(%f/%f)", aCPars->lSigma, aCPars->hSigma);
792 aCPars->lSigma, aCPars->hSigma);
796 cpl_msg_error(__func__,
"Unknown combination method: %s (%d)",
797 kCombinationStrings[aCPars->combine], aCPars->combine);
798 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT);
804 cpl_propertylist_copy_property_regexp(masterimage->
header,
808 cpl_propertylist_erase_regexp(masterimage->
header, MUSE_HDR_TMP_REGEXP, 0);
Structure definition for a collection of muse_images.
muse_image * muse_combine_minmax_create(muse_imagelist *aImages, int aMin, int aMax, int aKeep)
Average a list of input images with minmax rejection.
muse_image * muse_image_duplicate(const muse_image *aImage)
Duplicate the three image extensions and the FITS headers of a MUSE image.
muse_image * muse_combine_median_create(muse_imagelist *aImages)
Median combine a list of input images.
double muse_cplvector_get_median_dev(cpl_vector *aVector, double *aMedian)
Compute the median and average absolute deviation against the median of a vector. ...
muse_image * muse_combine_images(muse_combinepar *aCPars, muse_imagelist *aImages)
Combine several images into one.
Structure definition of MUSE three extension FITS file.
cpl_propertylist * header
the FITS header
muse_image * muse_combine_average_create(muse_imagelist *aImages)
Average a list of input images.
unsigned int muse_imagelist_get_size(muse_imagelist *aList)
Return the number of stored images.
void muse_combinepar_delete(muse_combinepar *aCPars)
Clear the combination parameters.
cpl_error_code muse_imagelist_scale_exptime(muse_imagelist *aList)
Scale muse_images to a common exposure time.
muse_image * muse_imagelist_get(muse_imagelist *aList, unsigned int aIdx)
Get the muse_image of given list index.
muse_image * muse_combine_sum_create(muse_imagelist *aImages)
Sum a list of input images.
muse_combinepar * muse_combinepar_new(cpl_parameterlist *aParameters, const char *aPrefix)
Create a new set of combination parameters.
cpl_parameter * muse_cplparamerterlist_find_prefix(cpl_parameterlist *aParameters, const char *aPrefix, const char *aName)
Return the full recipe parameter belonging to prefix and shortname.
muse_image * muse_combine_sigclip_create(muse_imagelist *aImages, double aLow, double aHigh)
Average a list of input images with sigma clipping rejection.