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

This file is part of the GIRAFFE Pipeline Reference Manual 2.19.4.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Fri Feb 6 2026 13:47:22 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2004