GIRAFFE Pipeline Reference Manual

gilocalize.c
1/*
2 * This file is part of the GIRAFFE Pipeline
3 * Copyright (C) 2002-2019 European Southern Observatory
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21# include <config.h>
22#endif
23
24#include <string.h>
25#include <math.h>
26
27#include <cxstring.h>
28#include <cxmemory.h>
29
30#include <cpl_image.h>
31#include <cpl_vector.h>
32#include <cpl_matrix.h>
33#include <cpl_mask.h>
34#include <cpl_parameterlist.h>
35#include <cpl_msg.h>
36
37#include "gimacros.h"
38#include "gierror.h"
39#include "gialias.h"
40#include "giarray.h"
41#include "giimage.h"
42#include "gitable.h"
43#include "gimatrix.h"
44#include "giarray.h"
45#include "gimask.h"
46#include "gimath.h"
47#include "gimessages.h"
48#include "giutils.h"
49#include "gilocalize.h"
50#include "gidebug.h"
51
52
53
62/*
63 * Main task identifier. Used for terminal output from internal functions.
64 */
65
66static const cxchar* _task = "giraffe_localize_spectra";
67
68
69/*
70 * Method used to compute the fiber centroid position
71 */
72
73enum GiLocalizeMethod
74{
75 GILOCALIZE_HALF_WIDTH,
76 GILOCALIZE_BARYCENTER
77};
78
79typedef enum GiLocalizeMethod GiLocalizeMethod;
80
81
82/*
83 * Thresholding policy used to detect valid spectrum pixels
84 */
85
86enum GiThresholdMethod
87{
88 GILOCALIZE_THRESHOLD_GLOBAL,
89 GILOCALIZE_THRESHOLD_LOCAL,
90 GILOCALIZE_THRESHOLD_ROW
91};
92
93typedef enum GiThresholdMethod GiThresholdMethod;
94
95
96
97/*
98 * @brief
99 * Check whether a pixel in a detection mask belongs to a spectrum.
100 *
101 * @param pixels The pixel buffer.
102 * @param xsize The size of the pixel buffer along x.
103 * @param ysize The size of the pixel buffer along y.
104 * @param xpos x-position of the pixel to check.
105 * @param ypos y-position of the pixel to check.
106 * @param xwidth Half width of the pixel neighbourhood along x.
107 * @param ywidth Half width of the pixel neighbourhood along y.
108 * @param count The number of required, non-zero mask pixels.
109 *
110 * @return The function returns 1 if the pixel at (@em xpos, @em ypos) is
111 * found to be valid, or 0 otherwise.
112 *
113 * The function checks whether the pixel at position (@em xpos, @em ypos) in
114 * the detection mask's pixel buffer @em pixels belongs to a spectrum. It
115 * expects in input a pixel buffer of a detection mask, i.e. the pixel values
116 * must be non-zero only at pixel positions associated to a positive
117 * detection.
118 *
119 * A pixel is considered to be valid if, at least, @em count non-zero pixels
120 * are found in the neighborhood of the pixel position (@em xpos, @em ypos).
121 * The neighborhood is specified by @em xsize and @em ysize the number of
122 * pixels to be checked on both sides of the pixel along the x and y axis.
123 * The pixel row given by @em ypos which contains the pixel to check is
124 * not considered when the pixel neighbourhood is checked. Assuming that
125 * the spectra extend along the y-axis only the neighbours along the
126 * dispersion axis are taken into account.
127 */
128
129inline static cxbool
130_giraffe_validate_pixel(cxint *pixels, cxint xsize, cxint ysize,
131 cxint xpos, cxint ypos, cxint xwidth, cxint ywidth,
132 cxsize count)
133{
134
135 cxint i;
136 cxint xstart = xpos - xwidth;
137 cxint ystart = ypos - ywidth;
138 cxint xend = xpos + xwidth;
139 cxint yend = ypos + ywidth;
140
141 cxsize _count = 0;
142
143
144
145 /*
146 * Clip start and end positions to pixel buffer boundaries
147 */
148
149 xstart = CX_MAX(0, xstart);
150 ystart = CX_MAX(0, ystart);
151
152 xend = CX_MIN(xsize - 1, xend);
153 yend = CX_MIN(ysize - 1, yend);
154
155 xwidth = CX_MAX(xwidth,1 );
156 ywidth = CX_MAX(ywidth,1 );
157
158
159 /*
160 * Search for count non-zero pixel values in the pixel region
161 * defined by the rectangle (xstart, ystart, xend, yend).
162 */
163
164 for (i = ystart; i <= yend; i++) {
165
166 cxint j;
167 cxint row;
168
169
170 /*
171 * Skip the pixel row containing the pixel to check. Since the pixel
172 * should be checked whether it belongs to a spectrum (extending
173 * along the y-axis) we only check the adjacent pixel rows on
174 * both sides.
175 */
176
177 if (i == ypos) {
178 continue;
179 }
180
181 row = i * xsize;
182
183 for (j = xstart; j <= xend; j++) {
184 if (pixels[row + j]) {
185 ++_count;
186 }
187
188 if (_count >= count) {
189 return 1;
190 }
191 }
192
193 }
194
195 return 0;
196
197}
198
199
200/*
201 * @brief
202 * Polynomial fit of raw spectrum region border.
203 *
204 * @param mborder Y of detected borders
205 * @param mbase Full Chebyshev base
206 * @param mxok Good abcissa
207 * @param nspectra Spectrum number
208 * @param sigma Sigma clipping: sigma threshold level
209 * @param niter Sigma clipping: number of iterations
210 * @param mfrac Sigma clipping: minimum fraction of points accepted/total
211 * @param mcoeff Computed Chebyshev coefficients
212 *
213 * @return Matrix with the polynomial fit of @em mborder.
214 *
215 * Computes Chebyshev polynomial fit of @em mborder[:,nspectra].
216 * The order of the polynomial fit is given by the Chebyshev base
217 * @em mbase 1st dimension. The matrices @em mxtmp and @em mcoeff
218 * are pre-allocated. The returned matrix @em mfit must be freed
219 * using @b cpl_matrix_delete().
220 *
221 * @code
222 * mfit = _giraffe_fit_border(mborder, mbase, mxtmp, mxok, nspectra,
223 * sigma, niter, mfrac, mcoeff);
224 * @endcode
225 */
226
227inline static cpl_matrix*
228_giraffe_fit_border(cpl_matrix* mborder, cpl_matrix* mbase,
229 cpl_matrix* mxok, cxint nspectra, cxdouble sigma,
230 cxint niter, cxdouble mfrac, cpl_matrix* mcoeff)
231{
232
233 const cxchar* const fctid = "_giraffe_fit_border";
234
235 register cxint x = 0;
236 register cxint naccept = 0;
237 register cxint ntotal = 0;
238 register cxint iteration = 0;
239 register cxint nx = cpl_matrix_get_ncol(mbase);
240 register cxint yorder = cpl_matrix_get_nrow(mbase);
241 register cxint nxok = cpl_matrix_get_nrow(mxok);
242
243 register cxdouble ratio = 1.0;
244
245 cpl_matrix* mtmp = NULL;
246 cpl_matrix* yraw = NULL;
247 cpl_matrix* ydiff = NULL;
248 cpl_matrix* mfit = NULL;
249 cpl_matrix* coeffs = NULL;
250
251
252
253 if (nxok < yorder) {
254 cpl_error_set(fctid, CPL_ERROR_INCOMPATIBLE_INPUT);
255
256 GIDEBUG(gi_warning("%s: not enough points mxok[%d] for %d order fit",
257 fctid, nxok, yorder));
258
259 return NULL;
260 }
261
262
263 /*
264 * Initialize X,Y to be fit
265 */
266
267 yraw = cpl_matrix_new(1, nxok);
268 ydiff = cpl_matrix_new(nxok, 1);
269
270 mtmp = cpl_matrix_duplicate(mxok);
271
272 /*
273 * For each good x bin
274 */
275
276 for (x = 0; x < nxok; x++) {
277 cxdouble data = cpl_matrix_get(mborder, x, nspectra);
278 cpl_matrix_set(yraw, 0, x, data);
279 }
280
281
282 /*
283 * Here comes the sigma clipping
284 */
285
286 ntotal = nxok;
287 naccept = ntotal;
288
289 while (naccept > 0 && iteration < niter && ratio > mfrac) {
290
291 register cxint k = 0;
292 register cxint l = 0;
293
294 register cxdouble ysigma = 0.;
295
296 cpl_matrix* rawbase = giraffe_chebyshev_base1d(0., nx, yorder, mtmp);
297 cx_assert(rawbase != NULL);
298
299 if (coeffs != NULL) {
300 cpl_matrix_delete(coeffs);
301 }
302
303 coeffs = giraffe_matrix_leastsq(rawbase, yraw);
304 if (coeffs == NULL) {
305 gi_warning("%s: error in giraffe_matrix_leastsq(), spectrum %d",
306 fctid, nspectra);
307 break;
308 }
309
310 cpl_matrix_delete(rawbase);
311 rawbase = NULL;
312
313 if (mfit != NULL) {
314 cpl_matrix_delete(mfit);
315 }
316
317 mfit = cpl_matrix_product_create(coeffs, mbase);
318
319 for (x = 0; x < cpl_matrix_get_nrow(ydiff); x++) {
320
321 cxint xok = (cxint) cpl_matrix_get(mtmp, x, 0);
322
323 cxdouble diff =
324 cpl_matrix_get(yraw, 0, x) - cpl_matrix_get(mfit, 0, xok);
325
326
327 cpl_matrix_set(ydiff, x , 0, diff);
328
329 }
330
331 ysigma = sigma * giraffe_matrix_sigma_mean(ydiff, 0.);
332
333
334 /*
335 * Reset sizes
336 */
337
338 k = 0;
339 for (l = 0; l < cpl_matrix_get_nrow(ydiff); l++) {
340
341 if (fabs(cpl_matrix_get(ydiff, l, 0)) <= ysigma) {
342
343 cxint xok = cpl_matrix_get(mtmp, l, 0);
344 cxdouble data = cpl_matrix_get(yraw, 0, l);
345
346 cpl_matrix_set(mtmp, k, 0, xok);
347 cpl_matrix_set(yraw, 0, k, data);
348
349 ++k;
350 }
351
352 }
353
354
355 /*
356 * No new points rejected, no more iterations
357 */
358
359 if (k == naccept) {
360 break;
361 }
362
363
364 /*
365 * Merry-go-round once more
366 */
367
368 naccept = k;
369 ratio = (cxdouble) naccept / (cxdouble) ntotal;
370
371 GIDEBUG(gi_message("Iteration %d: Sigma %f, accepted bins: %d, "
372 "rejected %d\n", iteration, ysigma, naccept,
373 ntotal - naccept));
374
375 /*
376 * Extract the new clipped matrices
377 */
378
379 cpl_matrix_resize(mtmp, 0,
380 naccept - cpl_matrix_get_nrow(mtmp), 0, 0);
381 cpl_matrix_resize(yraw, 0,
382 0, 0, naccept - cpl_matrix_get_ncol(yraw));
383 cpl_matrix_resize(ydiff, 0,
384 naccept - cpl_matrix_get_nrow(ydiff), 0, 0);
385
386 iteration++;
387 }
388
389 if (coeffs != NULL) {
390 register cxint l;
391
392 for (l = 0; l < cpl_matrix_get_nrow(mcoeff); l++) {
393 cpl_matrix_set(mcoeff, l, 0, cpl_matrix_get(coeffs, 0, l));
394 }
395 }
396
397
398 /*
399 * Cleanup
400 */
401
402 cpl_matrix_delete(coeffs);
403 cpl_matrix_delete(ydiff);
404 cpl_matrix_delete(yraw);
405 cpl_matrix_delete(mtmp);
406
407 return mfit;
408
409}
410
411
412inline static cpl_image*
413_giraffe_filter_gauss1d(const cpl_image* image, cxint radius, cxdouble width)
414{
415
416 cxdouble w2 = width * width;
417
418 cxint i = 0;
419
420 cpl_matrix* kernel = cpl_matrix_new(1, 2 * radius + 1);
421
422 cpl_image* fimage = NULL;
423
424
425 if (kernel == NULL) {
426 return NULL;
427 }
428
429 for (i = -radius; i <= radius; ++i) {
430 cxdouble x2 = i * i;
431 cxdouble y = exp(-x2 / (2. * w2));
432
433 cpl_matrix_set(kernel, 0, i + radius, y);
434 }
435
436
437 fimage = cpl_image_new(cpl_image_get_size_x(image),
438 cpl_image_get_size_y(image),
439 cpl_image_get_type(image));
440
441 if (fimage == NULL) {
442 cpl_matrix_delete(kernel);
443 return NULL;
444 }
445
446 cpl_image_filter(fimage, image, kernel, CPL_FILTER_LINEAR,
447 CPL_BORDER_FILTER);
448 cpl_matrix_delete(kernel);
449
450 return fimage;
451
452}
453
454
455inline static cpl_image*
456_giraffe_filter_sobel(const cpl_image* image, cxbool vertical)
457{
458 cpl_matrix* kernel = cpl_matrix_new(3, 3);
459
460 cpl_image* fimage = NULL;
461
462
463 if (kernel == NULL) {
464 return NULL;
465 }
466
467 if (vertical) {
468
469#if 1
470 cpl_matrix_set(kernel, 0, 0, -1);
471 cpl_matrix_set(kernel, 1, 0, -2);
472 cpl_matrix_set(kernel, 2, 0, -1);
473
474 cpl_matrix_set(kernel, 0, 2, 1);
475 cpl_matrix_set(kernel, 1, 2, 2);
476 cpl_matrix_set(kernel, 2, 2, 1);
477#else
478 cpl_matrix_set(kernel, 0, 0, 0);
479 cpl_matrix_set(kernel, 1, 0, -0.5);
480 cpl_matrix_set(kernel, 2, 0, 0);
481
482 cpl_matrix_set(kernel, 0, 2, 0);
483 cpl_matrix_set(kernel, 1, 2, 0.5);
484 cpl_matrix_set(kernel, 2, 2, 0);
485#endif
486
487 }
488 else {
489 cpl_matrix_set(kernel, 0, 0, 1);
490 cpl_matrix_set(kernel, 0, 1, 2);
491 cpl_matrix_set(kernel, 0, 2, 1);
492
493 cpl_matrix_set(kernel, 2, 0, -1);
494 cpl_matrix_set(kernel, 2, 1, -2);
495 cpl_matrix_set(kernel, 2, 2, -1);
496 }
497
498
499 fimage = cpl_image_new(cpl_image_get_size_x(image),
500 cpl_image_get_size_y(image),
501 cpl_image_get_type(image));
502
503 if (fimage == NULL) {
504 cpl_matrix_delete(kernel);
505 return NULL;
506 }
507
508 cpl_image_filter(fimage, image, kernel, CPL_FILTER_LINEAR,
509 CPL_BORDER_FILTER);
510 cpl_matrix_delete(kernel);
511
512 return fimage;
513
514}
515
516
517inline static cxint
518_giraffe_build_edge_mask(cpl_image* raw, cpl_image* bpixel, cxint nspectra,
519 cxdouble noise, GiMaskParameters* config,
520 cxint* ndetect, cpl_matrix* mxok, cpl_matrix* myup,
521 cpl_matrix* mylo)
522{
523
524 const cxint margin = 5;
525
526 cxint m = 0;
527 cxint itrace = 0;
528 cxint ispectra = 0;
529 cxint mmax = 0;
530 cxint smax = 0;
531 cxint naccepted = 0;
532 cxint nrows = cpl_image_get_size_y(raw);
533 cxint ncols = cpl_image_get_size_x(raw);
534
535 cxint* flags = NULL;
536
537 cxdouble* buffer = NULL;
538
539 cpl_mask* kernel = NULL;
540
541 cpl_image* fraw = NULL;
542 cpl_image* sraw = NULL;
543 cpl_image* vertical1 = NULL;
544 cpl_image* vertical2 = NULL;
545 cpl_image* center = NULL;
546
547
548 *ndetect = 0;
549
550 (void) bpixel; /* Not used. */
551
552
553 /*
554 * Simple cosmics removal. Median filter image along the dispersion
555 * direction.
556 */
557
558 kernel = cpl_mask_new(1, 15);
559
560 if (kernel != NULL) {
561
562 cpl_mask_not(kernel);
563
564 fraw = cpl_image_new(ncols, nrows, cpl_image_get_type(raw));
565
566 if (fraw == NULL) {
567 cpl_mask_delete(kernel);
568 kernel = NULL;
569
570 return -3;
571 }
572
573 cpl_image_filter_mask(fraw, raw, kernel, CPL_FILTER_MEDIAN,
574 CPL_BORDER_FILTER);
575
576 }
577
578 cpl_mask_delete(kernel);
579 kernel = NULL;
580
581
582 sraw = _giraffe_filter_gauss1d(fraw, 6, 1.);
583
584 if (sraw == NULL) {
585
586 cpl_image_delete(fraw);
587 fraw = NULL;
588
589 return -3;
590
591 }
592
593 vertical1 = _giraffe_filter_sobel(sraw, TRUE);
594 vertical2 = _giraffe_filter_sobel(vertical1, TRUE);
595
596 cpl_image_save(sraw, "master_flat_smooth.fits", -32, 0, CPL_IO_DEFAULT);
597 cpl_image_save(vertical1, "vertical.fits", -32, 0, CPL_IO_DEFAULT);
598 cpl_image_save(vertical2, "vertical2.fits", -32, 0, CPL_IO_DEFAULT);
599
600
601 /*
602 * Detection of fibers
603 */
604
605 center = cpl_image_new(ncols, nrows, CPL_TYPE_INT);
606
607 flags = cx_calloc(ncols, sizeof(cxint));
608 buffer = cx_calloc(ncols, sizeof(cxdouble));
609
610 if ((center == NULL) || (flags ==NULL) || (buffer == NULL)) {
611
612 cx_free(buffer);
613 buffer = NULL;
614
615 cx_free(flags);
616 flags = NULL;
617
618 cpl_image_delete(center);
619 center = NULL;
620
621 cpl_image_delete(vertical2);
622 vertical2 = NULL;
623
624 cpl_image_delete(vertical1);
625 vertical1 = NULL;
626
627 cpl_image_delete(sraw);
628 sraw = NULL;
629
630 cpl_image_delete(fraw);
631 fraw = NULL;
632
633 return -3;
634
635 }
636
637
638 for (m = 0; m < nrows; ++m) {
639
640 register cxint irow = m * ncols;
641 register cxint n = 0;
642
643 cxint scount = 0;
644 cxint iteration = 0;
645
646 cxint* _center = cpl_image_get_data_int(center) + irow;
647
648 const cxdouble* _vt1 = cpl_image_get_data_double_const(vertical1) +
649 irow;
650 const cxdouble* _vt2 = cpl_image_get_data_double_const(vertical2) +
651 irow;
652 const cxdouble* _fraw = cpl_image_get_data_double_const(fraw) +
653 irow;
654
655
656 memset(buffer, 0, ncols * sizeof(cxdouble));
657 memset(flags, 0, ncols * sizeof(cxint));
658
659#if 1
660 for (n = 0; n < ncols; ++n) {
661
662// if ((_vt2[n] > 0.) || (n <= margin) || (n >= ncols - margin)) {
663// buffer[n] = 0.;
664// }
665// if (_vt2[n] > 0.) {
666// buffer[n] = 0.;
667// }
668 if (_vt2[n] <= 0.) {
669 buffer[n] = _vt1[n];
670 if ((n - 1 >= 0) && (_vt2[n - 1] > 0.)) {
671 buffer[n - 1] = _vt1[n - 1];
672 }
673 if ((n + 1 < ncols) && (_vt2[n + 1] > 0.)) {
674 buffer[n + 1] = _vt1[n + 1];
675 }
676 }
677 }
678#endif
679
680 while (iteration < ncols) {
681
682 cxint pos = -1;
683
684 cxdouble dx = 3. * 2. * noise;
685
686
687 for (n = 0; n < ncols; ++n) {
688
689 if (!flags[n] && (buffer[n] > dx)) {
690 dx = buffer[n];
691 pos = n;
692 }
693
694 }
695
696
697 if (pos >= 0) {
698
699 register cxint k = 0;
700
701 cxint start = pos;
702 cxint end = pos;
703 cxint width = 0;
704
705 cxdouble sigma = 0.;
706 cxdouble signal = 0.;
707
708
709 flags[pos] = 1;
710
711 k = pos - 1;
712 while ((k >= 0) && (buffer[k] > 0.)) {
713 flags[k] = 1;
714 start = k;
715 --k;
716 }
717
718 k = pos + 1;
719 while ((k < ncols) && (buffer[k] > 0.)) {
720 flags[k] = 1;
721 ++k;
722 }
723 pos = k - 1;
724
725 while ((k < ncols) && (buffer[k] < 0.)) {
726 flags[k] = 1;
727 end = k;
728 ++k;
729 }
730 width = end - start + 1;
731
732
733 /*
734 * Compute signal to noise ratio at the expected central
735 * position.
736 */
737
738 signal = (_fraw[pos] > 0.) ? _fraw[pos] : 0.;
739 sigma = sqrt((noise * noise + signal) / config->xbin);
740
741 if ((signal / sigma > 10.) && (width > 1)) {
742
743 start = (start == pos) ? start - 1 : start;
744 end = (end == pos) ? end + 1 : end;
745
746 _center[pos] += 1;
747 _center[start] += -1;
748 _center[end] += -2;
749
750 }
751
752 }
753
754 ++iteration;
755
756 }
757
758 for (n = 0; n < ncols; ++n) {
759
760 if (_center[n] == 1) {
761 ++scount;
762 }
763
764 }
765
766 if (scount >= smax) {
767 smax = scount;
768 mmax = m;
769 }
770
771 }
772
773 cx_free(buffer);
774 buffer = NULL;
775
776 cx_free(flags);
777 flags = NULL;
778
779 // FIXME: Test code only! Turn this experimental code into a final
780 // implementation.
781
782 cx_print("scount: %d (%d) at %d\n", smax, nspectra, mmax);
783
784
785 /*
786 * Remove bad detections (incomplete fibers, missed spurious detections)
787 */
788
789 //const cxint limit = 0.95 * nrows;
790 const cxint limit = 0.85 * nrows;
791
792
793 /* Factor to scale the sigma of a Gaussian to its HWHM */
794
795 const cxdouble hwf = sqrt(2. * log(2.));
796
797 cxint* xtrace = cx_calloc(nrows, sizeof(cxint));
798 cxint* ytrace = cx_calloc(nrows, sizeof(cxint));
799
800 cpl_image* mask = cpl_image_new(ncols, nrows, CPL_TYPE_INT);
801
802 for (m = 0; m < ncols; ++m) {
803
804 const cxint* _center = cpl_image_get_data_int(center);
805 const cxint* _reference = _center + mmax * ncols;
806
807 cxbool out_of_bounds = FALSE;
808
809 cxint connected = 0;
810
811
812 if (_reference[m] == 1) {
813
814 register cxint j = mmax;
815 register cxint pos = m;
816
817
818 ++itrace;
819
820 xtrace[connected] = pos;
821 ytrace[connected] = j;
822
823 j = mmax + 1;
824
825 while (j < nrows) {
826
827 register cxint k = 0;
828 register cxint l = j * ncols;
829 register cxint kmin = (pos - 1 >= 0) ? pos - 1 : 0;
830 register cxint kmax = (pos + 1 < ncols) ? pos + 1 : ncols - 1;
831
832 for (k = kmin; k <= kmax; ++k) {
833
834 if (_center[l + k] == 1) {
835 pos = k;
836 if ((pos <= margin) || (pos >= ncols - margin)) {
837 out_of_bounds = TRUE;
838 }
839 else {
840 ++connected;
841 xtrace[connected] = k;
842 ytrace[connected] = j;
843 }
844 break;
845 }
846
847 }
848
849 ++j;
850
851 }
852
853
854 j = mmax - 1;
855 pos = m;
856
857 while (j >= 0) {
858
859 register cxint k = 0;
860 register cxint l = j * ncols;
861 register cxint kmin = (pos - 1 >= 0) ? pos - 1 : 0;
862 register cxint kmax = (pos + 1 < ncols) ? pos + 1 : ncols - 1;
863
864 for (k = kmin; k <= kmax; ++k) {
865
866 if (_center[l + k] == 1) {
867 pos = k;
868 if ((pos <= margin) || (pos >= ncols - margin)) {
869 out_of_bounds = TRUE;
870 }
871 else {
872 ++connected;
873 xtrace[connected] = k;
874 ytrace[connected] = j;
875 }
876 break;
877 }
878
879 }
880
881 --j;
882
883 }
884
885
886 if ((connected < limit) || (out_of_bounds == TRUE)) {
887
888 memset(xtrace, 0, nrows * sizeof(cxint));
889 memset(ytrace, 0, nrows * sizeof(cxint));
890
891 if (out_of_bounds == TRUE) {
892 cx_print("discarded candidate %d, going out of detector "
893 "boundaries.\n", itrace);
894
895 }
896 else {
897 cx_print("discarded candidate %d, not enough connected "
898 "centers (%d, required: %d)\n", itrace, connected,
899 limit);
900 }
901
902 }
903 else {
904
905 cxint* _mask = cpl_image_get_data_int(mask);
906
907 for (j = 0; j < connected; ++j) {
908
909 register cxint x = xtrace[j];
910 register cxint y = ytrace[j] * ncols;
911 register cxint ix = x;
912
913 _mask[y + x] = 1;
914
915 while ((_center[y + ix] != -1) && (ix > 0)) {
916 --ix;
917 }
918 _mask[y + ix] = -1;
919
920 ix = x;
921 while ((_center[y + ix] != -2) && (ix < ncols - 1)) {
922 ++ix;
923 }
924 _mask[y + ix] += -2;
925
926 }
927
928 ++ispectra;
929
930 }
931
932 }
933
934 }
935
936 cx_print("scount: %d (expected: %d)\n", ispectra, nspectra);
937
938 cx_free(ytrace);
939 ytrace = NULL;
940
941 cx_free(xtrace);
942 xtrace = NULL;
943
944 for (m = 0; m < nrows; ++m) {
945
946 register cxint j = 0;
947 register cxint ns = 0;
948
949 const cxint* _mask = cpl_image_get_data_int(mask) + m * ncols;
950 const cxint* _center = cpl_image_get_data_int(center) + m * ncols;
951
952
953 for (j = 0; j < ncols; ++j) {
954
955 if (_mask[j] == 1) {
956
957 register cxint x = j;
958 register cxint ix = x;
959
960
961 while ((_center[ix] != -1) && (ix > 0)) {
962 --ix;
963 }
964 cpl_matrix_set(mylo, naccepted, ns, x - hwf * fabs(x - ix));
965
966 ix = x;
967 while ((_center[ix] != -2) && (ix < ncols - 1)) {
968 ++ix;
969 }
970 cpl_matrix_set(myup, naccepted, ns, x + hwf * fabs(ix - x));
971
972 ++ns;
973 }
974
975 }
976
977 if (ns == ispectra) {
978 cpl_matrix_set(mxok, naccepted, 0, m);
979 ++naccepted;
980 }
981
982 }
983
984 *ndetect = ispectra;
985
986
987 cpl_image_save(center, "center.fits", -32, 0, CPL_IO_DEFAULT);
988 cpl_image_save(mask, "mask.fits", -32, 0, CPL_IO_DEFAULT);
989
990 cpl_image_delete(mask);
991 cpl_image_delete(center);
992 cpl_image_delete(vertical2);
993 cpl_image_delete(vertical1);
994 cpl_image_delete(sraw);
995 cpl_image_delete(fraw);
996
997 return naccepted;
998}
999
1000
1001/*
1002 * @brief
1003 * Computes initial raw localization borders.
1004 *
1005 * @param image The image to process [nx,ny]
1006 * @param nspectra Number of expected spectra
1007 * @param noise Spectra/noise threshold
1008 * @param config Mask parameters.
1009 * @param ndetect Number of spectra detected.
1010 * @param mxok Matrix[nx] of @em nxok good x bins.
1011 * @param myup Matrix[nx,nspectra] of @em nxok upper Y borders.
1012 * @param mylo Matrix[nx,nspectra] of @em nxok lower Y borders.
1013 *
1014 * @return The function returns the number of good X bins on success, or
1015 * a negative value on failure.
1016 *
1017 * Starting from @em config.start bin of the CCD, the function tries to
1018 * detect spectrum pixels pattern:
1019 *
1020 * '0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 0 0'
1021 *
1022 * for each X bin and sets @em mylo[nx,ns] and @em myup[nx,ns] to Y values
1023 * of first '1' and last '1' of each '1' group. '0' and '1' are determined
1024 * by a @em noise value.
1025 *
1026 * Each group of '1' is supposed to be a spectrum. @em nspectra is the
1027 * expected number of spectra defined by the current instrument setup.
1028 * if X bin is ok @em mxok, @em mylo and @em myup matrices are updated
1029 * otherwise go and try the next X bin until @em config.tries is reached.
1030 *
1031 * @em mxok[nx], @em mylo[nx,ns] and @em myup[nx,ns] are pre-allocated
1032 * matrices.
1033 */
1034
1035inline static cxint
1036_giraffe_build_raw_mask(cpl_image *raw, cpl_image *bpixel, cxint nspectra,
1037 cxdouble noise, GiMaskParameters *config,
1038 cxint *ndetect, cpl_matrix *mxok, cpl_matrix *myup,
1039 cpl_matrix *mylo)
1040{
1041
1042 register cxint x = 0;
1043 register cxint y = 0;
1044 register cxint xretry = 0;
1045 register cxint xok = 0;
1046
1047 cxint ny = 0;
1048 cxint nrows = 0;
1049 cxint ncols = 0;
1050 cxint *yabove = NULL;
1051 cxint *ybelow = NULL;
1052 cxint *good_pixels = NULL;
1053 cxint ywidth = config->ywidth > 1 ? config->ywidth : 2;
1054 cxint ckwidth = config->ckdata.width;
1055 cxint ckheight = config->ckdata.height;
1056 cxint ckcount = config->ckdata.count;
1057
1058
1059 cxdouble* pixels = NULL;
1060
1061 cpl_mask* med = NULL;
1062
1063 cpl_image* img = raw;
1064
1065 (void) bpixel; /* Not used. */
1066
1067
1068 med = cpl_mask_new(1, 15);
1069
1070 if (med != NULL) {
1071
1072 cpl_mask_not(med);
1073
1074 img = cpl_image_new(cpl_image_get_size_x(raw),
1075 cpl_image_get_size_y(raw),
1076 cpl_image_get_type(raw));
1077
1078 cpl_image_filter_mask(img, raw, med, CPL_FILTER_MEDIAN,
1079 CPL_BORDER_FILTER);
1080
1081 }
1082
1083 cpl_mask_delete(med);
1084 med = NULL;
1085
1086 *ndetect = 0;
1087
1088 GIDEBUG(gi_message("noise = %g start = %d tries = %d xbin = %d "
1089 "ywidth = %d", noise, config->start, config->retry,
1090 config->xbin, ywidth));
1091
1092 pixels = cpl_image_get_data_double(img);
1093
1094 nrows = cpl_image_get_size_y(img);
1095 ncols = cpl_image_get_size_x(img);
1096
1097
1098 if (config->xbin > 1) {
1099
1100 cxint nx = nrows;
1101
1102 cxdouble* _pixels = NULL;
1103
1104
1105 nrows = (cxint) ceil(nrows / config->xbin);
1106 config->start = (cxint) ceil(config->start / config->xbin);
1107
1108 _pixels = cx_calloc(ncols * nrows, sizeof(cxdouble));
1109
1110 for (y = 0; y < ncols; ++y) {
1111
1112 for (x = 0; x < nrows; ++x) {
1113
1114 register cxint xx = 0;
1115 register cxint zx = x * ncols;
1116 register cxint xr = x * config->xbin;
1117 register cxint zr = xr * ncols;
1118
1119
1120 _pixels[zx + y] = 0.;
1121
1122 for (xx = 0; xx < config->xbin && xr < nx; ++xx) {
1123 _pixels[zx + y] += pixels[zr + y];
1124 }
1125
1126 _pixels[zx + y] /= config->xbin;
1127
1128 }
1129
1130 }
1131
1132 pixels = _pixels;
1133
1134 }
1135
1136 good_pixels = cx_calloc(nrows * ncols, sizeof(cxint));
1137
1138 switch (config->method) {
1139
1140 case GILOCALIZE_THRESHOLD_LOCAL:
1141 {
1142
1143 cxint ywidth2 = ywidth / 2;
1144 cxint sz = 2 * ywidth2 + 1;
1145
1146 cpl_vector* ymins = cpl_vector_new(sz);
1147
1148
1149 /*
1150 * We define a window along y axis to compute a local minimum
1151 * and threshold. To handle variation of "background"
1152 * between spectra in subslits
1153 */
1154
1155 for (x = 0; x < nrows; x++) {
1156
1157 cpl_vector_fill(ymins, 0.);
1158
1159 for (y = 0; y < ncols; y++) {
1160
1161 register cxint k = 0;
1162 register cxint kk = 0;
1163
1164 cxdouble value = 0.;
1165 cxdouble bkg = 0.;
1166 cxdouble threshold = 0.;
1167
1168
1169 for (kk = 0, k = -ywidth2; k <= ywidth2; k++) {
1170
1171 register cxint ky = y + k;
1172
1173 if (ky < 0 || ky >= ncols) {
1174 continue;
1175 }
1176
1177 cpl_vector_set(ymins, kk, pixels[x * ncols + ky]);
1178 ++kk;
1179 }
1180
1181 if (kk == 0) {
1182 continue;
1183 }
1184
1185 if (config->threshold > 0.) {
1186
1187 const cxint count = 2;
1188
1189 cxint i = 0;
1190
1191
1192 /* Note that ymins has, by construction, an odd number
1193 * of elements which must be at least 3 at this point.
1194 * Also kk must be at least ywidth2 + 1, since at most
1195 * we loose ywidth2 pixels at the borders.
1196 */
1197
1198 giraffe_array_sort(cpl_vector_get_data(ymins), kk);
1199
1200 bkg = 0.;
1201
1202 for (i = 0; i < count; i++) {
1203 bkg += fabs(cpl_vector_get(ymins, i));
1204 }
1205 bkg /= (cxdouble)count;
1206
1207 threshold = sqrt((2. * noise * noise +
1208 fabs(pixels[x * ncols + y]) + bkg / count) / config->xbin);
1209
1210 }
1211 else {
1212
1213 register cxint i;
1214 register cxdouble mean = 0.;
1215
1216
1217 for (i = 0; i < kk; i++) {
1218 mean += cpl_vector_get(ymins, i);
1219 }
1220 mean /= kk;
1221
1222 giraffe_array_sort(cpl_vector_get_data(ymins), kk);
1223
1224 bkg = (cpl_vector_get(ymins, 0) +
1225 cpl_vector_get(ymins, 1)) / 2.0;
1226 threshold = mean - bkg;
1227
1228 }
1229
1230
1231 /*
1232 * Check background corrected pixel value
1233 */
1234
1235 value = pixels[x * ncols + y] - bkg;
1236
1237 if (value < 0.) {
1238 continue;
1239 }
1240
1241 if (value > fabs(config->threshold) * threshold) {
1242 good_pixels[x * ncols + y] = 1;
1243 }
1244 }
1245 }
1246
1247 cpl_vector_delete(ymins);
1248 ymins = NULL;
1249
1250 break;
1251
1252 }
1253
1254 case GILOCALIZE_THRESHOLD_ROW:
1255 {
1256
1257 cpl_image* snr = cpl_image_abs_create(raw);
1258
1259 cxint sx = cpl_image_get_size_x(snr);
1260
1261
1262 cpl_image_power(snr, 0.5);
1263
1264 for (x = 0; x < nrows; ++x) {
1265
1266 const cxdouble* _snr = cpl_image_get_data_double_const(snr);
1267
1268 cxdouble avsnr = giraffe_array_median(_snr + x * sx, sx);
1269
1270
1271 for (y = 0; y < ncols; ++y) {
1272
1273 if (pixels[x * ncols + y] <= 0.) {
1274 continue;
1275 }
1276
1277 if (_snr[x * ncols + y] > avsnr * fabs(config->threshold)) {
1278 good_pixels[x * ncols + y] = 1;
1279 }
1280
1281 }
1282
1283 }
1284
1285 cpl_image_delete(snr);
1286 snr = NULL;
1287
1288 break;
1289
1290 }
1291
1292 default:
1293 {
1294
1295 cxdouble threshold = 0.;
1296
1297
1298 /*
1299 * We use global background and threshold
1300 */
1301
1302 if (config->threshold > 0.) {
1303 threshold = config->threshold * noise;
1304 }
1305 else {
1306
1307 cxdouble mean = cpl_image_get_mean(raw);
1308
1309 threshold = -config->threshold * mean *
1310 (nspectra * config->wavg / ncols);
1311
1312 }
1313
1314 for (x = 0; x < nrows; x++) {
1315
1316 for (y = 0; y < ncols; y++) {
1317
1318 if (pixels[x * ncols + y] > threshold) {
1319 good_pixels[x * ncols + y] = 1;
1320 }
1321
1322 }
1323
1324 }
1325
1326 break;
1327
1328 }
1329
1330 }
1331
1332 GIDEBUG(cxint *data = cx_calloc(nrows * ncols, sizeof(cxint));
1333 memcpy(data, good_pixels, nrows * ncols * sizeof(cxint));
1334 cpl_image *gp = cpl_image_wrap_int(ncols, nrows, data);
1335 cpl_image_save(gp, "locmask.fits", 32, NULL, CPL_IO_DEFAULT);
1336 cpl_image_unwrap(gp);
1337 cx_free(data));
1338
1339
1340 /*
1341 * Buffers used to store the fiber boundaries.
1342 */
1343
1344 yabove = cx_calloc(nspectra + 1, sizeof(cxint));
1345 ybelow = cx_calloc(nspectra + 1, sizeof(cxint));
1346
1347
1348 /*
1349 * Start from <config->start> of CCD to first pixel
1350 */
1351
1352 ny = ncols - 1;
1353
1354 xretry = 0;
1355 xok = 0;
1356
1357 for (x = config->start; (x >= 0) && (xretry <= config->retry); x--) {
1358
1359 register cxint zx = x * ncols;
1360 register cxint nborders = 0;
1361 register cxint nbelow = 0;
1362 register cxint nabove = 0;
1363 register cxint in_spectrum = 0;
1364
1365
1366 for (y = 1; y < ny; y++) {
1367
1368 register cxint tmp = 2 * good_pixels[zx + y];
1369
1370 /*
1371 * Number of spectra = max number of borders
1372 */
1373
1374 nborders = CX_MAX(CX_MAX(nborders, nbelow), nabove);
1375
1376 if (nborders > nspectra) {
1377 break; /* Error: too many spectrum borders detected */
1378 }
1379
1380 /*
1381 * Try to detect spectrum pattern:
1382 * 0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1
1383 * we need at least two consecutive one to detect a spectrum.
1384 */
1385
1386 if (good_pixels[zx + y + 1]) {
1387
1388 /*
1389 * Next pixel is a spectrum pixel: it's a border if
1390 * previous one is zero
1391 */
1392
1393 if ((tmp - good_pixels[zx + y - 1]) == 2) {
1394
1395 /*
1396 * 0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1
1397 * ^ so, here we are...
1398 */
1399
1400 if (!in_spectrum) {
1401
1402 /*
1403 * Could not be a below border if we are already
1404 * into a spectrum
1405 */
1406
1407 ybelow[nbelow++] = y;
1408 in_spectrum = 1; /* entering */
1409
1410 }
1411
1412 }
1413
1414 }
1415
1416 if (good_pixels[zx + y - 1]) {
1417
1418 /*
1419 * Previous pixel is a spectrum pixel: it's a border if
1420 * next one is zero
1421 */
1422
1423 if ((tmp - good_pixels[zx + y + 1]) == 2) {
1424
1425 /*
1426 * 0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1
1427 * ^ and now, there
1428 */
1429
1430 if (in_spectrum) {
1431
1432 /*
1433 * taken into account only if we already found a
1434 * lower border, we really are into a spectrum
1435 */
1436
1437 yabove[nabove++] = y;
1438 in_spectrum = 0; /* going out */
1439
1440 }
1441
1442 }
1443
1444 }
1445
1446// FIXME: Just a try
1447
1448 if (tmp &&
1449 !good_pixels[zx + y - 1] && !good_pixels[zx + y + 1]) {
1450
1451 if (_giraffe_validate_pixel(good_pixels, ncols, nrows, y, x,
1452 ckwidth, ckheight, ckcount)) {
1453
1454 yabove[nabove++] = y;
1455 ybelow[nbelow++] = y;
1456 }
1457
1458 }
1459
1460 } /* finished with this x bin */
1461
1462 if (in_spectrum) {
1463 nborders--;
1464 nbelow--;
1465 in_spectrum = 0;
1466 }
1467
1468 *ndetect = nborders;
1469
1470 if (!in_spectrum && (nbelow == nspectra) && (nbelow == nabove)) {
1471
1472 /*
1473 * Good number of upper and lower cuples found for all spectra:
1474 * xend will be the first good value and the updated xstart is
1475 * the current value. We also do not want last CCD clipped
1476 * spectrum
1477 */
1478
1479 for (y = 0; y < nspectra; y++) {
1480 cpl_matrix_set(mylo, xok, y, (cxdouble) ybelow[y]);
1481 cpl_matrix_set(myup, xok, y, (cxdouble) yabove[y]);
1482 cpl_matrix_set(mxok, xok, 0, (config->xbin > 1) ?
1483 (cxdouble) (x + 0.5) * config->xbin :
1484 (cxdouble) x);
1485 }
1486 xok++;
1487 xretry = 0; /* back on your feet */
1488 }
1489 else if (xretry++ < config->retry) {
1490
1491 /*
1492 * Do not find good number of spectra but we still have some
1493 * credit for a next try
1494 */
1495
1496 continue;
1497 }
1498 else {
1499
1500 /*
1501 * This is the end of our rope
1502 */
1503
1504 break;
1505 }
1506 } /* next x bin */
1507
1508
1509 /*
1510 * Second half: start from <config->start+1> of CCD to last pixel
1511 */
1512
1513 /*
1514 * Oops we could have a 2 * xretry width hole around xstart!!!
1515 */
1516
1517 xretry = 0;
1518
1519 for (x = config->start + 1; (x < nrows) &&
1520 (xretry <= config->retry); x++) {
1521
1522 register cxint zx = x * ncols;
1523 register cxint nborders = 0;
1524 register cxint nbelow = 0;
1525 register cxint nabove = 0;
1526 register cxint in_spectrum = 0;
1527
1528
1529 for (y = 1; y < ny; y++) {
1530
1531 register cxint tmp = 2 * good_pixels[zx + y];
1532
1533 nborders = CX_MAX(CX_MAX(nborders, nbelow), nabove);
1534
1535 if (nborders > nspectra) {
1536 break;
1537 }
1538
1539 if (good_pixels[zx + y + 1]) {
1540 if ((tmp - good_pixels[zx + y - 1]) == 2) {
1541 if (!in_spectrum) {
1542 ybelow[nbelow++] = y;
1543 in_spectrum = 1;
1544 }
1545 }
1546 }
1547
1548 if (good_pixels[zx + y - 1]) {
1549 if ((tmp - good_pixels[zx + y + 1]) == 2) {
1550 if (in_spectrum) {
1551 yabove[nabove++] = y;
1552 in_spectrum = 0;
1553 }
1554 }
1555 }
1556
1557// FIXME: Just a try
1558
1559 if (tmp &&
1560 !good_pixels[zx + y - 1] && !good_pixels[zx + y + 1]) {
1561
1562 if (_giraffe_validate_pixel(good_pixels, ncols, nrows, y, x,
1563 ckwidth, ckheight, ckcount)) {
1564
1565 yabove[nabove++] = y;
1566 ybelow[nbelow++] = y;
1567 }
1568
1569 }
1570
1571 } /* finished with this x bin */
1572
1573 if (in_spectrum) {
1574 nborders--;
1575 nbelow--;
1576 in_spectrum = 0;
1577 }
1578
1579 *ndetect = nborders;
1580
1581 if (!in_spectrum && (nbelow == nspectra) && (nbelow == nabove)) {
1582
1583 for (y = 0; y < nspectra; y++) {
1584 cpl_matrix_set(mylo, xok, y, (cxdouble) ybelow[y]);
1585 cpl_matrix_set(myup, xok, y, (cxdouble) yabove[y]);
1586 cpl_matrix_set(mxok, xok, 0, (config->xbin > 1) ?
1587 (cxdouble) (x + 0.5) * config->xbin :
1588 (cxdouble) x);
1589 }
1590 xok++;
1591 xretry = 0;
1592 }
1593 else if (xretry++ < config->retry) {
1594 continue;
1595 }
1596 else {
1597 break;
1598 }
1599
1600 } /* next x bin */
1601
1602 cx_free(ybelow);
1603 cx_free(yabove);
1604 cx_free(good_pixels);
1605
1606 if (pixels != cpl_image_get_data_double(img)) {
1607 cx_free(pixels);
1608 pixels = NULL;
1609 }
1610
1611 if (img != raw) {
1612 cpl_image_delete(img);
1613 img = NULL;
1614 }
1615
1616 if (xok == 0) {
1617 if (*ndetect < nspectra) {
1618 return -1;
1619 }
1620 else if (*ndetect > nspectra) {
1621 return -1;
1622 }
1623 else {
1624 return -2;
1625 }
1626 }
1627 else {
1628 *ndetect = nspectra;
1629 }
1630
1631 return xok;
1632
1633}
1634
1635
1636/*
1637 * @brief
1638 * Computes fitted localization centroid and width.
1639 *
1640 * @param mxok good X bins (all nspectra detected) [nxok]
1641 * @param myup upper Y of spectra [nxok,nspectra]
1642 * @param mylo lower Y of spectra [nxok,nspectra]
1643 * @param fibers Table of spectra/fibers to localize [ns]
1644 * @param config localization mask parameters
1645 * @param position localization mask: locy[nx,ns] and locw[nx,ns]
1646 *
1647 * Computes Chebyshev polynomial fit of raw localization borders for each
1648 * spectrum specified in @em fibers.
1649 *
1650 * The @em myup[nxok,nspectra] and @em mylo[nxok,nspectra] border matrices
1651 * had been computed by @b _giraffe_build_raw_mask().
1652 *
1653 * The expected number of spectra to be localized is given by @em nspectra.
1654 * The computed results are stored in the pre-allocated matrices
1655 * @em position->my[nx,ns] and @em position->mw[nx,ns]. The fiber setup
1656 * for a particular observation is given by @em fibers, a table of all
1657 * fibers specifying the spectra to be processed where @em ns is number of
1658 * entries (fibers) in @em fibers defined by the current instrument setup.
1659 */
1660
1661inline static void
1662_giraffe_fit_raw_mask(cpl_matrix *mxok, cpl_matrix *myup, cpl_matrix *mylo,
1663 cpl_table *fibers, GiMaskParameters *config,
1664 GiMaskPosition *position)
1665{
1666
1667 register cxint nn, x, nspectra;
1668 register cxint nx = cpl_matrix_get_nrow(position->my);
1669 register cxint ns = cpl_table_get_nrow(fibers);
1670
1671 cpl_matrix *mxraw;
1672 cpl_matrix *base;
1673 cpl_matrix *mcoeff;
1674
1675
1676
1677 mxraw = cpl_matrix_new(nx, 1);
1678 mcoeff = cpl_matrix_new(config->ydeg + 1, 1);
1679
1680
1681 /*
1682 * Initialize with all abcissa
1683 */
1684
1685 for (x = 0; x < nx; x++) {
1686 cpl_matrix_set(mxraw, x, 0, x);
1687 }
1688
1689 /*
1690 * Compute Chebyshev base over all x bins
1691 */
1692
1693 base = giraffe_chebyshev_base1d(0., nx, config->ydeg + 1, mxraw);
1694 cpl_matrix_delete(mxraw);
1695
1696 nspectra = 0;
1697 for (nn = 0; nn < ns; nn++) {
1698 cpl_matrix *ylofit = NULL;
1699 cpl_matrix *yupfit = NULL;
1700
1701 /* FIXME: The fiber selection changed the following piece of code
1702 * should not be necessary but we have to check that the
1703 * accessed to the matrix rows correspond to the selected
1704 * fibers.
1705 */
1706
1707 //if (mButton->m[nn] != LOC_OK_SPECTRUM) {
1708 // continue;
1709 //}
1710
1711 /* Fitting the lower border */
1712 ylofit = _giraffe_fit_border(mylo, base, mxok, nspectra,
1713 config->sigma, config->niter,
1714 config->mfrac, mcoeff);
1715 if (ylofit == NULL) {
1716 cpl_msg_warning(_task, "Could not compute low border for "
1717 "spectrum %d", nn);
1718 nspectra++;
1719 continue;
1720 }
1721
1722 /* Fitting the upper border */
1723 yupfit = _giraffe_fit_border(myup, base, mxok, nspectra,
1724 config->sigma, config->niter,
1725 config->mfrac, mcoeff);
1726 if (yupfit == NULL) {
1727 cpl_msg_warning(_task, "Could not compute up border for "
1728 "spectrum %d", nn);
1729 nspectra++;
1730 continue;
1731 }
1732
1733 /*
1734 * For each X bin the centroid and the half-width of the
1735 * corresponding mask is computed as the half-sum and the
1736 * half-difference of the fitted borders.
1737 */
1738
1739 for (x = 0; x < nx; x++) {
1740
1741 cpl_matrix_set(position->my, x, nn, 0.5 *
1742 (cpl_matrix_get(yupfit, x, 0) +
1743 cpl_matrix_get(ylofit, x, 0)));
1744
1745 cpl_matrix_set(position->my, x, nn, 0.5 *
1746 (cpl_matrix_get(yupfit, x, 0) -
1747 cpl_matrix_get(ylofit, x, 0)) + config->ewid);
1748
1749 }
1750 cpl_matrix_delete(ylofit);
1751 cpl_matrix_delete(yupfit);
1752 nspectra++;
1753
1754 } /* each spectrum */
1755
1756 cpl_msg_info(_task, "%03d spectrum positions fitted", nspectra);
1757
1758 cpl_matrix_delete(base);
1759 cpl_matrix_delete(mcoeff);
1760
1761 if (nspectra == 0) {
1762 cpl_msg_warning(_task, "could not fit any spectra, check number "
1763 "of good wavelength bins");
1764 return;
1765 }
1766
1767 return;
1768
1769}
1770
1771
1772/*
1773 * @brief
1774 * Computes fitted localization centroid and width.
1775 *
1776 * @param mz Image[nx,ny] of pixels values
1777 * @param mxok Good x bins (all nspectra detected) [nxok]
1778 * @param myup Upper Y of spectra [nxok,nspectra]
1779 * @param mylo Lower Y of spectra [nxok,nspectra]
1780 * @param fibers Spectra used for localization [ns]
1781 * @param config Localization mask parameters
1782 * @param position Localization mask: my[nx, ns] and mw[nx, ns]
1783 * @param coeffs Localization mask Chebyshev fit coefficients
1784 *
1785 * Computes Chebyshev polynomial fit of raw localization borders for each
1786 * spectrum specified in fibers[ns].
1787 *
1788 * The @em myup[nxok, nspectra] and @em mylo[nxok, nspectra] border matrices
1789 * had been computed by @b _giraffe_build_raw_mask(). The expected number
1790 * of spectra to be localized is given by @em nspectra. The matrix
1791 * @em position->my[nx, ns] is the fitted barycenter of Y values between raw
1792 * localization borders, while @em position->mw[nx,ns] is the 2D fit of
1793 * the half-width of the raw localization borders (+ extra width:
1794 * @em config->ewid).
1795 *
1796 * The matrices @em position->my[nx, ns], @em position->mw[nx, ns],
1797 * @em coeffs->my[ydeg + 1, ns] and @em coeffs->mw[(config->wdeg + 1)^2]
1798 * are pre-allocated matrices.
1799 *
1800 * The fiber setup for a particular observation is given by @em fibers,
1801 * a table of all fibers specifying the spectra to be processed where
1802 * @em ns is number of entries (fibers) in @em fibers defined by the
1803 * current instrument setup.
1804 */
1805
1806inline static void
1807_giraffe_fit_raw_centroid(cpl_image* mz, cpl_matrix* mxok, cpl_matrix* myup,
1808 cpl_matrix* mylo, cpl_table* fibers,
1809 GiMaskParameters* config, GiMaskPosition* position,
1810 GiMaskPosition* coeffs)
1811{
1812
1813 const cxchar* const fctid = "_giraffe_fit_raw_centroid";
1814
1815 register cxint nn = 0;
1816 register cxint x = 0;
1817 register cxint y = 0;
1818 register cxint nspectra = 0;
1819 register cxint nx = cpl_image_get_size_y(mz);
1820 register cxint ny = cpl_image_get_size_x(mz);
1821 register cxint ns = cpl_table_get_nrow(fibers);
1822
1823 cxint yorder = config->ydeg + 1;
1824 cxint worder = config->wdeg + 1;
1825
1826 cpl_matrix* mxraw = NULL;
1827 cpl_matrix* base = NULL;
1828 cpl_matrix* mycenter = NULL;
1829 cpl_matrix* mywidth = NULL;
1830 cpl_matrix* mx = NULL;
1831 cpl_matrix* my = NULL;
1832 cpl_matrix* mw = NULL;
1833 cpl_matrix* chebcoeff = NULL;
1834 cpl_matrix* mfitlocw = NULL;
1835 cpl_matrix* ycenfit = NULL;
1836 cpl_matrix* ycencoeff = NULL;
1837
1838
1839
1840 if (cpl_matrix_get_nrow(position->my) != nx ||
1841 cpl_matrix_get_ncol(position->my) != ns) {
1842 gi_error("%s: invalid size for position->my[%" CPL_SIZE_FORMAT ",%"
1843 CPL_SIZE_FORMAT "], expected [%d,%d]", fctid,
1844 cpl_matrix_get_nrow(position->my),
1845 cpl_matrix_get_ncol(position->my), nx, ns);
1846 return;
1847 }
1848
1849 if (cpl_matrix_get_nrow(position->mw) != nx ||
1850 cpl_matrix_get_ncol(position->mw) != ns) {
1851 gi_error("%s: invalid size for position->mw[%" CPL_SIZE_FORMAT ",%"
1852 CPL_SIZE_FORMAT "], expected [%d,%d]", fctid,
1853 cpl_matrix_get_nrow(position->my),
1854 cpl_matrix_get_ncol(position->my), nx, ns);
1855 return;
1856 }
1857
1858
1859 /*
1860 * Initialize with all abcissa
1861 */
1862
1863 mxraw = cpl_matrix_new(nx, 1);
1864
1865 for (x = 0; x < nx; x++) {
1866 cpl_matrix_set(mxraw, x, 0, x);
1867 }
1868
1869
1870 /*
1871 * Compute Chebyshev base over all x bins
1872 */
1873
1874 base = giraffe_chebyshev_base1d(0., nx, yorder, mxraw);
1875 cpl_matrix_delete(mxraw);
1876
1877 mycenter = cpl_matrix_new(cpl_matrix_get_nrow(mxok), ns);
1878 mywidth = cpl_matrix_new(1, cpl_matrix_get_nrow(mxok) * ns);
1879
1880 ycencoeff = cpl_matrix_new(yorder, 1);
1881
1882 for (nn = 0; nn < ns; nn++) {
1883
1884 /* FIXME: The fiber selection changed the following piece of code
1885 * should not be necessary but we have to check that the
1886 * accessed to the matrix rows correspond to the selected
1887 * fibers.
1888 */
1889
1890 //if (mButton->m[nn] != LOC_OK_SPECTRUM) {
1891 // continue;
1892 //}
1893
1894 /*
1895 * compute the barycenter and half-width of the corresponding mask
1896 * between raw borders.
1897 */
1898
1899 cxdouble* pixels = cpl_image_get_data_double(mz);
1900
1901 for (x = 0; x < cpl_matrix_get_nrow(mxok); x++) {
1902
1903 register cxint zx = (cxint) cpl_matrix_get(mxok, x, 0);
1904
1905 register cxdouble zz = 0.;
1906 register cxdouble yy = 0.;
1907
1908 cxdouble lower = cpl_matrix_get(mylo, x, nspectra);
1909 cxdouble upper = cpl_matrix_get(myup, x, nspectra);
1910
1911
1912 for (y = (cxint) lower; y <= (cxint) upper; y++) {
1913 yy += pixels[zx * ny + y] * y;
1914 zz += pixels[zx * ny + y];
1915 }
1916
1917 cpl_matrix_set(mycenter, x, nspectra, yy / zz);
1918 cpl_matrix_set(mywidth, 0, x * ns + nspectra, config->ewid +
1919 (upper - lower) / 2.0);
1920
1921 } /* for each x bin */
1922
1923 /*
1924 * The matrix ycenfit[nx] stores the fitted centroid
1925 */
1926
1927 cpl_matrix_fill(ycencoeff, 0.);
1928 ycenfit = _giraffe_fit_border(mycenter, base, mxok, nspectra,
1929 config->sigma, config->niter,
1930 config->mfrac, ycencoeff);
1931 if (ycenfit == NULL) {
1932 cpl_msg_warning(_task, "Could not fit centroid for spectrum %d",
1933 nn);
1934 nspectra++;
1935 continue;
1936 }
1937
1938 /*
1939 * Save centroid Chebyshev fit coeffs
1940 */
1941
1942 for (x = 0; x < yorder; x++) {
1943 cpl_matrix_set(coeffs->my, x, nn,
1944 cpl_matrix_get(ycencoeff, x, 0));
1945 }
1946
1947 /*
1948 * The localization centroid is a Chebyshev polynomial fit
1949 * of Y barycenters in raw mask
1950 */
1951
1952 for (x = 0; x < nx; x++) {
1953 cpl_matrix_set(position->my, x, nn,
1954 cpl_matrix_get(ycenfit, 0, x));
1955 } /* for each x bin */
1956
1957 cpl_matrix_delete(ycenfit);
1958 nspectra++;
1959
1960 } /* each spectrum */
1961
1962 GIDEBUG(cpl_image *lycenter = giraffe_matrix_create_image(mycenter);
1963 cpl_image_save(lycenter, "lycenter.fits", -32, NULL,
1964 CPL_IO_DEFAULT);
1965 cpl_image_delete(lycenter);
1966
1967 lycenter = giraffe_matrix_create_image(position->my);
1968 cpl_image_save(lycenter, "lycenterfit.fits", -32, NULL,
1969 CPL_IO_DEFAULT);
1970 cpl_image_delete(lycenter);
1971
1972 cpl_image *lyxok = giraffe_matrix_create_image(mxok);
1973 cpl_image_save(lyxok, "lyxok.fits", -32, NULL,
1974 CPL_IO_DEFAULT);
1975 cpl_image_delete(lyxok));
1976
1977
1978 cpl_msg_info(_task, "%03d spectrum positions fitted", nspectra);
1979
1980 cpl_matrix_delete(base);
1981 cpl_matrix_delete(mycenter);
1982 cpl_matrix_delete(ycencoeff);
1983
1984 if (nspectra == 0) {
1985 cpl_msg_warning(_task, "Could not fit any spectra, check number of "
1986 "good wavelength bins");
1987
1988 cpl_matrix_delete(mywidth);
1989 return;
1990 }
1991
1992 /*
1993 * 2D fit of mask width
1994 */
1995
1996 cpl_msg_info(_task, "2D fit (order %dx%d) of mask width", worder,
1997 worder);
1998
1999 /*
2000 * Computes grid[nxok, nspectra]
2001 */
2002
2003 mx = cpl_matrix_new(cpl_matrix_get_nrow(mxok) * nspectra, 1);
2004 my = cpl_matrix_new(cpl_matrix_get_nrow(mxok) * nspectra, 1);
2005 mw = cpl_matrix_new(1, cpl_matrix_get_nrow(mxok) * nspectra);
2006
2007 for (y = 0, nn = 0; nn < nspectra; nn++) {
2008
2009 /* FIXME: The fiber selection changed the following piece of code
2010 * should not be necessary but we have to check that the
2011 * accessed to the matrix rows correspond to the selected
2012 * fibers.
2013 */
2014
2015 //if (mButton->m[nn] != LOC_OK_SPECTRUM) {
2016 // continue;
2017 //}
2018
2019 for (x = 0; x < cpl_matrix_get_nrow(mxok); x++) {
2020
2021 register cxint zx = (cxint) cpl_matrix_get(mxok, x, 0);
2022 register cxint lx = x * nspectra + y;
2023
2024
2025 cpl_matrix_set(mx, lx, 0, cpl_matrix_get(mxok, x, 0));
2026 cpl_matrix_set(my, lx, 0, cpl_matrix_get(position->my, zx, nn));
2027 cpl_matrix_set(mw, 0, lx, cpl_matrix_get(mywidth, 0, x * ns + y));
2028 }
2029 y++;
2030 }
2031
2032 base = giraffe_chebyshev_base2d(0., 0., nx, ny, worder, worder, mx, my);
2033
2034 cpl_matrix_delete(my);
2035 cpl_matrix_delete(mx);
2036
2037 chebcoeff = giraffe_matrix_leastsq(base, mw);
2038 cpl_matrix_delete(base);
2039 cpl_matrix_delete(mw);
2040
2041 cpl_matrix_delete(mywidth);
2042
2043 if (chebcoeff == NULL) {
2044 gi_warning("%s: error in giraffe_matrix_leastsq() for width 2D fit",
2045 fctid);
2046 return;
2047 }
2048
2049 /*
2050 * Save half-width Chebyshev 2-D fit coeffs
2051 */
2052
2053 for (nn = 0; nn < cpl_matrix_get_ncol(chebcoeff); nn++) {
2054 cpl_matrix_set(coeffs->mw, 0, nn, cpl_matrix_get(chebcoeff, 0, nn));
2055 }
2056
2057 /*
2058 * Computes grid[nx, nspectra]
2059 */
2060
2061 mx = cpl_matrix_new(nx * nspectra, 1);
2062 my = cpl_matrix_new(nx * nspectra, 1);
2063
2064 for (y = 0, nn = 0; nn < nspectra; nn++) {
2065
2066 /* FIXME: The fiber selection changed the following piece of code
2067 * should not be necessary but we have to check that the
2068 * accessed to the matrix rows correspond to the selected
2069 * fibers.
2070 */
2071
2072 //if (mButton->m[nn] != LOC_OK_SPECTRUM) {
2073 // continue;
2074 //}
2075
2076 for (x = 0; x < nx; x++) {
2077
2078 register cxint lx = x * nspectra + y;
2079
2080 cpl_matrix_set(mx, lx, 0, x);
2081 cpl_matrix_set(my, lx, 0, cpl_matrix_get(position->my, x, nn));
2082
2083 }
2084 y++;
2085 }
2086
2087 cpl_matrix_set_size(chebcoeff, worder, worder);
2088
2089 mfitlocw = giraffe_chebyshev_fit2d(0., 0., nx, ny, chebcoeff, mx, my);
2090 cpl_matrix_delete(chebcoeff);
2091
2092 cpl_matrix_delete(my);
2093 cpl_matrix_delete(mx);
2094
2095 for (y = 0, nn = 0; nn < nspectra; nn++) {
2096
2097 /* FIXME: The fiber selection changed the following piece of code
2098 * should not be necessary but we have to check that the
2099 * accessed to the matrix rows correspond to the selected
2100 * fibers.
2101 */
2102
2103 //if (mButton->m[nn] != LOC_OK_SPECTRUM) {
2104 // continue;
2105 //}
2106
2107 for (x = 0; x < nx; x++) {
2108
2109 register cxint lx = x * nspectra + y;
2110
2111 cpl_matrix_set(position->mw, x, nn,
2112 cpl_matrix_get(mfitlocw, lx, 0));
2113
2114 }
2115 y++;
2116 }
2117
2118 cpl_matrix_delete(mfitlocw);
2119
2120 return;
2121
2122}
2123
2124
2125/*
2126 * @brief
2127 * Computes fitted localization centroid and width on all spectra.
2128 *
2129 * @param mZraw Matrix[nx,ny] of pixels values
2130 * @param mButton Matrix[ns] of spectra used for localization
2131 * @param locMethod Centroid computation method:
2132 * HALF_WIDTH, BARYCENTER, PSF_PROFIL
2133 * @param sNormalize Normalize spectra along dispersion axis
2134 * @param noithresh Spectra/noise threshold
2135 * @param locPrms Localization mask parameters
2136 * @param locPos Localization mask: locY[nx,ns] and locW[nx,ns]
2137 * @param locCoeff Localization mask Chebyshev fit coefficients
2138 *
2139 * @return The function returns 0 on success, or a negative value otherwise.
2140 *
2141 * Computes localization mask (centroid and half-width) for the given
2142 * image @a mZraw[nx,ny]. @a mButton[nspectra] is a matrix of 0/1 values
2143 * specifying spectra to be processed. @a *noithresh is the threshold value
2144 * use to select spectra or inter-spectra pixels. @a locMethod defines the
2145 * method used to compute localization mask centroid and half-width.
2146 * @a locPos.mY[nx,ns], @a locPos.mW[nx,ns], @a locCoeff.mY[ydeg+1,ns] and
2147 * @a locCoeff.mW[(wdeg+1)**2] are pre-allocated matrices.
2148 */
2149
2150inline static cxint
2151_giraffe_localize_spectra(cpl_image *mzraw, cpl_image *bpixel,
2152 cpl_table *fibers, GiLocalizeMethod method,
2153 cxbool normalize, cxdouble noise,
2154 GiMaskParameters *config, GiMaskPosition *position,
2155 GiMaskPosition *coeffs)
2156{
2157
2158 cxint n, nn;
2159 cxint nx, ny, nxok;
2160 cxint ndetect, nspectra;
2161 cxint x, y;
2162
2163 cxdouble uplost = 0.;
2164 cxdouble lolost = 0.;
2165 cxdouble avglost = 0.;
2166 cxdouble avgmask = 0.;
2167 cxdouble sigmask = 0.;
2168 cxdouble sigmean = 0.;
2169 cxdouble avgborders = 0.;
2170
2171 cxdouble *_mzraw;
2172
2173 cpl_matrix *mxok; /* mylo[nx] abcissa og good x bins */
2174 cpl_matrix *myup; /* myup[nx,ns] of upper Y for each spectrum */
2175 cpl_matrix *mylo; /* mylo[nx,ns] of lower Y for each spectrum */
2176 cpl_matrix *mwid;
2177
2178 cpl_image *mz = NULL;
2179 cpl_image *mznorm = NULL;
2180
2181
2182
2183 nx = cpl_image_get_size_y(mzraw);
2184 ny = cpl_image_get_size_x(mzraw);
2185 _mzraw = cpl_image_get_data_double(mzraw);
2186
2187
2188 if (normalize == TRUE) {
2189
2190 cxdouble zxmax = 0.0;
2191 cxdouble *_mzx = NULL;
2192 cxdouble *_mznorm = NULL;
2193
2194 cpl_image *mzx = NULL;
2195
2196
2197 cpl_msg_info(_task, "Using normalized spectra for localization");
2198
2199
2200 /*
2201 * The matrix mznorm contains the pixel values from mz
2202 * normalized along X axis and the matrix mzx is the summ
2203 * of all spectra along X (dispersion) axis
2204 */
2205
2206 mznorm = cpl_image_new(ny, nx, CPL_TYPE_DOUBLE);
2207 _mznorm = cpl_image_get_data_double(mznorm);
2208
2209 mzx = cpl_image_new(1, nx, CPL_TYPE_DOUBLE);
2210 _mzx = cpl_image_get_data_double(mzx);
2211
2212
2213 /*
2214 * For each x bin, summ all y values
2215 */
2216
2217 for (x = 0 ; x < nx; x++) {
2218 for (y = 0 ; y < ny; y++) {
2219 _mzx[x] += _mzraw[x * ny + y];
2220 }
2221
2222 /*
2223 * Maximum value of summ
2224 */
2225
2226 if (_mzx[x] > zxmax) {
2227 zxmax = _mzx[x];
2228 }
2229 }
2230
2231 GIDEBUG(cpl_image_save(mzx, "mzx.fits", -32, NULL, CPL_IO_DEFAULT));
2232
2233 for (x = 0 ; x < nx; x++) {
2234
2235 register cxdouble zxnorm = zxmax / _mzx[x];
2236
2237 for (y = 0 ; y < ny; y++) {
2238 _mznorm[x * ny + y] = _mzraw[x * ny + y] * zxnorm;
2239 }
2240
2241 }
2242
2243 cpl_image_delete(mzx);
2244 mz = mznorm;
2245 }
2246 else {
2247
2248 /*
2249 * Use pixel values as they are
2250 */
2251
2252 cpl_msg_info(_task, "Using raw spectra for localization");
2253 mz = mzraw;
2254 }
2255
2256
2257 /*
2258 * Full localization: takes care of all spectra
2259 */
2260
2261 nspectra = cpl_table_get_nrow(fibers);
2262
2263 mxok = cpl_matrix_new(nx, 1);
2264 myup = cpl_matrix_new(nx, nspectra);
2265 mylo = cpl_matrix_new(nx, nspectra);
2266
2267
2268 /*
2269 * Make the bin size an even value if it is larger than 1
2270 */
2271
2272 config->xbin = (config->xbin > 1) ? 2 * (config->xbin / 2) : 1;
2273
2274 GIDEBUG(cpl_image_save(mz, "mz.fits", -32, NULL, CPL_IO_DEFAULT));
2275
2276
2277 /*
2278 * Find spectrum borders
2279 */
2280
2281 cpl_msg_info(_task, "Generating mask (%d spectra expected) ...",
2282 nspectra);
2283
2284 // FIXME: Finalize the implementation of this experimental method for
2285 // detecting fibers
2286#if 0
2287 nxok = _giraffe_build_edge_mask(mz, bpixel, nspectra, noise, config,
2288 &ndetect, mxok, myup, mylo);
2289#endif
2290 // End of test code
2291
2292
2293 nxok = _giraffe_build_raw_mask(mz, bpixel, nspectra, noise, config,
2294 &ndetect, mxok, myup, mylo);
2295
2296 if (nxok < 0) {
2297
2298 cpl_matrix_delete(mxok);
2299 cpl_matrix_delete(myup);
2300 cpl_matrix_delete(mylo);
2301
2302 switch (nxok) {
2303 case -1:
2304 cpl_msg_warning(_task, "Invalid number of spectra detected: "
2305 "%d != %d", ndetect, nspectra);
2306 break;
2307
2308 case -2:
2309 cpl_msg_warning(_task, "No abcissa with good number "
2310 "of spectra");
2311 break;
2312
2313 default:
2314 cpl_msg_warning(_task, "Error while searching for spectra");
2315 break;
2316 }
2317
2318 return nxok;
2319
2320 }
2321 else {
2322 cpl_msg_info(_task, "%d spectra detected in %d wavelength bins",
2323 ndetect, nxok);
2324 }
2325
2326
2327 /*
2328 * Only takes care of good values
2329 */
2330
2331 cpl_matrix_resize(mxok, 0, nxok - cpl_matrix_get_nrow(mxok), 0, 0);
2332 cpl_matrix_resize(myup, 0, nxok - cpl_matrix_get_nrow(myup), 0, 0);
2333 cpl_matrix_resize(mylo, 0, nxok - cpl_matrix_get_nrow(mylo), 0, 0);
2334
2335 GIDEBUG(gi_message("%s: mxok[0-%d]=[%g-%g]", __func__,
2336 cpl_matrix_get_nrow(mxok) - 1,
2337 cpl_matrix_get_min(mxok),
2338 cpl_matrix_get_max(mxok)));
2339
2340
2341 cpl_msg_info(_task, "Computing spectrum positions and widths in "
2342 "pixel range [%g,%g]", cpl_matrix_get_min(mxok),
2343 cpl_matrix_get_max(mxok));
2344
2345 if (cpl_matrix_get_nrow(mxok) <= config->ydeg) {
2346 cpl_msg_info(_task, "Not enough data points %" CPL_SIZE_FORMAT
2347 " for %d order fit", cpl_matrix_get_nrow(mxok),
2348 config->ydeg);
2349
2350 return -1;
2351 }
2352
2353 switch (method) {
2354 case GILOCALIZE_HALF_WIDTH:
2355 cpl_msg_info(_task, "Using half-width for localization");
2356 _giraffe_fit_raw_mask(mxok, myup, mylo, fibers, config,
2357 position);
2358 break;
2359
2360 case GILOCALIZE_BARYCENTER:
2361 default:
2362 cpl_msg_info(_task, "Using barycenter for localization");
2363 _giraffe_fit_raw_centroid(mz, mxok, myup, mylo, fibers, config,
2364 position, coeffs);
2365 break;
2366 }
2367
2368 if (normalize == 1) {
2369 cpl_image_delete(mznorm);
2370 }
2371
2372 /*
2373 * Compute the number of pixels rejected by the fit
2374 */
2375
2376
2377 /* FIXME: Here again nspectra equals cpl_table_get_nrow(fibers),
2378 * where OGL used mButtons->nr. We have to check the
2379 * correctness carefully here !!
2380 */
2381
2382 mwid = cpl_matrix_new(nxok, nspectra);
2383
2384 for (n = 0, nn = 0; nn < cpl_table_get_nrow(fibers); nn++) {
2385
2386 for (x = 0; x < nxok; x++) {
2387 register cxint lx = (cxint) cpl_matrix_get(mxok, x, 0);
2388
2389 cxdouble lower = cpl_matrix_get(mylo, x, n);
2390 cxdouble upper = cpl_matrix_get(myup, x, n);
2391 cxdouble width = cpl_matrix_get(position->mw, lx, nn);
2392
2393 uplost += cpl_matrix_get(position->my, lx, nn) + width - upper;
2394 lolost += cpl_matrix_get(position->my, lx, nn) - width - lower;
2395
2396 avgborders += upper - lower;
2397 avgmask += width;
2398
2399 cpl_matrix_set(mwid, x, n, 2. * width);
2400 }
2401 n++;
2402 }
2403
2404 sigmean = cpl_matrix_get_mean(mwid);
2405 sigmask = giraffe_matrix_sigma_mean(mwid, sigmean);
2406 avglost = (lolost + uplost) / (nspectra * nxok);
2407 avgmask = 2.0 * avgmask / nspectra;
2408
2409 cpl_msg_info(_task, "Mask was computed using %d of %d wavelength bins",
2410 nxok, nx);
2411 cpl_msg_info(_task, "Average # of pixels per spectra: %.4g",
2412 avgmask);
2413 cpl_msg_info(_task, "Average # of in-borders pixels per spectra: %.4g",
2414 avgborders / nspectra);
2415 cpl_msg_info(_task, "Average lost pixels per spectra: %.4g",
2416 avglost);
2417 cpl_msg_info(_task, "Average lost pixels at upper border: %.4g",
2418 uplost / (nspectra * nxok));
2419 cpl_msg_info(_task, "Average lost pixels at lower border: %.4g",
2420 lolost / (nspectra * nxok));
2421 cpl_msg_info(_task, "Average spectrum width: %.4g +/- %.4g, "
2422 "(min, max) = (%.4g, %.4g)", sigmean, sigmask,
2423 cpl_matrix_get_min(mwid), cpl_matrix_get_max(mwid));
2424
2425 cpl_matrix_delete(mwid);
2426
2427 cpl_matrix_delete(mylo);
2428 cpl_matrix_delete(myup);
2429 cpl_matrix_delete(mxok);
2430
2431 return 0;
2432
2433}
2434
2435
2436inline static cxint
2437_giraffe_finalize_fibers(cpl_table *fibers, cpl_matrix *locy, GiImage *mlocy,
2438 cxdouble maxoffset, cxdouble* maxshift)
2439{
2440
2441 cxint i = 0;
2442 cxint j = 0;
2443 cxint nx = 0;
2444 cxint ny = 0;
2445 cxint _nx = 0;
2446 cxint _ny = 0;
2447 cxint nfibers = 0;
2448 cxint irow = 0;
2449
2450 cxdouble max_shift = 0.;
2451 cxdouble *positions = NULL;
2452
2453 cpl_image *_mlocy = NULL;
2454
2455
2456 if (fibers == NULL || locy == NULL || mlocy == NULL) {
2457 return -1;
2458 }
2459
2460 if (cpl_table_has_column(fibers, "RINDEX") == FALSE) {
2461 return -1;
2462 }
2463
2464 nx = cpl_matrix_get_ncol(locy);
2465 ny = cpl_matrix_get_nrow(locy);
2466
2467 nfibers = cpl_table_get_nrow(fibers);
2468
2469 _mlocy = giraffe_image_get(mlocy);
2470 _nx = cpl_image_get_size_x(_mlocy);
2471 _ny = cpl_image_get_size_y(_mlocy);
2472
2473 if (ny != _ny) {
2474 return -2;
2475 }
2476
2477 if (nfibers > _nx) {
2478 return -3;
2479 }
2480
2481 cpl_table_select_all(fibers);
2482
2483
2484 /*
2485 * Get pointer to the central scan line.
2486 */
2487
2488 irow = (_ny - 1) / 2;
2489 positions = (cxdouble *)cpl_image_get_data(_mlocy) + irow * _nx;
2490
2491
2492 /*
2493 * Compare the detected fiber positions with the positions
2494 * from the reference localization. Select only those fibers
2495 * whose distance from the reference positions is less than
2496 * a given offset. All other fibers are removed from the
2497 * fiber table.
2498 */
2499
2500 for (i = 0; i < nfibers; i++) {
2501
2502 if (j < nx) {
2503
2504 cxint pos = cpl_table_get_int(fibers, "RINDEX", i, NULL) - 1;
2505
2506 cxdouble yc = cpl_matrix_get(locy, irow, j);
2507 cxdouble shift = fabs(yc - positions[pos]);
2508
2509 if (shift <= maxoffset) {
2510 cpl_table_unselect_row(fibers, i);
2511 ++j;
2512 }
2513 else {
2514 max_shift = CX_MAX(max_shift, shift);
2515 }
2516
2517 }
2518 }
2519
2520 cpl_table_erase_selected(fibers);
2521
2522 if (maxshift != NULL) {
2523 *maxshift = max_shift;
2524 }
2525
2526 return 0;
2527
2528}
2529
2530
2559cxint
2560giraffe_localize_spectra(GiLocalization *result, GiImage *image,
2561 GiTable *fibers, GiLocalization *master,
2562 GiImage *badpixels, GiLocalizeConfig *config)
2563{
2564
2565 const cxchar *fctid = "giraffe_localize_spectra";
2566
2567 cxint i;
2568 cxint status;
2569 cxint nrows;
2570 cxint nfibers;
2571 cxint nframes = 1;
2572 cxint ckwidth;
2573 cxint ckheight;
2574 cxint ckcount;
2575
2576 cxdouble mwidth;
2577 cxdouble conad = 0.;
2578 cxdouble bias_ron = 0.;
2579 cxdouble mask_sigma = 0.;
2580
2581 cx_string *pname;
2582
2583 cpl_propertylist *properties;
2584
2585 cpl_image *_image = giraffe_image_get(image);
2586 cpl_image *_bpixel = giraffe_image_get(badpixels);
2587 cpl_image *_result = NULL;
2588
2589 cpl_matrix *_my;
2590
2591 cpl_table *_fibers = NULL;
2592 cpl_table *fiber_setup = NULL;
2593 cpl_table *locc;
2594
2595 GiLocalizeMethod method;
2596
2597 GiInstrumentMode mode;
2598
2599 GiMaskParameters mask_config;
2600
2601 GiMaskPosition mask_position;
2602 GiMaskPosition mask_coeffs;
2603
2604
2605
2606 /*
2607 * Preprocessing
2608 */
2609
2610 if (result == NULL || image == NULL || fibers == NULL || config == NULL) {
2611 cpl_error_set(fctid, CPL_ERROR_NULL_INPUT);
2612 return 1;
2613 }
2614
2615 if (badpixels != NULL) {
2616 cpl_msg_debug(fctid,"Bad pixel correction is not available. Bad "
2617 "pixel map will be ignored.");
2618 }
2619
2620 _fibers = giraffe_table_get(fibers);
2621
2622 if (_fibers == NULL) {
2623 cpl_error_set(fctid, CPL_ERROR_DATA_NOT_FOUND);
2624 return 1;
2625 }
2626 else {
2627 fiber_setup = _fibers;
2628 }
2629
2630 properties = giraffe_image_get_properties(image);
2631
2632
2633 /*
2634 * Add the number of fibers to the image properties.
2635 */
2636
2637 nfibers = cpl_table_get_nrow(_fibers);
2638
2639 cpl_msg_info(fctid, "Setting number of fibers (%s) to %d",
2640 GIALIAS_NFIBERS, nfibers);
2641
2642 cpl_propertylist_update_int(properties, GIALIAS_NFIBERS, nfibers);
2643 cpl_propertylist_set_comment(properties, GIALIAS_NFIBERS,
2644 "Number of fibres");
2645
2646
2647 giraffe_error_push();
2648
2649 conad = giraffe_propertylist_get_conad(properties);
2650
2651 if (cpl_error_get_code() != CPL_ERROR_NONE) {
2652 return 1;
2653 }
2654
2655 giraffe_error_pop();
2656
2657
2658 /*
2659 * If a ron value is provided write it to the image properties.
2660 */
2661
2662 if (config->ron > 0.) {
2663 cpl_msg_info(fctid, "Setting bias sigma value (%s) to %.5g",
2664 GIALIAS_BIASSIGMA, config->ron);
2665 cpl_propertylist_update_double(properties, GIALIAS_BIASSIGMA,
2666 config->ron);
2667 }
2668
2669 bias_ron = giraffe_propertylist_get_ron(properties);
2670 cpl_msg_info(fctid, "Bias sigma value: %.3g e-", bias_ron);
2671
2672
2673 if (cpl_propertylist_has(properties, GIALIAS_DATANCOM)) {
2674 nframes = cpl_propertylist_get_int(properties, GIALIAS_DATANCOM);
2675 }
2676
2677
2678 if (config->noise > 0.) {
2679 cpl_msg_info(fctid, "Noise multiplier: %.3g",
2680 config->noise);
2681 }
2682 else {
2683 cpl_msg_info(fctid, "Threshold multiplier: %.3g",
2684 fabs(config->noise));
2685 }
2686
2687
2688 /*
2689 * Setup localization start position in the dispersion direction.
2690 */
2691
2692 nrows = cpl_image_get_size_y(_image);
2693
2694 if (config->start < 0) {
2695 config->start = nrows / 2;
2696 }
2697
2698 /*
2699 * Set instrument mode specific parameters like the width of a spectrum
2700 * and the width of the intra-spectrum gap.
2701 */
2702
2703 mode = giraffe_get_mode(properties);
2704
2705 if (config->ywidth < 1) {
2706
2707 cpl_msg_info(fctid, "Configuring equilizing filter width from "
2708 "instrument mode");
2709
2710 switch (mode) {
2711 case GIMODE_MEDUSA:
2712 config->ywidth = 16;
2713 break;
2714
2715 case GIMODE_IFU:
2716 config->ywidth = 6;
2717 break;
2718
2719 case GIMODE_ARGUS:
2720 config->ywidth = 6;
2721 break;
2722
2723 default:
2724 cpl_msg_error(fctid, "Invalid instrument mode!");
2725 return 1;
2726 break;
2727 }
2728
2729
2730 if (!cpl_propertylist_has(properties, GIALIAS_SLITNAME)) {
2731 cpl_msg_error(fctid, "Property (%s) not found in raw image",
2732 GIALIAS_SLITNAME);
2733 return 1;
2734 }
2735 else {
2736 const cxchar *slit =
2737 cpl_propertylist_get_string(properties, GIALIAS_SLITNAME);
2738
2739 cpl_msg_info(fctid, "Setting equilizing filter to %d [pxl] "
2740 "for slit configuration `%s'", config->ywidth,
2741 slit);
2742 }
2743
2744 }
2745
2746
2747 /*
2748 * Set mean spectrum width according to the instrument mode
2749 */
2750
2751 switch (mode) {
2752 case GIMODE_MEDUSA:
2753 mwidth = GISPECTRUM_MWIDTH_MEDUSA;
2754
2755 ckwidth = 1;
2756 ckheight = 3;
2757 ckcount = 8;
2758
2759 break;
2760
2761 case GIMODE_IFU:
2762 mwidth = GISPECTRUM_MWIDTH_IFU;
2763
2764 ckwidth = 0;
2765 ckheight = 3;
2766 ckcount = 4;
2767
2768 break;
2769
2770 case GIMODE_ARGUS:
2771 mwidth = GISPECTRUM_MWIDTH_IFU;
2772
2773 ckwidth = 0;
2774 ckheight = 3;
2775 ckcount = 4;
2776
2777 break;
2778
2779 default:
2780 cpl_msg_error(fctid, "Invalid instrument mode!");
2781 return 1;
2782 break;
2783 }
2784
2785
2786 /*
2787 * Setup localization method
2788 */
2789
2790 if (config->centroid == TRUE) {
2791 method = GILOCALIZE_BARYCENTER;
2792 }
2793 else {
2794 method = GILOCALIZE_HALF_WIDTH;
2795 }
2796
2797
2798 /*
2799 * Fill the parameter structure for the localization mask computation
2800 * with the actual values.
2801 */
2802
2803 mask_config.ywidth = config->ywidth;
2804 mask_config.method = config->threshold;
2805 mask_config.threshold = config->noise;
2806 mask_config.ydeg = config->yorder;
2807 mask_config.wdeg = config->worder;
2808 mask_config.ewid = config->ewidth;
2809 mask_config.wavg = mwidth;
2810 mask_config.ckdata.width = ckwidth;
2811 mask_config.ckdata.height = ckheight;
2812 mask_config.ckdata.count = ckcount;
2813 mask_config.sigma = config->sigma;
2814 mask_config.niter = config->iterations;
2815 mask_config.mfrac = config->fraction;
2816 mask_config.start = config->start;
2817 mask_config.retry = config->retries;
2818 mask_config.xbin = config->binsize;
2819
2820
2821 /*
2822 * If config->noise is larger than 0. it is basically a signal-to-noise
2823 * ratio, given in ADU for a single/average frame. Since the fiber
2824 * detection is carried out on a summed frame of electrons, the
2825 * signal-to-noise limit has to be re-scaled properly.
2826 */
2827
2828 if (config->noise > 0.) {
2829 mask_config.threshold *= sqrt(nframes * conad);
2830 }
2831
2832
2833 /*
2834 * Processing
2835 */
2836
2837 /*
2838 * Localize spectra. Depending on the setup we either do a full
2839 * localization or we just localize the simultaneous calibration
2840 * fibers using an existing, full master localization.
2841 */
2842
2843 if (config->full != TRUE) {
2844
2845 cpl_msg_info(fctid, "Computing spectrum localization using SIWC "
2846 "spectra");
2847
2848 if (!master || !master->locy || !master->locy) {
2849 cpl_msg_error(fctid, "Required full master localization is "
2850 "missing!");
2851 return 1;
2852 }
2853
2854
2855 /*
2856 * Select SIWC fibers from the fiber table. The simultaneous
2857 * calibration spectra are indicated by a -1 as retractor position.
2858 */
2859
2860 cpl_table_unselect_all(_fibers);
2861 cpl_table_or_selected_int(_fibers, "RP", CPL_EQUAL_TO, -1);
2862
2863 fiber_setup = cpl_table_extract_selected(_fibers);
2864 nfibers = cpl_table_get_nrow(fiber_setup);
2865
2866 }
2867
2868
2869 /*
2870 * Allocate required output matrices and hook them into the appropriate
2871 * structures.
2872 */
2873
2874 mask_position.type = GIMASK_FITTED_DATA;
2875 mask_position.my = cpl_matrix_new(nrows, nfibers);
2876 mask_position.mw = cpl_matrix_new(nrows, nfibers);
2877
2878 mask_coeffs.type = GIMASK_FIT_COEFFS;
2879 mask_coeffs.my = cpl_matrix_new(mask_config.ydeg + 1, nfibers);
2880 mask_coeffs.mw = cpl_matrix_new(1, (mask_config.wdeg + 1) *
2881 (mask_config.wdeg + 1));
2882
2883 /*
2884 * Convert raw image from ADU to electrons, and adjust the readout
2885 * noise to match the number of images that were used to create the
2886 * raw image.
2887 */
2888
2889 _image = cpl_image_multiply_scalar_create(_image, nframes * conad);
2890
2891 mask_sigma = sqrt(nframes) * bias_ron;
2892
2893
2894 /*
2895 * Compute the position of the spectra on the CCD
2896 */
2897
2898 status = _giraffe_localize_spectra(_image, _bpixel, fiber_setup,
2899 method, config->normalize,
2900 mask_sigma,
2901 &mask_config, &mask_position,
2902 &mask_coeffs);
2903
2904 cpl_image_delete(_image);
2905 _image = NULL;
2906
2907 if (status) {
2908 result->locy = NULL;
2909 result->locw = NULL;
2910 result->locc = NULL;
2911 result->psf = NULL;
2912
2913 cpl_matrix_delete(mask_position.my);
2914 cpl_matrix_delete(mask_position.mw);
2915
2916 cpl_matrix_delete(mask_coeffs.my);
2917 cpl_matrix_delete(mask_coeffs.mw);
2918
2919 if (config->full != TRUE) {
2920 cpl_table_delete(fiber_setup);
2921 }
2922
2923 cpl_msg_error(fctid, "Spectrum localization computation failed!");
2924
2925 return 1;
2926 }
2927
2928
2929 /*
2930 * Post processing
2931 */
2932
2933 if (config->full != TRUE) {
2934
2935 /*
2936 * TBD: Postprocessing of localization. Compare computed spectrum
2937 * locations with master, i.e. calculate differences.
2938 */
2939
2940 cpl_table_delete(fiber_setup);
2941
2942 }
2943 else {
2944
2945 if (master != NULL && master->locy != NULL) {
2946
2947 cxint nf = cpl_table_get_nrow(_fibers);
2948
2949 cxdouble maxoffset = 0.5 * mask_config.wavg;
2950 cxdouble maxshift = 0.;
2951
2952
2953 cpl_msg_info(fctid, "Comparing detected and expected fiber "
2954 "positions.");
2955
2956 status = _giraffe_finalize_fibers(_fibers, mask_position.my,
2957 master->locy, maxoffset,
2958 &maxshift);
2959
2960 if (status != 0) {
2961
2962 if (status == -3) {
2963
2964 const cpl_image* mlocy = giraffe_image_get(master->locy);
2965 cxint _nf = cpl_image_get_size_x(mlocy);
2966
2967 cpl_msg_error(fctid, "More fibers (%d) than expected "
2968 "(%d) were found!", nf, _nf);
2969
2970 }
2971
2972 result->locy = NULL;
2973 result->locw = NULL;
2974 result->locc = NULL;
2975 result->psf = NULL;
2976
2977 cpl_matrix_delete(mask_position.my);
2978 cpl_matrix_delete(mask_position.mw);
2979
2980 cpl_matrix_delete(mask_coeffs.my);
2981 cpl_matrix_delete(mask_coeffs.mw);
2982
2983 if (config->full != TRUE) {
2984 cpl_table_delete(fiber_setup);
2985 }
2986
2987 cpl_msg_error(fctid, "Comparison of fiber positions "
2988 "failed!");
2989
2990 return 1;
2991 }
2992
2993 cx_assert(cpl_table_get_nrow(_fibers) <= nf);
2994
2995 cpl_msg_info(fctid, "%" CPL_SIZE_FORMAT " of %d expected fibers "
2996 "were detected.", cpl_table_get_nrow(_fibers), nf);
2997
2998 if (cpl_table_get_nrow(_fibers) < nf) {
2999 cpl_msg_debug(fctid, "Maximum offset from the expected "
3000 "position is %.2f, maximum allowed offset is %.2f",
3001 maxshift, maxoffset);
3002 cpl_msg_warning(fctid, "%" CPL_SIZE_FORMAT " fibers are "
3003 "missing!", nf - cpl_table_get_nrow(_fibers));
3004 }
3005
3006 }
3007
3008 }
3009
3010
3011 /*
3012 * Convert matrices into images and tables and add the necessary
3013 * properties.
3014 */
3015
3016 /* Spectrum center position */
3017
3018 result->locy =
3019 giraffe_image_create(CPL_TYPE_DOUBLE,
3020 cpl_matrix_get_ncol(mask_position.my),
3021 cpl_matrix_get_nrow(mask_position.my));
3022
3023 giraffe_image_copy_matrix(result->locy, mask_position.my);
3024 cpl_matrix_delete(mask_position.my);
3025
3026 giraffe_image_set_properties(result->locy, properties);
3027 properties = giraffe_image_get_properties(result->locy);
3028
3029 _result = giraffe_image_get(result->locy);
3030
3031 cpl_propertylist_set_int(properties, GIALIAS_NAXIS1,
3032 cpl_image_get_size_x(_result));
3033 cpl_propertylist_set_int(properties, GIALIAS_NAXIS2,
3034 cpl_image_get_size_y(_result));
3035 cpl_propertylist_set_int(properties, GIALIAS_BITPIX, -32);
3036 cpl_propertylist_set_double(properties, GIALIAS_BZERO, 0.);
3037 cpl_propertylist_set_double(properties, GIALIAS_BSCALE, 1.);
3038
3039 cpl_propertylist_append_int(properties, GIALIAS_LOCNX,
3040 cpl_image_get_size_y(_result));
3041 cpl_propertylist_append_int(properties, GIALIAS_LOCNS,
3042 cpl_image_get_size_x(_result));
3043
3044 if (config->centroid) {
3045 cpl_propertylist_append_string(properties, GIALIAS_LMETHOD,
3046 "BARYCENTER");
3047 }
3048 else {
3049 cpl_propertylist_append_string(properties, GIALIAS_LMETHOD,
3050 "HALF_WIDTH");
3051 }
3052
3053 if (config->normalize) {
3054 cpl_propertylist_append_int(properties, GIALIAS_LNORMALIZE,
3055 config->ywidth);
3056 }
3057 else {
3058 cpl_propertylist_append_int(properties, GIALIAS_LNORMALIZE,
3059 -config->ywidth);
3060 }
3061
3062 cpl_propertylist_append_bool(properties, GIALIAS_LFULLLOC, config->full);
3063 cpl_propertylist_append_int(properties, GIALIAS_LOCYDEG, config->yorder);
3064 cpl_propertylist_append_int(properties, GIALIAS_LOCWDEG, config->worder);
3065 cpl_propertylist_append_double(properties, GIALIAS_LEXTRAWID,
3066 config->ewidth);
3067 cpl_propertylist_append_double(properties, GIALIAS_LNOISEMULT,
3068 config->noise);
3069
3070 cpl_propertylist_append_double(properties, GIALIAS_LCLIPSIGMA,
3071 config->sigma);
3072 cpl_propertylist_append_int(properties, GIALIAS_LCLIPNITER,
3073 config->iterations);
3074 cpl_propertylist_append_double(properties, GIALIAS_LCLIPMFRAC,
3075 config->fraction);
3076
3077
3078 if (cpl_propertylist_has(properties, GIALIAS_GIRFTYPE)) {
3079 cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE, "LOCY");
3080 }
3081 else {
3082 cpl_propertylist_append_string(properties, GIALIAS_GIRFTYPE, "LOCY");
3083 }
3084 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE, "GIRAFFE "
3085 "localization centroid");
3086
3087
3088 /* Spectrum width */
3089
3090 result->locw =
3091 giraffe_image_create(CPL_TYPE_DOUBLE,
3092 cpl_matrix_get_ncol(mask_position.mw),
3093 cpl_matrix_get_nrow(mask_position.mw));
3094
3095 giraffe_image_copy_matrix(result->locw, mask_position.mw);
3096 cpl_matrix_delete(mask_position.mw);
3097
3098 giraffe_image_set_properties(result->locw, properties);
3099 properties = giraffe_image_get_properties(result->locw);
3100
3101 _result = giraffe_image_get(result->locw);
3102
3103 cpl_propertylist_set_int(properties, GIALIAS_NAXIS1,
3104 cpl_image_get_size_x(_result));
3105 cpl_propertylist_set_int(properties, GIALIAS_NAXIS2,
3106 cpl_image_get_size_y(_result));
3107
3108 if (cpl_propertylist_has(properties, GIALIAS_GIRFTYPE)) {
3109 cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE,
3110 "LOCWY");
3111 }
3112 else {
3113 cpl_propertylist_append_string(properties, GIALIAS_GIRFTYPE,
3114 "LOCWY");
3115 }
3116 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE, "GIRAFFE "
3117 "localization half-width");
3118
3119
3120 /* Coefficients table */
3121
3122 locc = cpl_table_new(cpl_matrix_get_ncol(mask_coeffs.my));
3123
3124 cpl_table_new_column(locc, "BUTTON", CPL_TYPE_INT);
3125 for (i = 0; i < cpl_table_get_nrow(locc); i++) {
3126 cpl_table_set_int(locc, "BUTTON", i, i);
3127 }
3128
3129 for (i = 0; i < cpl_matrix_get_nrow(mask_coeffs.my); i++) {
3130 cxchar *label = NULL;
3131
3132 cx_asprintf(&label, "YC%d", i);
3133 cpl_table_new_column(locc, label, CPL_TYPE_DOUBLE);
3134 cx_free(label);
3135 }
3136
3137
3138 result->locc = giraffe_table_create(locc, properties);
3139 cpl_table_delete(locc);
3140
3141 _my = cpl_matrix_transpose_create(mask_coeffs.my);
3142 giraffe_table_copy_matrix(result->locc, "YC0", _my);
3143 cpl_matrix_delete(_my);
3144 cpl_matrix_delete(mask_coeffs.my);
3145
3146 properties = giraffe_table_get_properties(result->locc);
3147
3148
3149 /* Add coefficients of the 2D fit to the table properties */
3150
3151 pname = cx_string_new();
3152
3153 for (i = 0; i < cpl_matrix_get_ncol(mask_coeffs.mw); i++) {
3154 cx_string_sprintf(pname, "%s%d", GIALIAS_LOCWIDCOEF, i);
3155 cpl_propertylist_append_double(properties, cx_string_get(pname),
3156 cpl_matrix_get(mask_coeffs.mw, 0, i));
3157 }
3158
3159 cx_string_delete(pname);
3160 cpl_matrix_delete(mask_coeffs.mw);
3161
3162 cpl_propertylist_update_string(properties, GIALIAS_GIRFTYPE,
3163 "LOCYWCHEB");
3164 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE, "GIRAFFE "
3165 "localization fit coefficients");
3166
3167
3168 /* Not used */
3169
3170 result->psf = NULL;
3171
3172 return 0;
3173
3174}
3175
3176
3187GiLocalizeConfig *
3188giraffe_localize_config_create(cpl_parameterlist *list)
3189{
3190
3191 const cxchar *s;
3192 cpl_parameter *p;
3193
3194 GiLocalizeConfig *config = NULL;
3195
3196
3197 if (list == NULL) {
3198 return NULL;
3199 }
3200
3201 config = cx_calloc(1, sizeof *config);
3202
3203
3204 /*
3205 * Some defaults
3206 */
3207
3208 config->full = TRUE;
3209 config->centroid = TRUE;
3210 config->threshold = GILOCALIZE_THRESHOLD_LOCAL;
3211
3212
3213 p = cpl_parameterlist_find(list, "giraffe.localization.mode");
3214 s = cpl_parameter_get_string(p);
3215 if (strcmp(s, "siwc") == 0) {
3216 config->full = FALSE;
3217 }
3218
3219 p = cpl_parameterlist_find(list, "giraffe.localization.start");
3220 config->start = cpl_parameter_get_int(p);
3221
3222 p = cpl_parameterlist_find(list, "giraffe.localization.retries");
3223 config->retries = cpl_parameter_get_int(p);
3224
3225 p = cpl_parameterlist_find(list, "giraffe.localization.binsize");
3226 config->binsize = cpl_parameter_get_int(p);
3227
3228 p = cpl_parameterlist_find(list, "giraffe.localization.ewidth");
3229 config->ewidth = cpl_parameter_get_double(p);
3230
3231 p = cpl_parameterlist_find(list, "giraffe.localization.ywidth");
3232 config->ywidth = cpl_parameter_get_int(p);
3233
3234 p = cpl_parameterlist_find(list, "giraffe.localization.center");
3235 s = cpl_parameter_get_string(p);
3236 if (!strcmp(s, "hwidth")) {
3237 config->centroid = FALSE;
3238 }
3239
3240 p = cpl_parameterlist_find(list, "giraffe.localization.normalize");
3241 config->normalize = cpl_parameter_get_bool(p);
3242
3243 p = cpl_parameterlist_find(list, "giraffe.localization.threshold");
3244 s = cpl_parameter_get_string(p);
3245
3246 if (strncmp(s, "global", 6) == 0) {
3247 config->threshold = GILOCALIZE_THRESHOLD_GLOBAL;
3248 }
3249 else if (strncmp(s, "row", 3) == 0) {
3250 config->threshold = GILOCALIZE_THRESHOLD_ROW;
3251 }
3252 else {
3253 config->threshold = GILOCALIZE_THRESHOLD_LOCAL;
3254 }
3255
3256 p = cpl_parameterlist_find(list, "giraffe.localization.noise");
3257 config->noise = cpl_parameter_get_double(p);
3258
3259 p = cpl_parameterlist_find(list, "giraffe.localization.ron");
3260 config->ron = cpl_parameter_get_double(p);
3261
3262 p = cpl_parameterlist_find(list, "giraffe.localization.yorder");
3263 config->yorder = cpl_parameter_get_int(p);
3264
3265 p = cpl_parameterlist_find(list, "giraffe.localization.worder");
3266 config->worder = cpl_parameter_get_int(p);
3267
3268 p = cpl_parameterlist_find(list, "giraffe.localization.sigma");
3269 config->sigma = cpl_parameter_get_double(p);
3270
3271 p = cpl_parameterlist_find(list, "giraffe.localization.iterations");
3272 config->iterations = cpl_parameter_get_int(p);
3273
3274 p = cpl_parameterlist_find(list, "giraffe.localization.fraction");
3275 config->fraction = cpl_parameter_get_double(p);
3276
3277 return config;
3278
3279}
3280
3281
3294void
3295giraffe_localize_config_destroy(GiLocalizeConfig *config)
3296{
3297
3298 if (config) {
3299 cx_free(config);
3300 }
3301
3302 return;
3303
3304}
3305
3306
3318void
3319giraffe_localize_config_add(cpl_parameterlist *list)
3320{
3321
3322 cpl_parameter *p;
3323
3324
3325 if (list == NULL) {
3326 return;
3327 }
3328
3329 p = cpl_parameter_new_enum("giraffe.localization.mode",
3330 CPL_TYPE_STRING,
3331 "Localization mode: Use all spectra "
3332 "or the 5 SIWC spectra",
3333 "giraffe.localization",
3334 "all", 2, "all", "siwc");
3335 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-mode");
3336 cpl_parameterlist_append(list, p);
3337
3338
3339 p = cpl_parameter_new_value("giraffe.localization.start",
3340 CPL_TYPE_INT,
3341 "Bin along x-axis",
3342 "giraffe.localization",
3343 -1);
3344 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-start");
3345 cpl_parameterlist_append(list, p);
3346
3347
3348 p = cpl_parameter_new_value("giraffe.localization.retries",
3349 CPL_TYPE_INT,
3350 "Initial localization detection "
3351 "xbin retries.",
3352 "giraffe.localization",
3353 10);
3354 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-retries");
3355 cpl_parameterlist_append(list, p);
3356
3357
3358 p = cpl_parameter_new_value("giraffe.localization.binsize",
3359 CPL_TYPE_INT,
3360 "Initial localization detection "
3361 "xbin size.",
3362 "giraffe.localization",
3363 -1);
3364 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-binsize");
3365 cpl_parameterlist_append(list, p);
3366
3367
3368 p = cpl_parameter_new_value("giraffe.localization.ewidth",
3369 CPL_TYPE_DOUBLE,
3370 "Localization detection extra width.",
3371 "giraffe.localization",
3372 1.0);
3373 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-ewidth");
3374 cpl_parameterlist_append(list, p);
3375
3376
3377 p = cpl_parameter_new_value("giraffe.localization.ywidth",
3378 CPL_TYPE_INT,
3379 "Full width [pxl] of the equilizing "
3380 "filter (distance between two "
3381 "adjacent fibers).",
3382 "giraffe.localization",
3383 -1);
3384 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-ywidth");
3385 cpl_parameterlist_append(list, p);
3386
3387
3388 p = cpl_parameter_new_enum("giraffe.localization.center",
3389 CPL_TYPE_STRING,
3390 "Method used for mask center "
3391 "computation.",
3392 "giraffe.localization",
3393 "centroid", 2, "centroid",
3394 "hwidth");
3395 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-center");
3396 cpl_parameterlist_append(list, p);
3397
3398
3399 p = cpl_parameter_new_value("giraffe.localization.normalize",
3400 CPL_TYPE_BOOL,
3401 "Enable spectrum normalization along "
3402 "the dispersion axis.",
3403 "giraffe.localization",
3404 FALSE);
3405 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-norm");
3406 cpl_parameterlist_append(list, p);
3407
3408
3409 p = cpl_parameter_new_value("giraffe.localization.noise",
3410 CPL_TYPE_DOUBLE,
3411 "Threshold multiplier.",
3412 "giraffe.localization",
3413 7.0);
3414 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-noise");
3415 cpl_parameterlist_append(list, p);
3416
3417
3418 p = cpl_parameter_new_enum("giraffe.localization.threshold",
3419 CPL_TYPE_STRING,
3420 "Selects thresholding algorithm: local, "
3421 "row or global",
3422 "giraffe.localization",
3423 "local", 3, "local", "row", "global");
3424 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-threshold");
3425 cpl_parameterlist_append(list, p);
3426
3427
3428 p = cpl_parameter_new_value("giraffe.localization.ron",
3429 CPL_TYPE_DOUBLE,
3430 "New bias sigma (RON) value for dark "
3431 "subtraction",
3432 "giraffe.localization",
3433 -1.);
3434 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-ron");
3435 cpl_parameterlist_append(list, p);
3436
3437
3438 p = cpl_parameter_new_value("giraffe.localization.yorder",
3439 CPL_TYPE_INT,
3440 "Order of Chebyshev polynomial fit.",
3441 "giraffe.localization",
3442 4);
3443 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-yorder");
3444 cpl_parameterlist_append(list, p);
3445
3446
3447 p = cpl_parameter_new_value("giraffe.localization.worder",
3448 CPL_TYPE_INT,
3449 "Order of Chebyshev 2D polynomial fit.",
3450 "giraffe.localization",
3451 2);
3452 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-worder");
3453 cpl_parameterlist_append(list, p);
3454
3455
3456 p = cpl_parameter_new_value("giraffe.localization.sigma",
3457 CPL_TYPE_DOUBLE,
3458 "Localization clipping: sigma threshold "
3459 "factor",
3460 "giraffe.localization",
3461 2.5);
3462 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-sigma");
3463 cpl_parameterlist_append(list, p);
3464
3465
3466 p = cpl_parameter_new_value("giraffe.localization.iterations",
3467 CPL_TYPE_INT,
3468 "Localization clipping: number of "
3469 "iterations",
3470 "giraffe.localization",
3471 5);
3472 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-niter");
3473 cpl_parameterlist_append(list, p);
3474
3475
3476 p = cpl_parameter_new_range("giraffe.localization.fraction",
3477 CPL_TYPE_DOUBLE,
3478 "Localization clipping: minimum fraction "
3479 "of points accepted/total.",
3480 "giraffe.localization",
3481 0.9, 0.0, 1.0);
3482 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-mfrac");
3483 cpl_parameterlist_append(list, p);
3484
3485 return;
3486
3487}
cxint giraffe_array_sort(cxdouble *array, cxsize size)
Sorts an array in ascending order.
Definition: giarray.c:169
cpl_image * giraffe_image_get(const GiImage *self)
Gets the image data.
Definition: giimage.c:218
cpl_propertylist * giraffe_image_get_properties(const GiImage *self)
Get the properties of an image.
Definition: giimage.c:282
GiImage * giraffe_image_create(cpl_type type, cxint nx, cxint ny)
Creates an image container of a given type.
Definition: giimage.c:95
cxint giraffe_image_copy_matrix(GiImage *self, cpl_matrix *matrix)
Copies matrix elements into an image.
Definition: giimage.c:345
cxint giraffe_image_set_properties(GiImage *self, cpl_propertylist *properties)
Attaches a property list to an image.
Definition: giimage.c:312
GiLocalizeConfig * giraffe_localize_config_create(cpl_parameterlist *list)
Creates a setup structure for the spectrum localization.
Definition: gilocalize.c:3188
void giraffe_localize_config_destroy(GiLocalizeConfig *config)
Destroys a spectrum localization setup structure.
Definition: gilocalize.c:3295
void giraffe_localize_config_add(cpl_parameterlist *list)
Adds parameters for the spectrum localization.
Definition: gilocalize.c:3319
cxint giraffe_localize_spectra(GiLocalization *result, GiImage *image, GiTable *fibers, GiLocalization *master, GiImage *badpixels, GiLocalizeConfig *config)
Finds the location of spectra in a Giraffe observation.
Definition: gilocalize.c:2560
cpl_image * giraffe_matrix_create_image(const cpl_matrix *matrix)
Converts a matrix into an image.
Definition: gimatrix.c:337
cpl_matrix * giraffe_matrix_leastsq(const cpl_matrix *mA, const cpl_matrix *mB)
Computes the solution of an equation using a pseudo-inverse.
Definition: gimatrix.c:503
cxdouble giraffe_matrix_sigma_mean(const cpl_matrix *matrix, cxdouble mean)
Compute sigma of matrix elements, with a given mean value.
Definition: gimatrix.c:229
void gi_warning(const cxchar *format,...)
Log a warning.
Definition: gimessages.c:119
void gi_error(const cxchar *format,...)
Log an error message.
Definition: gimessages.c:59
void gi_message(const cxchar *format,...)
Log a normal message.
Definition: gimessages.c:146
GiTable * giraffe_table_create(cpl_table *table, cpl_propertylist *properties)
Creates a Giraffe table from a table and a property list.
Definition: gitable.c:115
cxint giraffe_table_copy_matrix(GiTable *table, const cxchar *name, cpl_matrix *matrix)
Copies matrix elements into a table.
Definition: gitable.c:259
cpl_table * giraffe_table_get(const GiTable *self)
Get the table data from a Giraffe table.
Definition: gitable.c:433
cpl_propertylist * giraffe_table_get_properties(const GiTable *self)
Gets the table properties.
Definition: gitable.c:489
cxdouble giraffe_propertylist_get_conad(const cpl_propertylist *properties)
Retrieve the ADU to electrons conversion factor from the given properties.
Definition: giutils.c:1469
cxdouble giraffe_propertylist_get_ron(const cpl_propertylist *properties)
Retrieve the read-out noise from the given properties.
Definition: giutils.c:1553
GiInstrumentMode giraffe_get_mode(cpl_propertylist *properties)
Determines the instrument mode from a property list.
Definition: giutils.c:440

This file is part of the GIRAFFE Pipeline Reference Manual 2.16.11.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Wed Apr 17 2024 20:34:24 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2004