37 #include <cxstrutils.h>
39 #include <cpl_parameterlist.h>
40 #include <cpl_matrix.h>
41 #include <cpl_table.h>
51 #include "gipsfdata.h"
54 #include "gilocalization.h"
55 #include "gimessages.h"
56 #include "gifiberutils.h"
58 #include "giextract.h"
70 PROFILE_PSFEXP = 1 << 1,
71 PROFILE_PSFEXP2 = 1 << 2,
72 PROFILE_GAUSSIAN = 1 << 3
75 typedef enum GiProfileId GiProfileId;
82 struct GiExtractOptimalConfig {
96 typedef struct GiExtractOptimalConfig GiExtractOptimalConfig;
103 struct GiExtractHorneConfig {
112 typedef struct GiExtractHorneConfig GiExtractHorneConfig;
115 struct GiExtractionData {
122 typedef struct GiExtractionData GiExtractionData;
125 struct GiExtractionSlice {
133 cpl_matrix* variance;
137 typedef struct GiExtractionSlice GiExtractionSlice;
140 struct GiExtractionPsfLimits {
147 typedef struct GiExtractionPsfLimits GiExtractionPsfLimits;
150 struct GiExtractionWorkspace {
158 typedef struct GiExtractionWorkspace GiExtractionWorkspace;
161 struct GiVirtualSlit {
165 cxdouble extra_width;
176 typedef struct GiVirtualSlit GiVirtualSlit;
183 inline static GiExtractionSlice*
184 _giraffe_extractionslice_new(cxint nflx, cxint ndata, cxint nbkg)
187 GiExtractionSlice*
self = cx_malloc(
sizeof *
self);
192 self->fsize = nflx + nbkg;
195 self->flux = cpl_matrix_new(self->fsize, 1);
196 self->variance = cpl_matrix_new(self->fsize, 1);
197 self->model = cpl_matrix_new(self->msize, 1);
205 _giraffe_extractionslice_delete(GiExtractionSlice*
self)
209 if (self->model != NULL) {
210 cpl_matrix_delete(self->model);
214 if (self->variance != NULL) {
215 cpl_matrix_delete(self->variance);
216 self->variance = NULL;
219 if (self->flux != NULL) {
220 cpl_matrix_delete(self->flux);
232 inline static GiExtractionPsfLimits*
233 _giraffe_extraction_psflimits_new(cxint size)
236 GiExtractionPsfLimits*
self = cx_malloc(
sizeof *
self);
240 self->ymin = cx_calloc(self->size,
sizeof(cxint));
241 self->ymax = cx_calloc(self->size,
sizeof(cxint));
249 _giraffe_extraction_psflimits_delete(GiExtractionPsfLimits*
self)
253 if (self->ymin != NULL) {
257 if (self->ymax != NULL) {
269 inline static GiExtractionWorkspace*
270 _giraffe_optimal_workspace_new(cxint m, cxint n)
273 GiExtractionWorkspace*
self = cx_malloc(
sizeof *
self);
276 self->atw = cpl_matrix_new(m, n);
277 self->atwa = cpl_matrix_new(m, m);
278 self->c = cpl_matrix_new(m, m);
279 self->atws = cpl_matrix_new(m, 1);
281 self->tmp = cpl_matrix_new(m, m);
289 _giraffe_optimal_workspace_delete(GiExtractionWorkspace*
self)
293 if (self->atws != NULL) {
294 cpl_matrix_delete(self->atws);
297 if (self->atwa != NULL) {
298 cpl_matrix_delete(self->atwa);
301 if (self->c != NULL) {
302 cpl_matrix_delete(self->c);
305 if (self->atw != NULL) {
306 cpl_matrix_delete(self->atw);
309 if (self->tmp != NULL) {
310 cpl_matrix_delete(self->tmp);
327 _giraffe_virtualslit_allocate(GiVirtualSlit*
self)
330 if ((
self != NULL) && (self->width > 0)) {
332 self->position = cx_calloc(self->width,
sizeof(cxdouble));
333 self->signal = cx_calloc(self->width,
sizeof(cxdouble));
334 self->variance = cx_calloc(self->width,
sizeof(cxdouble));
335 self->fraction = cx_calloc(self->width,
sizeof(cxdouble));
337 self->mask = cx_calloc(self->width,
sizeof(cxdouble));
338 self->offset = cx_calloc(self->width,
sizeof(cxdouble));
347 inline static GiVirtualSlit*
348 _giraffe_virtualslit_new(cxdouble extra_width)
351 GiVirtualSlit*
self = cx_calloc(1,
sizeof *
self);
355 self->extra_width = extra_width;
357 self->position = NULL;
359 self->variance = NULL;
360 self->fraction = NULL;
370 _giraffe_virtualslit_clear(GiVirtualSlit*
self)
375 if (self->position != NULL) {
376 cx_free(self->position);
377 self->position = NULL;
380 if (self->signal != NULL) {
381 cx_free(self->signal);
385 if (self->variance != NULL) {
386 cx_free(self->variance);
387 self->variance = NULL;
390 if (self->fraction != NULL) {
391 cx_free(self->fraction);
392 self->fraction = NULL;
395 if (self->mask != NULL) {
400 if (self->offset != NULL) {
401 cx_free(self->offset);
405 self->extra_width = 0.;
417 _giraffe_virtualslit_delete(GiVirtualSlit*
self)
421 _giraffe_virtualslit_clear(
self);
432 _giraffe_virtualslit_setup(GiVirtualSlit*
self, cxint bin,
433 cxdouble center, cxdouble width,
434 const cpl_image* signal,
const cpl_image* variance,
435 const cpl_image* bpixel)
438 register cxint ny = cpl_image_get_size_x(signal);
439 register cxint offset = bin * cpl_image_get_size_x(signal);
441 register cxdouble lower = center - (width +
self->extra_width);
442 register cxdouble upper = center + (width +
self->extra_width);
444 register cxint first = (cxint) floor(lower);
445 register cxint last = (cxint) ceil(upper);
447 const cxdouble* s = cpl_image_get_data_double_const(signal);
448 const cxdouble* v = cpl_image_get_data_double_const(variance);
455 lower = CX_MAX(0., lower);
456 upper = CX_MIN(ny, upper);
458 first = CX_MAX(0, first);
459 last = CX_MIN(ny, last);
461 self->center = center;
462 self->width = last - first + 1;
469 _giraffe_virtualslit_allocate(
self);
471 if (bpixel != NULL) {
473 register cxint k = 0;
474 register cxint y = 0;
476 const cxint* _bpixel = cpl_image_get_data_int_const(bpixel);
479 for (y = first; y <= last; y++) {
481 register cxint ypos = offset + y;
483 cxint ok = (_bpixel[ypos] & GIR_M_PIX_SET) == 0 ? 1 : 0;
486 self->position[k] = y - center;
487 self->fraction[k] = 1.;
489 self->signal[k] = s[ypos];
490 self->variance[k] = v[ypos];
493 self->offset[k] = ypos;
501 register cxint k = 0;
502 register cxint y = 0;
505 for (y = first; y <= last; y++) {
507 register cxint ypos = offset + y;
512 self->position[k] = y - center;
513 self->fraction[k] = 1.;
515 self->signal[k] = s[ypos];
516 self->variance[k] = v[ypos];
519 self->offset[k] = ypos;
533 self->fraction[0] = ((cxdouble)first + 1.) - lower;
534 self->fraction[
self->width - 1] = upper - ((cxdouble)last - 1.);
546 _giraffe_matrix_invert(cpl_matrix* m_inv,
const cpl_matrix* m, cpl_matrix* lu)
551 cxint n = cpl_matrix_get_ncol(m);
553 register cxint sz = n * n *
sizeof(cxdouble);
555 const cxdouble* _m = cpl_matrix_get_data_const(m);
557 cxdouble* _m_inv = cpl_matrix_get_data(m_inv);
558 cxdouble* _m_lu = cpl_matrix_get_data(lu);
560 cpl_array* perm = cpl_array_new(n, CPL_TYPE_INT);
562 register cxint* perm_data = cpl_array_get_data_int(perm);
565 memset(_m_inv, 0, sz);
566 memcpy(_m_lu, _m, sz);
568 if (cpl_matrix_decomp_lu(lu, perm, &i) != 0) {
569 cpl_array_delete(perm);
578 for (i = 0; i < n; ++i) {
579 _m_inv[i * n + perm_data[i]] = 1.;
582 cpl_array_delete(perm);
585 status = cpl_matrix_solve_lu(lu, m_inv, NULL);
588 cpl_matrix_delete(m_inv);
601 inline static cpl_matrix*
602 _giraffe_compute_psf(GiModel* psf,
const cpl_matrix* x)
605 register cxint i = 0;
606 register cxint n = 0;
610 const cxdouble* _x = NULL;
614 cpl_matrix* y = NULL;
616 cx_assert(psf != NULL);
617 cx_assert(x != NULL);
618 cx_assert(cpl_matrix_get_ncol(x) == 1);
620 n = cpl_matrix_get_nrow(x);
622 y = cpl_matrix_new(n, 1);
624 _x = cpl_matrix_get_data_const(x);
625 _y = cpl_matrix_get_data(y);
627 for (i = 0; i < n; i++) {
628 giraffe_model_set_argument(psf,
"x", _x[i]);
629 giraffe_model_evaluate(psf, &_y[i], &status);
632 cpl_matrix_delete(y);
648 _giraffe_horne_extract_slit(GiExtractionData* result,
649 const GiVirtualSlit* vslit, GiModel* psf,
650 const GiExtractHorneConfig* config)
660 cxdouble* tdata = NULL;
661 cxdouble* _mnpsf = NULL;
663 cpl_matrix* mnpsf = NULL;
664 cpl_matrix* mvslit = NULL;
672 mvslit = cpl_matrix_wrap(vslit->width, 1, vslit->position);
673 mnpsf = _giraffe_compute_psf(psf, mvslit);
675 cpl_matrix_unwrap(mvslit);
687 _mnpsf = cpl_matrix_get_data(mnpsf);
691 for (i = 0; i < vslit->width; ++i) {
692 _mnpsf[i] = CX_MAX(_mnpsf[i], 0.);
696 for (i = 0; i < vslit->width; ++i) {
705 tdata = cx_malloc(vslit->width *
sizeof(cxdouble));
710 while (i < vslit->width) {
711 if (vslit->mask[i] > 0) {
712 tdata[ngood] = CX_MAX(vslit->signal[i], 0.);
720 bkg = 0.5 * (tdata[0] + tdata[1]);
737 cxint niter = config->clip.iterations;
738 cxint nmin = (cxint)config->clip.fraction;
740 cxdouble sigma = config->clip.level * config->clip.level;
741 cxdouble* variance = NULL;
752 for (i = 0; i < vslit->width; ++i) {
753 if (vslit->mask[i] != 0) {
754 flx += (vslit->signal[i] - bkg) * vslit->fraction[i];
755 norm += vslit->fraction[i] * _mnpsf[i];
767 variance = cx_calloc(vslit->width,
sizeof(cxdouble));
769 for (i = 0; i < vslit->width; ++i) {
771 register cxdouble ve = flx * _mnpsf[i] + bkg;
773 variance[i] = vslit->variance[i] + fabs(vslit->fraction[i] * ve);
784 while ((iteration < niter) && (ngood > nmin) && (nreject != 0)) {
801 for (i = 0; i < vslit->width; ++i) {
803 if (vslit->mask[i] != 0) {
805 cxdouble m = vslit->signal[i] - bkg - flx * _mnpsf[i];
807 m *= vslit->fraction[i];
808 m *= m / variance[i] ;
819 if ((sigma > 0.) && (mmax > sigma)) {
820 vslit->mask[imax] = 0;
830 for (i = 0; i < vslit->width; ++i) {
832 if (vslit->mask[i] != 0) {
834 register cxdouble data = vslit->signal[i] - bkg;
835 register cxdouble p = _mnpsf[i];
837 data *= vslit->fraction[i];
838 p *= vslit->fraction[i];
840 norm += p * p / variance[i];
841 _flx += p * data / variance[i];
856 for (i = 0; i < vslit->width; ++i) {
858 register cxdouble ve = flx * _mnpsf[i] + bkg;
860 variance[i] = vslit->variance[i] + fabs(vslit->fraction[i] * ve);
873 cpl_matrix_delete(mnpsf);
877 result->error = sqrt(var);
878 result->position = vslit->center;
879 result->npixels = ngood;
881 return ngood == 0 ? 1 : 0;
930 _giraffe_optimal_extract_slice(GiExtractionSlice* slice,
931 const cpl_matrix* AT,
934 GiExtractionPsfLimits* limits,
935 GiExtractionWorkspace* ws)
938 register cxint i = 0;
939 register cxint n = cpl_matrix_get_ncol(AT);
940 register cxint m = cpl_matrix_get_nrow(AT);
944 const cxdouble* at = cpl_matrix_get_data_const(AT);
945 const cxdouble* w = cpl_matrix_get_data_const(W);
946 const cxdouble* s = cpl_matrix_get_data_const(S);
947 const cxdouble* c = cpl_matrix_get_data_const(ws->c);
949 cxdouble* atw = cpl_matrix_get_data(ws->atw);
950 cxdouble* atwa = cpl_matrix_get_data(ws->atwa);
951 cxdouble* atws = cpl_matrix_get_data(ws->atws);
952 cxdouble* sf = cpl_matrix_get_data(slice->flux);
953 cxdouble* sv = cpl_matrix_get_data(slice->variance);
954 cxdouble* sm = cpl_matrix_get_data(slice->model);
957 for (i = 0; i < m; ++i) {
959 register cxint j = 0;
960 register cxint im = i * m;
961 register cxint in = i * n;
962 register cxint ymin = limits->ymin[i];
963 register cxint ymax = limits->ymax[i];
968 for (j = 0; j < n; ++j) {
970 register cxint k = in + j;
973 atw[k] = w[j] * at[k];
974 atws[i] += atw[k] * s[j];
978 for (j = 0; j < i; ++j) {
980 register cxint k = 0;
981 register cxint l = im + j;
984 for (k = ymin; k < ymax; ++k) {
985 atwa[l] += atw[in + k] * at[j * n + k];
988 atwa[j * m + i] = atwa[l];
994 for (j = ymin; j < ymax; ++j) {
995 atwa[im + i] += atw[in + j] * at[in + j];
1001 status = _giraffe_matrix_invert(ws->c, ws->atwa, ws->tmp);
1007 for (i = 0; i < m; ++i) {
1009 register cxint j = 0;
1010 register cxint im = i * m;
1016 for (j = 0; j < m; ++j) {
1017 sf[i] += c[im + j] * atws[j];
1022 for (i = 0; i < n; ++i) {
1024 register cxint j = 0;
1029 for (j = 0; j < m; ++j) {
1030 sm[i] += at[j * n + i] * sf[j];
1064 _giraffe_extract_summation(
const cpl_image* mz,
const cpl_image* mvarz,
1065 const cpl_table* fibers,
const cpl_image* my,
1066 const cpl_image* mw, cpl_image* mbpx,
1067 cpl_image* ms, cpl_image* mse,
1068 cpl_image* msn, cpl_image* msy)
1073 const cxchar* idx = NULL;
1075 cxint ny = cpl_image_get_size_x(mz);
1076 cxint nfibers = cpl_table_get_nrow(fibers);
1077 cxint nspectra = cpl_image_get_size_x(my);
1078 cxint nbins = cpl_image_get_size_y(my);
1080 const cxdouble* pixels = cpl_image_get_data_double_const(mz);
1081 const cxdouble* variances = cpl_image_get_data_double_const(mvarz);
1082 const cxdouble* locy = cpl_image_get_data_double_const(my);
1083 const cxdouble* locw = cpl_image_get_data_double_const(mw);
1085 cxdouble* flux = cpl_image_get_data_double(ms);
1086 cxdouble* flux_error = cpl_image_get_data_double(mse);
1087 cxdouble* flux_npixels = cpl_image_get_data_double(msn);
1088 cxdouble* flux_ypos = cpl_image_get_data_double(msy);
1096 cx_assert(nfibers <= nspectra);
1100 cx_assert(cpl_table_has_column(fibers, idx) != 0);
1104 const cxint* bpx = cpl_image_get_data_int(mbpx);
1106 for (nn = 0; nn < nfibers; nn++) {
1109 register cxint ns = cpl_table_get_int(fibers, idx, nn, NULL) - 1;
1112 for (x = 0; x < cpl_image_get_size_y(mz) && x < nbins; x++) {
1116 cxint lx = x * nspectra + ns;
1117 cxint sx = x * nfibers + nn;
1119 cxdouble ylower = locy[lx] - locw[lx];
1120 cxdouble yupper = locy[lx] + locw[lx];
1123 cxdouble error2 = 0.;
1127 flux_npixels[sx] = 0.;
1128 flux_error[sx] = 0.;
1136 if (locw[lx] <= 0.0) {
1148 ylo = (cxint) ceil(ylower);
1149 yup = (cxint) floor(yupper);
1152 if (yup < 0. || ylo - 1 >= ny) {
1172 if (!(bpx[x * ny + y] & GIR_M_PIX_SET)) {
1174 cxdouble extcoeff = (cxdouble)ylo - ylower;
1175 cxdouble extcoeff2 = extcoeff * extcoeff;
1176 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1178 flux[sx] = pixels[x * ny + y] * extcoeff;
1179 flux_npixels[sx] = extcoeff;
1180 error2 = variances[x * ny + y] * extcoeff2;
1182 zsum = px * extcoeff;
1183 ysum = y * px * extcoeff;
1194 for (y = CX_MAX(ylo, 0); y < yup && y < ny; y++) {
1196 if (!(bpx[x * ny + y] & GIR_M_PIX_SET)) {
1198 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1200 flux[sx] += pixels[x * ny + y];
1201 flux_npixels[sx] += 1.0;
1202 error2 += variances[x * ny + y];
1220 if (!(bpx[x * ny + y] & GIR_M_PIX_SET)) {
1222 cxdouble extcoeff = yupper - (cxdouble)yup;
1223 cxdouble extcoeff2 = extcoeff * extcoeff;
1224 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1226 flux[sx] += pixels[x * ny + y] * extcoeff;
1227 flux_npixels[sx] += extcoeff;
1228 error2 += variances[x * ny + y] * extcoeff2;
1230 zsum += px * extcoeff;
1231 ysum += y * px * extcoeff;
1237 flux_error[sx] = sqrt(error2);
1242 if (fabs(ysum) < DBL_EPSILON || fabs(zsum) < DBL_EPSILON) {
1243 flux_ypos[sx] = 0.5 * (yupper + ylower);
1246 flux_ypos[sx] = ysum / zsum;
1256 for (nn = 0; nn < nfibers; nn++) {
1259 register cxint ns = cpl_table_get_int(fibers, idx,
1263 for (x = 0; x < cpl_image_get_size_y(mz) && x < nbins; x++) {
1267 cxint lx = x * nspectra + ns;
1268 cxint sx = x * nfibers + nn;
1270 cxdouble yupper, ylower;
1273 cxdouble error2 = 0.;
1277 flux_npixels[sx] = 0.;
1278 flux_error[sx] = 0.;
1286 if (locw[lx] <= 0.0) {
1298 yupper = locy[lx] + locw[lx];
1299 ylower = locy[lx] - locw[lx];
1301 ylo = (cxint) ceil(ylower);
1302 yup = (cxint) floor(yupper);
1305 if (yup < 0. || ylo - 1 >= ny) {
1325 cxdouble extcoeff = (cxdouble)ylo - ylower;
1326 cxdouble extcoeff2 = extcoeff * extcoeff;
1327 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1329 flux[sx] = pixels[x * ny + y] * extcoeff;
1330 flux_npixels[sx] = extcoeff;
1331 error2 = variances[x * ny + y] * extcoeff2;
1333 zsum = px * extcoeff;
1334 ysum = y * px * extcoeff;
1343 for (y = CX_MAX(ylo, 0); y < yup && y < ny; y++) {
1345 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1347 flux[sx] += pixels[x * ny + y];
1348 flux_npixels[sx] += 1.0;
1349 error2 += variances[x * ny + y];
1364 cxdouble extcoeff = yupper - (cxdouble)yup;
1365 cxdouble extcoeff2 = extcoeff * extcoeff;
1366 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1368 flux[sx] += pixels[x * ny + y] * extcoeff;
1369 flux_npixels[sx] += extcoeff;
1370 error2 += variances[x * ny + y] * extcoeff2;
1372 zsum += px * extcoeff;
1373 ysum += y * px * extcoeff;
1377 flux_error[sx] = sqrt(error2);
1382 if (fabs(ysum) < DBL_EPSILON || fabs(zsum) < DBL_EPSILON) {
1383 flux_ypos[sx] = 0.5 * (yupper + ylower);
1386 flux_ypos[sx] = ysum / zsum;
1422 _giraffe_extract_horne(
const cpl_image* mz,
const cpl_image* mzvar,
1423 const cpl_table* fibers,
const cpl_image* my,
1424 const cpl_image* mw,
const GiPsfData* psfdata,
1425 cpl_image* mbpx, cpl_image* ms, cpl_image* mse,
1426 cpl_image* msn, cpl_image* msy,
1427 const GiExtractHorneConfig* config)
1430 const cxchar* idx = NULL;
1437 const cxdouble* locy = NULL;
1438 const cxdouble* locw = NULL;
1439 const cxdouble* width = NULL;
1440 const cxdouble* exponent = NULL;
1442 GiModel* psfmodel = NULL;
1445 cx_assert(mz != NULL);
1446 cx_assert(mzvar != NULL);
1448 cx_assert(fibers != NULL);
1450 cx_assert(my != NULL);
1451 cx_assert(mw != NULL);
1453 cx_assert(psfdata != NULL);
1455 cx_assert(ms != NULL);
1456 cx_assert(mse != NULL);
1457 cx_assert(msn != NULL);
1458 cx_assert(msy != NULL);
1460 cx_assert(config != NULL);
1462 ny = cpl_image_get_size_x(mz);
1463 nx = cpl_image_get_size_y(mz);
1464 nfibers = cpl_table_get_nrow(fibers);
1466 locy = cpl_image_get_data_double_const(my);
1467 locw = cpl_image_get_data_double_const(mw);
1469 cx_assert((ny == cpl_image_get_size_x(mzvar)) &&
1470 (nx == cpl_image_get_size_y(mzvar)));
1472 cx_assert(cpl_image_get_size_x(my) == cpl_image_get_size_x(mw));
1473 cx_assert(cpl_image_get_size_y(my) == cpl_image_get_size_y(mw));
1475 cx_assert(giraffe_psfdata_fibers(psfdata) ==
1476 (cxsize)cpl_image_get_size_x(my));
1477 cx_assert(giraffe_psfdata_bins(psfdata) ==
1478 (cxsize)cpl_image_get_size_y(my));
1480 cx_assert((nfibers == cpl_image_get_size_x(ms)) &&
1481 (nx == cpl_image_get_size_y(ms)));
1482 cx_assert((nfibers == cpl_image_get_size_x(mse)) &&
1483 (nx == cpl_image_get_size_y(mse)));
1484 cx_assert((nfibers == cpl_image_get_size_x(msn)) &&
1485 (nx == cpl_image_get_size_y(msn)));
1486 cx_assert((nfibers == cpl_image_get_size_x(msy)) &&
1487 (nx == cpl_image_get_size_y(msy)));
1489 cx_assert((mbpx == NULL) || ((ny == cpl_image_get_size_x(mbpx)) &&
1490 (nx == cpl_image_get_size_y(mbpx))));
1500 cx_assert(cpl_table_has_column(fibers, idx) != 0);
1508 if (giraffe_psfdata_contains(psfdata,
"Center") == FALSE) {
1512 if (giraffe_psfdata_contains(psfdata,
"Width2") == TRUE) {
1513 exponent = cpl_image_get_data_const(giraffe_psfdata_get_data(psfdata,
1517 width = cpl_image_get_data_const(giraffe_psfdata_get_data(psfdata,
1525 psfmodel = giraffe_model_new(giraffe_psfdata_get_model(psfdata));
1527 if (psfmodel == NULL) {
1531 giraffe_model_set_parameter(psfmodel,
"Center", 0.);
1532 giraffe_model_set_parameter(psfmodel,
"Amplitude", 1.);
1533 giraffe_model_set_parameter(psfmodel,
"Background", 0.);
1540 for (fiber = 0; fiber < nfibers; ++fiber) {
1542 register cxint bin = 0;
1543 register cxint fidx = cpl_table_get_int(fibers, idx, fiber, NULL) - 1;
1545 cxint nbins = CX_MIN(nx, cpl_image_get_size_y(my));
1547 cxdouble* _ms = cpl_image_get_data_double(ms);
1548 cxdouble* _mse = cpl_image_get_data_double(mse);
1549 cxdouble* _msy = cpl_image_get_data_double(msy);
1550 cxdouble* _msn = cpl_image_get_data_double(msn);
1553 for (bin = 0; bin < nbins; bin++) {
1555 register cxint lpos = bin * cpl_image_get_size_x(my) + fidx;
1556 register cxint spos = bin * nfibers + fiber;
1561 register cxdouble lcenter = locy[lpos];
1562 register cxdouble lwidth = locw[lpos];
1564 register cxdouble ylower = lcenter - lwidth;
1565 register cxdouble yupper = lcenter + lwidth;
1567 GiVirtualSlit* vslit = NULL;
1569 GiExtractionData result = {0., 0., 0., 0.};
1576 if ((lwidth <= 0.) || (yupper < 0.) || (ylower > ny)) {
1584 vslit = _giraffe_virtualslit_new(config->ewidth);
1586 vwidth = _giraffe_virtualslit_setup(vslit, bin, lcenter, lwidth,
1590 _giraffe_virtualslit_delete(vslit);
1601 giraffe_model_set_parameter(psfmodel,
"Width1", width[lpos]);
1603 if (exponent != NULL) {
1604 giraffe_model_set_parameter(psfmodel,
"Width2",
1614 status = _giraffe_horne_extract_slit(&result, vslit, psfmodel,
1617 _giraffe_virtualslit_delete(vslit);
1622 giraffe_model_delete(psfmodel);
1628 _ms[spos] = result.value;
1629 _mse[spos] = result.error;
1630 _msy[spos] = result.position;
1631 _msn[spos] = result.npixels;
1638 giraffe_model_delete(psfmodel);
1652 _giraffe_optimal_build_profiles(cpl_matrix* profiles,
1653 GiExtractionPsfLimits* limits,
1654 const cpl_image* my,
const cpl_image* mw,
1655 const cpl_table* fibers, cxint bin,
1656 GiModel* psf,
const cxdouble* width,
1657 const cxdouble* exponent, cxdouble wfactor)
1663 cxint nfibers = cpl_table_get_nrow(fibers);
1664 cxint ny = cpl_matrix_get_ncol(profiles);
1666 const cxdouble* locy = cpl_image_get_data_double_const(my);
1667 const cxdouble* locw = cpl_image_get_data_double_const(mw);
1669 cxdouble* _profiles = cpl_matrix_get_data(profiles);
1671 cxdouble* ypos = NULL;
1674 cx_assert(cpl_table_has_column(fibers, idx) != 0);
1675 cx_assert((limits == NULL) ||
1676 (cpl_matrix_get_nrow(profiles) == limits->size));
1678 ypos = cx_calloc(ny,
sizeof(cxdouble));
1680 for (fiber = 0; fiber < nfibers; ++fiber) {
1682 register cxint i = 0;
1683 register cxint y = 0;
1684 register cxint k = 0;
1686 cxint fidx = cpl_table_get_int(fibers, idx, fiber, NULL) - 1;
1687 cxint lpos = bin * cpl_image_get_size_x(my) + fidx;
1689 register cxdouble lcenter = locy[lpos];
1690 register cxdouble lwidth = locw[lpos];
1692 register cxdouble ylower = lcenter - fabs(wfactor) * lwidth;
1693 register cxdouble yupper = lcenter + fabs(wfactor) * lwidth;
1695 register cxint first = (cxint) floor(ylower);
1696 register cxint last = (cxint) ceil(yupper);
1698 register cxint vwidth = 0;
1701 cxdouble* _mnpsf = NULL;
1703 cpl_matrix* positions = NULL;
1704 cpl_matrix* mnpsf = NULL;
1711 ylower = CX_MAX(0., ylower);
1712 yupper = CX_MIN(ny - 1., yupper);
1714 first = CX_MAX(0, first);
1715 last = CX_MIN(ny - 1, last);
1717 vwidth = last - first + 1;
1719 if (limits != NULL) {
1720 limits->ymin[fiber] = first;
1721 limits->ymax[fiber] = last + 1;
1729 giraffe_model_set_parameter(psf,
"Width1", width[lpos]);
1731 if (exponent != NULL) {
1732 giraffe_model_set_parameter(psf,
"Width2", exponent[lpos]);
1741 for (y = first; y <= last; ++y) {
1742 ypos[k] = y - lcenter;
1746 positions = cpl_matrix_wrap(vwidth, 1, ypos);
1747 mnpsf = _giraffe_compute_psf(psf, positions);
1749 cpl_matrix_unwrap(positions);
1752 if (mnpsf == NULL) {
1759 _mnpsf = cpl_matrix_get_data(mnpsf);
1761 for (i = 0; i < vwidth; ++i) {
1762 _mnpsf[i] = CX_MAX(_mnpsf[i], 0.);
1766 for (i = 0; i < vwidth; ++i) {
1770 k = fiber * ny + first;
1771 for (y = 0; y < vwidth; ++y) {
1772 _profiles[k + y] = _mnpsf[y];
1775 cpl_matrix_delete(mnpsf);
1789 _giraffe_extract_optimal(
const cpl_image* mz,
const cpl_image* mzvar,
1790 const cpl_table* fibers,
const cpl_image* my,
1791 const cpl_image* mw,
const GiPsfData* psfdata,
1792 cpl_image* mbpx, cpl_image* ms, cpl_image* mse,
1793 cpl_image* msm, cpl_image* msy,
1794 const GiExtractOptimalConfig* config)
1797 const cxbool nolimits = (config->limits == TRUE) ? FALSE : TRUE;
1799 const cxint bkg_nc = config->bkgorder + 1;
1800 const cxint niter = config->clip.iterations;
1802 register cxint i = 0;
1810 const cxdouble wfactor = config->ewidth;
1811 const cxdouble sigma = config->clip.level * config->clip.level;
1812 const cxdouble fraction = config->clip.fraction;
1814 const cxdouble* width = NULL;
1815 const cxdouble* exponent = NULL;
1817 cxdouble* _ypos = NULL;
1818 cxdouble* _bkg_base = NULL;
1819 cxdouble* _profiles = NULL;
1820 cxdouble* _signal = NULL;
1821 cxdouble* _variance = NULL;
1822 cxdouble* _mask = NULL;
1823 cxdouble* _weights = NULL;
1825 cpl_matrix* ypos = NULL;
1826 cpl_matrix* bkg_base = NULL;
1827 cpl_matrix* profiles = NULL;
1828 cpl_matrix* weights = NULL;
1829 cpl_matrix* signal = NULL;
1830 cpl_matrix* variance = NULL;
1831 cpl_matrix* mask = NULL;
1833 GiModel* psfmodel = NULL;
1835 GiExtractionPsfLimits* limits = NULL;
1837 GiExtractionSlice* slice = NULL;
1839 GiExtractionWorkspace* workspace;
1842 cx_assert(mz != NULL);
1843 cx_assert(mzvar != NULL);
1845 cx_assert(fibers != NULL);
1847 cx_assert(my != NULL);
1848 cx_assert(mw != NULL);
1850 cx_assert(psfdata != NULL);
1852 cx_assert(ms != NULL);
1853 cx_assert(mse != NULL);
1854 cx_assert(msm != NULL);
1855 cx_assert(msy != NULL);
1857 ny = cpl_image_get_size_x(mz);
1858 nx = cpl_image_get_size_y(mz);
1860 nfibers = cpl_table_get_nrow(fibers);
1861 nbins = CX_MIN(nx, cpl_image_get_size_y(my));
1863 cx_assert((ny == cpl_image_get_size_x(mzvar)) &&
1864 (nx == cpl_image_get_size_y(mzvar)));
1866 cx_assert(cpl_image_get_size_x(my) == cpl_image_get_size_x(mw));
1867 cx_assert(cpl_image_get_size_y(my) == cpl_image_get_size_y(mw));
1869 cx_assert(giraffe_psfdata_fibers(psfdata) ==
1870 (cxsize)cpl_image_get_size_x(my));
1871 cx_assert(giraffe_psfdata_bins(psfdata) ==
1872 (cxsize)cpl_image_get_size_y(my));
1874 cx_assert((nfibers == cpl_image_get_size_x(ms)) &&
1875 (nx == cpl_image_get_size_y(ms)));
1876 cx_assert((nfibers == cpl_image_get_size_x(mse)) &&
1877 (nx == cpl_image_get_size_y(mse)));
1878 cx_assert((nfibers == cpl_image_get_size_x(msy)) &&
1879 (nx == cpl_image_get_size_y(msy)));
1880 cx_assert((ny == cpl_image_get_size_x(msm)) &&
1881 (nx == cpl_image_get_size_y(msm)));
1883 cx_assert((mbpx == NULL) || ((ny == cpl_image_get_size_x(mbpx)) &&
1884 (nx == cpl_image_get_size_y(mbpx))));
1892 if (giraffe_psfdata_contains(psfdata,
"Center") == FALSE) {
1896 if (giraffe_psfdata_contains(psfdata,
"Width2") == TRUE) {
1897 exponent = cpl_image_get_data_const(giraffe_psfdata_get_data(psfdata,
1901 width = cpl_image_get_data_const(giraffe_psfdata_get_data(psfdata,
1909 psfmodel = giraffe_model_new(giraffe_psfdata_get_model(psfdata));
1911 if (psfmodel == NULL) {
1915 giraffe_model_set_parameter(psfmodel,
"Amplitude", 1.);
1916 giraffe_model_set_parameter(psfmodel,
"Background", 0.);
1917 giraffe_model_set_parameter(psfmodel,
"Center", 0.);
1924 ypos = cpl_matrix_new(ny, 1);
1927 giraffe_model_delete(psfmodel);
1933 _ypos = cpl_matrix_get_data(ypos);
1935 for (i = 0; i < ny; ++i) {
1945 profiles = cpl_matrix_new(nfibers + bkg_nc, ny);
1947 if (profiles == NULL) {
1948 cpl_matrix_delete(ypos);
1951 giraffe_model_delete(psfmodel);
1957 _profiles = cpl_matrix_get_data(profiles);
1960 signal = cpl_matrix_new(ny, 1);
1962 if (signal == NULL) {
1963 cpl_matrix_delete(profiles);
1966 cpl_matrix_delete(ypos);
1969 giraffe_model_delete(psfmodel);
1975 _signal = cpl_matrix_get_data(signal);
1978 variance = cpl_matrix_new(ny, 1);
1980 if (variance == NULL) {
1981 cpl_matrix_delete(signal);
1984 cpl_matrix_delete(profiles);
1987 cpl_matrix_delete(ypos);
1990 giraffe_model_delete(psfmodel);
1996 _variance = cpl_matrix_get_data(variance);
1999 mask = cpl_matrix_new(ny, 1);
2002 cpl_matrix_delete(variance);
2005 cpl_matrix_delete(signal);
2008 cpl_matrix_delete(profiles);
2011 cpl_matrix_delete(ypos);
2014 giraffe_model_delete(psfmodel);
2020 _mask = cpl_matrix_get_data(mask);
2023 weights = cpl_matrix_new(ny, 1);
2026 cpl_matrix_delete(mask);
2029 cpl_matrix_delete(variance);
2032 cpl_matrix_delete(signal);
2035 cpl_matrix_delete(profiles);
2038 cpl_matrix_delete(ypos);
2041 giraffe_model_delete(psfmodel);
2047 _weights = cpl_matrix_get_data(weights);
2055 bkg_base = giraffe_chebyshev_base1d(0., ny, bkg_nc, ypos);
2057 cpl_matrix_delete(ypos);
2060 if (bkg_base == NULL) {
2061 cpl_matrix_delete(weights);
2064 cpl_matrix_delete(mask);
2067 cpl_matrix_delete(variance);
2070 cpl_matrix_delete(signal);
2073 cpl_matrix_delete(profiles);
2076 cpl_matrix_delete(ypos);
2079 giraffe_model_delete(psfmodel);
2085 _bkg_base = cpl_matrix_get_data(bkg_base);
2087 for (i = 0; i < bkg_nc; ++i) {
2089 register cxint j = 0;
2090 register cxint offset = nfibers * ny;
2092 for (j = 0; j < ny; ++j) {
2093 _profiles[i * ny + j + offset] = _bkg_base[i * ny + j];
2100 cpl_matrix_delete(bkg_base);
2108 slice = _giraffe_extractionslice_new(nfibers, ny, bkg_nc);
2110 if (slice == NULL) {
2111 cpl_matrix_delete(weights);
2114 cpl_matrix_delete(mask);
2117 cpl_matrix_delete(variance);
2120 cpl_matrix_delete(signal);
2123 cpl_matrix_delete(profiles);
2126 cpl_matrix_delete(ypos);
2129 giraffe_model_delete(psfmodel);
2136 limits = _giraffe_extraction_psflimits_new(nfibers + bkg_nc);
2138 if (limits == NULL) {
2140 _giraffe_extractionslice_delete(slice);
2143 cpl_matrix_delete(weights);
2146 cpl_matrix_delete(mask);
2149 cpl_matrix_delete(variance);
2152 cpl_matrix_delete(signal);
2155 cpl_matrix_delete(profiles);
2158 cpl_matrix_delete(ypos);
2161 giraffe_model_delete(psfmodel);
2168 for (i = 0; i < limits->size; ++i) {
2169 limits->ymin[i] = 0;
2170 limits->ymax[i] = ny;
2178 workspace = _giraffe_optimal_workspace_new(nfibers + bkg_nc, ny);
2180 for (bin = 0; bin < nbins; ++bin) {
2182 cxbool stop = FALSE;
2188 const cxdouble* _my = cpl_image_get_data_double_const(my);
2189 const cxdouble* _mz = cpl_image_get_data_double_const(mz);
2190 const cxdouble* _mzvar = cpl_image_get_data_double_const(mzvar);
2192 cxdouble* _ms = cpl_image_get_data_double(ms);
2193 cxdouble* _mse = cpl_image_get_data_double(mse);
2194 cxdouble* _msy = cpl_image_get_data_double(msy);
2195 cxdouble* _msm = cpl_image_get_data_double(msm);
2199 GiExtractionPsfLimits* _limits = (nolimits == FALSE) ? limits : NULL;
2201 cx_assert(_mz != NULL);
2202 cx_assert(_mzvar != NULL);
2210 status = _giraffe_optimal_build_profiles(profiles, _limits, my, mw,
2211 fibers, bin, psfmodel, width,
2215 _giraffe_optimal_workspace_delete(workspace);
2218 _giraffe_extraction_psflimits_delete(limits);
2221 _giraffe_extractionslice_delete(slice);
2224 cpl_matrix_delete(weights);
2227 cpl_matrix_delete(mask);
2230 cpl_matrix_delete(variance);
2233 cpl_matrix_delete(signal);
2236 cpl_matrix_delete(profiles);
2239 cpl_matrix_delete(ypos);
2242 giraffe_model_delete(psfmodel);
2256 const cxint* _mbpx = cpl_image_get_data_int_const(mbpx);
2259 cx_assert(_mbpx != NULL);
2261 for (i = 0; i < ny; ++i) {
2263 cxbool bad = (_mbpx[bin * ny + i] & GIR_M_PIX_SET) ||
2264 (_mz[bin * ny + i] < 0.);
2266 _signal[i] = _mz[bin * ny + i];
2267 _variance[i] = _signal[i] + _mzvar[bin * ny + i];
2275 _weights[i] = _mask[i] / _variance[i];
2282 for (i = 0; i < ny; ++i) {
2284 cxbool bad = (_mz[bin * ny + i] < 0.);
2286 _signal[i] = _mz[bin * ny + i];
2287 _variance[i] = _signal[i] + _mzvar[bin * ny + i];
2295 _weights[i] = _mask[i] / _variance[i];
2307 nmin = (cxint)(fraction * ngood);
2309 while ((iter < niter) && (stop == FALSE)) {
2313 const cxdouble* _model = NULL;
2316 status = _giraffe_optimal_extract_slice(slice, profiles,
2317 signal, weights, limits, workspace);
2320 _giraffe_optimal_workspace_delete(workspace);
2323 _giraffe_extraction_psflimits_delete(limits);
2326 _giraffe_extractionslice_delete(slice);
2329 cpl_matrix_delete(weights);
2332 cpl_matrix_delete(mask);
2335 cpl_matrix_delete(variance);
2338 cpl_matrix_delete(signal);
2341 cpl_matrix_delete(profiles);
2344 cpl_matrix_delete(ypos);
2347 giraffe_model_delete(psfmodel);
2358 _model = cpl_matrix_get_data(slice->model);
2360 for (i = 0; i < ny; ++i) {
2362 if (_mask[i] > 0.) {
2365 cxdouble residual = _signal[i] - _model[i];
2368 _variance[i] = _model[i] + _mzvar[bin * ny + i];
2370 bad = (residual * residual) > (sigma * _variance[i]) ?
2379 _weights[i] = _mask[i] / _variance[i];
2385 if ((nreject == 0) || (ngood <= nmin)) {
2399 memcpy(&_ms[bin * nfibers], cpl_matrix_get_data(slice->flux),
2400 slice->nflx *
sizeof(cxdouble));
2401 memcpy(&_mse[bin * nfibers], cpl_matrix_get_data(slice->variance),
2402 slice->nflx *
sizeof(cxdouble));
2403 memcpy(&_msm[bin * ny], cpl_matrix_get_data(slice->model),
2404 slice->msize *
sizeof(cxdouble));
2406 memcpy(&_msy[bin * nfibers], &_my[bin * nfibers],
2407 nfibers *
sizeof(cxdouble));
2414 cpl_matrix_fill_window(profiles, 0., 0, 0, nfibers, ny);
2423 cpl_image_power(mse, 0.5);
2425 _giraffe_optimal_workspace_delete(workspace);
2428 _giraffe_extraction_psflimits_delete(limits);
2431 _giraffe_extractionslice_delete(slice);
2434 cpl_matrix_delete(weights);
2437 cpl_matrix_delete(mask);
2440 cpl_matrix_delete(variance);
2443 cpl_matrix_delete(signal);
2446 cpl_matrix_delete(profiles);
2449 giraffe_model_delete(psfmodel);
2483 GiTable* fibers, GiLocalization* sloc,
2484 GiImage* bpixel, GiImage* slight,
2485 GiExtractConfig* config)
2488 const cxchar *fctid =
"giraffe_extract_spectra";
2497 cxdouble bias_ron = 0.;
2498 cxdouble bias_sigma = 0.;
2499 cxdouble dark_value = 0.;
2500 cxdouble exptime = 0.;
2501 cxdouble conad = 1.;
2503 cpl_propertylist *properties;
2505 cpl_image* _image = NULL;
2506 cpl_image* _locy = NULL;
2507 cpl_image* _locw = NULL;
2508 cpl_image* _spectra = NULL;
2509 cpl_image* _error = NULL;
2510 cpl_image* _npixels = NULL;
2511 cpl_image* _centroid = NULL;
2512 cpl_image* _model = NULL;
2514 cpl_table* _fibers = NULL;
2521 if (!result || !image || !fibers || !sloc || !config) {
2522 cpl_error_set(fctid, CPL_ERROR_NULL_INPUT);
2527 if ((sloc->locy == NULL) || (sloc->locw == NULL)) {
2528 cpl_error_set(fctid, CPL_ERROR_NULL_INPUT);
2533 if (result->spectra != NULL || result->error != NULL ||
2534 result->npixels != NULL || result->centroid != NULL ||
2535 result->model != NULL) {
2536 gi_warning(
"%s: Results structure at %p is not empty! Contents "
2537 "might be lost.", fctid, result);
2543 if (_fibers == NULL) {
2544 cpl_error_set(fctid, CPL_ERROR_DATA_NOT_FOUND);
2549 if ((config->emethod == GIEXTRACT_OPTIMAL) && (sloc->psf == NULL)) {
2550 cpl_msg_error(fctid,
"Missing data: PSF profile data is required "
2551 "for optimal spectrum extraction!");
2552 cpl_error_set(fctid, CPL_ERROR_NULL_INPUT);
2560 if (properties == NULL) {
2561 cpl_error_set(fctid, CPL_ERROR_DATA_NOT_FOUND);
2566 if (!cpl_propertylist_has(properties, GIALIAS_CONAD)) {
2567 cpl_msg_error(fctid,
"Missing detector gain property (%s)! ",
2572 conad = cpl_propertylist_get_double(properties, GIALIAS_CONAD);
2576 if (!cpl_propertylist_has(properties, GIALIAS_BIASERROR)) {
2577 cpl_msg_warning(fctid,
"Missing bias error property (%s)! Setting "
2578 "bias error to 0.", GIALIAS_BIASERROR);
2582 bias_sigma = cpl_propertylist_get_double(properties, GIALIAS_BIASERROR);
2586 if (config->ron > 0.) {
2588 cpl_msg_info(fctid,
"Setting bias RMS property (%s) to %.4g ADU",
2589 GIALIAS_BIASSIGMA, config->ron);
2591 cpl_propertylist_update_double(properties, GIALIAS_BIASSIGMA,
2598 if (!cpl_propertylist_has(properties, GIALIAS_DARKVALUE)) {
2602 cpl_msg_warning(fctid,
"Missing dark value property (%s), will be "
2603 "set to 0.!", GIALIAS_DARKVALUE);
2604 cpl_propertylist_append_double(properties, GIALIAS_DARKVALUE,
2609 dark_value = cpl_propertylist_get_double(properties,
2614 if (!cpl_propertylist_has(properties, GIALIAS_EXPTIME)) {
2615 cpl_msg_error(fctid,
"Missing exposure time property (%s)!",
2620 exptime = cpl_propertylist_get_double(properties, GIALIAS_EXPTIME);
2624 if (cpl_propertylist_has(properties, GIALIAS_DATANCOM)) {
2625 nframes = cpl_propertylist_get_int(properties, GIALIAS_DATANCOM);
2637 bias_sigma *= conad;
2638 dark_value *= conad;
2658 ny = cpl_image_get_size_x(_image);
2659 nx = cpl_image_get_size_y(_locw);
2660 ns = cpl_table_get_nrow(_fibers);
2663 switch (config->emethod) {
2667 cxint xsize = cpl_image_get_size_x(_image);
2668 cxint ysize = cpl_image_get_size_y(_image);
2670 cxdouble ron_variance = bias_ron * bias_ron;
2671 cxdouble bias_variance = bias_sigma * bias_sigma;
2672 cxdouble dark_variance = dark_value * exptime;
2674 cpl_image* bpixmap = NULL;
2675 cpl_image* variance = NULL;
2682 result->model = NULL;
2689 if (bpixel != NULL) {
2693 if (cpl_image_get_size_x(bpixmap) != xsize ||
2694 cpl_image_get_size_y(bpixmap) != ysize) {
2696 cxbool crop = FALSE;
2698 cpl_propertylist *p =
2701 GiWindow w = {1, 1, 0, 0};
2704 w.x1 = cpl_image_get_size_x(bpixmap);
2705 w.y1 = cpl_image_get_size_y(bpixmap);
2707 if (cpl_propertylist_has(p, GIALIAS_PRSCX)) {
2708 w.x0 += cpl_propertylist_get_int(p, GIALIAS_PRSCX);
2712 if (cpl_propertylist_has(p, GIALIAS_OVSCX)) {
2713 w.x1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCX);
2717 if (cpl_propertylist_has(p, GIALIAS_PRSCY)) {
2718 w.y0 += cpl_propertylist_get_int(p, GIALIAS_PRSCY);
2722 if (cpl_propertylist_has(p, GIALIAS_OVSCY)) {
2723 w.y1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCY);
2727 if ((w.x1 - w.x0 + 1) != xsize ||
2728 (w.y1 - w.y0 + 1) != ysize) {
2729 cpl_msg_error(fctid,
"Invalid bad pixel map! Image "
2730 "sizes do not match!");
2733 result->spectra = NULL;
2736 result->error = NULL;
2739 result->npixels = NULL;
2742 result->centroid = NULL;
2745 result->model = NULL;
2747 cpl_image_delete(_image);
2754 bpixmap = cpl_image_extract(bpixmap, w.x0, w.y0,
2762 if (slight != NULL) {
2763 cpl_msg_warning(fctid,
"Scattered light model will be "
2764 "ignored for extraction method `SUM'");
2767 variance = cpl_image_abs_create(_image);
2775 cpl_image_add_scalar(variance, nframes * (ron_variance + nframes *
2776 (bias_variance + dark_variance)));
2778 status = _giraffe_extract_summation(_image, variance, _fibers,
2779 _locy, _locw, bpixmap,
2780 _spectra, _error, _npixels,
2783 cpl_image_delete(variance);
2785 cpl_image_delete(bpixmap);
2793 case GIEXTRACT_OPTIMAL:
2796 cxint xsize = cpl_image_get_size_x(_image);
2797 cxint ysize = cpl_image_get_size_y(_image);
2800 cxdouble ron_variance = bias_ron * bias_ron;
2801 cxdouble bias_variance = bias_sigma * bias_sigma;
2802 cxdouble dark_variance = dark_value * exptime;
2804 cpl_image* variance = NULL;
2805 cpl_image* bpixmap = NULL;
2807 GiExtractOptimalConfig setup;
2812 result->npixels = NULL;
2821 setup.clip.iterations = config->psf.iterations;
2822 setup.clip.level = config->psf.sigma;
2823 setup.clip.fraction = config->optimal.fraction;
2824 setup.limits = config->optimal.wfactor < 0. ? FALSE : TRUE;
2825 setup.ewidth = CX_MAX(1., fabs(config->optimal.wfactor));
2826 setup.bkgorder = config->optimal.bkgorder;
2827 setup.exptime = exptime;
2828 setup.ron = bias_sigma;
2829 setup.dark = dark_value;
2832 if (bpixel != NULL) {
2836 if (cpl_image_get_size_x(bpixmap) != xsize ||
2837 cpl_image_get_size_y(bpixmap) != ysize) {
2839 cxbool crop = FALSE;
2841 cpl_propertylist *p =
2844 GiWindow w = {1, 1, 0, 0};
2847 w.x1 = cpl_image_get_size_x(bpixmap);
2848 w.y1 = cpl_image_get_size_y(bpixmap);
2850 if (cpl_propertylist_has(p, GIALIAS_PRSCX)) {
2851 w.x0 += cpl_propertylist_get_int(p, GIALIAS_PRSCX);
2855 if (cpl_propertylist_has(p, GIALIAS_OVSCX)) {
2856 w.x1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCX);
2860 if (cpl_propertylist_has(p, GIALIAS_PRSCY)) {
2861 w.y0 += cpl_propertylist_get_int(p, GIALIAS_PRSCY);
2865 if (cpl_propertylist_has(p, GIALIAS_OVSCY)) {
2866 w.y1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCY);
2870 if ((w.x1 - w.x0 + 1) != xsize ||
2871 (w.y1 - w.y0 + 1) != ysize) {
2873 cpl_msg_error(fctid,
"Invalid bad pixel map! "
2874 "Image sizes do not match!");
2877 result->spectra = NULL;
2880 result->error = NULL;
2883 result->npixels = NULL;
2886 result->centroid = NULL;
2889 result->model = NULL;
2891 cpl_image_delete(_image);
2899 bpixmap = cpl_image_extract(bpixmap, w.x0, w.y0,
2907 variance = cpl_image_new(xsize, ysize, CPL_TYPE_DOUBLE);
2915 v0 = nframes * (ron_variance + nframes *
2916 (bias_variance + dark_variance));
2925 if (slight != NULL) {
2927 register cxsize i = 0;
2928 register cxsize npixels = xsize * ysize;
2930 const cxdouble* _slight =
2933 cxdouble* _variance = cpl_image_get_data_double(variance);
2935 for (i = 0; i < npixels; i++) {
2936 _variance[i] = v0 + fabs(_slight[i]) * conad * nframes;
2942 register cxsize i = 0;
2943 register cxsize npixels = xsize * ysize;
2945 cxdouble* _variance = cpl_image_get_data_double(variance);
2947 for (i = 0; i < npixels; i++) {
2954 status = _giraffe_extract_optimal(_image, variance, _fibers,
2955 _locy, _locw, sloc->psf,
2956 bpixmap, _spectra, _error,
2957 _model, _centroid, &setup);
2959 cpl_image_delete(variance);
2963 cpl_image_delete(bpixmap);
2971 case GIEXTRACT_HORNE:
2974 cxint xsize = cpl_image_get_size_x(_image);
2975 cxint ysize = cpl_image_get_size_y(_image);
2978 cxdouble ron_variance = bias_ron * bias_ron;
2979 cxdouble bias_variance = bias_sigma * bias_sigma;
2980 cxdouble dark_variance = dark_value * exptime;
2982 cpl_image* variance = NULL;
2983 cpl_image* bpixmap = NULL;
2985 GiExtractHorneConfig setup;
2992 result->model = NULL;
2999 setup.clip.iterations = config->psf.iterations;
3000 setup.clip.level = config->psf.sigma;
3001 setup.clip.fraction = config->horne.mingood;
3002 setup.ewidth = config->horne.ewidth;
3003 setup.exptime = exptime;
3004 setup.ron = bias_sigma;
3005 setup.dark = dark_value;
3007 if (bpixel != NULL) {
3011 if (cpl_image_get_size_x(bpixmap) != xsize ||
3012 cpl_image_get_size_y(bpixmap) != ysize) {
3014 cxbool crop = FALSE;
3016 cpl_propertylist *p =
3019 GiWindow w = {1, 1, 0, 0};
3022 w.x1 = cpl_image_get_size_x(bpixmap);
3023 w.y1 = cpl_image_get_size_y(bpixmap);
3025 if (cpl_propertylist_has(p, GIALIAS_PRSCX)) {
3026 w.x0 += cpl_propertylist_get_int(p, GIALIAS_PRSCX);
3030 if (cpl_propertylist_has(p, GIALIAS_OVSCX)) {
3031 w.x1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCX);
3035 if (cpl_propertylist_has(p, GIALIAS_PRSCY)) {
3036 w.y0 += cpl_propertylist_get_int(p, GIALIAS_PRSCY);
3040 if (cpl_propertylist_has(p, GIALIAS_OVSCY)) {
3041 w.y1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCY);
3045 if ((w.x1 - w.x0 + 1) != xsize ||
3046 (w.y1 - w.y0 + 1) != ysize) {
3048 cpl_msg_error(fctid,
"Invalid bad pixel map! "
3049 "Image sizes do not match!");
3052 result->spectra = NULL;
3055 result->error = NULL;
3058 result->npixels = NULL;
3061 result->centroid = NULL;
3064 result->model = NULL;
3066 cpl_image_delete(_image);
3074 bpixmap = cpl_image_extract(bpixmap, w.x0, w.y0,
3082 variance = cpl_image_new(xsize, ysize, CPL_TYPE_DOUBLE);
3090 v0 = nframes * (ron_variance + nframes *
3091 (bias_variance + dark_variance));
3101 if (slight != NULL) {
3103 register cxsize i = 0;
3104 register cxsize npixels = xsize * ysize;
3106 const cxdouble* _slight =
3109 cxdouble* _variance = cpl_image_get_data_double(variance);
3111 for (i = 0; i < npixels; i++) {
3112 _variance[i] = v0 + fabs(_slight[i]) * nframes * conad;
3118 register cxsize i = 0;
3119 register cxsize npixels = xsize * ysize;
3121 cxdouble* _variance = cpl_image_get_data_double(variance);
3123 for (i = 0; i < npixels; i++) {
3130 status = _giraffe_extract_horne(_image, variance, _fibers,
3131 _locy, _locw, sloc->psf,
3132 bpixmap, _spectra, _error,
3133 _npixels, _centroid, &setup);
3135 cpl_image_delete(variance);
3139 cpl_image_delete(bpixmap);
3148 gi_message(
"%s: Method %d selected for spectrum extraction.",
3149 fctid, config->emethod);
3150 cpl_msg_error(fctid,
"Invalid extraction method!");
3156 cpl_image_delete(_image);
3162 result->spectra = NULL;
3165 result->error = NULL;
3168 result->npixels = NULL;
3171 result->centroid = NULL;
3174 result->model = NULL;
3176 cpl_msg_error(fctid,
"Spectrum extraction (method %d) failed!",
3179 cpl_image_delete(_image);
3197 if (result->spectra) {
3202 if (result->model) {
3207 if (result->error) {
3232 cpl_propertylist_set_int(properties, GIALIAS_NAXIS1,
3233 cpl_image_get_size_x(_spectra));
3234 cpl_propertylist_set_int(properties, GIALIAS_NAXIS2,
3235 cpl_image_get_size_y(_spectra));
3237 cpl_propertylist_set_int(properties, GIALIAS_BITPIX, -32);
3238 cpl_propertylist_set_double(properties, GIALIAS_BZERO, 0.);
3239 cpl_propertylist_set_double(properties, GIALIAS_BSCALE, 1.);
3241 cpl_propertylist_update_int(properties, GIALIAS_NFIBERS,
3242 cpl_image_get_size_x(_spectra));
3244 cpl_propertylist_append_int(properties, GIALIAS_EXT_NX,
3245 cpl_image_get_size_y(_spectra));
3246 cpl_propertylist_append_int(properties, GIALIAS_EXT_NS,
3247 cpl_image_get_size_x(_spectra));
3249 switch (config->emethod) {
3251 cpl_propertylist_append_string(properties, GIALIAS_EXT_METHOD,
3253 cpl_propertylist_set_comment(properties, GIALIAS_EXT_METHOD,
3254 "Spectrum extraction method");
3257 case GIEXTRACT_HORNE:
3260 cpl_propertylist_append_string(properties, GIALIAS_EXT_METHOD,
3262 cpl_propertylist_set_comment(properties, GIALIAS_EXT_METHOD,
3263 "Spectrum extraction method");
3265 cpl_propertylist_append_string(properties, GIALIAS_EXTPSF_MODEL,
3267 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_MODEL,
3269 cpl_propertylist_append_double(properties, GIALIAS_EXTPSF_SIGMA,
3271 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_SIGMA,
3272 "PSF fit sigma clipping threshold");
3273 cpl_propertylist_append_int(properties, GIALIAS_EXTPSF_NITER,
3274 config->psf.iterations);
3275 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_NITER,
3276 "PSF fit maximum number of "
3279 cpl_propertylist_append_int(properties, GIALIAS_EXTHRN_EWIDTH,
3280 config->horne.ewidth);
3281 cpl_propertylist_set_comment(properties, GIALIAS_EXTHRN_EWIDTH,
3282 "Number of extra pixels used");
3283 cpl_propertylist_append_int(properties, GIALIAS_EXTHRN_MINGOOD,
3284 config->horne.mingood);
3285 cpl_propertylist_set_comment(properties, GIALIAS_EXTHRN_MINGOOD,
3286 "Minimum number of pixels to keep");
3292 case GIEXTRACT_OPTIMAL:
3293 cpl_propertylist_append_string(properties, GIALIAS_EXT_METHOD,
3295 cpl_propertylist_set_comment(properties, GIALIAS_EXT_METHOD,
3296 "Spectrum extraction method");
3298 cpl_propertylist_append_string(properties, GIALIAS_EXTPSF_MODEL,
3300 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_MODEL,
3302 cpl_propertylist_append_double(properties, GIALIAS_EXTPSF_SIGMA,
3304 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_SIGMA,
3305 "PSF fit sigma clipping threshold");
3306 cpl_propertylist_append_int(properties, GIALIAS_EXTPSF_NITER,
3307 config->psf.iterations);
3308 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_NITER,
3309 "PSF fit maximum number of "
3312 cpl_propertylist_append_double(properties, GIALIAS_EXTOPT_FRACTION,
3313 config->optimal.fraction);
3314 cpl_propertylist_set_comment(properties, GIALIAS_EXTOPT_FRACTION,
3315 "Minimum fraction of pixels used.");
3316 cpl_propertylist_append_double(properties, GIALIAS_EXTOPT_WFACTOR,
3317 config->optimal.wfactor);
3318 cpl_propertylist_set_comment(properties, GIALIAS_EXTOPT_WFACTOR,
3319 "Multiple of the fiber PSF half "
3320 "width used for spectrum "
3322 cpl_propertylist_append_int(properties, GIALIAS_EXTOPT_BGORDER,
3323 config->optimal.bkgorder);
3324 cpl_propertylist_set_comment(properties, GIALIAS_EXTOPT_BGORDER,
3325 "Order of the background polynomial "
3326 "model along the spatial direction.");
3334 cpl_propertylist_update_string(properties, GIALIAS_GIRFTYPE,
"EXTSP");
3335 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3336 "Extracted spectra");
3346 cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE,
"EXTERRS");
3347 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3348 "Extracted spectra errors");
3358 cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE,
"EXTYCEN");
3359 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3360 "Extracted spectra centroids");
3367 if (result->npixels != NULL) {
3371 cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE,
"EXTNPIX");
3372 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3373 "Extracted spectra npixels");
3381 if (result->model != NULL) {
3385 cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE,
"EXTMODEL");
3386 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3387 "Model spectra used for extraction");
3412 GiExtractConfig* config = NULL;
3419 config = cx_calloc(1,
sizeof *config);
3421 p = cpl_parameterlist_find(list,
"giraffe.extraction.method");
3422 s = cpl_parameter_get_string(p);
3423 if (!strcmp(s,
"OPTIMAL")) {
3424 config->emethod = GIEXTRACT_OPTIMAL;
3426 else if (!strcmp(s,
"HORNE")) {
3427 config->emethod = GIEXTRACT_HORNE;
3430 config->emethod = GIEXTRACT_SUM;
3433 p = cpl_parameterlist_find(list,
"giraffe.extraction.ron");
3434 config->ron = cpl_parameter_get_double(p);
3436 p = cpl_parameterlist_find(list,
"giraffe.extraction.psf.model");
3437 config->psf.model = cx_strdup(cpl_parameter_get_string(p));
3439 p = cpl_parameterlist_find(list,
"giraffe.extraction.psf.sigma");
3440 config->psf.sigma = cpl_parameter_get_double(p);
3442 p = cpl_parameterlist_find(list,
"giraffe.extraction.psf.iterations");
3443 config->psf.iterations = cpl_parameter_get_int(p);
3446 p = cpl_parameterlist_find(list,
"giraffe.extraction.horne.extrawidth");
3447 config->horne.ewidth = cpl_parameter_get_int(p);
3449 p = cpl_parameterlist_find(list,
"giraffe.extraction.horne.mingood");
3450 config->horne.mingood = cpl_parameter_get_double(p);
3453 p = cpl_parameterlist_find(list,
"giraffe.extraction.optimal.fraction");
3454 config->optimal.fraction = cpl_parameter_get_double(p);
3456 p = cpl_parameterlist_find(list,
"giraffe.extraction.optimal.wfactor");
3457 config->optimal.wfactor = cpl_parameter_get_double(p);
3459 p = cpl_parameterlist_find(list,
"giraffe.extraction.optimal.bkgorder");
3460 config->optimal.bkgorder = cpl_parameter_get_int(p);
3485 if (config->psf.model) {
3486 cx_free(config->psf.model);
3513 cpl_parameter* p = NULL;
3520 p = cpl_parameter_new_enum(
"giraffe.extraction.method",
3522 "Extraction method: 'SUM', 'HORNE' or "
3524 "giraffe.extraction",
3525 "SUM", 3,
"SUM",
"OPTIMAL",
"HORNE");
3526 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-method");
3527 cpl_parameterlist_append(list, p);
3530 p = cpl_parameter_new_value(
"giraffe.extraction.ron",
3532 "New bias sigma (RON) value for "
3535 "giraffe.extraction",
3537 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-ron");
3538 cpl_parameterlist_append(list, p);
3541 p = cpl_parameter_new_enum(
"giraffe.extraction.psf.model",
3543 "PSF profile model: `psfexp', `psfexp2'",
3544 "giraffe.extraction.psf",
3545 "psfexp2", 2,
"psfexp",
"psfexp2");
3546 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-psfmodel");
3547 cpl_parameterlist_append(list, p);
3550 p = cpl_parameter_new_value(
"giraffe.extraction.psf.sigma",
3552 "Sigma clippging threshold used for "
3553 "rejecting data points during PSF fitting "
3554 "(Horne's sigma). It is used to reject bad "
3555 "pixels and cosmics.",
3556 "giraffe.extraction.psf",
3558 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-psfsigma");
3559 cpl_parameterlist_append(list, p);
3562 p = cpl_parameter_new_value(
"giraffe.extraction.psf.iterations",
3564 "Maximum number of iterations used for "
3565 "fitting the PSF profile.",
3566 "giraffe.extraction.psf",
3568 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-psfniter");
3569 cpl_parameterlist_append(list, p);
3572 p = cpl_parameter_new_value(
"giraffe.extraction.horne.extrawidth",
3574 "Horne extraction method: Number of "
3575 "extra pixels added to the fiber "
3577 "giraffe.extraction.horne",
3579 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-hewidth");
3580 cpl_parameterlist_append(list, p);
3583 p = cpl_parameter_new_value(
"giraffe.extraction.horne.mingood",
3585 "Horne extraction method: Minimum number of "
3586 "points used for the profile fit. It sets "
3587 "the lower limit of data points for the "
3589 "giraffe.extraction.horne",
3591 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-hmingood");
3592 cpl_parameterlist_append(list, p);
3595 p = cpl_parameter_new_range(
"giraffe.extraction.optimal.fraction",
3597 "Optimal extraction method: Minimum fraction "
3598 "of the data points used for fitting the "
3599 "fiber profiles. It sets the lower limit "
3600 "for the pixel rejection.",
3601 "giraffe.extraction.optimal",
3603 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-omfrac");
3604 cpl_parameterlist_append(list, p);
3607 p = cpl_parameter_new_value(
"giraffe.extraction.optimal.wfactor",
3609 "Optimal extraction method: Factor by which "
3610 "the fiber PSF half width is multiplied. "
3611 "Adjacent spectra within this area are "
3612 "assumed to affect the spectrum being "
3614 "giraffe.extraction.optimal",
3616 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-owfactor");
3617 cpl_parameterlist_append(list, p);
3620 p = cpl_parameter_new_value(
"giraffe.extraction.optimal.bkgorder",
3622 "Optimal extraction method: Order of the "
3623 "polynomial background model, which is "
3624 "fitted for each wavelength bin along the "
3625 "spatial direction.",
3626 "giraffe.extraction.optimal",
3628 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"extr-obkgorder");
3629 cpl_parameterlist_append(list, p);