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

This file is part of the GIRAFFE Pipeline Reference Manual 2.12.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Mon Mar 24 2014 11:43:52 by doxygen 1.8.2 written by Dimitri van Heesch, © 1997-2004