28#include <cxmessages.h>
30#include <cxstrutils.h>
32#include <cpl_parameterlist.h>
33#include <cpl_matrix.h>
48#include "gilocalization.h"
49#include "gimessages.h"
50#include "gifiberutils.h"
64 PROFILE_PSFEXP = 1 << 1,
65 PROFILE_PSFEXP2 = 1 << 2,
66 PROFILE_GAUSSIAN = 1 << 3
69typedef enum GiProfileId GiProfileId;
76struct GiExtractOptimalConfig {
90typedef struct GiExtractOptimalConfig GiExtractOptimalConfig;
97struct GiExtractHorneConfig {
106typedef struct GiExtractHorneConfig GiExtractHorneConfig;
109struct GiExtractionData {
116typedef struct GiExtractionData GiExtractionData;
119struct GiExtractionSlice {
127 cpl_matrix* variance;
131typedef struct GiExtractionSlice GiExtractionSlice;
134struct GiExtractionPsfLimits {
141typedef struct GiExtractionPsfLimits GiExtractionPsfLimits;
144struct GiExtractionWorkspace {
152typedef struct GiExtractionWorkspace GiExtractionWorkspace;
155struct GiVirtualSlit {
159 cxdouble extra_width;
170typedef struct GiVirtualSlit GiVirtualSlit;
177inline static GiExtractionSlice*
178_giraffe_extractionslice_new(cxint nflx, cxint ndata, cxint nbkg)
181 GiExtractionSlice* self = cx_malloc(
sizeof *self);
186 self->fsize = nflx + nbkg;
189 self->flux = cpl_matrix_new(self->fsize, 1);
190 self->variance = cpl_matrix_new(self->fsize, 1);
191 self->model = cpl_matrix_new(self->msize, 1);
199_giraffe_extractionslice_delete(GiExtractionSlice* self)
203 if (self->model != NULL) {
204 cpl_matrix_delete(self->model);
208 if (self->variance != NULL) {
209 cpl_matrix_delete(self->variance);
210 self->variance = NULL;
213 if (self->flux != NULL) {
214 cpl_matrix_delete(self->flux);
226inline static GiExtractionPsfLimits*
227_giraffe_extraction_psflimits_new(cxint size)
230 GiExtractionPsfLimits* self = cx_malloc(
sizeof *self);
234 self->ymin = cx_calloc(self->size,
sizeof(cxint));
235 self->ymax = cx_calloc(self->size,
sizeof(cxint));
243_giraffe_extraction_psflimits_delete(GiExtractionPsfLimits* self)
247 if (self->ymin != NULL) {
251 if (self->ymax != NULL) {
263inline static GiExtractionWorkspace*
264_giraffe_optimal_workspace_new(cxint m, cxint n)
267 GiExtractionWorkspace* self = cx_malloc(
sizeof *self);
270 self->atw = cpl_matrix_new(m, n);
271 self->atwa = cpl_matrix_new(m, m);
272 self->c = cpl_matrix_new(m, m);
273 self->atws = cpl_matrix_new(m, 1);
275 self->tmp = cpl_matrix_new(m, m);
283_giraffe_optimal_workspace_delete(GiExtractionWorkspace* self)
287 if (self->atws != NULL) {
288 cpl_matrix_delete(self->atws);
291 if (self->atwa != NULL) {
292 cpl_matrix_delete(self->atwa);
295 if (self->c != NULL) {
296 cpl_matrix_delete(self->c);
299 if (self->atw != NULL) {
300 cpl_matrix_delete(self->atw);
303 if (self->tmp != NULL) {
304 cpl_matrix_delete(self->tmp);
321_giraffe_virtualslit_allocate(GiVirtualSlit* self)
324 if ((self != NULL) && (self->width > 0)) {
326 self->position = cx_calloc(self->width,
sizeof(cxdouble));
327 self->signal = cx_calloc(self->width,
sizeof(cxdouble));
328 self->variance = cx_calloc(self->width,
sizeof(cxdouble));
329 self->fraction = cx_calloc(self->width,
sizeof(cxdouble));
331 self->mask = cx_calloc(self->width,
sizeof(cxdouble));
332 self->offset = cx_calloc(self->width,
sizeof(cxdouble));
341inline static GiVirtualSlit*
342_giraffe_virtualslit_new(cxdouble extra_width)
345 GiVirtualSlit* self = cx_calloc(1,
sizeof *self);
349 self->extra_width = extra_width;
351 self->position = NULL;
353 self->variance = NULL;
354 self->fraction = NULL;
364_giraffe_virtualslit_clear(GiVirtualSlit* self)
369 if (self->position != NULL) {
370 cx_free(self->position);
371 self->position = NULL;
374 if (self->signal != NULL) {
375 cx_free(self->signal);
379 if (self->variance != NULL) {
380 cx_free(self->variance);
381 self->variance = NULL;
384 if (self->fraction != NULL) {
385 cx_free(self->fraction);
386 self->fraction = NULL;
389 if (self->mask != NULL) {
394 if (self->offset != NULL) {
395 cx_free(self->offset);
399 self->extra_width = 0.;
411_giraffe_virtualslit_delete(GiVirtualSlit* self)
415 _giraffe_virtualslit_clear(self);
426_giraffe_virtualslit_setup(GiVirtualSlit* self, cxint bin,
427 cxdouble center, cxdouble width,
428 const cpl_image* signal,
const cpl_image* variance,
429 const cpl_image* bpixel)
432 register cxint ny = cpl_image_get_size_x(signal);
433 register cxint offset = bin * cpl_image_get_size_x(signal);
435 register cxdouble lower = center - (width + self->extra_width);
436 register cxdouble upper = center + (width + self->extra_width);
438 register cxint first = (cxint) floor(lower);
439 register cxint last = (cxint) ceil(upper);
441 const cxdouble* s = cpl_image_get_data_double_const(signal);
442 const cxdouble* v = cpl_image_get_data_double_const(variance);
449 lower = CX_MAX(0., lower);
450 upper = CX_MIN(ny-1, upper);
452 first = CX_MAX(0, first);
453 last = CX_MIN(ny-1, last);
455 self->center = center;
456 self->width = last - first + 1;
463 _giraffe_virtualslit_allocate(self);
465 if (bpixel != NULL) {
467 register cxint k = 0;
468 register cxint y = 0;
470 const cxint* _bpixel = cpl_image_get_data_int_const(bpixel);
473 for (y = first; y <= last; y++) {
475 register cxint ypos = offset + y;
477 cxint ok = (_bpixel[ypos] & GIR_M_PIX_SET) == 0 ? 1 : 0;
480 self->position[k] = y - center;
481 self->fraction[k] = 1.;
483 self->signal[k] = s[ypos];
484 self->variance[k] = v[ypos];
487 self->offset[k] = ypos;
495 register cxint k = 0;
496 register cxint y = 0;
499 for (y = first; y <= last; y++) {
501 register cxint ypos = offset + y;
506 self->position[k] = y - center;
507 self->fraction[k] = 1.;
509 self->signal[k] = s[ypos];
510 self->variance[k] = v[ypos];
513 self->offset[k] = ypos;
527 self->fraction[0] = ((cxdouble)first + 1.) - lower;
528 self->fraction[self->width - 1] = upper - ((cxdouble)last - 1.);
540_giraffe_matrix_invert(cpl_matrix* m_inv,
const cpl_matrix* m, cpl_matrix* lu)
545 cxint n = cpl_matrix_get_ncol(m);
547 register cxint sz = n * n *
sizeof(cxdouble);
549 const cxdouble* _m = cpl_matrix_get_data_const(m);
551 cxdouble* _m_inv = cpl_matrix_get_data(m_inv);
552 cxdouble* _m_lu = cpl_matrix_get_data(lu);
554 cpl_array* perm = cpl_array_new(n, CPL_TYPE_INT);
556 register cxint* perm_data = cpl_array_get_data_int(perm);
559 memset(_m_inv, 0, sz);
560 memcpy(_m_lu, _m, sz);
562 if (cpl_matrix_decomp_lu(lu, perm, &i) != 0) {
563 cpl_array_delete(perm);
572 for (i = 0; i < n; ++i) {
573 _m_inv[i * n + perm_data[i]] = 1.;
576 cpl_array_delete(perm);
579 status = cpl_matrix_solve_lu(lu, m_inv, NULL);
582 cpl_matrix_delete(m_inv);
595inline static cpl_matrix*
596_giraffe_compute_psf(GiModel* psf,
const cpl_matrix* x)
599 register cxint i = 0;
600 register cxint n = 0;
604 const cxdouble* _x = NULL;
608 cpl_matrix* y = NULL;
610 cx_assert(psf != NULL);
611 cx_assert(x != NULL);
612 cx_assert(cpl_matrix_get_ncol(x) == 1);
614 n = cpl_matrix_get_nrow(x);
616 y = cpl_matrix_new(n, 1);
618 _x = cpl_matrix_get_data_const(x);
619 _y = cpl_matrix_get_data(y);
621 for (i = 0; i < n; i++) {
622 giraffe_model_set_argument(psf,
"x", _x[i]);
623 giraffe_model_evaluate(psf, &_y[i], &status);
626 cpl_matrix_delete(y);
642_giraffe_horne_extract_slit(GiExtractionData* result,
643 const GiVirtualSlit* vslit, GiModel* psf,
644 const GiExtractHorneConfig* config)
654 cxdouble* tdata = NULL;
655 cxdouble* _mnpsf = NULL;
657 cpl_matrix* mnpsf = NULL;
658 cpl_matrix* mvslit = NULL;
666 mvslit = cpl_matrix_wrap(vslit->width, 1, vslit->position);
667 mnpsf = _giraffe_compute_psf(psf, mvslit);
669 cpl_matrix_unwrap(mvslit);
681 _mnpsf = cpl_matrix_get_data(mnpsf);
685 for (i = 0; i < vslit->width; ++i) {
686 _mnpsf[i] = CX_MAX(_mnpsf[i], 0.);
690 for (i = 0; i < vslit->width; ++i) {
699 tdata = cx_malloc(vslit->width *
sizeof(cxdouble));
704 while (i < vslit->width) {
705 if (vslit->mask[i] > 0) {
706 tdata[ngood] = CX_MAX(vslit->signal[i], 0.);
714 bkg = 0.5 * (tdata[0] + tdata[1]);
731 cxint niter = config->clip.iterations;
732 cxint nmin = (cxint)config->clip.fraction;
734 cxdouble sigma = config->clip.level * config->clip.level;
735 cxdouble* variance = NULL;
746 for (i = 0; i < vslit->width; ++i) {
747 if (vslit->mask[i] != 0) {
748 flx += (vslit->signal[i] - bkg) * vslit->fraction[i];
749 norm += vslit->fraction[i] * _mnpsf[i];
761 variance = cx_calloc(vslit->width,
sizeof(cxdouble));
763 for (i = 0; i < vslit->width; ++i) {
765 register cxdouble ve = flx * _mnpsf[i] + bkg;
767 variance[i] = vslit->variance[i] + fabs(vslit->fraction[i] * ve);
778 while ((iteration < niter) && (ngood > nmin) && (nreject != 0)) {
795 for (i = 0; i < vslit->width; ++i) {
797 if (vslit->mask[i] != 0) {
799 cxdouble m = vslit->signal[i] - bkg - flx * _mnpsf[i];
801 m *= vslit->fraction[i];
802 m *= m / variance[i] ;
813 if ((sigma > 0.) && (mmax > sigma)) {
814 vslit->mask[imax] = 0;
824 for (i = 0; i < vslit->width; ++i) {
826 if (vslit->mask[i] != 0) {
828 register cxdouble data = vslit->signal[i] - bkg;
829 register cxdouble p = _mnpsf[i];
831 data *= vslit->fraction[i];
832 p *= vslit->fraction[i];
834 norm += p * p / variance[i];
835 _flx += p * data / variance[i];
850 for (i = 0; i < vslit->width; ++i) {
852 register cxdouble ve = flx * _mnpsf[i] + bkg;
854 variance[i] = vslit->variance[i] + fabs(vslit->fraction[i] * ve);
867 cpl_matrix_delete(mnpsf);
871 result->error = sqrt(var);
872 result->position = vslit->center;
873 result->npixels = ngood;
875 return ngood == 0 ? 1 : 0;
924_giraffe_optimal_extract_slice(GiExtractionSlice* slice,
925 const cpl_matrix* AT,
928 GiExtractionPsfLimits* limits,
929 GiExtractionWorkspace* ws)
932 register cxint i = 0;
933 register cxint n = cpl_matrix_get_ncol(AT);
934 register cxint m = cpl_matrix_get_nrow(AT);
938 const cxdouble* at = cpl_matrix_get_data_const(AT);
939 const cxdouble* w = cpl_matrix_get_data_const(W);
940 const cxdouble* s = cpl_matrix_get_data_const(S);
941 const cxdouble* c = cpl_matrix_get_data_const(ws->c);
943 cxdouble* atw = cpl_matrix_get_data(ws->atw);
944 cxdouble* atwa = cpl_matrix_get_data(ws->atwa);
945 cxdouble* atws = cpl_matrix_get_data(ws->atws);
946 cxdouble* sf = cpl_matrix_get_data(slice->flux);
947 cxdouble* sv = cpl_matrix_get_data(slice->variance);
948 cxdouble* sm = cpl_matrix_get_data(slice->model);
951 for (i = 0; i < m; ++i) {
953 register cxint j = 0;
954 register cxint im = i * m;
955 register cxint in = i * n;
956 register cxint ymin = limits->ymin[i];
957 register cxint ymax = limits->ymax[i];
962 for (j = 0; j < n; ++j) {
964 register cxint k = in + j;
967 atw[k] = w[j] * at[k];
968 atws[i] += atw[k] * s[j];
972 for (j = 0; j < i; ++j) {
974 register cxint k = 0;
975 register cxint l = im + j;
978 for (k = ymin; k < ymax; ++k) {
979 atwa[l] += atw[in + k] * at[j * n + k];
982 atwa[j * m + i] = atwa[l];
988 for (j = ymin; j < ymax; ++j) {
989 atwa[im + i] += atw[in + j] * at[in + j];
995 status = _giraffe_matrix_invert(ws->c, ws->atwa, ws->tmp);
1001 for (i = 0; i < m; ++i) {
1003 register cxint j = 0;
1004 register cxint im = i * m;
1010 for (j = 0; j < m; ++j) {
1011 sf[i] += c[im + j] * atws[j];
1016 for (i = 0; i < n; ++i) {
1018 register cxint j = 0;
1023 for (j = 0; j < m; ++j) {
1024 sm[i] += at[j * n + i] * sf[j];
1058_giraffe_extract_summation(
const cpl_image* mz,
const cpl_image* mvarz,
1059 const cpl_table* fibers,
const cpl_image* my,
1060 const cpl_image* mw, cpl_image* mbpx,
1061 cpl_image* ms, cpl_image* mse,
1062 cpl_image* msn, cpl_image* msy)
1067 const cxchar* idx = NULL;
1069 cxint ny = cpl_image_get_size_x(mz);
1070 cxint nfibers = cpl_table_get_nrow(fibers);
1071 cxint nspectra = cpl_image_get_size_x(my);
1072 cxint nbins = cpl_image_get_size_y(my);
1074 const cxdouble* pixels = cpl_image_get_data_double_const(mz);
1075 const cxdouble* variances = cpl_image_get_data_double_const(mvarz);
1076 const cxdouble* locy = cpl_image_get_data_double_const(my);
1077 const cxdouble* locw = cpl_image_get_data_double_const(mw);
1079 cxdouble* flux = cpl_image_get_data_double(ms);
1080 cxdouble* flux_error = cpl_image_get_data_double(mse);
1081 cxdouble* flux_npixels = cpl_image_get_data_double(msn);
1082 cxdouble* flux_ypos = cpl_image_get_data_double(msy);
1090 cx_assert(nfibers <= nspectra);
1094 cx_assert(cpl_table_has_column(fibers, idx) != 0);
1098 const cxint* bpx = cpl_image_get_data_int(mbpx);
1100 for (nn = 0; nn < nfibers; nn++) {
1103 register cxint ns = cpl_table_get_int(fibers, idx, nn, NULL) - 1;
1106 for (x = 0; x < cpl_image_get_size_y(mz) && x < nbins; x++) {
1110 cxint lx = x * nspectra + ns;
1111 cxint sx = x * nfibers + nn;
1113 cxdouble ylower = locy[lx] - locw[lx];
1114 cxdouble yupper = locy[lx] + locw[lx];
1117 cxdouble error2 = 0.;
1121 flux_npixels[sx] = 0.;
1122 flux_error[sx] = 0.;
1130 if (locw[lx] <= 0.0) {
1142 ylo = (cxint) ceil(ylower);
1143 yup = (cxint) floor(yupper);
1146 if (yup < 0. || ylo - 1 >= ny) {
1166 if (!(bpx[x * ny + y] & GIR_M_PIX_SET)) {
1168 cxdouble extcoeff = (cxdouble)ylo - ylower;
1169 cxdouble extcoeff2 = extcoeff * extcoeff;
1170 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1172 flux[sx] = pixels[x * ny + y] * extcoeff;
1173 flux_npixels[sx] = extcoeff;
1174 error2 = variances[x * ny + y] * extcoeff2;
1176 zsum = px * extcoeff;
1177 ysum = y * px * extcoeff;
1188 for (y = CX_MAX(ylo, 0); (y < yup) && (y < ny); y++) {
1190 if (!(bpx[x * ny + y] & GIR_M_PIX_SET)) {
1192 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1194 flux[sx] += pixels[x * ny + y];
1195 flux_npixels[sx] += 1.0;
1196 error2 += variances[x * ny + y];
1214 if (!(bpx[x * ny + y] & GIR_M_PIX_SET)) {
1216 cxdouble extcoeff = yupper - (cxdouble)yup;
1217 cxdouble extcoeff2 = extcoeff * extcoeff;
1218 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1220 flux[sx] += pixels[x * ny + y] * extcoeff;
1221 flux_npixels[sx] += extcoeff;
1222 error2 += variances[x * ny + y] * extcoeff2;
1224 zsum += px * extcoeff;
1225 ysum += y * px * extcoeff;
1231 flux_error[sx] = sqrt(error2);
1236 if (fabs(ysum) < DBL_EPSILON || fabs(zsum) < DBL_EPSILON) {
1237 flux_ypos[sx] = 0.5 * (yupper + ylower);
1240 flux_ypos[sx] = ysum / zsum;
1250 for (nn = 0; nn < nfibers; nn++) {
1253 register cxint ns = cpl_table_get_int(fibers, idx,
1257 for (x = 0; x < cpl_image_get_size_y(mz) && x < nbins; x++) {
1261 cxint lx = x * nspectra + ns;
1262 cxint sx = x * nfibers + nn;
1264 cxdouble yupper, ylower;
1267 cxdouble error2 = 0.;
1271 flux_npixels[sx] = 0.;
1272 flux_error[sx] = 0.;
1280 if (locw[lx] <= 0.0) {
1292 yupper = locy[lx] + locw[lx];
1293 ylower = locy[lx] - locw[lx];
1295 ylo = (cxint) ceil(ylower);
1296 yup = (cxint) floor(yupper);
1299 if (yup < 0. || ylo - 1 >= ny) {
1319 cxdouble extcoeff = (cxdouble)ylo - ylower;
1320 cxdouble extcoeff2 = extcoeff * extcoeff;
1321 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1323 flux[sx] = pixels[x * ny + y] * extcoeff;
1324 flux_npixels[sx] = extcoeff;
1325 error2 = variances[x * ny + y] * extcoeff2;
1327 zsum = px * extcoeff;
1328 ysum = y * px * extcoeff;
1337 for (y = CX_MAX(ylo, 0); (y < yup) && (y < ny); y++) {
1339 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1341 flux[sx] += pixels[x * ny + y];
1342 flux_npixels[sx] += 1.0;
1343 error2 += variances[x * ny + y];
1358 cxdouble extcoeff = yupper - (cxdouble)yup;
1359 cxdouble extcoeff2 = extcoeff * extcoeff;
1360 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1362 flux[sx] += pixels[x * ny + y] * extcoeff;
1363 flux_npixels[sx] += extcoeff;
1364 error2 += variances[x * ny + y] * extcoeff2;
1366 zsum += px * extcoeff;
1367 ysum += y * px * extcoeff;
1371 flux_error[sx] = sqrt(error2);
1376 if (fabs(ysum) < DBL_EPSILON || fabs(zsum) < DBL_EPSILON) {
1377 flux_ypos[sx] = 0.5 * (yupper + ylower);
1380 flux_ypos[sx] = ysum / zsum;
1416_giraffe_extract_horne(
const cpl_image* mz,
const cpl_image* mzvar,
1417 const cpl_table* fibers,
const cpl_image* my,
1418 const cpl_image* mw,
const GiPsfData* psfdata,
1419 cpl_image* mbpx, cpl_image* ms, cpl_image* mse,
1420 cpl_image* msn, cpl_image* msy,
1421 const GiExtractHorneConfig* config)
1424 const cxchar* idx = NULL;
1431 const cxdouble* locy = NULL;
1432 const cxdouble* locw = NULL;
1433 const cxdouble* width = NULL;
1434 const cxdouble* exponent = NULL;
1436 GiModel* psfmodel = NULL;
1439 cx_assert(mz != NULL);
1440 cx_assert(mzvar != NULL);
1442 cx_assert(fibers != NULL);
1444 cx_assert(my != NULL);
1445 cx_assert(mw != NULL);
1447 cx_assert(psfdata != NULL);
1449 cx_assert(ms != NULL);
1450 cx_assert(mse != NULL);
1451 cx_assert(msn != NULL);
1452 cx_assert(msy != NULL);
1454 cx_assert(config != NULL);
1456 ny = cpl_image_get_size_x(mz);
1457 nx = cpl_image_get_size_y(mz);
1458 nfibers = cpl_table_get_nrow(fibers);
1460 locy = cpl_image_get_data_double_const(my);
1461 locw = cpl_image_get_data_double_const(mw);
1463 cx_assert((ny == cpl_image_get_size_x(mzvar)) &&
1464 (nx == cpl_image_get_size_y(mzvar)));
1466 cx_assert(cpl_image_get_size_x(my) == cpl_image_get_size_x(mw));
1467 cx_assert(cpl_image_get_size_y(my) == cpl_image_get_size_y(mw));
1469 cx_assert(giraffe_psfdata_fibers(psfdata) ==
1470 (cxsize)cpl_image_get_size_x(my));
1471 cx_assert(giraffe_psfdata_bins(psfdata) ==
1472 (cxsize)cpl_image_get_size_y(my));
1474 cx_assert((nfibers == cpl_image_get_size_x(ms)) &&
1475 (nx == cpl_image_get_size_y(ms)));
1476 cx_assert((nfibers == cpl_image_get_size_x(mse)) &&
1477 (nx == cpl_image_get_size_y(mse)));
1478 cx_assert((nfibers == cpl_image_get_size_x(msn)) &&
1479 (nx == cpl_image_get_size_y(msn)));
1480 cx_assert((nfibers == cpl_image_get_size_x(msy)) &&
1481 (nx == cpl_image_get_size_y(msy)));
1483 cx_assert((mbpx == NULL) || ((ny == cpl_image_get_size_x(mbpx)) &&
1484 (nx == cpl_image_get_size_y(mbpx))));
1494 cx_assert(cpl_table_has_column(fibers, idx) != 0);
1502 if (giraffe_psfdata_contains(psfdata,
"Center") == FALSE) {
1506 if (giraffe_psfdata_contains(psfdata,
"Width2") == TRUE) {
1507 exponent = cpl_image_get_data_const(giraffe_psfdata_get_data(psfdata,
1511 width = cpl_image_get_data_const(giraffe_psfdata_get_data(psfdata,
1519 psfmodel = giraffe_model_new(giraffe_psfdata_get_model(psfdata));
1521 if (psfmodel == NULL) {
1525 giraffe_model_set_parameter(psfmodel,
"Center", 0.);
1526 giraffe_model_set_parameter(psfmodel,
"Amplitude", 1.);
1527 giraffe_model_set_parameter(psfmodel,
"Background", 0.);
1534 for (fiber = 0; fiber < nfibers; ++fiber) {
1536 register cxint bin = 0;
1537 register cxint fidx = cpl_table_get_int(fibers, idx, fiber, NULL) - 1;
1539 cxint nbins = CX_MIN(nx, cpl_image_get_size_y(my));
1541 cxdouble* _ms = cpl_image_get_data_double(ms);
1542 cxdouble* _mse = cpl_image_get_data_double(mse);
1543 cxdouble* _msy = cpl_image_get_data_double(msy);
1544 cxdouble* _msn = cpl_image_get_data_double(msn);
1547 for (bin = 0; bin < nbins; bin++) {
1549 register cxint lpos = bin * cpl_image_get_size_x(my) + fidx;
1550 register cxint spos = bin * nfibers + fiber;
1555 register cxdouble lcenter = locy[lpos];
1556 register cxdouble lwidth = locw[lpos];
1558 register cxdouble ylower = lcenter - lwidth;
1559 register cxdouble yupper = lcenter + lwidth;
1561 GiVirtualSlit* vslit = NULL;
1563 GiExtractionData result = {0., 0., 0., 0.};
1570 if ((lwidth <= 0.) || (yupper < 0.) || (ylower > ny)) {
1578 vslit = _giraffe_virtualslit_new(config->ewidth);
1580 vwidth = _giraffe_virtualslit_setup(vslit, bin, lcenter, lwidth,
1584 _giraffe_virtualslit_delete(vslit);
1595 giraffe_model_set_parameter(psfmodel,
"Width1", width[lpos]);
1597 if (exponent != NULL) {
1598 giraffe_model_set_parameter(psfmodel,
"Width2",
1608 status = _giraffe_horne_extract_slit(&result, vslit, psfmodel,
1611 _giraffe_virtualslit_delete(vslit);
1616 giraffe_model_delete(psfmodel);
1622 _ms[spos] = result.value;
1623 _mse[spos] = result.error;
1624 _msy[spos] = result.position;
1625 _msn[spos] = result.npixels;
1632 giraffe_model_delete(psfmodel);
1646_giraffe_optimal_build_profiles(cpl_matrix* profiles,
1647 GiExtractionPsfLimits* limits,
1648 const cpl_image* my,
const cpl_image* mw,
1649 const cpl_table* fibers, cxint bin,
1650 GiModel* psf,
const cxdouble* width,
1651 const cxdouble* exponent, cxdouble wfactor)
1657 cxint nfibers = cpl_table_get_nrow(fibers);
1658 cxint ny = cpl_matrix_get_ncol(profiles);
1660 const cxdouble* locy = cpl_image_get_data_double_const(my);
1661 const cxdouble* locw = cpl_image_get_data_double_const(mw);
1663 cxdouble* _profiles = cpl_matrix_get_data(profiles);
1665 cxdouble* ypos = NULL;
1668 cx_assert(cpl_table_has_column(fibers, idx) != 0);
1669 cx_assert((limits == NULL) ||
1670 (cpl_matrix_get_nrow(profiles) == limits->size));
1672 ypos = cx_calloc(ny,
sizeof(cxdouble));
1674 for (fiber = 0; fiber < nfibers; ++fiber) {
1676 register cxint i = 0;
1677 register cxint y = 0;
1678 register cxint k = 0;
1680 cxint fidx = cpl_table_get_int(fibers, idx, fiber, NULL) - 1;
1681 cxint lpos = bin * cpl_image_get_size_x(my) + fidx;
1683 register cxdouble lcenter = locy[lpos];
1684 register cxdouble lwidth = locw[lpos];
1686 register cxdouble ylower = lcenter - fabs(wfactor) * lwidth;
1687 register cxdouble yupper = lcenter + fabs(wfactor) * lwidth;
1689 register cxint first = (cxint) floor(ylower);
1690 register cxint last = (cxint) ceil(yupper);
1692 register cxint vwidth = 0;
1695 cxdouble* _mnpsf = NULL;
1697 cpl_matrix* positions = NULL;
1698 cpl_matrix* mnpsf = NULL;
1705 ylower = CX_MAX(0., ylower);
1706 yupper = CX_MIN(ny - 1., yupper);
1708 first = CX_MAX(0, first);
1709 last = CX_MIN(ny - 1, last);
1711 vwidth = last - first + 1;
1717 if (limits != NULL) {
1718 limits->ymin[fiber] = first;
1719 limits->ymax[fiber] = last + 1;
1727 giraffe_model_set_parameter(psf,
"Width1", width[lpos]);
1729 if (exponent != NULL) {
1730 giraffe_model_set_parameter(psf,
"Width2", exponent[lpos]);
1739 for (y = first; y <= last; ++y) {
1740 ypos[k] = y - lcenter;
1744 positions = cpl_matrix_wrap(vwidth, 1, ypos);
1745 mnpsf = _giraffe_compute_psf(psf, positions);
1747 cpl_matrix_unwrap(positions);
1750 if (mnpsf == NULL) {
1757 _mnpsf = cpl_matrix_get_data(mnpsf);
1759 for (i = 0; i < vwidth; ++i) {
1760 _mnpsf[i] = CX_MAX(_mnpsf[i], 0.);
1764 for (i = 0; i < vwidth; ++i) {
1768 k = fiber * ny + first;
1769 for (y = 0; y < vwidth; ++y) {
1770 _profiles[k + y] = _mnpsf[y];
1773 cpl_matrix_delete(mnpsf);
1787_giraffe_extract_optimal(
const cpl_image* mz,
const cpl_image* mzvar,
1788 const cpl_table* fibers,
const cpl_image* my,
1789 const cpl_image* mw,
const GiPsfData* psfdata,
1790 cpl_image* mbpx, cpl_image* ms, cpl_image* mse,
1791 cpl_image* msm, cpl_image* msy,
1792 const GiExtractOptimalConfig* config)
1795 const cxbool nolimits = (config->limits == TRUE) ? FALSE : TRUE;
1797 const cxint bkg_nc = config->bkgorder + 1;
1798 const cxint niter = config->clip.iterations;
1800 register cxint i = 0;
1808 const cxdouble wfactor = config->ewidth;
1809 const cxdouble sigma = config->clip.level * config->clip.level;
1810 const cxdouble fraction = config->clip.fraction;
1812 const cxdouble* width = NULL;
1813 const cxdouble* exponent = NULL;
1815 cxdouble* _ypos = NULL;
1816 cxdouble* _bkg_base = NULL;
1817 cxdouble* _profiles = NULL;
1818 cxdouble* _signal = NULL;
1819 cxdouble* _variance = NULL;
1820 cxdouble* _mask = NULL;
1821 cxdouble* _weights = NULL;
1823 cpl_matrix* ypos = NULL;
1824 cpl_matrix* bkg_base = NULL;
1825 cpl_matrix* profiles = NULL;
1826 cpl_matrix* weights = NULL;
1827 cpl_matrix* signal = NULL;
1828 cpl_matrix* variance = NULL;
1829 cpl_matrix* mask = NULL;
1831 GiModel* psfmodel = NULL;
1833 GiExtractionPsfLimits* limits = NULL;
1835 GiExtractionSlice* slice = NULL;
1837 GiExtractionWorkspace* workspace;
1840 cx_assert(mz != NULL);
1841 cx_assert(mzvar != NULL);
1843 cx_assert(fibers != NULL);
1845 cx_assert(my != NULL);
1846 cx_assert(mw != NULL);
1848 cx_assert(psfdata != NULL);
1850 cx_assert(ms != NULL);
1851 cx_assert(mse != NULL);
1852 cx_assert(msm != NULL);
1853 cx_assert(msy != NULL);
1855 ny = cpl_image_get_size_x(mz);
1856 nx = cpl_image_get_size_y(mz);
1858 nfibers = cpl_table_get_nrow(fibers);
1859 nbins = CX_MIN(nx, cpl_image_get_size_y(my));
1861 cx_assert((ny == cpl_image_get_size_x(mzvar)) &&
1862 (nx == cpl_image_get_size_y(mzvar)));
1864 cx_assert(cpl_image_get_size_x(my) == cpl_image_get_size_x(mw));
1865 cx_assert(cpl_image_get_size_y(my) == cpl_image_get_size_y(mw));
1867 cx_assert(giraffe_psfdata_fibers(psfdata) ==
1868 (cxsize)cpl_image_get_size_x(my));
1869 cx_assert(giraffe_psfdata_bins(psfdata) ==
1870 (cxsize)cpl_image_get_size_y(my));
1872 cx_assert((nfibers == cpl_image_get_size_x(ms)) &&
1873 (nx == cpl_image_get_size_y(ms)));
1874 cx_assert((nfibers == cpl_image_get_size_x(mse)) &&
1875 (nx == cpl_image_get_size_y(mse)));
1876 cx_assert((nfibers == cpl_image_get_size_x(msy)) &&
1877 (nx == cpl_image_get_size_y(msy)));
1878 cx_assert((ny == cpl_image_get_size_x(msm)) &&
1879 (nx == cpl_image_get_size_y(msm)));
1881 cx_assert((mbpx == NULL) || ((ny == cpl_image_get_size_x(mbpx)) &&
1882 (nx == cpl_image_get_size_y(mbpx))));
1890 if (giraffe_psfdata_contains(psfdata,
"Center") == FALSE) {
1894 if (giraffe_psfdata_contains(psfdata,
"Width2") == TRUE) {
1895 exponent = cpl_image_get_data_const(giraffe_psfdata_get_data(psfdata,
1899 width = cpl_image_get_data_const(giraffe_psfdata_get_data(psfdata,
1907 psfmodel = giraffe_model_new(giraffe_psfdata_get_model(psfdata));
1909 if (psfmodel == NULL) {
1913 giraffe_model_set_parameter(psfmodel,
"Amplitude", 1.);
1914 giraffe_model_set_parameter(psfmodel,
"Background", 0.);
1915 giraffe_model_set_parameter(psfmodel,
"Center", 0.);
1922 ypos = cpl_matrix_new(ny, 1);
1925 giraffe_model_delete(psfmodel);
1931 _ypos = cpl_matrix_get_data(ypos);
1933 for (i = 0; i < ny; ++i) {
1943 profiles = cpl_matrix_new(nfibers + bkg_nc, ny);
1945 if (profiles == NULL) {
1946 cpl_matrix_delete(ypos);
1949 giraffe_model_delete(psfmodel);
1955 _profiles = cpl_matrix_get_data(profiles);
1958 signal = cpl_matrix_new(ny, 1);
1960 if (signal == NULL) {
1961 cpl_matrix_delete(profiles);
1964 cpl_matrix_delete(ypos);
1967 giraffe_model_delete(psfmodel);
1973 _signal = cpl_matrix_get_data(signal);
1976 variance = cpl_matrix_new(ny, 1);
1978 if (variance == NULL) {
1979 cpl_matrix_delete(signal);
1982 cpl_matrix_delete(profiles);
1985 cpl_matrix_delete(ypos);
1988 giraffe_model_delete(psfmodel);
1994 _variance = cpl_matrix_get_data(variance);
1997 mask = cpl_matrix_new(ny, 1);
2000 cpl_matrix_delete(variance);
2003 cpl_matrix_delete(signal);
2006 cpl_matrix_delete(profiles);
2009 cpl_matrix_delete(ypos);
2012 giraffe_model_delete(psfmodel);
2018 _mask = cpl_matrix_get_data(mask);
2021 weights = cpl_matrix_new(ny, 1);
2023 if (weights == NULL) {
2024 cpl_matrix_delete(mask);
2027 cpl_matrix_delete(variance);
2030 cpl_matrix_delete(signal);
2033 cpl_matrix_delete(profiles);
2036 cpl_matrix_delete(ypos);
2039 giraffe_model_delete(psfmodel);
2045 _weights = cpl_matrix_get_data(weights);
2053 bkg_base = giraffe_chebyshev_base1d(0., ny, bkg_nc, ypos);
2055 cpl_matrix_delete(ypos);
2058 if (bkg_base == NULL) {
2059 cpl_matrix_delete(weights);
2062 cpl_matrix_delete(mask);
2065 cpl_matrix_delete(variance);
2068 cpl_matrix_delete(signal);
2071 cpl_matrix_delete(profiles);
2074 cpl_matrix_delete(ypos);
2077 giraffe_model_delete(psfmodel);
2083 _bkg_base = cpl_matrix_get_data(bkg_base);
2085 for (i = 0; i < bkg_nc; ++i) {
2087 register cxint j = 0;
2088 register cxint offset = nfibers * ny;
2090 for (j = 0; j < ny; ++j) {
2091 _profiles[i * ny + j + offset] = _bkg_base[i * ny + j];
2098 cpl_matrix_delete(bkg_base);
2106 slice = _giraffe_extractionslice_new(nfibers, ny, bkg_nc);
2108 if (slice == NULL) {
2109 cpl_matrix_delete(weights);
2112 cpl_matrix_delete(mask);
2115 cpl_matrix_delete(variance);
2118 cpl_matrix_delete(signal);
2121 cpl_matrix_delete(profiles);
2124 cpl_matrix_delete(ypos);
2127 giraffe_model_delete(psfmodel);
2134 limits = _giraffe_extraction_psflimits_new(nfibers + bkg_nc);
2136 if (limits == NULL) {
2138 _giraffe_extractionslice_delete(slice);
2141 cpl_matrix_delete(weights);
2144 cpl_matrix_delete(mask);
2147 cpl_matrix_delete(variance);
2150 cpl_matrix_delete(signal);
2153 cpl_matrix_delete(profiles);
2156 cpl_matrix_delete(ypos);
2159 giraffe_model_delete(psfmodel);
2166 for (i = 0; i < limits->size; ++i) {
2167 limits->ymin[i] = 0;
2168 limits->ymax[i] = ny;
2176 workspace = _giraffe_optimal_workspace_new(nfibers + bkg_nc, ny);
2178 for (bin = 0; bin < nbins; ++bin) {
2180 cxbool stop = FALSE;
2186 const cxdouble* _my = cpl_image_get_data_double_const(my);
2187 const cxdouble* _mz = cpl_image_get_data_double_const(mz);
2188 const cxdouble* _mzvar = cpl_image_get_data_double_const(mzvar);
2190 cxdouble* _ms = cpl_image_get_data_double(ms);
2191 cxdouble* _mse = cpl_image_get_data_double(mse);
2192 cxdouble* _msy = cpl_image_get_data_double(msy);
2193 cxdouble* _msm = cpl_image_get_data_double(msm);
2197 GiExtractionPsfLimits* _limits = (nolimits == FALSE) ? limits : NULL;
2199 cx_assert(_mz != NULL);
2200 cx_assert(_mzvar != NULL);
2208 status = _giraffe_optimal_build_profiles(profiles, _limits, my, mw,
2209 fibers, bin, psfmodel, width,
2213 _giraffe_optimal_workspace_delete(workspace);
2216 _giraffe_extraction_psflimits_delete(limits);
2219 _giraffe_extractionslice_delete(slice);
2222 cpl_matrix_delete(weights);
2225 cpl_matrix_delete(mask);
2228 cpl_matrix_delete(variance);
2231 cpl_matrix_delete(signal);
2234 cpl_matrix_delete(profiles);
2237 cpl_matrix_delete(ypos);
2240 giraffe_model_delete(psfmodel);
2254 const cxint* _mbpx = cpl_image_get_data_int_const(mbpx);
2257 cx_assert(_mbpx != NULL);
2259 for (i = 0; i < ny; ++i) {
2261 cxbool bad = (_mbpx[bin * ny + i] & GIR_M_PIX_SET) ||
2262 (_mz[bin * ny + i] < 0.);
2264 _signal[i] = _mz[bin * ny + i];
2265 _variance[i] = _signal[i] + _mzvar[bin * ny + i];
2273 _weights[i] = _mask[i] / _variance[i];
2280 for (i = 0; i < ny; ++i) {
2282 cxbool bad = (_mz[bin * ny + i] < 0.);
2284 _signal[i] = _mz[bin * ny + i];
2285 _variance[i] = _signal[i] + _mzvar[bin * ny + i];
2293 _weights[i] = _mask[i] / _variance[i];
2305 nmin = (cxint)(fraction * ngood);
2307 while ((iter < niter) && (stop == FALSE)) {
2311 const cxdouble* _model = NULL;
2314 status = _giraffe_optimal_extract_slice(slice, profiles,
2315 signal, weights, limits, workspace);
2318 _giraffe_optimal_workspace_delete(workspace);
2321 _giraffe_extraction_psflimits_delete(limits);
2324 _giraffe_extractionslice_delete(slice);
2327 cpl_matrix_delete(weights);
2330 cpl_matrix_delete(mask);
2333 cpl_matrix_delete(variance);
2336 cpl_matrix_delete(signal);
2339 cpl_matrix_delete(profiles);
2342 cpl_matrix_delete(ypos);
2345 giraffe_model_delete(psfmodel);
2356 _model = cpl_matrix_get_data(slice->model);
2358 for (i = 0; i < ny; ++i) {
2360 if (_mask[i] > 0.) {
2363 cxdouble residual = _signal[i] - _model[i];
2366 _variance[i] = _model[i] + _mzvar[bin * ny + i];
2368 bad = (residual * residual) > (sigma * _variance[i]) ?
2377 _weights[i] = _mask[i] / _variance[i];
2383 if ((nreject == 0) || (ngood <= nmin)) {
2397 memcpy(&_ms[bin * nfibers], cpl_matrix_get_data(slice->flux),
2398 slice->nflx *
sizeof(cxdouble));
2399 memcpy(&_mse[bin * nfibers], cpl_matrix_get_data(slice->variance),
2400 slice->nflx *
sizeof(cxdouble));
2401 memcpy(&_msm[bin * ny], cpl_matrix_get_data(slice->model),
2402 slice->msize *
sizeof(cxdouble));
2404 memcpy(&_msy[bin * nfibers], &_my[bin * nfibers],
2405 nfibers *
sizeof(cxdouble));
2412 cpl_matrix_fill_window(profiles, 0., 0, 0, nfibers, ny);
2421 cpl_image_power(mse, 0.5);
2423 _giraffe_optimal_workspace_delete(workspace);
2426 _giraffe_extraction_psflimits_delete(limits);
2429 _giraffe_extractionslice_delete(slice);
2432 cpl_matrix_delete(weights);
2435 cpl_matrix_delete(mask);
2438 cpl_matrix_delete(variance);
2441 cpl_matrix_delete(signal);
2444 cpl_matrix_delete(profiles);
2447 giraffe_model_delete(psfmodel);
2481 GiTable* fibers, GiLocalization* sloc,
2482 GiImage* bpixel, GiImage* slight,
2483 GiExtractConfig* config)
2486 const cxchar *fctid =
"giraffe_extract_spectra";
2495 cxdouble bias_ron = 0.;
2496 cxdouble bias_sigma = 0.;
2497 cxdouble dark_value = 0.;
2498 cxdouble exptime = 0.;
2499 cxdouble conad = 1.;
2501 cpl_propertylist *properties;
2503 cpl_image* _image = NULL;
2504 cpl_image* _locy = NULL;
2505 cpl_image* _locw = NULL;
2506 cpl_image* _spectra = NULL;
2507 cpl_image* _error = NULL;
2508 cpl_image* _npixels = NULL;
2509 cpl_image* _centroid = NULL;
2510 cpl_image* _model = NULL;
2512 cpl_table* _fibers = NULL;
2519 if (!result || !image || !fibers || !sloc || !config) {
2520 cpl_error_set(fctid, CPL_ERROR_NULL_INPUT);
2525 if ((sloc->locy == NULL) || (sloc->locw == NULL)) {
2526 cpl_error_set(fctid, CPL_ERROR_NULL_INPUT);
2531 if (result->spectra != NULL || result->error != NULL ||
2532 result->npixels != NULL || result->centroid != NULL ||
2533 result->model != NULL) {
2534 gi_warning(
"%s: Results structure at %p is not empty! Contents "
2535 "might be lost.", fctid, result);
2541 if (_fibers == NULL) {
2542 cpl_error_set(fctid, CPL_ERROR_DATA_NOT_FOUND);
2547 if ((config->emethod == GIEXTRACT_OPTIMAL) && (sloc->psf == NULL)) {
2548 cpl_msg_error(fctid,
"Missing data: PSF profile data is required "
2549 "for optimal spectrum extraction!");
2550 cpl_error_set(fctid, CPL_ERROR_NULL_INPUT);
2558 if (properties == NULL) {
2559 cpl_error_set(fctid, CPL_ERROR_DATA_NOT_FOUND);
2564 giraffe_error_push();
2568 if (cpl_error_get_code() != CPL_ERROR_NONE) {
2572 giraffe_error_pop();
2575 if (!cpl_propertylist_has(properties, GIALIAS_BIASERROR)) {
2576 cpl_msg_warning(fctid,
"Missing bias error property (%s)! Setting "
2577 "bias error to 0.", GIALIAS_BIASERROR);
2581 bias_sigma = cpl_propertylist_get_double(properties, GIALIAS_BIASERROR);
2585 if (config->ron > 0.) {
2587 cpl_msg_info(fctid,
"Setting bias RMS property (%s) to %.4g ADU",
2588 GIALIAS_BIASSIGMA, config->ron);
2590 cpl_propertylist_update_double(properties, GIALIAS_BIASSIGMA,
2597 if (!cpl_propertylist_has(properties, GIALIAS_DARKVALUE)) {
2601 cpl_msg_warning(fctid,
"Missing dark value property (%s), will be "
2602 "set to 0.!", GIALIAS_DARKVALUE);
2603 cpl_propertylist_append_double(properties, GIALIAS_DARKVALUE,
2608 dark_value = cpl_propertylist_get_double(properties,
2613 if (!cpl_propertylist_has(properties, GIALIAS_EXPTIME)) {
2614 cpl_msg_error(fctid,
"Missing exposure time property (%s)!",
2619 exptime = cpl_propertylist_get_double(properties, GIALIAS_EXPTIME);
2623 if (cpl_propertylist_has(properties, GIALIAS_DATANCOM)) {
2624 nframes = cpl_propertylist_get_int(properties, GIALIAS_DATANCOM);
2636 bias_sigma *= conad;
2637 dark_value *= conad;
2657 ny = cpl_image_get_size_x(_image);
2658 nx = cpl_image_get_size_y(_locw);
2659 ns = cpl_table_get_nrow(_fibers);
2662 switch (config->emethod) {
2666 cxint xsize = cpl_image_get_size_x(_image);
2667 cxint ysize = cpl_image_get_size_y(_image);
2669 cxdouble ron_variance = bias_ron * bias_ron;
2670 cxdouble bias_variance = bias_sigma * bias_sigma;
2671 cxdouble dark_variance = dark_value * exptime;
2673 cpl_image* bpixmap = NULL;
2674 cpl_image* variance = NULL;
2681 result->model = NULL;
2688 if (bpixel != NULL) {
2692 if (cpl_image_get_size_x(bpixmap) != xsize ||
2693 cpl_image_get_size_y(bpixmap) != ysize) {
2695 cxbool crop = FALSE;
2697 cpl_propertylist *p =
2700 GiWindow w = {1, 1, 0, 0};
2703 w.x1 = cpl_image_get_size_x(bpixmap);
2704 w.y1 = cpl_image_get_size_y(bpixmap);
2706 if (cpl_propertylist_has(p, GIALIAS_PRSCX)) {
2707 w.x0 += cpl_propertylist_get_int(p, GIALIAS_PRSCX);
2711 if (cpl_propertylist_has(p, GIALIAS_OVSCX)) {
2712 w.x1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCX);
2716 if (cpl_propertylist_has(p, GIALIAS_PRSCY)) {
2717 w.y0 += cpl_propertylist_get_int(p, GIALIAS_PRSCY);
2721 if (cpl_propertylist_has(p, GIALIAS_OVSCY)) {
2722 w.y1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCY);
2726 if ((w.x1 - w.x0 + 1) != xsize ||
2727 (w.y1 - w.y0 + 1) != ysize) {
2728 cpl_msg_error(fctid,
"Invalid bad pixel map! Image "
2729 "sizes do not match!");
2732 result->spectra = NULL;
2735 result->error = NULL;
2738 result->npixels = NULL;
2741 result->centroid = NULL;
2744 result->model = NULL;
2746 cpl_image_delete(_image);
2753 bpixmap = cpl_image_extract(bpixmap, w.x0, w.y0,
2761 if (slight != NULL) {
2762 cpl_msg_warning(fctid,
"Scattered light model will be "
2763 "ignored for extraction method `SUM'");
2766 variance = cpl_image_abs_create(_image);
2774 cpl_image_add_scalar(variance, nframes * (ron_variance + nframes *
2775 (bias_variance + dark_variance)));
2777 status = _giraffe_extract_summation(_image, variance, _fibers,
2778 _locy, _locw, bpixmap,
2779 _spectra, _error, _npixels,
2782 cpl_image_delete(variance);
2784 cpl_image_delete(bpixmap);
2792 case GIEXTRACT_OPTIMAL:
2795 cxint xsize = cpl_image_get_size_x(_image);
2796 cxint ysize = cpl_image_get_size_y(_image);
2799 cxdouble ron_variance = bias_ron * bias_ron;
2800 cxdouble bias_variance = bias_sigma * bias_sigma;
2801 cxdouble dark_variance = dark_value * exptime;
2803 cpl_image* variance = NULL;
2804 cpl_image* bpixmap = NULL;
2806 GiExtractOptimalConfig setup;
2811 result->npixels = NULL;
2820 setup.clip.iterations = config->psf.iterations;
2821 setup.clip.level = config->psf.sigma;
2822 setup.clip.fraction = config->optimal.fraction;
2823 setup.limits = config->optimal.wfactor < 0. ? FALSE : TRUE;
2824 setup.ewidth = CX_MAX(1., fabs(config->optimal.wfactor));
2825 setup.bkgorder = config->optimal.bkgorder;
2826 setup.exptime = exptime;
2827 setup.ron = bias_sigma;
2828 setup.dark = dark_value;
2831 if (bpixel != NULL) {
2835 if (cpl_image_get_size_x(bpixmap) != xsize ||
2836 cpl_image_get_size_y(bpixmap) != ysize) {
2838 cxbool crop = FALSE;
2840 cpl_propertylist *p =
2843 GiWindow w = {1, 1, 0, 0};
2846 w.x1 = cpl_image_get_size_x(bpixmap);
2847 w.y1 = cpl_image_get_size_y(bpixmap);
2849 if (cpl_propertylist_has(p, GIALIAS_PRSCX)) {
2850 w.x0 += cpl_propertylist_get_int(p, GIALIAS_PRSCX);
2854 if (cpl_propertylist_has(p, GIALIAS_OVSCX)) {
2855 w.x1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCX);
2859 if (cpl_propertylist_has(p, GIALIAS_PRSCY)) {
2860 w.y0 += cpl_propertylist_get_int(p, GIALIAS_PRSCY);
2864 if (cpl_propertylist_has(p, GIALIAS_OVSCY)) {
2865 w.y1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCY);
2869 if ((w.x1 - w.x0 + 1) != xsize ||
2870 (w.y1 - w.y0 + 1) != ysize) {
2872 cpl_msg_error(fctid,
"Invalid bad pixel map! "
2873 "Image sizes do not match!");
2876 result->spectra = NULL;
2879 result->error = NULL;
2882 result->npixels = NULL;
2885 result->centroid = NULL;
2888 result->model = NULL;
2890 cpl_image_delete(_image);
2898 bpixmap = cpl_image_extract(bpixmap, w.x0, w.y0,
2906 variance = cpl_image_new(xsize, ysize, CPL_TYPE_DOUBLE);
2914 v0 = nframes * (ron_variance + nframes *
2915 (bias_variance + dark_variance));
2924 if (slight != NULL) {
2926 register cxsize i = 0;
2927 register cxsize npixels = xsize * ysize;
2929 const cxdouble* _slight =
2932 cxdouble* _variance = cpl_image_get_data_double(variance);
2934 for (i = 0; i < npixels; i++) {
2935 _variance[i] = v0 + fabs(_slight[i]) * conad * nframes;
2941 register cxsize i = 0;
2942 register cxsize npixels = xsize * ysize;
2944 cxdouble* _variance = cpl_image_get_data_double(variance);
2946 for (i = 0; i < npixels; i++) {
2953 status = _giraffe_extract_optimal(_image, variance, _fibers,
2954 _locy, _locw, sloc->psf,
2955 bpixmap, _spectra, _error,
2956 _model, _centroid, &setup);
2958 cpl_image_delete(variance);
2962 cpl_image_delete(bpixmap);
2970 case GIEXTRACT_HORNE:
2973 cxint xsize = cpl_image_get_size_x(_image);
2974 cxint ysize = cpl_image_get_size_y(_image);
2977 cxdouble ron_variance = bias_ron * bias_ron;
2978 cxdouble bias_variance = bias_sigma * bias_sigma;
2979 cxdouble dark_variance = dark_value * exptime;
2981 cpl_image* variance = NULL;
2982 cpl_image* bpixmap = NULL;
2984 GiExtractHorneConfig setup;
2991 result->model = NULL;
2998 setup.clip.iterations = config->psf.iterations;
2999 setup.clip.level = config->psf.sigma;
3000 setup.clip.fraction = config->horne.mingood;
3001 setup.ewidth = config->horne.ewidth;
3002 setup.exptime = exptime;
3003 setup.ron = bias_sigma;
3004 setup.dark = dark_value;
3006 if (bpixel != NULL) {
3010 if (cpl_image_get_size_x(bpixmap) != xsize ||
3011 cpl_image_get_size_y(bpixmap) != ysize) {
3013 cxbool crop = FALSE;
3015 cpl_propertylist *p =
3018 GiWindow w = {1, 1, 0, 0};
3021 w.x1 = cpl_image_get_size_x(bpixmap);
3022 w.y1 = cpl_image_get_size_y(bpixmap);
3024 if (cpl_propertylist_has(p, GIALIAS_PRSCX)) {
3025 w.x0 += cpl_propertylist_get_int(p, GIALIAS_PRSCX);
3029 if (cpl_propertylist_has(p, GIALIAS_OVSCX)) {
3030 w.x1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCX);
3034 if (cpl_propertylist_has(p, GIALIAS_PRSCY)) {
3035 w.y0 += cpl_propertylist_get_int(p, GIALIAS_PRSCY);
3039 if (cpl_propertylist_has(p, GIALIAS_OVSCY)) {
3040 w.y1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCY);
3044 if ((w.x1 - w.x0 + 1) != xsize ||
3045 (w.y1 - w.y0 + 1) != ysize) {
3047 cpl_msg_error(fctid,
"Invalid bad pixel map! "
3048 "Image sizes do not match!");
3051 result->spectra = NULL;
3054 result->error = NULL;
3057 result->npixels = NULL;
3060 result->centroid = NULL;
3063 result->model = NULL;
3065 cpl_image_delete(_image);
3073 bpixmap = cpl_image_extract(bpixmap, w.x0, w.y0,
3081 variance = cpl_image_new(xsize, ysize, CPL_TYPE_DOUBLE);
3089 v0 = nframes * (ron_variance + nframes *
3090 (bias_variance + dark_variance));
3100 if (slight != NULL) {
3102 register cxsize i = 0;
3103 register cxsize npixels = xsize * ysize;
3105 const cxdouble* _slight =
3108 cxdouble* _variance = cpl_image_get_data_double(variance);
3110 for (i = 0; i < npixels; i++) {
3111 _variance[i] = v0 + fabs(_slight[i]) * nframes * conad;
3117 register cxsize i = 0;
3118 register cxsize npixels = xsize * ysize;
3120 cxdouble* _variance = cpl_image_get_data_double(variance);
3122 for (i = 0; i < npixels; i++) {
3129 status = _giraffe_extract_horne(_image, variance, _fibers,
3130 _locy, _locw, sloc->psf,
3131 bpixmap, _spectra, _error,
3132 _npixels, _centroid, &setup);
3134 cpl_image_delete(variance);
3138 cpl_image_delete(bpixmap);
3147 gi_message(
"%s: Method %d selected for spectrum extraction.",
3148 fctid, config->emethod);
3149 cpl_msg_error(fctid,
"Invalid extraction method!");
3155 cpl_image_delete(_image);
3161 result->spectra = NULL;
3164 result->error = NULL;
3167 result->npixels = NULL;
3170 result->centroid = NULL;
3173 result->model = NULL;
3175 cpl_msg_error(fctid,
"Spectrum extraction (method %d) failed!",
3178 cpl_image_delete(_image);
3196 if (result->spectra) {
3201 if (result->model) {
3206 if (result->error) {
3231 cpl_propertylist_set_int(properties, GIALIAS_NAXIS1,
3232 cpl_image_get_size_x(_spectra));
3233 cpl_propertylist_set_int(properties, GIALIAS_NAXIS2,
3234 cpl_image_get_size_y(_spectra));
3236 cpl_propertylist_set_int(properties, GIALIAS_BITPIX, -32);
3237 cpl_propertylist_set_double(properties, GIALIAS_BZERO, 0.);
3238 cpl_propertylist_set_double(properties, GIALIAS_BSCALE, 1.);
3240 cpl_propertylist_update_int(properties, GIALIAS_NFIBERS,
3241 cpl_image_get_size_x(_spectra));
3243 cpl_propertylist_append_int(properties, GIALIAS_EXT_NX,
3244 cpl_image_get_size_y(_spectra));
3245 cpl_propertylist_append_int(properties, GIALIAS_EXT_NS,
3246 cpl_image_get_size_x(_spectra));
3248 switch (config->emethod) {
3250 cpl_propertylist_append_string(properties, GIALIAS_EXT_METHOD,
3252 cpl_propertylist_set_comment(properties, GIALIAS_EXT_METHOD,
3253 "Spectrum extraction method");
3256 case GIEXTRACT_HORNE:
3259 cpl_propertylist_append_string(properties, GIALIAS_EXT_METHOD,
3261 cpl_propertylist_set_comment(properties, GIALIAS_EXT_METHOD,
3262 "Spectrum extraction method");
3264 cpl_propertylist_append_string(properties, GIALIAS_EXTPSF_MODEL,
3266 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_MODEL,
3268 cpl_propertylist_append_double(properties, GIALIAS_EXTPSF_SIGMA,
3270 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_SIGMA,
3271 "PSF fit sigma clipping threshold");
3272 cpl_propertylist_append_int(properties, GIALIAS_EXTPSF_NITER,
3273 config->psf.iterations);
3274 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_NITER,
3275 "PSF fit maximum number of "
3278 cpl_propertylist_append_int(properties, GIALIAS_EXTHRN_EWIDTH,
3279 config->horne.ewidth);
3280 cpl_propertylist_set_comment(properties, GIALIAS_EXTHRN_EWIDTH,
3281 "Number of extra pixels used");
3282 cpl_propertylist_append_int(properties, GIALIAS_EXTHRN_MINGOOD,
3283 config->horne.mingood);
3284 cpl_propertylist_set_comment(properties, GIALIAS_EXTHRN_MINGOOD,
3285 "Minimum number of pixels to keep");
3291 case GIEXTRACT_OPTIMAL:
3292 cpl_propertylist_append_string(properties, GIALIAS_EXT_METHOD,
3294 cpl_propertylist_set_comment(properties, GIALIAS_EXT_METHOD,
3295 "Spectrum extraction method");
3297 cpl_propertylist_append_string(properties, GIALIAS_EXTPSF_MODEL,
3299 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_MODEL,
3301 cpl_propertylist_append_double(properties, GIALIAS_EXTPSF_SIGMA,
3303 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_SIGMA,
3304 "PSF fit sigma clipping threshold");
3305 cpl_propertylist_append_int(properties, GIALIAS_EXTPSF_NITER,
3306 config->psf.iterations);
3307 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_NITER,
3308 "PSF fit maximum number of "
3311 cpl_propertylist_append_double(properties, GIALIAS_EXTOPT_FRACTION,
3312 config->optimal.fraction);
3313 cpl_propertylist_set_comment(properties, GIALIAS_EXTOPT_FRACTION,
3314 "Minimum fraction of pixels used.");
3315 cpl_propertylist_append_double(properties, GIALIAS_EXTOPT_WFACTOR,
3316 config->optimal.wfactor);
3317 cpl_propertylist_set_comment(properties, GIALIAS_EXTOPT_WFACTOR,
3318 "Multiple of the fiber PSF half "
3319 "width used for spectrum "
3321 cpl_propertylist_append_int(properties, GIALIAS_EXTOPT_BGORDER,
3322 config->optimal.bkgorder);
3323 cpl_propertylist_set_comment(properties, GIALIAS_EXTOPT_BGORDER,
3324 "Order of the background polynomial "
3325 "model along the spatial direction.");
3333 cpl_propertylist_update_string(properties, GIALIAS_GIRFTYPE,
"EXTSP");
3334 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3335 "Extracted spectra");
3345 cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE,
"EXTERRS");
3346 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3347 "Extracted spectra errors");
3357 cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE,
"EXTYCEN");
3358 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3359 "Extracted spectra centroids");
3366 if (result->npixels != NULL) {
3370 cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE,
"EXTNPIX");
3371 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3372 "Extracted spectra npixels");
3380 if (result->model != NULL) {
3384 cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE,
"EXTMODEL");
3385 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3386 "Model spectra used for extraction");
3411 GiExtractConfig* config = NULL;
3418 config = cx_calloc(1,
sizeof *config);
3420 p = cpl_parameterlist_find(list,
"giraffe.extraction.method");
3421 s = cpl_parameter_get_string(p);
3422 if (!strcmp(s,
"OPTIMAL")) {
3423 config->emethod = GIEXTRACT_OPTIMAL;
3425 else if (!strcmp(s,
"HORNE")) {
3426 config->emethod = GIEXTRACT_HORNE;
3429 config->emethod = GIEXTRACT_SUM;
3432 p = cpl_parameterlist_find(list,
"giraffe.extraction.ron");
3433 config->ron = cpl_parameter_get_double(p);
3435 p = cpl_parameterlist_find(list,
"giraffe.extraction.psf.model");
3436 config->psf.model = cx_strdup(cpl_parameter_get_string(p));
3438 p = cpl_parameterlist_find(list,
"giraffe.extraction.psf.sigma");
3439 config->psf.sigma = cpl_parameter_get_double(p);
3441 p = cpl_parameterlist_find(list,
"giraffe.extraction.psf.iterations");
3442 config->psf.iterations = cpl_parameter_get_int(p);
3445 p = cpl_parameterlist_find(list,
"giraffe.extraction.horne.extrawidth");
3446 config->horne.ewidth = cpl_parameter_get_int(p);
3448 p = cpl_parameterlist_find(list,
"giraffe.extraction.horne.mingood");
3449 config->horne.mingood = cpl_parameter_get_double(p);
3452 p = cpl_parameterlist_find(list,
"giraffe.extraction.optimal.fraction");
3453 config->optimal.fraction = cpl_parameter_get_double(p);
3455 p = cpl_parameterlist_find(list,
"giraffe.extraction.optimal.wfactor");
3456 config->optimal.wfactor = cpl_parameter_get_double(p);
3458 p = cpl_parameterlist_find(list,
"giraffe.extraction.optimal.bkgorder");
3459 config->optimal.bkgorder = cpl_parameter_get_int(p);
3484 if (config->psf.model) {
3485 cx_free(config->psf.model);
3512 cpl_parameter* p = NULL;
3519 p = cpl_parameter_new_enum(
"giraffe.extraction.method",
3521 "Extraction method: 'SUM', 'HORNE' or "
3523 "giraffe.extraction",
3524 "SUM", 3,
"SUM",
"OPTIMAL",
"HORNE");
3525 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-method");
3526 cpl_parameterlist_append(list, p);
3529 p = cpl_parameter_new_value(
"giraffe.extraction.ron",
3531 "New bias sigma (RON) value for "
3534 "giraffe.extraction",
3536 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-ron");
3537 cpl_parameterlist_append(list, p);
3540 p = cpl_parameter_new_enum(
"giraffe.extraction.psf.model",
3542 "PSF profile model: `psfexp', `psfexp2'",
3543 "giraffe.extraction.psf",
3544 "psfexp2", 2,
"psfexp",
"psfexp2");
3545 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-psfmodel");
3546 cpl_parameterlist_append(list, p);
3549 p = cpl_parameter_new_value(
"giraffe.extraction.psf.sigma",
3551 "Sigma clippging threshold used for "
3552 "rejecting data points during PSF fitting "
3553 "(Horne's sigma). It is used to reject bad "
3554 "pixels and cosmics.",
3555 "giraffe.extraction.psf",
3557 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-psfsigma");
3558 cpl_parameterlist_append(list, p);
3561 p = cpl_parameter_new_value(
"giraffe.extraction.psf.iterations",
3563 "Maximum number of iterations used for "
3564 "fitting the PSF profile.",
3565 "giraffe.extraction.psf",
3567 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-psfniter");
3568 cpl_parameterlist_append(list, p);
3571 p = cpl_parameter_new_value(
"giraffe.extraction.horne.extrawidth",
3573 "Horne extraction method: Number of "
3574 "extra pixels added to the fiber "
3576 "giraffe.extraction.horne",
3578 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-hewidth");
3579 cpl_parameterlist_append(list, p);
3582 p = cpl_parameter_new_value(
"giraffe.extraction.horne.mingood",
3584 "Horne extraction method: Minimum number of "
3585 "points used for the profile fit. It sets "
3586 "the lower limit of data points for the "
3588 "giraffe.extraction.horne",
3590 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-hmingood");
3591 cpl_parameterlist_append(list, p);
3594 p = cpl_parameter_new_range(
"giraffe.extraction.optimal.fraction",
3596 "Optimal extraction method: Minimum fraction "
3597 "of the data points used for fitting the "
3598 "fiber profiles. It sets the lower limit "
3599 "for the pixel rejection.",
3600 "giraffe.extraction.optimal",
3602 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-omfrac");
3603 cpl_parameterlist_append(list, p);
3606 p = cpl_parameter_new_value(
"giraffe.extraction.optimal.wfactor",
3608 "Optimal extraction method: Factor by which "
3609 "the fiber PSF half width is multiplied. "
3610 "Adjacent spectra within this area are "
3611 "assumed to affect the spectrum being "
3613 "giraffe.extraction.optimal",
3615 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-owfactor");
3616 cpl_parameterlist_append(list, p);
3619 p = cpl_parameter_new_value(
"giraffe.extraction.optimal.bkgorder",
3621 "Optimal extraction method: Order of the "
3622 "polynomial background model, which is "
3623 "fitted for each wavelength bin along the "
3624 "spatial direction.",
3625 "giraffe.extraction.optimal",
3627 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-obkgorder");
3628 cpl_parameterlist_append(list, p);
cxint giraffe_array_sort(cxdouble *array, cxsize size)
Sorts an array in ascending order.
const cxchar * giraffe_fiberlist_query_index(const cpl_table *fibers)
Query a fiber list for the name of the fiber reference index column.
cpl_image * giraffe_image_get(const GiImage *self)
Gets the image data.
cpl_propertylist * giraffe_image_get_properties(const GiImage *self)
Get the properties of an image.
void giraffe_image_delete(GiImage *self)
Destroys an image.
GiImage * giraffe_image_create(cpl_type type, cxint nx, cxint ny)
Creates an image container of a given type.
cxint giraffe_image_set_properties(GiImage *self, cpl_propertylist *properties)
Attaches a property list to an image.
void gi_warning(const cxchar *format,...)
Log a warning.
void gi_message(const cxchar *format,...)
Log a normal message.
cpl_table * giraffe_table_get(const GiTable *self)
Get the table data from a Giraffe table.
cxdouble giraffe_propertylist_get_conad(const cpl_propertylist *properties)
Retrieve the ADU to electrons conversion factor from the given properties.
cxint giraffe_propertylist_update(cpl_propertylist *self, cpl_propertylist *properties, const cxchar *regexp)
Update a property list.
cxdouble giraffe_propertylist_get_ron(const cpl_propertylist *properties)
Retrieve the read-out noise from the given properties.