00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #ifdef HAVE_CONFIG_H
00028 #include <config.h>
00029 #endif
00030
00031
00032
00033
00034 #include <string.h>
00035 #include <cpl.h>
00036
00037 #include "muse_combine.h"
00038
00039 #include "muse_basicproc.h"
00040 #include "muse_cplwrappers.h"
00041 #include "muse_pfits.h"
00042 #include "muse_quality.h"
00043 #include "muse_utils.h"
00044
00045
00046
00047
00048
00049
00050 const char *kCombinationStrings[] = {
00051 "sum",
00052 "average",
00053 "median",
00054 "minmax",
00055 "sigclip",
00056 "none",
00057 "unknown"
00058 };
00059
00060
00071
00072
00075 #define MUSE_COMBINE_DATA_DELETE \
00076 cpl_free(indata); \
00077 cpl_free(indq); \
00078 cpl_free(instat);
00079
00080 #define MUSE_COMBINE_DATA_NEW \
00081 \
00082 int nx = cpl_image_get_size_x(muse_imagelist_get(aImages, 0)->data), \
00083 ny = cpl_image_get_size_y(muse_imagelist_get(aImages, 0)->data); \
00084 \
00085 \
00086 muse_image *combined = muse_image_new(); \
00087 combined->data = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); \
00088 combined->dq = cpl_image_new(nx, ny, CPL_TYPE_INT); \
00089 combined->stat = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); \
00090 combined->header = cpl_propertylist_new(); \
00091 if (!combined || !combined->data || !combined->dq || !combined->stat) { \
00092 cpl_msg_error(__func__, "Could not allocate all parts of output image"); \
00093 muse_image_delete(combined); \
00094 return NULL; \
00095 } \
00096 \
00097 \
00098 float *pixdata = cpl_image_get_data_float(combined->data), \
00099 *pixstat = cpl_image_get_data_float(combined->stat); \
00100 unsigned int *pixdq = (unsigned int *)cpl_image_get_data_int(combined->dq); \
00101 \
00102 \
00103 float **indata = (float **)cpl_malloc(n * sizeof(float *)), \
00104 **instat = (float **)cpl_malloc(n * sizeof(float *)); \
00105 unsigned int **indq = (unsigned int **)cpl_malloc(n * sizeof(unsigned int *));\
00106 cpl_errorstate state = cpl_errorstate_get(); \
00107 unsigned int k; \
00108 for (k = 0; k < n; k++) { \
00109 indata[k] = cpl_image_get_data_float(muse_imagelist_get(aImages, k)->data);\
00110 indq[k] = (unsigned int *)cpl_image_get_data_int(muse_imagelist_get(aImages, k)->dq);\
00111 instat[k] = cpl_image_get_data_float(muse_imagelist_get(aImages, k)->stat);\
00112 } \
00113 if (!cpl_errorstate_is_equal(state)) { \
00114 \
00115 \
00116 cpl_errorstate_set(state); \
00117 muse_image_delete(combined); \
00118 MUSE_COMBINE_DATA_DELETE \
00119 cpl_error_set_message(__func__, CPL_ERROR_INCOMPATIBLE_INPUT, \
00120 "An image component in the input list was missing"); \
00121 return NULL; \
00122 }
00123
00124
00151
00152 muse_image *
00153 muse_combine_sum_create(muse_imagelist *aImages)
00154 {
00155 cpl_ensure(aImages, CPL_ERROR_NULL_INPUT, NULL);
00156
00157 unsigned int n = muse_imagelist_get_size(aImages);
00158 MUSE_COMBINE_DATA_NEW
00159
00160
00161 int i;
00162 for (i = 0; i < nx; i++) {
00163 int j;
00164 for (j = 0; j < ny; j++) {
00165 unsigned int ngood = 0, outdq = EURO3D_GOODPIXEL;
00166 double sumdata = 0., sumstat = 0.;
00167
00168
00169 for (k = 0; k < n; k++) {
00170 if (indq[k][i + j*nx] != EURO3D_GOODPIXEL) {
00171
00172 continue;
00173 }
00174
00175 ngood++;
00176 sumdata += indata[k][i + j*nx];
00177 sumstat += instat[k][i + j*nx];
00178 }
00179 if (!ngood) {
00180
00181
00182 outdq = 1 << 31;
00183 unsigned int kbest = 0;
00184 for (k = 0; k < n; k++) {
00185 if (indq[k][i + j*nx] < outdq) {
00186 kbest = k;
00187 outdq = indq[k][i + j*nx];
00188 }
00189 }
00190 sumdata = indata[kbest][i + j*nx];
00191 sumstat = instat[kbest][i + j*nx];
00192
00193 ngood = 1;
00194 }
00195
00196
00197 pixdata[i + j*nx] = sumdata * n / ngood;
00198 pixdq[i + j*nx] = outdq;
00199
00200 pixstat[i + j*nx] = sumstat * n * n / ngood / ngood;
00201 }
00202 }
00203
00204 MUSE_COMBINE_DATA_DELETE
00205
00206 return combined;
00207 }
00208
00209
00232
00233 muse_image *
00234 muse_combine_average_create(muse_imagelist *aImages)
00235 {
00236 cpl_ensure(aImages, CPL_ERROR_NULL_INPUT, NULL);
00237
00238 unsigned int n = muse_imagelist_get_size(aImages);
00239 MUSE_COMBINE_DATA_NEW
00240
00241
00242 int i;
00243 for (i = 0; i < nx; i++) {
00244 int j;
00245 for (j = 0; j < ny; j++) {
00246 unsigned int ngood = 0, outdq = EURO3D_GOODPIXEL;
00247 double sumdata = 0., sumstat = 0.;
00248
00249
00250 for (k = 0; k < n; k++) {
00251 if (indq[k][i + j*nx] != EURO3D_GOODPIXEL) {
00252
00253 continue;
00254 }
00255
00256 ngood++;
00257 sumdata += indata[k][i + j*nx];
00258 sumstat += instat[k][i + j*nx];
00259 }
00260 if (!ngood) {
00261
00262
00263 outdq = 1 << 31;
00264 unsigned int kbest = 0;
00265 for (k = 0; k < n; k++) {
00266 if (indq[k][i + j*nx] < outdq) {
00267 kbest = k;
00268 outdq = indq[k][i + j*nx];
00269 }
00270 }
00271 sumdata = indata[kbest][i + j*nx];
00272 sumstat = instat[kbest][i + j*nx];
00273 ngood = 1;
00274 }
00275
00276
00277 pixdata[i + j*nx] = sumdata / ngood;
00278 pixdq[i + j*nx] = outdq;
00279
00280
00281 pixstat[i + j*nx] = sumstat / ngood / ngood;
00282 }
00283 }
00284
00285 MUSE_COMBINE_DATA_DELETE
00286
00287 return combined;
00288 }
00289
00290
00314
00315 muse_image *
00316 muse_combine_median_create(muse_imagelist *aImages)
00317 {
00318 cpl_ensure(aImages, CPL_ERROR_NULL_INPUT, NULL);
00319
00320 unsigned int n = muse_imagelist_get_size(aImages);
00321 MUSE_COMBINE_DATA_NEW
00322
00323
00324 int i;
00325 for (i = 0; i < nx; i++) {
00326 int j;
00327 for (j = 0; j < ny; j++) {
00328 unsigned int ngood = 0;
00329
00330
00331
00332 cpl_matrix *values = cpl_matrix_new(n, 2);
00333 for (k = 0; k < n; k++) {
00334 if (indq[k][i + j*nx] != EURO3D_GOODPIXEL) {
00335 continue;
00336 }
00337 cpl_matrix_set(values, ngood, 0, indata[k][i + j*nx]);
00338 cpl_matrix_set(values, ngood, 1, instat[k][i + j*nx]);
00339 ngood++;
00340 }
00341
00342 if (ngood) {
00343 cpl_matrix_set_size(values, ngood, 2);
00344 cpl_matrix_sort_rows(values, 1);
00345
00346
00347 if (ngood & 1) {
00348
00349 pixdata[i + j*nx] = cpl_matrix_get(values, ngood/2, 0);
00350
00351 pixstat[i + j*nx] = cpl_matrix_get(values, ngood/2, 1);
00352 } else {
00353
00354 pixdata[i + j*nx] = 0.5 * (cpl_matrix_get(values, ngood/2, 0)
00355 + cpl_matrix_get(values, ngood/2-1, 0));
00356
00357 pixstat[i + j*nx] = cpl_matrix_get(values, ngood/2, 1)
00358 + cpl_matrix_get(values, ngood/2-1, 1);
00359 }
00360 pixdq[i + j*nx] = EURO3D_GOODPIXEL;
00361 } else {
00362
00363
00364 unsigned int outdq = 1 << 31,
00365 kbest = 0;
00366 for (k = 0; k < n; k++) {
00367 if (indq[k][i + j*nx] < outdq) {
00368 kbest = k;
00369 outdq = indq[k][i + j*nx];
00370 }
00371 }
00372 pixdata[i + j*nx] = indata[kbest][i + j*nx];
00373 pixdq[i + j*nx] = outdq;
00374 pixstat[i + j*nx] = instat[kbest][i + j*nx];
00375 ngood = 1;
00376 }
00377
00378 cpl_matrix_delete(values);
00379 }
00380 }
00381
00382 MUSE_COMBINE_DATA_DELETE
00383
00384 return combined;
00385 }
00386
00387
00425
00426 muse_image *
00427 muse_combine_minmax_create(muse_imagelist *aImages, int aMin, int aMax, int aKeep)
00428 {
00429 cpl_ensure(aImages, CPL_ERROR_NULL_INPUT, NULL);
00430
00431 unsigned int n = muse_imagelist_get_size(aImages);
00432 int nMax = n - aMax;
00433
00434 if ((nMax-aMin) < aKeep || nMax <= 0) {
00435 cpl_msg_error(__func__, "Not enough images left after minmax rejection: %d "
00436 "input images, min=%d, max=%d, keep=%d", n, aMin, aMax, aKeep);
00437 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT);
00438 return NULL;
00439 }
00440
00441 MUSE_COMBINE_DATA_NEW
00442
00443
00444 int i;
00445 for (i = 0; i < nx; i++) {
00446 int j;
00447 for (j = 0; j < ny; j++) {
00448 unsigned int ngood = 0;
00449
00450
00451
00452 cpl_matrix *values = cpl_matrix_new(n, 2);
00453 for (k = 0; k < n; k++) {
00454 if (indq[k][i + j*nx] != EURO3D_GOODPIXEL) {
00455 continue;
00456 }
00457 cpl_matrix_set(values, ngood, 0, indata[k][i + j*nx]);
00458 cpl_matrix_set(values, ngood, 1, instat[k][i + j*nx]);
00459 ngood++;
00460 }
00461
00462 int npix = ngood - aMax - aMin;
00463 if (ngood > 0) {
00464 unsigned int outdq = EURO3D_GOODPIXEL;
00465 if (npix > 0 && npix < aKeep) {
00466
00467 for (k = 0; k < n && npix < aKeep; k++) {
00468 if (indq[k][i + j*nx] != EURO3D_GOODPIXEL) {
00469 cpl_matrix_set(values, ngood, 0, indata[k][i + j*nx]);
00470 cpl_matrix_set(values, ngood, 1, instat[k][i + j*nx]);
00471 outdq |= indq[k][i + j*nx];
00472 ngood++, npix++;
00473 }
00474 }
00475 }
00476
00477
00478 cpl_matrix_set_size(values, ngood, 2);
00479 cpl_matrix_sort_rows(values, 1);
00480
00481
00482 if (aMin > 0) {
00483 cpl_matrix_erase_rows(values, ngood - aMin, aMin);
00484 }
00485 if (aMax > 0) {
00486 cpl_matrix_erase_rows(values, 0, aMax);
00487 }
00488
00489
00490 double sumdata = 0., sumstat = 0.;
00491 for (k = 0; (int)k < npix; k++) {
00492 sumdata += cpl_matrix_get(values, k, 0);
00493 sumstat += cpl_matrix_get(values, k, 1);
00494 }
00495 pixdata[i + j*nx] = sumdata / npix;
00496 pixstat[i + j*nx] = sumstat / npix / npix;
00497 pixdq[i + j*nx] = outdq;
00498 } else {
00499
00500
00501 unsigned int outdq = 1 << 31,
00502 kbest = 0;
00503 for (k = 0; k < n; k++) {
00504 if (indq[k][i + j*nx] < outdq) {
00505 kbest = k;
00506 outdq = indq[k][i + j*nx];
00507 }
00508 }
00509 pixdata[i + j*nx] = indata[kbest][i + j*nx];
00510 pixdq[i + j*nx] = outdq;
00511 pixstat[i + j*nx] = instat[kbest][i + j*nx];
00512 }
00513
00514 cpl_matrix_delete(values);
00515 }
00516 }
00517
00518 MUSE_COMBINE_DATA_DELETE
00519
00520 return combined;
00521 }
00522
00523
00555
00556 muse_image *
00557 muse_combine_sigclip_create(muse_imagelist *aImages, double aLow, double aHigh)
00558 {
00559 cpl_ensure(aImages, CPL_ERROR_NULL_INPUT, NULL);
00560
00561 unsigned int n = muse_imagelist_get_size(aImages);
00562 if (n < 3) {
00563 cpl_msg_error(__func__, "Sigma clipping requires at least 3 images!");
00564 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT);
00565 return NULL;
00566 }
00567 MUSE_COMBINE_DATA_NEW
00568
00569
00570 double *pdata = cpl_malloc(n * sizeof(double)),
00571 *pdtmp = cpl_malloc(n * sizeof(double)),
00572 *pstat = cpl_malloc(n * sizeof(double));
00573 unsigned int *idx = cpl_malloc(n * sizeof(unsigned int));
00574
00575
00576 int i;
00577 for (i = 0; i < nx; i++) {
00578 int j;
00579 for (j = 0; j < ny; j++) {
00580 unsigned int ngood = 0;
00581
00582
00583
00584 for (k = 0; k < n; k++) {
00585 if (indq[k][i + j*nx] != EURO3D_GOODPIXEL) {
00586 continue;
00587 }
00588 pdata[ngood] = indata[k][i + j*nx];
00589 pstat[ngood] = instat[k][i + j*nx];
00590 pdtmp[ngood] = pdata[ngood];
00591 ngood++;
00592 }
00593
00594 if (ngood > 0) {
00595
00596 cpl_vector *vtmp = cpl_vector_wrap(ngood, pdtmp);
00597 double median, mdev = muse_cplvector_get_median_dev(vtmp, &median),
00598 lo = median - aLow * mdev,
00599 hi = median + aHigh * mdev;
00600 cpl_vector_unwrap(vtmp);
00601
00602
00603 if (hi > lo) {
00604
00605 unsigned int ntest = ngood;
00606 ngood = 0;
00607 for (k = 0; k < ntest; k++) {
00608 if (pdata[k] >= lo && pdata[k] <= hi) {
00609 idx[ngood++] = k;
00610 }
00611 }
00612 } else {
00613 for (k = 0; k < ngood; k++) {
00614 idx[k] = k;
00615 }
00616 }
00617
00618
00619 double sumdata = 0., sumstat = 0.;
00620 for (k = 0; k < ngood; k++) {
00621 sumdata += pdata[idx[k]];
00622 sumstat += pstat[idx[k]];
00623 }
00624 pixdata[i + j*nx] = sumdata / ngood;
00625 pixstat[i + j*nx] = sumstat / ngood / ngood;
00626 pixdq[i + j*nx] = EURO3D_GOODPIXEL;
00627 } else {
00628
00629
00630 unsigned int outdq = 1 << 31,
00631 kbest = 0;
00632 for (k = 0; k < n; k++) {
00633 if (indq[k][i + j*nx] < outdq) {
00634 kbest = k;
00635 outdq = indq[k][i + j*nx];
00636 }
00637 }
00638 pixdata[i + j*nx] = indata[kbest][i + j*nx];
00639 pixdq[i + j*nx] = outdq;
00640 pixstat[i + j*nx] = instat[kbest][i + j*nx];
00641 }
00642 }
00643 }
00644 cpl_free(pdata);
00645 cpl_free(pdtmp);
00646 cpl_free(pstat);
00647 cpl_free(idx);
00648
00649 MUSE_COMBINE_DATA_DELETE
00650
00651 return combined;
00652 }
00653
00654
00670
00671 muse_combinepar *
00672 muse_combinepar_new(cpl_parameterlist *aParameters, const char *aPrefix)
00673 {
00674 cpl_ensure(aParameters && aPrefix, CPL_ERROR_NULL_INPUT, NULL);
00675 muse_combinepar *cpars = cpl_calloc(1, sizeof(muse_combinepar));
00676 cpars->combine = MUSE_COMBINE_UNKNOWN;
00677
00678 cpl_parameter *param;
00679
00680 param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "combine");
00681 const char *method = param ? cpl_parameter_get_string(param) : "median";
00682 int i;
00683 for (i = 0; i < MUSE_COMBINE_UNKNOWN; i++) {
00684 if (!strcmp(kCombinationStrings[i], method)) {
00685 cpars->combine = i;
00686 }
00687 }
00688
00689 param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "nlow");
00690 cpars->nLow = param ? cpl_parameter_get_int(param) : 1;
00691 param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "nhigh");
00692 cpars->nHigh = param ? cpl_parameter_get_int(param) : 1;
00693 param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "nkeep");
00694 cpars->nKeep = param ? cpl_parameter_get_int(param) : 1;
00695
00696 param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "lsigma");
00697 cpars->lSigma = param ? cpl_parameter_get_double(param) : 3.0;
00698 param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "hsigma");
00699 cpars->hSigma = param ? cpl_parameter_get_double(param) : 3.0;
00700
00701 param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "scale");
00702 cpars->scale = param ? cpl_parameter_get_bool(param) : FALSE;
00703
00704 return cpars;
00705 }
00706
00707
00708
00713
00714 void
00715 muse_combinepar_delete(muse_combinepar *aCPars)
00716 {
00717 cpl_free(aCPars);
00718 }
00719
00720
00739
00740 muse_image *
00741 muse_combine_images(muse_combinepar *aCPars, muse_imagelist *aImages)
00742 {
00743 if (!aImages) {
00744 cpl_msg_error(__func__, "Image list missing!");
00745 cpl_error_set(__func__, CPL_ERROR_NULL_INPUT);
00746 return NULL;
00747 }
00748 if (!aCPars) {
00749 cpl_msg_error(__func__, "Parameters missing!");
00750 cpl_error_set(__func__, CPL_ERROR_NULL_INPUT);
00751 return NULL;
00752 }
00753
00754 if (muse_imagelist_get_size(aImages) == 1) {
00755 cpl_msg_debug(__func__, "Only one image in list, duplicate instead of combine...");
00756 return muse_image_duplicate(muse_imagelist_get(aImages, 0));
00757 }
00758
00759 if (aCPars->scale) {
00760 muse_imagelist_scale_exptime(aImages);
00761 }
00762
00763 muse_image *masterimage = NULL;
00764 switch (aCPars->combine) {
00765 case MUSE_COMBINE_SUM:
00766 cpl_msg_info(__func__, "Combination method: sum (without rejection)");
00767 masterimage = muse_combine_sum_create(aImages);
00768 break;
00769
00770 case MUSE_COMBINE_AVERAGE:
00771 cpl_msg_info(__func__, "Combination method: average (without rejection)");
00772 masterimage = muse_combine_average_create(aImages);
00773 break;
00774
00775 case MUSE_COMBINE_MEDIAN:
00776 cpl_msg_info(__func__, "Combination method: median (without rejection)");
00777 masterimage = muse_combine_median_create(aImages);
00778 break;
00779
00780 case MUSE_COMBINE_MINMAX:
00781 cpl_msg_info(__func__, "Combination method: average with minmax rejection "
00782 "(%d/%d/%d)", aCPars->nLow, aCPars->nHigh, aCPars->nKeep);
00783 masterimage = muse_combine_minmax_create(aImages, aCPars->nLow, aCPars->nHigh,
00784 aCPars->nKeep);
00785 break;
00786
00787 case MUSE_COMBINE_SIGCLIP:
00788 cpl_msg_info(__func__, "Combination method: average with sigma clipping "
00789 "(%f/%f)", aCPars->lSigma, aCPars->hSigma);
00790 masterimage = muse_combine_sigclip_create(aImages,
00791 aCPars->lSigma, aCPars->hSigma);
00792 break;
00793
00794 default:
00795 cpl_msg_error(__func__, "Unknown combination method: %s (%d)",
00796 kCombinationStrings[aCPars->combine], aCPars->combine);
00797 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT);
00798 }
00799 if (!masterimage) {
00800 return NULL;
00801 }
00802
00803
00804
00805 cpl_propertylist_copy_property_regexp(masterimage->header,
00806 muse_imagelist_get(aImages, 0)->header,
00807 MUSE_HDR_TMP_REGEXP, 1);
00808
00809 return masterimage;
00810 }
00811