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