GIRAFFE Pipeline Reference Manual

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

This file is part of the GIRAFFE Pipeline Reference Manual 2.16.10.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Thu Dec 15 2022 21:18:51 by doxygen 1.9.1 written by Dimitri van Heesch, © 1997-2004