37 #include <cxstrutils.h>
39 #include <cpl_parameterlist.h>
40 #include <cpl_matrix.h>
41 #include <cpl_table.h>
52 #include "gipsfdata.h"
55 #include "gilocalization.h"
56 #include "gimessages.h"
57 #include "gifiberutils.h"
59 #include "giextract.h"
71 PROFILE_PSFEXP = 1 << 1,
72 PROFILE_PSFEXP2 = 1 << 2,
73 PROFILE_GAUSSIAN = 1 << 3
76 typedef enum GiProfileId GiProfileId;
83 struct GiExtractOptimalConfig {
97 typedef struct GiExtractOptimalConfig GiExtractOptimalConfig;
104 struct GiExtractHorneConfig {
113 typedef struct GiExtractHorneConfig GiExtractHorneConfig;
116 struct GiExtractionData {
123 typedef struct GiExtractionData GiExtractionData;
126 struct GiExtractionSlice {
134 cpl_matrix* variance;
138 typedef struct GiExtractionSlice GiExtractionSlice;
141 struct GiExtractionPsfLimits {
148 typedef struct GiExtractionPsfLimits GiExtractionPsfLimits;
151 struct GiExtractionWorkspace {
159 typedef struct GiExtractionWorkspace GiExtractionWorkspace;
162 struct GiVirtualSlit {
166 cxdouble extra_width;
177 typedef struct GiVirtualSlit GiVirtualSlit;
184 inline static GiExtractionSlice*
185 _giraffe_extractionslice_new(cxint nflx, cxint ndata, cxint nbkg)
188 GiExtractionSlice*
self = cx_malloc(
sizeof *
self);
193 self->fsize = nflx + nbkg;
196 self->flux = cpl_matrix_new(self->fsize, 1);
197 self->variance = cpl_matrix_new(self->fsize, 1);
198 self->model = cpl_matrix_new(self->msize, 1);
206 _giraffe_extractionslice_delete(GiExtractionSlice*
self)
210 if (self->model != NULL) {
211 cpl_matrix_delete(self->model);
215 if (self->variance != NULL) {
216 cpl_matrix_delete(self->variance);
217 self->variance = NULL;
220 if (self->flux != NULL) {
221 cpl_matrix_delete(self->flux);
233 inline static GiExtractionPsfLimits*
234 _giraffe_extraction_psflimits_new(cxint size)
237 GiExtractionPsfLimits*
self = cx_malloc(
sizeof *
self);
241 self->ymin = cx_calloc(self->size,
sizeof(cxint));
242 self->ymax = cx_calloc(self->size,
sizeof(cxint));
250 _giraffe_extraction_psflimits_delete(GiExtractionPsfLimits*
self)
254 if (self->ymin != NULL) {
258 if (self->ymax != NULL) {
270 inline static GiExtractionWorkspace*
271 _giraffe_optimal_workspace_new(cxint m, cxint n)
274 GiExtractionWorkspace*
self = cx_malloc(
sizeof *
self);
277 self->atw = cpl_matrix_new(m, n);
278 self->atwa = cpl_matrix_new(m, m);
279 self->c = cpl_matrix_new(m, m);
280 self->atws = cpl_matrix_new(m, 1);
282 self->tmp = cpl_matrix_new(m, m);
290 _giraffe_optimal_workspace_delete(GiExtractionWorkspace*
self)
294 if (self->atws != NULL) {
295 cpl_matrix_delete(self->atws);
298 if (self->atwa != NULL) {
299 cpl_matrix_delete(self->atwa);
302 if (self->c != NULL) {
303 cpl_matrix_delete(self->c);
306 if (self->atw != NULL) {
307 cpl_matrix_delete(self->atw);
310 if (self->tmp != NULL) {
311 cpl_matrix_delete(self->tmp);
328 _giraffe_virtualslit_allocate(GiVirtualSlit*
self)
331 if ((
self != NULL) && (self->width > 0)) {
333 self->position = cx_calloc(self->width,
sizeof(cxdouble));
334 self->signal = cx_calloc(self->width,
sizeof(cxdouble));
335 self->variance = cx_calloc(self->width,
sizeof(cxdouble));
336 self->fraction = cx_calloc(self->width,
sizeof(cxdouble));
338 self->mask = cx_calloc(self->width,
sizeof(cxdouble));
339 self->offset = cx_calloc(self->width,
sizeof(cxdouble));
348 inline static GiVirtualSlit*
349 _giraffe_virtualslit_new(cxdouble extra_width)
352 GiVirtualSlit*
self = cx_calloc(1,
sizeof *
self);
356 self->extra_width = extra_width;
358 self->position = NULL;
360 self->variance = NULL;
361 self->fraction = NULL;
371 _giraffe_virtualslit_clear(GiVirtualSlit*
self)
376 if (self->position != NULL) {
377 cx_free(self->position);
378 self->position = NULL;
381 if (self->signal != NULL) {
382 cx_free(self->signal);
386 if (self->variance != NULL) {
387 cx_free(self->variance);
388 self->variance = NULL;
391 if (self->fraction != NULL) {
392 cx_free(self->fraction);
393 self->fraction = NULL;
396 if (self->mask != NULL) {
401 if (self->offset != NULL) {
402 cx_free(self->offset);
406 self->extra_width = 0.;
418 _giraffe_virtualslit_delete(GiVirtualSlit*
self)
422 _giraffe_virtualslit_clear(
self);
433 _giraffe_virtualslit_setup(GiVirtualSlit*
self, cxint bin,
434 cxdouble center, cxdouble width,
435 const cpl_image* signal,
const cpl_image* variance,
436 const cpl_image* bpixel)
439 register cxint ny = cpl_image_get_size_x(signal);
440 register cxint offset = bin * cpl_image_get_size_x(signal);
442 register cxdouble lower = center - (width +
self->extra_width);
443 register cxdouble upper = center + (width +
self->extra_width);
445 register cxint first = (cxint) floor(lower);
446 register cxint last = (cxint) ceil(upper);
448 const cxdouble* s = cpl_image_get_data_double_const(signal);
449 const cxdouble* v = cpl_image_get_data_double_const(variance);
456 lower = CX_MAX(0., lower);
457 upper = CX_MIN(ny, upper);
459 first = CX_MAX(0, first);
460 last = CX_MIN(ny, last);
462 self->center = center;
463 self->width = last - first + 1;
470 _giraffe_virtualslit_allocate(
self);
472 if (bpixel != NULL) {
474 register cxint k = 0;
475 register cxint y = 0;
477 const cxint* _bpixel = cpl_image_get_data_int_const(bpixel);
480 for (y = first; y <= last; y++) {
482 register cxint ypos = offset + y;
484 cxint ok = (_bpixel[ypos] & GIR_M_PIX_SET) == 0 ? 1 : 0;
487 self->position[k] = y - center;
488 self->fraction[k] = 1.;
490 self->signal[k] = s[ypos];
491 self->variance[k] = v[ypos];
494 self->offset[k] = ypos;
502 register cxint k = 0;
503 register cxint y = 0;
506 for (y = first; y <= last; y++) {
508 register cxint ypos = offset + y;
513 self->position[k] = y - center;
514 self->fraction[k] = 1.;
516 self->signal[k] = s[ypos];
517 self->variance[k] = v[ypos];
520 self->offset[k] = ypos;
534 self->fraction[0] = ((cxdouble)first + 1.) - lower;
535 self->fraction[
self->width - 1] = upper - ((cxdouble)last - 1.);
547 _giraffe_matrix_invert(cpl_matrix* m_inv,
const cpl_matrix* m, cpl_matrix* lu)
552 cxint n = cpl_matrix_get_ncol(m);
554 register cxint sz = n * n *
sizeof(cxdouble);
556 const cxdouble* _m = cpl_matrix_get_data_const(m);
558 cxdouble* _m_inv = cpl_matrix_get_data(m_inv);
559 cxdouble* _m_lu = cpl_matrix_get_data(lu);
561 cpl_array* perm = cpl_array_new(n, CPL_TYPE_INT);
563 register cxint* perm_data = cpl_array_get_data_int(perm);
566 memset(_m_inv, 0, sz);
567 memcpy(_m_lu, _m, sz);
569 if (cpl_matrix_decomp_lu(lu, perm, &i) != 0) {
570 cpl_array_delete(perm);
579 for (i = 0; i < n; ++i) {
580 _m_inv[i * n + perm_data[i]] = 1.;
583 cpl_array_delete(perm);
586 status = cpl_matrix_solve_lu(lu, m_inv, NULL);
589 cpl_matrix_delete(m_inv);
602 inline static cpl_matrix*
603 _giraffe_compute_psf(GiModel* psf,
const cpl_matrix* x)
606 register cxint i = 0;
607 register cxint n = 0;
611 const cxdouble* _x = NULL;
615 cpl_matrix* y = NULL;
617 cx_assert(psf != NULL);
618 cx_assert(x != NULL);
619 cx_assert(cpl_matrix_get_ncol(x) == 1);
621 n = cpl_matrix_get_nrow(x);
623 y = cpl_matrix_new(n, 1);
625 _x = cpl_matrix_get_data_const(x);
626 _y = cpl_matrix_get_data(y);
628 for (i = 0; i < n; i++) {
629 giraffe_model_set_argument(psf,
"x", _x[i]);
630 giraffe_model_evaluate(psf, &_y[i], &status);
633 cpl_matrix_delete(y);
649 _giraffe_horne_extract_slit(GiExtractionData* result,
650 const GiVirtualSlit* vslit, GiModel* psf,
651 const GiExtractHorneConfig* config)
661 cxdouble* tdata = NULL;
662 cxdouble* _mnpsf = NULL;
664 cpl_matrix* mnpsf = NULL;
665 cpl_matrix* mvslit = NULL;
673 mvslit = cpl_matrix_wrap(vslit->width, 1, vslit->position);
674 mnpsf = _giraffe_compute_psf(psf, mvslit);
676 cpl_matrix_unwrap(mvslit);
688 _mnpsf = cpl_matrix_get_data(mnpsf);
692 for (i = 0; i < vslit->width; ++i) {
693 _mnpsf[i] = CX_MAX(_mnpsf[i], 0.);
697 for (i = 0; i < vslit->width; ++i) {
706 tdata = cx_malloc(vslit->width *
sizeof(cxdouble));
711 while (i < vslit->width) {
712 if (vslit->mask[i] > 0) {
713 tdata[ngood] = CX_MAX(vslit->signal[i], 0.);
721 bkg = 0.5 * (tdata[0] + tdata[1]);
738 cxint niter = config->clip.iterations;
739 cxint nmin = (cxint)config->clip.fraction;
741 cxdouble sigma = config->clip.level * config->clip.level;
742 cxdouble* variance = NULL;
753 for (i = 0; i < vslit->width; ++i) {
754 if (vslit->mask[i] != 0) {
755 flx += (vslit->signal[i] - bkg) * vslit->fraction[i];
756 norm += vslit->fraction[i] * _mnpsf[i];
768 variance = cx_calloc(vslit->width,
sizeof(cxdouble));
770 for (i = 0; i < vslit->width; ++i) {
772 register cxdouble ve = flx * _mnpsf[i] + bkg;
774 variance[i] = vslit->variance[i] + fabs(vslit->fraction[i] * ve);
785 while ((iteration < niter) && (ngood > nmin) && (nreject != 0)) {
802 for (i = 0; i < vslit->width; ++i) {
804 if (vslit->mask[i] != 0) {
806 cxdouble m = vslit->signal[i] - bkg - flx * _mnpsf[i];
808 m *= vslit->fraction[i];
809 m *= m / variance[i] ;
820 if ((sigma > 0.) && (mmax > sigma)) {
821 vslit->mask[imax] = 0;
831 for (i = 0; i < vslit->width; ++i) {
833 if (vslit->mask[i] != 0) {
835 register cxdouble data = vslit->signal[i] - bkg;
836 register cxdouble p = _mnpsf[i];
838 data *= vslit->fraction[i];
839 p *= vslit->fraction[i];
841 norm += p * p / variance[i];
842 _flx += p * data / variance[i];
857 for (i = 0; i < vslit->width; ++i) {
859 register cxdouble ve = flx * _mnpsf[i] + bkg;
861 variance[i] = vslit->variance[i] + fabs(vslit->fraction[i] * ve);
874 cpl_matrix_delete(mnpsf);
878 result->error = sqrt(var);
879 result->position = vslit->center;
880 result->npixels = ngood;
882 return ngood == 0 ? 1 : 0;
931 _giraffe_optimal_extract_slice(GiExtractionSlice* slice,
932 const cpl_matrix* AT,
935 GiExtractionPsfLimits* limits,
936 GiExtractionWorkspace* ws)
939 register cxint i = 0;
940 register cxint n = cpl_matrix_get_ncol(AT);
941 register cxint m = cpl_matrix_get_nrow(AT);
945 const cxdouble* at = cpl_matrix_get_data_const(AT);
946 const cxdouble* w = cpl_matrix_get_data_const(W);
947 const cxdouble* s = cpl_matrix_get_data_const(S);
948 const cxdouble* c = cpl_matrix_get_data_const(ws->c);
950 cxdouble* atw = cpl_matrix_get_data(ws->atw);
951 cxdouble* atwa = cpl_matrix_get_data(ws->atwa);
952 cxdouble* atws = cpl_matrix_get_data(ws->atws);
953 cxdouble* sf = cpl_matrix_get_data(slice->flux);
954 cxdouble* sv = cpl_matrix_get_data(slice->variance);
955 cxdouble* sm = cpl_matrix_get_data(slice->model);
958 for (i = 0; i < m; ++i) {
960 register cxint j = 0;
961 register cxint im = i * m;
962 register cxint in = i * n;
963 register cxint ymin = limits->ymin[i];
964 register cxint ymax = limits->ymax[i];
969 for (j = 0; j < n; ++j) {
971 register cxint k = in + j;
974 atw[k] = w[j] * at[k];
975 atws[i] += atw[k] * s[j];
979 for (j = 0; j < i; ++j) {
981 register cxint k = 0;
982 register cxint l = im + j;
985 for (k = ymin; k < ymax; ++k) {
986 atwa[l] += atw[in + k] * at[j * n + k];
989 atwa[j * m + i] = atwa[l];
995 for (j = ymin; j < ymax; ++j) {
996 atwa[im + i] += atw[in + j] * at[in + j];
1002 status = _giraffe_matrix_invert(ws->c, ws->atwa, ws->tmp);
1008 for (i = 0; i < m; ++i) {
1010 register cxint j = 0;
1011 register cxint im = i * m;
1017 for (j = 0; j < m; ++j) {
1018 sf[i] += c[im + j] * atws[j];
1023 for (i = 0; i < n; ++i) {
1025 register cxint j = 0;
1030 for (j = 0; j < m; ++j) {
1031 sm[i] += at[j * n + i] * sf[j];
1065 _giraffe_extract_summation(
const cpl_image* mz,
const cpl_image* mvarz,
1066 const cpl_table* fibers,
const cpl_image* my,
1067 const cpl_image* mw, cpl_image* mbpx,
1068 cpl_image* ms, cpl_image* mse,
1069 cpl_image* msn, cpl_image* msy)
1074 const cxchar* idx = NULL;
1076 cxint ny = cpl_image_get_size_x(mz);
1077 cxint nfibers = cpl_table_get_nrow(fibers);
1078 cxint nspectra = cpl_image_get_size_x(my);
1079 cxint nbins = cpl_image_get_size_y(my);
1081 const cxdouble* pixels = cpl_image_get_data_double_const(mz);
1082 const cxdouble* variances = cpl_image_get_data_double_const(mvarz);
1083 const cxdouble* locy = cpl_image_get_data_double_const(my);
1084 const cxdouble* locw = cpl_image_get_data_double_const(mw);
1086 cxdouble* flux = cpl_image_get_data_double(ms);
1087 cxdouble* flux_error = cpl_image_get_data_double(mse);
1088 cxdouble* flux_npixels = cpl_image_get_data_double(msn);
1089 cxdouble* flux_ypos = cpl_image_get_data_double(msy);
1097 cx_assert(nfibers <= nspectra);
1101 cx_assert(cpl_table_has_column(fibers, idx) != 0);
1105 const cxint* bpx = cpl_image_get_data_int(mbpx);
1107 for (nn = 0; nn < nfibers; nn++) {
1110 register cxint ns = cpl_table_get_int(fibers, idx, nn, NULL) - 1;
1113 for (x = 0; x < cpl_image_get_size_y(mz) && x < nbins; x++) {
1117 cxint lx = x * nspectra + ns;
1118 cxint sx = x * nfibers + nn;
1120 cxdouble ylower = locy[lx] - locw[lx];
1121 cxdouble yupper = locy[lx] + locw[lx];
1124 cxdouble error2 = 0.;
1128 flux_npixels[sx] = 0.;
1129 flux_error[sx] = 0.;
1137 if (locw[lx] <= 0.0) {
1149 ylo = (cxint) ceil(ylower);
1150 yup = (cxint) floor(yupper);
1153 if (yup < 0. || ylo - 1 >= ny) {
1173 if (!(bpx[x * ny + y] & GIR_M_PIX_SET)) {
1175 cxdouble extcoeff = (cxdouble)ylo - ylower;
1176 cxdouble extcoeff2 = extcoeff * extcoeff;
1177 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1179 flux[sx] = pixels[x * ny + y] * extcoeff;
1180 flux_npixels[sx] = extcoeff;
1181 error2 = variances[x * ny + y] * extcoeff2;
1183 zsum = px * extcoeff;
1184 ysum = y * px * extcoeff;
1195 for (y = CX_MAX(ylo, 0); (y < yup) && (y < ny); y++) {
1197 if (!(bpx[x * ny + y] & GIR_M_PIX_SET)) {
1199 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1201 flux[sx] += pixels[x * ny + y];
1202 flux_npixels[sx] += 1.0;
1203 error2 += variances[x * ny + y];
1221 if (!(bpx[x * ny + y] & GIR_M_PIX_SET)) {
1223 cxdouble extcoeff = yupper - (cxdouble)yup;
1224 cxdouble extcoeff2 = extcoeff * extcoeff;
1225 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1227 flux[sx] += pixels[x * ny + y] * extcoeff;
1228 flux_npixels[sx] += extcoeff;
1229 error2 += variances[x * ny + y] * extcoeff2;
1231 zsum += px * extcoeff;
1232 ysum += y * px * extcoeff;
1238 flux_error[sx] = sqrt(error2);
1243 if (fabs(ysum) < DBL_EPSILON || fabs(zsum) < DBL_EPSILON) {
1244 flux_ypos[sx] = 0.5 * (yupper + ylower);
1247 flux_ypos[sx] = ysum / zsum;
1257 for (nn = 0; nn < nfibers; nn++) {
1260 register cxint ns = cpl_table_get_int(fibers, idx,
1264 for (x = 0; x < cpl_image_get_size_y(mz) && x < nbins; x++) {
1268 cxint lx = x * nspectra + ns;
1269 cxint sx = x * nfibers + nn;
1271 cxdouble yupper, ylower;
1274 cxdouble error2 = 0.;
1278 flux_npixels[sx] = 0.;
1279 flux_error[sx] = 0.;
1287 if (locw[lx] <= 0.0) {
1299 yupper = locy[lx] + locw[lx];
1300 ylower = locy[lx] - locw[lx];
1302 ylo = (cxint) ceil(ylower);
1303 yup = (cxint) floor(yupper);
1306 if (yup < 0. || ylo - 1 >= ny) {
1326 cxdouble extcoeff = (cxdouble)ylo - ylower;
1327 cxdouble extcoeff2 = extcoeff * extcoeff;
1328 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1330 flux[sx] = pixels[x * ny + y] * extcoeff;
1331 flux_npixels[sx] = extcoeff;
1332 error2 = variances[x * ny + y] * extcoeff2;
1334 zsum = px * extcoeff;
1335 ysum = y * px * extcoeff;
1344 for (y = CX_MAX(ylo, 0); (y < yup) && (y < ny); y++) {
1346 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1348 flux[sx] += pixels[x * ny + y];
1349 flux_npixels[sx] += 1.0;
1350 error2 += variances[x * ny + y];
1365 cxdouble extcoeff = yupper - (cxdouble)yup;
1366 cxdouble extcoeff2 = extcoeff * extcoeff;
1367 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1369 flux[sx] += pixels[x * ny + y] * extcoeff;
1370 flux_npixels[sx] += extcoeff;
1371 error2 += variances[x * ny + y] * extcoeff2;
1373 zsum += px * extcoeff;
1374 ysum += y * px * extcoeff;
1378 flux_error[sx] = sqrt(error2);
1383 if (fabs(ysum) < DBL_EPSILON || fabs(zsum) < DBL_EPSILON) {
1384 flux_ypos[sx] = 0.5 * (yupper + ylower);
1387 flux_ypos[sx] = ysum / zsum;
1423 _giraffe_extract_horne(
const cpl_image* mz,
const cpl_image* mzvar,
1424 const cpl_table* fibers,
const cpl_image* my,
1425 const cpl_image* mw,
const GiPsfData* psfdata,
1426 cpl_image* mbpx, cpl_image* ms, cpl_image* mse,
1427 cpl_image* msn, cpl_image* msy,
1428 const GiExtractHorneConfig* config)
1431 const cxchar* idx = NULL;
1438 const cxdouble* locy = NULL;
1439 const cxdouble* locw = NULL;
1440 const cxdouble* width = NULL;
1441 const cxdouble* exponent = NULL;
1443 GiModel* psfmodel = NULL;
1446 cx_assert(mz != NULL);
1447 cx_assert(mzvar != NULL);
1449 cx_assert(fibers != NULL);
1451 cx_assert(my != NULL);
1452 cx_assert(mw != NULL);
1454 cx_assert(psfdata != NULL);
1456 cx_assert(ms != NULL);
1457 cx_assert(mse != NULL);
1458 cx_assert(msn != NULL);
1459 cx_assert(msy != NULL);
1461 cx_assert(config != NULL);
1463 ny = cpl_image_get_size_x(mz);
1464 nx = cpl_image_get_size_y(mz);
1465 nfibers = cpl_table_get_nrow(fibers);
1467 locy = cpl_image_get_data_double_const(my);
1468 locw = cpl_image_get_data_double_const(mw);
1470 cx_assert((ny == cpl_image_get_size_x(mzvar)) &&
1471 (nx == cpl_image_get_size_y(mzvar)));
1473 cx_assert(cpl_image_get_size_x(my) == cpl_image_get_size_x(mw));
1474 cx_assert(cpl_image_get_size_y(my) == cpl_image_get_size_y(mw));
1476 cx_assert(giraffe_psfdata_fibers(psfdata) ==
1477 (cxsize)cpl_image_get_size_x(my));
1478 cx_assert(giraffe_psfdata_bins(psfdata) ==
1479 (cxsize)cpl_image_get_size_y(my));
1481 cx_assert((nfibers == cpl_image_get_size_x(ms)) &&
1482 (nx == cpl_image_get_size_y(ms)));
1483 cx_assert((nfibers == cpl_image_get_size_x(mse)) &&
1484 (nx == cpl_image_get_size_y(mse)));
1485 cx_assert((nfibers == cpl_image_get_size_x(msn)) &&
1486 (nx == cpl_image_get_size_y(msn)));
1487 cx_assert((nfibers == cpl_image_get_size_x(msy)) &&
1488 (nx == cpl_image_get_size_y(msy)));
1490 cx_assert((mbpx == NULL) || ((ny == cpl_image_get_size_x(mbpx)) &&
1491 (nx == cpl_image_get_size_y(mbpx))));
1501 cx_assert(cpl_table_has_column(fibers, idx) != 0);
1509 if (giraffe_psfdata_contains(psfdata,
"Center") == FALSE) {
1513 if (giraffe_psfdata_contains(psfdata,
"Width2") == TRUE) {
1514 exponent = cpl_image_get_data_const(giraffe_psfdata_get_data(psfdata,
1518 width = cpl_image_get_data_const(giraffe_psfdata_get_data(psfdata,
1526 psfmodel = giraffe_model_new(giraffe_psfdata_get_model(psfdata));
1528 if (psfmodel == NULL) {
1532 giraffe_model_set_parameter(psfmodel,
"Center", 0.);
1533 giraffe_model_set_parameter(psfmodel,
"Amplitude", 1.);
1534 giraffe_model_set_parameter(psfmodel,
"Background", 0.);
1541 for (fiber = 0; fiber < nfibers; ++fiber) {
1543 register cxint bin = 0;
1544 register cxint fidx = cpl_table_get_int(fibers, idx, fiber, NULL) - 1;
1546 cxint nbins = CX_MIN(nx, cpl_image_get_size_y(my));
1548 cxdouble* _ms = cpl_image_get_data_double(ms);
1549 cxdouble* _mse = cpl_image_get_data_double(mse);
1550 cxdouble* _msy = cpl_image_get_data_double(msy);
1551 cxdouble* _msn = cpl_image_get_data_double(msn);
1554 for (bin = 0; bin < nbins; bin++) {
1556 register cxint lpos = bin * cpl_image_get_size_x(my) + fidx;
1557 register cxint spos = bin * nfibers + fiber;
1562 register cxdouble lcenter = locy[lpos];
1563 register cxdouble lwidth = locw[lpos];
1565 register cxdouble ylower = lcenter - lwidth;
1566 register cxdouble yupper = lcenter + lwidth;
1568 GiVirtualSlit* vslit = NULL;
1570 GiExtractionData result = {0., 0., 0., 0.};
1577 if ((lwidth <= 0.) || (yupper < 0.) || (ylower > ny)) {
1585 vslit = _giraffe_virtualslit_new(config->ewidth);
1587 vwidth = _giraffe_virtualslit_setup(vslit, bin, lcenter, lwidth,
1591 _giraffe_virtualslit_delete(vslit);
1602 giraffe_model_set_parameter(psfmodel,
"Width1", width[lpos]);
1604 if (exponent != NULL) {
1605 giraffe_model_set_parameter(psfmodel,
"Width2",
1615 status = _giraffe_horne_extract_slit(&result, vslit, psfmodel,
1618 _giraffe_virtualslit_delete(vslit);
1623 giraffe_model_delete(psfmodel);
1629 _ms[spos] = result.value;
1630 _mse[spos] = result.error;
1631 _msy[spos] = result.position;
1632 _msn[spos] = result.npixels;
1639 giraffe_model_delete(psfmodel);
1653 _giraffe_optimal_build_profiles(cpl_matrix* profiles,
1654 GiExtractionPsfLimits* limits,
1655 const cpl_image* my,
const cpl_image* mw,
1656 const cpl_table* fibers, cxint bin,
1657 GiModel* psf,
const cxdouble* width,
1658 const cxdouble* exponent, cxdouble wfactor)
1664 cxint nfibers = cpl_table_get_nrow(fibers);
1665 cxint ny = cpl_matrix_get_ncol(profiles);
1667 const cxdouble* locy = cpl_image_get_data_double_const(my);
1668 const cxdouble* locw = cpl_image_get_data_double_const(mw);
1670 cxdouble* _profiles = cpl_matrix_get_data(profiles);
1672 cxdouble* ypos = NULL;
1675 cx_assert(cpl_table_has_column(fibers, idx) != 0);
1676 cx_assert((limits == NULL) ||
1677 (cpl_matrix_get_nrow(profiles) == limits->size));
1679 ypos = cx_calloc(ny,
sizeof(cxdouble));
1681 for (fiber = 0; fiber < nfibers; ++fiber) {
1683 register cxint i = 0;
1684 register cxint y = 0;
1685 register cxint k = 0;
1687 cxint fidx = cpl_table_get_int(fibers, idx, fiber, NULL) - 1;
1688 cxint lpos = bin * cpl_image_get_size_x(my) + fidx;
1690 register cxdouble lcenter = locy[lpos];
1691 register cxdouble lwidth = locw[lpos];
1693 register cxdouble ylower = lcenter - fabs(wfactor) * lwidth;
1694 register cxdouble yupper = lcenter + fabs(wfactor) * lwidth;
1696 register cxint first = (cxint) floor(ylower);
1697 register cxint last = (cxint) ceil(yupper);
1699 register cxint vwidth = 0;
1702 cxdouble* _mnpsf = NULL;
1704 cpl_matrix* positions = NULL;
1705 cpl_matrix* mnpsf = NULL;
1712 ylower = CX_MAX(0., ylower);
1713 yupper = CX_MIN(ny - 1., yupper);
1715 first = CX_MAX(0, first);
1716 last = CX_MIN(ny - 1, last);
1718 vwidth = last - first + 1;
1720 if (limits != NULL) {
1721 limits->ymin[fiber] = first;
1722 limits->ymax[fiber] = last + 1;
1730 giraffe_model_set_parameter(psf,
"Width1", width[lpos]);
1732 if (exponent != NULL) {
1733 giraffe_model_set_parameter(psf,
"Width2", exponent[lpos]);
1742 for (y = first; y <= last; ++y) {
1743 ypos[k] = y - lcenter;
1747 positions = cpl_matrix_wrap(vwidth, 1, ypos);
1748 mnpsf = _giraffe_compute_psf(psf, positions);
1750 cpl_matrix_unwrap(positions);
1753 if (mnpsf == NULL) {
1760 _mnpsf = cpl_matrix_get_data(mnpsf);
1762 for (i = 0; i < vwidth; ++i) {
1763 _mnpsf[i] = CX_MAX(_mnpsf[i], 0.);
1767 for (i = 0; i < vwidth; ++i) {
1771 k = fiber * ny + first;
1772 for (y = 0; y < vwidth; ++y) {
1773 _profiles[k + y] = _mnpsf[y];
1776 cpl_matrix_delete(mnpsf);
1790 _giraffe_extract_optimal(
const cpl_image* mz,
const cpl_image* mzvar,
1791 const cpl_table* fibers,
const cpl_image* my,
1792 const cpl_image* mw,
const GiPsfData* psfdata,
1793 cpl_image* mbpx, cpl_image* ms, cpl_image* mse,
1794 cpl_image* msm, cpl_image* msy,
1795 const GiExtractOptimalConfig* config)
1798 const cxbool nolimits = (config->limits == TRUE) ? FALSE : TRUE;
1800 const cxint bkg_nc = config->bkgorder + 1;
1801 const cxint niter = config->clip.iterations;
1803 register cxint i = 0;
1811 const cxdouble wfactor = config->ewidth;
1812 const cxdouble sigma = config->clip.level * config->clip.level;
1813 const cxdouble fraction = config->clip.fraction;
1815 const cxdouble* width = NULL;
1816 const cxdouble* exponent = NULL;
1818 cxdouble* _ypos = NULL;
1819 cxdouble* _bkg_base = NULL;
1820 cxdouble* _profiles = NULL;
1821 cxdouble* _signal = NULL;
1822 cxdouble* _variance = NULL;
1823 cxdouble* _mask = NULL;
1824 cxdouble* _weights = NULL;
1826 cpl_matrix* ypos = NULL;
1827 cpl_matrix* bkg_base = NULL;
1828 cpl_matrix* profiles = NULL;
1829 cpl_matrix* weights = NULL;
1830 cpl_matrix* signal = NULL;
1831 cpl_matrix* variance = NULL;
1832 cpl_matrix* mask = NULL;
1834 GiModel* psfmodel = NULL;
1836 GiExtractionPsfLimits* limits = NULL;
1838 GiExtractionSlice* slice = NULL;
1840 GiExtractionWorkspace* workspace;
1843 cx_assert(mz != NULL);
1844 cx_assert(mzvar != NULL);
1846 cx_assert(fibers != NULL);
1848 cx_assert(my != NULL);
1849 cx_assert(mw != NULL);
1851 cx_assert(psfdata != NULL);
1853 cx_assert(ms != NULL);
1854 cx_assert(mse != NULL);
1855 cx_assert(msm != NULL);
1856 cx_assert(msy != NULL);
1858 ny = cpl_image_get_size_x(mz);
1859 nx = cpl_image_get_size_y(mz);
1861 nfibers = cpl_table_get_nrow(fibers);
1862 nbins = CX_MIN(nx, cpl_image_get_size_y(my));
1864 cx_assert((ny == cpl_image_get_size_x(mzvar)) &&
1865 (nx == cpl_image_get_size_y(mzvar)));
1867 cx_assert(cpl_image_get_size_x(my) == cpl_image_get_size_x(mw));
1868 cx_assert(cpl_image_get_size_y(my) == cpl_image_get_size_y(mw));
1870 cx_assert(giraffe_psfdata_fibers(psfdata) ==
1871 (cxsize)cpl_image_get_size_x(my));
1872 cx_assert(giraffe_psfdata_bins(psfdata) ==
1873 (cxsize)cpl_image_get_size_y(my));
1875 cx_assert((nfibers == cpl_image_get_size_x(ms)) &&
1876 (nx == cpl_image_get_size_y(ms)));
1877 cx_assert((nfibers == cpl_image_get_size_x(mse)) &&
1878 (nx == cpl_image_get_size_y(mse)));
1879 cx_assert((nfibers == cpl_image_get_size_x(msy)) &&
1880 (nx == cpl_image_get_size_y(msy)));
1881 cx_assert((ny == cpl_image_get_size_x(msm)) &&
1882 (nx == cpl_image_get_size_y(msm)));
1884 cx_assert((mbpx == NULL) || ((ny == cpl_image_get_size_x(mbpx)) &&
1885 (nx == cpl_image_get_size_y(mbpx))));
1893 if (giraffe_psfdata_contains(psfdata,
"Center") == FALSE) {
1897 if (giraffe_psfdata_contains(psfdata,
"Width2") == TRUE) {
1898 exponent = cpl_image_get_data_const(giraffe_psfdata_get_data(psfdata,
1902 width = cpl_image_get_data_const(giraffe_psfdata_get_data(psfdata,
1910 psfmodel = giraffe_model_new(giraffe_psfdata_get_model(psfdata));
1912 if (psfmodel == NULL) {
1916 giraffe_model_set_parameter(psfmodel,
"Amplitude", 1.);
1917 giraffe_model_set_parameter(psfmodel,
"Background", 0.);
1918 giraffe_model_set_parameter(psfmodel,
"Center", 0.);
1925 ypos = cpl_matrix_new(ny, 1);
1928 giraffe_model_delete(psfmodel);
1934 _ypos = cpl_matrix_get_data(ypos);
1936 for (i = 0; i < ny; ++i) {
1946 profiles = cpl_matrix_new(nfibers + bkg_nc, ny);
1948 if (profiles == NULL) {
1949 cpl_matrix_delete(ypos);
1952 giraffe_model_delete(psfmodel);
1958 _profiles = cpl_matrix_get_data(profiles);
1961 signal = cpl_matrix_new(ny, 1);
1963 if (signal == NULL) {
1964 cpl_matrix_delete(profiles);
1967 cpl_matrix_delete(ypos);
1970 giraffe_model_delete(psfmodel);
1976 _signal = cpl_matrix_get_data(signal);
1979 variance = cpl_matrix_new(ny, 1);
1981 if (variance == NULL) {
1982 cpl_matrix_delete(signal);
1985 cpl_matrix_delete(profiles);
1988 cpl_matrix_delete(ypos);
1991 giraffe_model_delete(psfmodel);
1997 _variance = cpl_matrix_get_data(variance);
2000 mask = cpl_matrix_new(ny, 1);
2003 cpl_matrix_delete(variance);
2006 cpl_matrix_delete(signal);
2009 cpl_matrix_delete(profiles);
2012 cpl_matrix_delete(ypos);
2015 giraffe_model_delete(psfmodel);
2021 _mask = cpl_matrix_get_data(mask);
2024 weights = cpl_matrix_new(ny, 1);
2026 if (weights == NULL) {
2027 cpl_matrix_delete(mask);
2030 cpl_matrix_delete(variance);
2033 cpl_matrix_delete(signal);
2036 cpl_matrix_delete(profiles);
2039 cpl_matrix_delete(ypos);
2042 giraffe_model_delete(psfmodel);
2048 _weights = cpl_matrix_get_data(weights);
2056 bkg_base = giraffe_chebyshev_base1d(0., ny, bkg_nc, ypos);
2058 cpl_matrix_delete(ypos);
2061 if (bkg_base == NULL) {
2062 cpl_matrix_delete(weights);
2065 cpl_matrix_delete(mask);
2068 cpl_matrix_delete(variance);
2071 cpl_matrix_delete(signal);
2074 cpl_matrix_delete(profiles);
2077 cpl_matrix_delete(ypos);
2080 giraffe_model_delete(psfmodel);
2086 _bkg_base = cpl_matrix_get_data(bkg_base);
2088 for (i = 0; i < bkg_nc; ++i) {
2090 register cxint j = 0;
2091 register cxint offset = nfibers * ny;
2093 for (j = 0; j < ny; ++j) {
2094 _profiles[i * ny + j + offset] = _bkg_base[i * ny + j];
2101 cpl_matrix_delete(bkg_base);
2109 slice = _giraffe_extractionslice_new(nfibers, ny, bkg_nc);
2111 if (slice == NULL) {
2112 cpl_matrix_delete(weights);
2115 cpl_matrix_delete(mask);
2118 cpl_matrix_delete(variance);
2121 cpl_matrix_delete(signal);
2124 cpl_matrix_delete(profiles);
2127 cpl_matrix_delete(ypos);
2130 giraffe_model_delete(psfmodel);
2137 limits = _giraffe_extraction_psflimits_new(nfibers + bkg_nc);
2139 if (limits == NULL) {
2141 _giraffe_extractionslice_delete(slice);
2144 cpl_matrix_delete(weights);
2147 cpl_matrix_delete(mask);
2150 cpl_matrix_delete(variance);
2153 cpl_matrix_delete(signal);
2156 cpl_matrix_delete(profiles);
2159 cpl_matrix_delete(ypos);
2162 giraffe_model_delete(psfmodel);
2169 for (i = 0; i < limits->size; ++i) {
2170 limits->ymin[i] = 0;
2171 limits->ymax[i] = ny;
2179 workspace = _giraffe_optimal_workspace_new(nfibers + bkg_nc, ny);
2181 for (bin = 0; bin < nbins; ++bin) {
2183 cxbool stop = FALSE;
2189 const cxdouble* _my = cpl_image_get_data_double_const(my);
2190 const cxdouble* _mz = cpl_image_get_data_double_const(mz);
2191 const cxdouble* _mzvar = cpl_image_get_data_double_const(mzvar);
2193 cxdouble* _ms = cpl_image_get_data_double(ms);
2194 cxdouble* _mse = cpl_image_get_data_double(mse);
2195 cxdouble* _msy = cpl_image_get_data_double(msy);
2196 cxdouble* _msm = cpl_image_get_data_double(msm);
2200 GiExtractionPsfLimits* _limits = (nolimits == FALSE) ? limits : NULL;
2202 cx_assert(_mz != NULL);
2203 cx_assert(_mzvar != NULL);
2211 status = _giraffe_optimal_build_profiles(profiles, _limits, my, mw,
2212 fibers, bin, psfmodel, width,
2216 _giraffe_optimal_workspace_delete(workspace);
2219 _giraffe_extraction_psflimits_delete(limits);
2222 _giraffe_extractionslice_delete(slice);
2225 cpl_matrix_delete(weights);
2228 cpl_matrix_delete(mask);
2231 cpl_matrix_delete(variance);
2234 cpl_matrix_delete(signal);
2237 cpl_matrix_delete(profiles);
2240 cpl_matrix_delete(ypos);
2243 giraffe_model_delete(psfmodel);
2257 const cxint* _mbpx = cpl_image_get_data_int_const(mbpx);
2260 cx_assert(_mbpx != NULL);
2262 for (i = 0; i < ny; ++i) {
2264 cxbool bad = (_mbpx[bin * ny + i] & GIR_M_PIX_SET) ||
2265 (_mz[bin * ny + i] < 0.);
2267 _signal[i] = _mz[bin * ny + i];
2268 _variance[i] = _signal[i] + _mzvar[bin * ny + i];
2276 _weights[i] = _mask[i] / _variance[i];
2283 for (i = 0; i < ny; ++i) {
2285 cxbool bad = (_mz[bin * ny + i] < 0.);
2287 _signal[i] = _mz[bin * ny + i];
2288 _variance[i] = _signal[i] + _mzvar[bin * ny + i];
2296 _weights[i] = _mask[i] / _variance[i];
2308 nmin = (cxint)(fraction * ngood);
2310 while ((iter < niter) && (stop == FALSE)) {
2314 const cxdouble* _model = NULL;
2317 status = _giraffe_optimal_extract_slice(slice, profiles,
2318 signal, weights, limits, workspace);
2321 _giraffe_optimal_workspace_delete(workspace);
2324 _giraffe_extraction_psflimits_delete(limits);
2327 _giraffe_extractionslice_delete(slice);
2330 cpl_matrix_delete(weights);
2333 cpl_matrix_delete(mask);
2336 cpl_matrix_delete(variance);
2339 cpl_matrix_delete(signal);
2342 cpl_matrix_delete(profiles);
2345 cpl_matrix_delete(ypos);
2348 giraffe_model_delete(psfmodel);
2359 _model = cpl_matrix_get_data(slice->model);
2361 for (i = 0; i < ny; ++i) {
2363 if (_mask[i] > 0.) {
2366 cxdouble residual = _signal[i] - _model[i];
2369 _variance[i] = _model[i] + _mzvar[bin * ny + i];
2371 bad = (residual * residual) > (sigma * _variance[i]) ?
2380 _weights[i] = _mask[i] / _variance[i];
2386 if ((nreject == 0) || (ngood <= nmin)) {
2400 memcpy(&_ms[bin * nfibers], cpl_matrix_get_data(slice->flux),
2401 slice->nflx *
sizeof(cxdouble));
2402 memcpy(&_mse[bin * nfibers], cpl_matrix_get_data(slice->variance),
2403 slice->nflx *
sizeof(cxdouble));
2404 memcpy(&_msm[bin * ny], cpl_matrix_get_data(slice->model),
2405 slice->msize *
sizeof(cxdouble));
2407 memcpy(&_msy[bin * nfibers], &_my[bin * nfibers],
2408 nfibers *
sizeof(cxdouble));
2415 cpl_matrix_fill_window(profiles, 0., 0, 0, nfibers, ny);
2424 cpl_image_power(mse, 0.5);
2426 _giraffe_optimal_workspace_delete(workspace);
2429 _giraffe_extraction_psflimits_delete(limits);
2432 _giraffe_extractionslice_delete(slice);
2435 cpl_matrix_delete(weights);
2438 cpl_matrix_delete(mask);
2441 cpl_matrix_delete(variance);
2444 cpl_matrix_delete(signal);
2447 cpl_matrix_delete(profiles);
2450 giraffe_model_delete(psfmodel);
2484 GiTable* fibers, GiLocalization* sloc,
2485 GiImage* bpixel, GiImage* slight,
2486 GiExtractConfig* config)
2489 const cxchar *fctid =
"giraffe_extract_spectra";
2498 cxdouble bias_ron = 0.;
2499 cxdouble bias_sigma = 0.;
2500 cxdouble dark_value = 0.;
2501 cxdouble exptime = 0.;
2502 cxdouble conad = 1.;
2504 cpl_propertylist *properties;
2506 cpl_image* _image = NULL;
2507 cpl_image* _locy = NULL;
2508 cpl_image* _locw = NULL;
2509 cpl_image* _spectra = NULL;
2510 cpl_image* _error = NULL;
2511 cpl_image* _npixels = NULL;
2512 cpl_image* _centroid = NULL;
2513 cpl_image* _model = NULL;
2515 cpl_table* _fibers = NULL;
2522 if (!result || !image || !fibers || !sloc || !config) {
2523 cpl_error_set(fctid, CPL_ERROR_NULL_INPUT);
2528 if ((sloc->locy == NULL) || (sloc->locw == NULL)) {
2529 cpl_error_set(fctid, CPL_ERROR_NULL_INPUT);
2534 if (result->spectra != NULL || result->error != NULL ||
2535 result->npixels != NULL || result->centroid != NULL ||
2536 result->model != NULL) {
2537 gi_warning(
"%s: Results structure at %p is not empty! Contents "
2538 "might be lost.", fctid, result);
2544 if (_fibers == NULL) {
2545 cpl_error_set(fctid, CPL_ERROR_DATA_NOT_FOUND);
2550 if ((config->emethod == GIEXTRACT_OPTIMAL) && (sloc->psf == NULL)) {
2551 cpl_msg_error(fctid,
"Missing data: PSF profile data is required "
2552 "for optimal spectrum extraction!");
2553 cpl_error_set(fctid, CPL_ERROR_NULL_INPUT);
2561 if (properties == NULL) {
2562 cpl_error_set(fctid, CPL_ERROR_DATA_NOT_FOUND);
2567 giraffe_error_push();
2571 if (cpl_error_get_code() != CPL_ERROR_NONE) {
2575 giraffe_error_pop();
2578 if (!cpl_propertylist_has(properties, GIALIAS_BIASERROR)) {
2579 cpl_msg_warning(fctid,
"Missing bias error property (%s)! Setting "
2580 "bias error to 0.", GIALIAS_BIASERROR);
2584 bias_sigma = cpl_propertylist_get_double(properties, GIALIAS_BIASERROR);
2588 if (config->ron > 0.) {
2590 cpl_msg_info(fctid,
"Setting bias RMS property (%s) to %.4g ADU",
2591 GIALIAS_BIASSIGMA, config->ron);
2593 cpl_propertylist_update_double(properties, GIALIAS_BIASSIGMA,
2600 if (!cpl_propertylist_has(properties, GIALIAS_DARKVALUE)) {
2604 cpl_msg_warning(fctid,
"Missing dark value property (%s), will be "
2605 "set to 0.!", GIALIAS_DARKVALUE);
2606 cpl_propertylist_append_double(properties, GIALIAS_DARKVALUE,
2611 dark_value = cpl_propertylist_get_double(properties,
2616 if (!cpl_propertylist_has(properties, GIALIAS_EXPTIME)) {
2617 cpl_msg_error(fctid,
"Missing exposure time property (%s)!",
2622 exptime = cpl_propertylist_get_double(properties, GIALIAS_EXPTIME);
2626 if (cpl_propertylist_has(properties, GIALIAS_DATANCOM)) {
2627 nframes = cpl_propertylist_get_int(properties, GIALIAS_DATANCOM);
2639 bias_sigma *= conad;
2640 dark_value *= conad;
2660 ny = cpl_image_get_size_x(_image);
2661 nx = cpl_image_get_size_y(_locw);
2662 ns = cpl_table_get_nrow(_fibers);
2665 switch (config->emethod) {
2669 cxint xsize = cpl_image_get_size_x(_image);
2670 cxint ysize = cpl_image_get_size_y(_image);
2672 cxdouble ron_variance = bias_ron * bias_ron;
2673 cxdouble bias_variance = bias_sigma * bias_sigma;
2674 cxdouble dark_variance = dark_value * exptime;
2676 cpl_image* bpixmap = NULL;
2677 cpl_image* variance = NULL;
2684 result->model = NULL;
2691 if (bpixel != NULL) {
2695 if (cpl_image_get_size_x(bpixmap) != xsize ||
2696 cpl_image_get_size_y(bpixmap) != ysize) {
2698 cxbool crop = FALSE;
2700 cpl_propertylist *p =
2703 GiWindow w = {1, 1, 0, 0};
2706 w.x1 = cpl_image_get_size_x(bpixmap);
2707 w.y1 = cpl_image_get_size_y(bpixmap);
2709 if (cpl_propertylist_has(p, GIALIAS_PRSCX)) {
2710 w.x0 += cpl_propertylist_get_int(p, GIALIAS_PRSCX);
2714 if (cpl_propertylist_has(p, GIALIAS_OVSCX)) {
2715 w.x1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCX);
2719 if (cpl_propertylist_has(p, GIALIAS_PRSCY)) {
2720 w.y0 += cpl_propertylist_get_int(p, GIALIAS_PRSCY);
2724 if (cpl_propertylist_has(p, GIALIAS_OVSCY)) {
2725 w.y1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCY);
2729 if ((w.x1 - w.x0 + 1) != xsize ||
2730 (w.y1 - w.y0 + 1) != ysize) {
2731 cpl_msg_error(fctid,
"Invalid bad pixel map! Image "
2732 "sizes do not match!");
2735 result->spectra = NULL;
2738 result->error = NULL;
2741 result->npixels = NULL;
2744 result->centroid = NULL;
2747 result->model = NULL;
2749 cpl_image_delete(_image);
2756 bpixmap = cpl_image_extract(bpixmap, w.x0, w.y0,
2764 if (slight != NULL) {
2765 cpl_msg_warning(fctid,
"Scattered light model will be "
2766 "ignored for extraction method `SUM'");
2769 variance = cpl_image_abs_create(_image);
2777 cpl_image_add_scalar(variance, nframes * (ron_variance + nframes *
2778 (bias_variance + dark_variance)));
2780 status = _giraffe_extract_summation(_image, variance, _fibers,
2781 _locy, _locw, bpixmap,
2782 _spectra, _error, _npixels,
2785 cpl_image_delete(variance);
2787 cpl_image_delete(bpixmap);
2795 case GIEXTRACT_OPTIMAL:
2798 cxint xsize = cpl_image_get_size_x(_image);
2799 cxint ysize = cpl_image_get_size_y(_image);
2802 cxdouble ron_variance = bias_ron * bias_ron;
2803 cxdouble bias_variance = bias_sigma * bias_sigma;
2804 cxdouble dark_variance = dark_value * exptime;
2806 cpl_image* variance = NULL;
2807 cpl_image* bpixmap = NULL;
2809 GiExtractOptimalConfig setup;
2814 result->npixels = NULL;
2823 setup.clip.iterations = config->psf.iterations;
2824 setup.clip.level = config->psf.sigma;
2825 setup.clip.fraction = config->optimal.fraction;
2826 setup.limits = config->optimal.wfactor < 0. ? FALSE : TRUE;
2827 setup.ewidth = CX_MAX(1., fabs(config->optimal.wfactor));
2828 setup.bkgorder = config->optimal.bkgorder;
2829 setup.exptime = exptime;
2830 setup.ron = bias_sigma;
2831 setup.dark = dark_value;
2834 if (bpixel != NULL) {
2838 if (cpl_image_get_size_x(bpixmap) != xsize ||
2839 cpl_image_get_size_y(bpixmap) != ysize) {
2841 cxbool crop = FALSE;
2843 cpl_propertylist *p =
2846 GiWindow w = {1, 1, 0, 0};
2849 w.x1 = cpl_image_get_size_x(bpixmap);
2850 w.y1 = cpl_image_get_size_y(bpixmap);
2852 if (cpl_propertylist_has(p, GIALIAS_PRSCX)) {
2853 w.x0 += cpl_propertylist_get_int(p, GIALIAS_PRSCX);
2857 if (cpl_propertylist_has(p, GIALIAS_OVSCX)) {
2858 w.x1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCX);
2862 if (cpl_propertylist_has(p, GIALIAS_PRSCY)) {
2863 w.y0 += cpl_propertylist_get_int(p, GIALIAS_PRSCY);
2867 if (cpl_propertylist_has(p, GIALIAS_OVSCY)) {
2868 w.y1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCY);
2872 if ((w.x1 - w.x0 + 1) != xsize ||
2873 (w.y1 - w.y0 + 1) != ysize) {
2875 cpl_msg_error(fctid,
"Invalid bad pixel map! "
2876 "Image sizes do not match!");
2879 result->spectra = NULL;
2882 result->error = NULL;
2885 result->npixels = NULL;
2888 result->centroid = NULL;
2891 result->model = NULL;
2893 cpl_image_delete(_image);
2901 bpixmap = cpl_image_extract(bpixmap, w.x0, w.y0,
2909 variance = cpl_image_new(xsize, ysize, CPL_TYPE_DOUBLE);
2917 v0 = nframes * (ron_variance + nframes *
2918 (bias_variance + dark_variance));
2927 if (slight != NULL) {
2929 register cxsize i = 0;
2930 register cxsize npixels = xsize * ysize;
2932 const cxdouble* _slight =
2935 cxdouble* _variance = cpl_image_get_data_double(variance);
2937 for (i = 0; i < npixels; i++) {
2938 _variance[i] = v0 + fabs(_slight[i]) * conad * nframes;
2944 register cxsize i = 0;
2945 register cxsize npixels = xsize * ysize;
2947 cxdouble* _variance = cpl_image_get_data_double(variance);
2949 for (i = 0; i < npixels; i++) {
2956 status = _giraffe_extract_optimal(_image, variance, _fibers,
2957 _locy, _locw, sloc->psf,
2958 bpixmap, _spectra, _error,
2959 _model, _centroid, &setup);
2961 cpl_image_delete(variance);
2965 cpl_image_delete(bpixmap);
2973 case GIEXTRACT_HORNE:
2976 cxint xsize = cpl_image_get_size_x(_image);
2977 cxint ysize = cpl_image_get_size_y(_image);
2980 cxdouble ron_variance = bias_ron * bias_ron;
2981 cxdouble bias_variance = bias_sigma * bias_sigma;
2982 cxdouble dark_variance = dark_value * exptime;
2984 cpl_image* variance = NULL;
2985 cpl_image* bpixmap = NULL;
2987 GiExtractHorneConfig setup;
2994 result->model = NULL;
3001 setup.clip.iterations = config->psf.iterations;
3002 setup.clip.level = config->psf.sigma;
3003 setup.clip.fraction = config->horne.mingood;
3004 setup.ewidth = config->horne.ewidth;
3005 setup.exptime = exptime;
3006 setup.ron = bias_sigma;
3007 setup.dark = dark_value;
3009 if (bpixel != NULL) {
3013 if (cpl_image_get_size_x(bpixmap) != xsize ||
3014 cpl_image_get_size_y(bpixmap) != ysize) {
3016 cxbool crop = FALSE;
3018 cpl_propertylist *p =
3021 GiWindow w = {1, 1, 0, 0};
3024 w.x1 = cpl_image_get_size_x(bpixmap);
3025 w.y1 = cpl_image_get_size_y(bpixmap);
3027 if (cpl_propertylist_has(p, GIALIAS_PRSCX)) {
3028 w.x0 += cpl_propertylist_get_int(p, GIALIAS_PRSCX);
3032 if (cpl_propertylist_has(p, GIALIAS_OVSCX)) {
3033 w.x1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCX);
3037 if (cpl_propertylist_has(p, GIALIAS_PRSCY)) {
3038 w.y0 += cpl_propertylist_get_int(p, GIALIAS_PRSCY);
3042 if (cpl_propertylist_has(p, GIALIAS_OVSCY)) {
3043 w.y1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCY);
3047 if ((w.x1 - w.x0 + 1) != xsize ||
3048 (w.y1 - w.y0 + 1) != ysize) {
3050 cpl_msg_error(fctid,
"Invalid bad pixel map! "
3051 "Image sizes do not match!");
3054 result->spectra = NULL;
3057 result->error = NULL;
3060 result->npixels = NULL;
3063 result->centroid = NULL;
3066 result->model = NULL;
3068 cpl_image_delete(_image);
3076 bpixmap = cpl_image_extract(bpixmap, w.x0, w.y0,
3084 variance = cpl_image_new(xsize, ysize, CPL_TYPE_DOUBLE);
3092 v0 = nframes * (ron_variance + nframes *
3093 (bias_variance + dark_variance));
3103 if (slight != NULL) {
3105 register cxsize i = 0;
3106 register cxsize npixels = xsize * ysize;
3108 const cxdouble* _slight =
3111 cxdouble* _variance = cpl_image_get_data_double(variance);
3113 for (i = 0; i < npixels; i++) {
3114 _variance[i] = v0 + fabs(_slight[i]) * nframes * conad;
3120 register cxsize i = 0;
3121 register cxsize npixels = xsize * ysize;
3123 cxdouble* _variance = cpl_image_get_data_double(variance);
3125 for (i = 0; i < npixels; i++) {
3132 status = _giraffe_extract_horne(_image, variance, _fibers,
3133 _locy, _locw, sloc->psf,
3134 bpixmap, _spectra, _error,
3135 _npixels, _centroid, &setup);
3137 cpl_image_delete(variance);
3141 cpl_image_delete(bpixmap);
3150 gi_message(
"%s: Method %d selected for spectrum extraction.",
3151 fctid, config->emethod);
3152 cpl_msg_error(fctid,
"Invalid extraction method!");
3158 cpl_image_delete(_image);
3164 result->spectra = NULL;
3167 result->error = NULL;
3170 result->npixels = NULL;
3173 result->centroid = NULL;
3176 result->model = NULL;
3178 cpl_msg_error(fctid,
"Spectrum extraction (method %d) failed!",
3181 cpl_image_delete(_image);
3199 if (result->spectra) {
3204 if (result->model) {
3209 if (result->error) {
3234 cpl_propertylist_set_int(properties, GIALIAS_NAXIS1,
3235 cpl_image_get_size_x(_spectra));
3236 cpl_propertylist_set_int(properties, GIALIAS_NAXIS2,
3237 cpl_image_get_size_y(_spectra));
3239 cpl_propertylist_set_int(properties, GIALIAS_BITPIX, -32);
3240 cpl_propertylist_set_double(properties, GIALIAS_BZERO, 0.);
3241 cpl_propertylist_set_double(properties, GIALIAS_BSCALE, 1.);
3243 cpl_propertylist_update_int(properties, GIALIAS_NFIBERS,
3244 cpl_image_get_size_x(_spectra));
3246 cpl_propertylist_append_int(properties, GIALIAS_EXT_NX,
3247 cpl_image_get_size_y(_spectra));
3248 cpl_propertylist_append_int(properties, GIALIAS_EXT_NS,
3249 cpl_image_get_size_x(_spectra));
3251 switch (config->emethod) {
3253 cpl_propertylist_append_string(properties, GIALIAS_EXT_METHOD,
3255 cpl_propertylist_set_comment(properties, GIALIAS_EXT_METHOD,
3256 "Spectrum extraction method");
3259 case GIEXTRACT_HORNE:
3262 cpl_propertylist_append_string(properties, GIALIAS_EXT_METHOD,
3264 cpl_propertylist_set_comment(properties, GIALIAS_EXT_METHOD,
3265 "Spectrum extraction method");
3267 cpl_propertylist_append_string(properties, GIALIAS_EXTPSF_MODEL,
3269 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_MODEL,
3271 cpl_propertylist_append_double(properties, GIALIAS_EXTPSF_SIGMA,
3273 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_SIGMA,
3274 "PSF fit sigma clipping threshold");
3275 cpl_propertylist_append_int(properties, GIALIAS_EXTPSF_NITER,
3276 config->psf.iterations);
3277 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_NITER,
3278 "PSF fit maximum number of "
3281 cpl_propertylist_append_int(properties, GIALIAS_EXTHRN_EWIDTH,
3282 config->horne.ewidth);
3283 cpl_propertylist_set_comment(properties, GIALIAS_EXTHRN_EWIDTH,
3284 "Number of extra pixels used");
3285 cpl_propertylist_append_int(properties, GIALIAS_EXTHRN_MINGOOD,
3286 config->horne.mingood);
3287 cpl_propertylist_set_comment(properties, GIALIAS_EXTHRN_MINGOOD,
3288 "Minimum number of pixels to keep");
3294 case GIEXTRACT_OPTIMAL:
3295 cpl_propertylist_append_string(properties, GIALIAS_EXT_METHOD,
3297 cpl_propertylist_set_comment(properties, GIALIAS_EXT_METHOD,
3298 "Spectrum extraction method");
3300 cpl_propertylist_append_string(properties, GIALIAS_EXTPSF_MODEL,
3302 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_MODEL,
3304 cpl_propertylist_append_double(properties, GIALIAS_EXTPSF_SIGMA,
3306 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_SIGMA,
3307 "PSF fit sigma clipping threshold");
3308 cpl_propertylist_append_int(properties, GIALIAS_EXTPSF_NITER,
3309 config->psf.iterations);
3310 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_NITER,
3311 "PSF fit maximum number of "
3314 cpl_propertylist_append_double(properties, GIALIAS_EXTOPT_FRACTION,
3315 config->optimal.fraction);
3316 cpl_propertylist_set_comment(properties, GIALIAS_EXTOPT_FRACTION,
3317 "Minimum fraction of pixels used.");
3318 cpl_propertylist_append_double(properties, GIALIAS_EXTOPT_WFACTOR,
3319 config->optimal.wfactor);
3320 cpl_propertylist_set_comment(properties, GIALIAS_EXTOPT_WFACTOR,
3321 "Multiple of the fiber PSF half "
3322 "width used for spectrum "
3324 cpl_propertylist_append_int(properties, GIALIAS_EXTOPT_BGORDER,
3325 config->optimal.bkgorder);
3326 cpl_propertylist_set_comment(properties, GIALIAS_EXTOPT_BGORDER,
3327 "Order of the background polynomial "
3328 "model along the spatial direction.");
3336 cpl_propertylist_update_string(properties, GIALIAS_GIRFTYPE,
"EXTSP");
3337 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3338 "Extracted spectra");
3348 cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE,
"EXTERRS");
3349 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3350 "Extracted spectra errors");
3360 cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE,
"EXTYCEN");
3361 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3362 "Extracted spectra centroids");
3369 if (result->npixels != NULL) {
3373 cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE,
"EXTNPIX");
3374 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3375 "Extracted spectra npixels");
3383 if (result->model != NULL) {
3387 cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE,
"EXTMODEL");
3388 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3389 "Model spectra used for extraction");
3414 GiExtractConfig* config = NULL;
3421 config = cx_calloc(1,
sizeof *config);
3423 p = cpl_parameterlist_find(list,
"giraffe.extraction.method");
3424 s = cpl_parameter_get_string(p);
3425 if (!strcmp(s,
"OPTIMAL")) {
3426 config->emethod = GIEXTRACT_OPTIMAL;
3428 else if (!strcmp(s,
"HORNE")) {
3429 config->emethod = GIEXTRACT_HORNE;
3432 config->emethod = GIEXTRACT_SUM;
3435 p = cpl_parameterlist_find(list,
"giraffe.extraction.ron");
3436 config->ron = cpl_parameter_get_double(p);
3438 p = cpl_parameterlist_find(list,
"giraffe.extraction.psf.model");
3439 config->psf.model = cx_strdup(cpl_parameter_get_string(p));
3441 p = cpl_parameterlist_find(list,
"giraffe.extraction.psf.sigma");
3442 config->psf.sigma = cpl_parameter_get_double(p);
3444 p = cpl_parameterlist_find(list,
"giraffe.extraction.psf.iterations");
3445 config->psf.iterations = cpl_parameter_get_int(p);
3448 p = cpl_parameterlist_find(list,
"giraffe.extraction.horne.extrawidth");
3449 config->horne.ewidth = cpl_parameter_get_int(p);
3451 p = cpl_parameterlist_find(list,
"giraffe.extraction.horne.mingood");
3452 config->horne.mingood = cpl_parameter_get_double(p);
3455 p = cpl_parameterlist_find(list,
"giraffe.extraction.optimal.fraction");
3456 config->optimal.fraction = cpl_parameter_get_double(p);
3458 p = cpl_parameterlist_find(list,
"giraffe.extraction.optimal.wfactor");
3459 config->optimal.wfactor = cpl_parameter_get_double(p);
3461 p = cpl_parameterlist_find(list,
"giraffe.extraction.optimal.bkgorder");
3462 config->optimal.bkgorder = cpl_parameter_get_int(p);
3487 if (config->psf.model) {
3488 cx_free(config->psf.model);
3515 cpl_parameter* p = NULL;
3522 p = cpl_parameter_new_enum(
"giraffe.extraction.method",
3524 "Extraction method: 'SUM', 'HORNE' or "
3526 "giraffe.extraction",
3527 "SUM", 3,
"SUM",
"OPTIMAL",
"HORNE");
3528 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-method");
3529 cpl_parameterlist_append(list, p);
3532 p = cpl_parameter_new_value(
"giraffe.extraction.ron",
3534 "New bias sigma (RON) value for "
3537 "giraffe.extraction",
3539 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-ron");
3540 cpl_parameterlist_append(list, p);
3543 p = cpl_parameter_new_enum(
"giraffe.extraction.psf.model",
3545 "PSF profile model: `psfexp', `psfexp2'",
3546 "giraffe.extraction.psf",
3547 "psfexp2", 2,
"psfexp",
"psfexp2");
3548 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-psfmodel");
3549 cpl_parameterlist_append(list, p);
3552 p = cpl_parameter_new_value(
"giraffe.extraction.psf.sigma",
3554 "Sigma clippging threshold used for "
3555 "rejecting data points during PSF fitting "
3556 "(Horne's sigma). It is used to reject bad "
3557 "pixels and cosmics.",
3558 "giraffe.extraction.psf",
3560 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-psfsigma");
3561 cpl_parameterlist_append(list, p);
3564 p = cpl_parameter_new_value(
"giraffe.extraction.psf.iterations",
3566 "Maximum number of iterations used for "
3567 "fitting the PSF profile.",
3568 "giraffe.extraction.psf",
3570 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-psfniter");
3571 cpl_parameterlist_append(list, p);
3574 p = cpl_parameter_new_value(
"giraffe.extraction.horne.extrawidth",
3576 "Horne extraction method: Number of "
3577 "extra pixels added to the fiber "
3579 "giraffe.extraction.horne",
3581 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-hewidth");
3582 cpl_parameterlist_append(list, p);
3585 p = cpl_parameter_new_value(
"giraffe.extraction.horne.mingood",
3587 "Horne extraction method: Minimum number of "
3588 "points used for the profile fit. It sets "
3589 "the lower limit of data points for the "
3591 "giraffe.extraction.horne",
3593 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-hmingood");
3594 cpl_parameterlist_append(list, p);
3597 p = cpl_parameter_new_range(
"giraffe.extraction.optimal.fraction",
3599 "Optimal extraction method: Minimum fraction "
3600 "of the data points used for fitting the "
3601 "fiber profiles. It sets the lower limit "
3602 "for the pixel rejection.",
3603 "giraffe.extraction.optimal",
3605 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-omfrac");
3606 cpl_parameterlist_append(list, p);
3609 p = cpl_parameter_new_value(
"giraffe.extraction.optimal.wfactor",
3611 "Optimal extraction method: Factor by which "
3612 "the fiber PSF half width is multiplied. "
3613 "Adjacent spectra within this area are "
3614 "assumed to affect the spectrum being "
3616 "giraffe.extraction.optimal",
3618 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-owfactor");
3619 cpl_parameterlist_append(list, p);
3622 p = cpl_parameter_new_value(
"giraffe.extraction.optimal.bkgorder",
3624 "Optimal extraction method: Order of the "
3625 "polynomial background model, which is "
3626 "fitted for each wavelength bin along the "
3627 "spatial direction.",
3628 "giraffe.extraction.optimal",
3630 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-obkgorder");
3631 cpl_parameterlist_append(list, p);
void gi_warning(const cxchar *format,...)
Log a warning.
cpl_table * giraffe_table_get(const GiTable *self)
Get the table data from a Giraffe table.
const cxchar * giraffe_fiberlist_query_index(const cpl_table *fibers)
Query a fiber list for the name of the fiber reference index column.
void gi_message(const cxchar *format,...)
Log a normal message.
void giraffe_image_delete(GiImage *self)
Destroys an image.
cxdouble giraffe_propertylist_get_conad(const cpl_propertylist *properties)
Retrieve the ADU to electrons conversion factor from the given properties.
GiImage * giraffe_image_create(cpl_type type, cxint nx, cxint ny)
Creates an image container of a given type.
cpl_image * giraffe_image_get(const GiImage *self)
Gets the image data.
cxint giraffe_array_sort(cxdouble *array, cxsize size)
Sorts an array in ascending order.
cxdouble giraffe_propertylist_get_ron(const cpl_propertylist *properties)
Retrieve the read-out noise from the given properties.
cxint giraffe_image_set_properties(GiImage *self, cpl_propertylist *properties)
Attaches a property list to an image.
cxint giraffe_propertylist_update(cpl_propertylist *self, cpl_propertylist *properties, const cxchar *regexp)
Update a property list.
cpl_propertylist * giraffe_image_get_properties(const GiImage *self)
Get the properties of an image.