GIRAFFE Pipeline Reference Manual

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

This file is part of the GIRAFFE Pipeline Reference Manual 2.14.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Wed Mar 11 2015 13:19:41 by doxygen 1.8.9.1 written by Dimitri van Heesch, © 1997-2004