GIRAFFE Pipeline Reference Manual

gilocalize.c

00001 /* $Id: gilocalize.c,v 1.52 2011/12/23 13:41:08 rpalsa Exp $
00002  *
00003  * This file is part of the GIRAFFE Pipeline
00004  * Copyright (C) 2002-2006 European Southern Observatory
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00019  */
00020 
00021 /*
00022  * $Author: rpalsa $
00023  * $Date: 2011/12/23 13:41:08 $
00024  * $Revision: 1.52 $
00025  * $Name: giraffe-2_10 $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #  include <config.h>
00030 #endif
00031 
00032 #include <string.h>
00033 #include <math.h>
00034 
00035 #include <cxstring.h>
00036 #include <cxmemory.h>
00037 
00038 #include <cpl_image.h>
00039 #include <cpl_vector.h>
00040 #include <cpl_matrix.h>
00041 #include <cpl_mask.h>
00042 #include <cpl_parameterlist.h>
00043 #include <cpl_msg.h>
00044 
00045 #include "gimacros.h"
00046 #include "gialias.h"
00047 #include "giarray.h"
00048 #include "giimage.h"
00049 #include "gitable.h"
00050 #include "gimatrix.h"
00051 #include "giarray.h"
00052 #include "gimask.h"
00053 #include "gimath.h"
00054 #include "gimessages.h"
00055 #include "giutils.h"
00056 #include "gilocalize.h"
00057 #include "gidebug.h"
00058 
00059 
00060 
00069 /*
00070  * Main task identifier. Used for terminal output from internal functions.
00071  */
00072 
00073 static const cxchar* _task = "giraffe_localize_spectra";
00074 
00075 
00076 /*
00077  * Method used to compute the fiber centroid position
00078  */
00079 
00080 enum GiLocalizeMethod
00081 {
00082     GILOCALIZE_HALF_WIDTH,
00083     GILOCALIZE_BARYCENTER
00084 };
00085 
00086 typedef enum GiLocalizeMethod GiLocalizeMethod;
00087 
00088 
00089 /*
00090  * Thresholding policy used to detect valid spectrum pixels
00091  */
00092 
00093 enum GiThresholdMethod
00094 {
00095     GILOCALIZE_THRESHOLD_GLOBAL,
00096     GILOCALIZE_THRESHOLD_LOCAL,
00097     GILOCALIZE_THRESHOLD_ROW
00098 };
00099 
00100 typedef enum GiThresholdMethod GiThresholdMethod;
00101 
00102 
00103 
00104 /*
00105  * @brief
00106  *   Check whether a pixel in a detection mask belongs to a spectrum.
00107  *
00108  * @param pixels  The pixel buffer.
00109  * @param xsize   The size of the pixel buffer along x.
00110  * @param ysize   The size of the pixel buffer along y.
00111  * @param xpos    x-position of the pixel to check.
00112  * @param ypos    y-position of the pixel to check.
00113  * @param xwidth  Half width of the pixel neighbourhood along x.
00114  * @param ywidth  Half width of the pixel neighbourhood along y.
00115  * @param count   The number of required, non-zero mask pixels.
00116  *
00117  * @return The function returns 1 if the pixel at (@em xpos, @em ypos) is
00118  *   found to be valid, or 0 otherwise.
00119  *
00120  * The function checks whether the pixel at position (@em xpos, @em ypos) in
00121  * the detection mask's pixel buffer @em pixels belongs to a spectrum. It
00122  * expects in input a pixel buffer of a detection mask, i.e. the pixel values
00123  * must be non-zero only at pixel positions associated to a positive
00124  * detection.
00125  *
00126  * A pixel is considered to be valid if, at least, @em count non-zero pixels
00127  * are found in the neighborhood of the pixel position (@em xpos, @em ypos).
00128  * The neighborhood is specified by @em xsize and @em ysize the number of
00129  * pixels to be checked on both sides of the pixel along the x and y axis.
00130  * The pixel row given by @em ypos which contains the pixel to check is
00131  * not considered when the pixel neighbourhood is checked. Assuming that
00132  * the spectra extend along the y-axis only the neighbours along the
00133  * dispersion axis are taken into account.
00134  */
00135 
00136 inline static cxbool
00137 _giraffe_validate_pixel(cxint *pixels, cxint xsize, cxint ysize,
00138                         cxint xpos, cxint ypos, cxint xwidth, cxint ywidth,
00139                         cxsize count)
00140 {
00141 
00142     cxint i;
00143     cxint xstart = xpos - xwidth;
00144     cxint ystart = ypos - ywidth;
00145     cxint xend = xpos + xwidth;
00146     cxint yend = ypos + ywidth;
00147 
00148     cxsize _count = 0;
00149 
00150 
00151 
00152     /*
00153      * Clip start and end positions to pixel buffer boundaries
00154      */
00155 
00156     xstart = CX_MAX(0, xstart);
00157     ystart = CX_MAX(0, ystart);
00158 
00159     xend = CX_MIN(xsize - 1, xend);
00160     yend = CX_MIN(ysize - 1, yend);
00161 
00162     xwidth = CX_MAX(xwidth,1 );
00163     ywidth = CX_MAX(ywidth,1 );
00164 
00165 
00166     /*
00167      * Search for count non-zero pixel values in the pixel region
00168      * defined by the rectangle (xstart, ystart, xend, yend).
00169      */
00170 
00171     for (i = ystart; i <= yend; i++) {
00172 
00173         cxint j;
00174         cxint row;
00175 
00176 
00177         /*
00178          * Skip the pixel row containing the pixel to check. Since the pixel
00179          * should be checked whether it belongs to a spectrum (extending
00180          * along the y-axis) we only check the adjacent pixel rows on
00181          * both sides.
00182          */
00183 
00184         if (i == ypos) {
00185             continue;
00186         }
00187 
00188         row = i * xsize;
00189 
00190         for (j = xstart; j <= xend; j++) {
00191             if (pixels[row + j]) {
00192                 ++_count;
00193             }
00194 
00195             if (_count >= count) {
00196                 return 1;
00197             }
00198         }
00199 
00200     }
00201 
00202     return 0;
00203 
00204 }
00205 
00206 
00207 /*
00208  * @brief
00209  *   Polynomial fit of raw spectrum region border.
00210  *
00211  * @param mborder   Y of detected borders
00212  * @param mbase     Full Chebyshev base
00213  * @param mxok      Good abcissa
00214  * @param nspectra  Spectrum number
00215  * @param sigma     Sigma clipping: sigma threshold level
00216  * @param niter     Sigma clipping: number of iterations
00217  * @param mfrac     Sigma clipping: minimum fraction of points accepted/total
00218  * @param mcoeff    Computed Chebyshev coefficients
00219  *
00220  * @return Matrix with the polynomial fit of @em mborder.
00221  *
00222  * Computes Chebyshev polynomial fit of @em mborder[:,nspectra].
00223  * The order of the polynomial fit is given by the Chebyshev base
00224  * @em mbase 1st dimension. The matrices @em mxtmp and @em mcoeff
00225  * are pre-allocated. The returned matrix @em mfit must be freed
00226  * using @b cpl_matrix_delete().
00227  *
00228  * @code
00229  *   mfit = _giraffe_fit_border(mborder, mbase, mxtmp, mxok, nspectra,
00230  *                               sigma, niter, mfrac, mcoeff);
00231  * @endcode
00232  */
00233 
00234 inline static cpl_matrix*
00235 _giraffe_fit_border(cpl_matrix* mborder, cpl_matrix* mbase,
00236                     cpl_matrix* mxok, cxint nspectra, cxdouble sigma,
00237                     cxint niter, cxdouble mfrac, cpl_matrix* mcoeff)
00238 {
00239 
00240     const cxchar* const fctid = "_giraffe_fit_border";
00241 
00242     register cxint x = 0;
00243     register cxint naccept = 0;
00244     register cxint ntotal = 0;
00245     register cxint iteration = 0;
00246     register cxint nx = cpl_matrix_get_ncol(mbase);
00247     register cxint yorder = cpl_matrix_get_nrow(mbase);
00248     register cxint nxok = cpl_matrix_get_nrow(mxok);
00249 
00250     register cxdouble ratio = 1.0;
00251 
00252     cpl_matrix* mtmp = NULL;
00253     cpl_matrix* yraw = NULL;
00254     cpl_matrix* ydiff = NULL;
00255     cpl_matrix* mfit = NULL;
00256     cpl_matrix* coeffs = NULL;
00257 
00258 
00259 
00260     if (nxok < yorder) {
00261         cpl_error_set(fctid, CPL_ERROR_INCOMPATIBLE_INPUT);
00262 
00263         GIDEBUG(gi_warning("%s: not enough points mxok[%d] for %d order fit",
00264                            fctid, nxok, yorder));
00265 
00266         return NULL;
00267     }
00268 
00269 
00270     /*
00271      * Initialize X,Y to be fit
00272      */
00273 
00274     yraw = cpl_matrix_new(1, nxok);
00275     ydiff = cpl_matrix_new(nxok, 1);
00276 
00277     mtmp = cpl_matrix_duplicate(mxok);
00278 
00279     /*
00280      * For each good x bin
00281      */
00282 
00283     for (x = 0; x < nxok; x++) {
00284         cxdouble data = cpl_matrix_get(mborder, x, nspectra);
00285         cpl_matrix_set(yraw, 0, x, data);
00286     }
00287 
00288 
00289     /*
00290      * Here comes the sigma clipping
00291      */
00292 
00293     ntotal = nxok;
00294     naccept = ntotal;
00295 
00296     while (naccept > 0 && iteration < niter && ratio > mfrac) {
00297 
00298         register cxint k = 0;
00299         register cxint l = 0;
00300 
00301         register cxdouble ysigma = 0.;
00302 
00303         cpl_matrix* rawbase = giraffe_chebyshev_base1d(0., nx, yorder, mtmp);
00304         cx_assert(rawbase != NULL);
00305 
00306         if (coeffs != NULL) {
00307             cpl_matrix_delete(coeffs);
00308         }
00309 
00310         coeffs = giraffe_matrix_leastsq(rawbase, yraw);
00311         if (coeffs == NULL) {
00312             gi_warning("%s: error in giraffe_matrix_leastsq(), spectrum %d",
00313                        fctid, nspectra);
00314             break;
00315         }
00316 
00317         cpl_matrix_delete(rawbase);
00318         rawbase = NULL;
00319 
00320         if (mfit != NULL) {
00321             cpl_matrix_delete(mfit);
00322         }
00323 
00324         mfit = cpl_matrix_product_create(coeffs, mbase);
00325 
00326         for (x = 0; x < cpl_matrix_get_nrow(ydiff); x++) {
00327 
00328             cxint xok = (cxint) cpl_matrix_get(mtmp, x, 0);
00329 
00330             cxdouble diff =
00331                 cpl_matrix_get(yraw, 0, x) - cpl_matrix_get(mfit, 0, xok);
00332 
00333 
00334             cpl_matrix_set(ydiff, x , 0, diff);
00335 
00336         }
00337 
00338         ysigma = sigma * giraffe_matrix_sigma_mean(ydiff, 0.);
00339 
00340 
00341         /*
00342          * Reset sizes
00343          */
00344 
00345         k = 0;
00346         for (l = 0; l < cpl_matrix_get_nrow(ydiff); l++) {
00347 
00348             if (fabs(cpl_matrix_get(ydiff, l, 0)) <= ysigma) {
00349 
00350                 cxint xok = cpl_matrix_get(mtmp, l, 0);
00351                 cxdouble data = cpl_matrix_get(yraw, 0, l);
00352 
00353                 cpl_matrix_set(mtmp, k, 0, xok);
00354                 cpl_matrix_set(yraw, 0, k, data);
00355 
00356                 ++k;
00357             }
00358 
00359         }
00360 
00361 
00362         /*
00363          * No new points rejected, no more iterations
00364          */
00365 
00366         if (k == naccept) {
00367             break;
00368         }
00369 
00370 
00371         /*
00372          * Merry-go-round once more
00373          */
00374 
00375         naccept = k;
00376         ratio = (cxdouble) naccept / (cxdouble) ntotal;
00377 
00378         GIDEBUG(gi_message("Iteration %d: Sigma %f, accepted bins: %d, "
00379                            "rejected %d\n", iteration, ysigma, naccept,
00380                            ntotal - naccept));
00381 
00382         /*
00383          * Extract the new clipped matrices
00384          */
00385 
00386         cpl_matrix_resize(mtmp, 0,
00387                           naccept - cpl_matrix_get_nrow(mtmp), 0, 0);
00388         cpl_matrix_resize(yraw, 0,
00389                           0, 0, naccept - cpl_matrix_get_ncol(yraw));
00390         cpl_matrix_resize(ydiff, 0,
00391                           naccept - cpl_matrix_get_nrow(ydiff), 0, 0);
00392 
00393         iteration++;
00394     }
00395 
00396     if (coeffs != NULL) {
00397         register cxint l;
00398 
00399         for (l = 0; l < cpl_matrix_get_nrow(mcoeff); l++) {
00400             cpl_matrix_set(mcoeff, l, 0, cpl_matrix_get(coeffs, 0, l));
00401         }
00402     }
00403 
00404 
00405     /*
00406      * Cleanup
00407      */
00408 
00409     cpl_matrix_delete(coeffs);
00410     cpl_matrix_delete(ydiff);
00411     cpl_matrix_delete(yraw);
00412     cpl_matrix_delete(mtmp);
00413 
00414     return mfit;
00415 
00416 }
00417 
00418 
00419 inline static cpl_image*
00420 _giraffe_filter_gauss1d(const cpl_image* image, cxint radius, cxdouble width)
00421 {
00422 
00423     cxdouble w2 = width * width;
00424 
00425     cxint i = 0;
00426 
00427     cpl_matrix* kernel = cpl_matrix_new(1, 2 * radius + 1);
00428 
00429     cpl_image* fimage = NULL;
00430 
00431 
00432     if (kernel == NULL) {
00433         return NULL;
00434     }
00435 
00436     for (i = -radius; i <= radius; ++i) {
00437         cxdouble x2 = i * i;
00438         cxdouble y    = exp(-x2 / (2. * w2));
00439 
00440         cpl_matrix_set(kernel, 0, i + radius, y);
00441     }
00442 
00443 
00444     fimage = cpl_image_new(cpl_image_get_size_x(image),
00445                            cpl_image_get_size_y(image),
00446                            cpl_image_get_type(image));
00447 
00448     if (fimage == NULL) {
00449         cpl_matrix_delete(kernel);
00450         return NULL;
00451     }
00452 
00453     cpl_image_filter(fimage, image, kernel, CPL_FILTER_LINEAR,
00454                      CPL_BORDER_FILTER);
00455     cpl_matrix_delete(kernel);
00456 
00457     return fimage;
00458 
00459 }
00460 
00461 
00462 inline static cpl_image*
00463 _giraffe_filter_sobel(const cpl_image* image, cxbool vertical)
00464 {
00465     cpl_matrix* kernel = cpl_matrix_new(3, 3);
00466 
00467     cpl_image* fimage = NULL;
00468 
00469 
00470     if (kernel == NULL) {
00471         return NULL;
00472     }
00473 
00474     if (vertical) {
00475 
00476 #if 1
00477         cpl_matrix_set(kernel, 0, 0, -1);
00478         cpl_matrix_set(kernel, 1, 0, -2);
00479         cpl_matrix_set(kernel, 2, 0, -1);
00480 
00481         cpl_matrix_set(kernel, 0, 2, 1);
00482         cpl_matrix_set(kernel, 1, 2, 2);
00483         cpl_matrix_set(kernel, 2, 2, 1);
00484 #else
00485         cpl_matrix_set(kernel, 0, 0,  0);
00486         cpl_matrix_set(kernel, 1, 0, -0.5);
00487         cpl_matrix_set(kernel, 2, 0,  0);
00488 
00489         cpl_matrix_set(kernel, 0, 2, 0);
00490         cpl_matrix_set(kernel, 1, 2, 0.5);
00491         cpl_matrix_set(kernel, 2, 2, 0);
00492 #endif
00493 
00494     }
00495     else {
00496         cpl_matrix_set(kernel, 0, 0, 1);
00497         cpl_matrix_set(kernel, 0, 1, 2);
00498         cpl_matrix_set(kernel, 0, 2, 1);
00499 
00500         cpl_matrix_set(kernel, 2, 0, -1);
00501         cpl_matrix_set(kernel, 2, 1, -2);
00502         cpl_matrix_set(kernel, 2, 2, -1);
00503     }
00504 
00505 
00506     fimage = cpl_image_new(cpl_image_get_size_x(image),
00507                            cpl_image_get_size_y(image),
00508                            cpl_image_get_type(image));
00509 
00510     if (fimage == NULL) {
00511         cpl_matrix_delete(kernel);
00512         return NULL;
00513     }
00514 
00515     cpl_image_filter(fimage, image, kernel, CPL_FILTER_LINEAR,
00516                      CPL_BORDER_FILTER);
00517     cpl_matrix_delete(kernel);
00518 
00519     return fimage;
00520 
00521 }
00522 
00523 
00524 inline static cxint
00525 _giraffe_build_edge_mask(cpl_image* raw, cpl_image* bpixel, cxint nspectra,
00526                         cxdouble noise, GiMaskParameters* config,
00527                         cxint* ndetect, cpl_matrix* mxok, cpl_matrix* myup,
00528                         cpl_matrix* mylo)
00529 {
00530 
00531     const cxint margin = 5;
00532 
00533     cxint m         = 0;
00534     cxint itrace    = 0;
00535     cxint ispectra  = 0;
00536     cxint mmax      = 0;
00537     cxint smax      = 0;
00538     cxint naccepted = 0;
00539     cxint nrows     = cpl_image_get_size_y(raw);
00540     cxint ncols     = cpl_image_get_size_x(raw);
00541 
00542     cxint* flags = NULL;
00543 
00544     cxdouble* buffer = NULL;
00545 
00546     cpl_mask* kernel = NULL;
00547 
00548     cpl_image* fraw       = NULL;
00549     cpl_image* sraw       = NULL;
00550     cpl_image* vertical1  = NULL;
00551     cpl_image* vertical2  = NULL;
00552     cpl_image* center     = NULL;
00553 
00554 
00555     *ndetect = 0;
00556 
00557 
00558     /*
00559      * Simple cosmics removal. Median filter image along the dispersion
00560      * direction.
00561      */
00562 
00563     kernel = cpl_mask_new(1, 15);
00564 
00565     if (kernel != NULL) {
00566 
00567         cpl_mask_not(kernel);
00568 
00569         fraw = cpl_image_new(ncols, nrows, cpl_image_get_type(raw));
00570 
00571         if (fraw == NULL) {
00572             cpl_mask_delete(kernel);
00573             kernel = NULL;
00574 
00575             return -3;
00576         }
00577 
00578         cpl_image_filter_mask(fraw, raw, kernel, CPL_FILTER_MEDIAN,
00579                               CPL_BORDER_FILTER);
00580 
00581     }
00582 
00583     cpl_mask_delete(kernel);
00584     kernel = NULL;
00585 
00586 
00587     sraw = _giraffe_filter_gauss1d(fraw, 6, 1.);
00588 
00589     if (sraw == NULL) {
00590 
00591         cpl_image_delete(fraw);
00592         fraw = NULL;
00593 
00594         return -3;
00595 
00596     }
00597 
00598     vertical1  = _giraffe_filter_sobel(sraw, TRUE);
00599     vertical2  = _giraffe_filter_sobel(vertical1, TRUE);
00600 
00601     cpl_image_save(sraw, "master_flat_smooth.fits", -32, 0, CPL_IO_DEFAULT);
00602     cpl_image_save(vertical1, "vertical.fits", -32, 0, CPL_IO_DEFAULT);
00603     cpl_image_save(vertical2, "vertical2.fits", -32, 0, CPL_IO_DEFAULT);
00604 
00605 
00606     /*
00607      *  Detection of fibers
00608      */
00609 
00610     center = cpl_image_new(ncols, nrows, CPL_TYPE_INT);
00611 
00612     flags  = cx_calloc(ncols, sizeof(cxint));
00613     buffer = cx_calloc(ncols, sizeof(cxdouble));
00614 
00615     if ((center == NULL) || (flags ==NULL) || (buffer == NULL)) {
00616 
00617         cx_free(buffer);
00618         buffer = NULL;
00619 
00620         cx_free(flags);
00621         flags = NULL;
00622 
00623         cpl_image_delete(center);
00624         center = NULL;
00625 
00626         cpl_image_delete(vertical2);
00627         vertical2 = NULL;
00628 
00629         cpl_image_delete(vertical1);
00630         vertical1 = NULL;
00631 
00632         cpl_image_delete(sraw);
00633         sraw = NULL;
00634 
00635         cpl_image_delete(fraw);
00636         fraw = NULL;
00637 
00638         return -3;
00639 
00640     }
00641 
00642 
00643     for (m = 0; m < nrows; ++m) {
00644 
00645         register cxint irow = m * ncols;
00646         register cxint n    = 0;
00647 
00648         cxint scount    = 0;
00649         cxint iteration = 0;
00650 
00651         cxint* _center = cpl_image_get_data_int(center) + irow;
00652 
00653         const cxdouble* _vt1  = cpl_image_get_data_double_const(vertical1) +
00654                 irow;
00655         const cxdouble* _vt2  = cpl_image_get_data_double_const(vertical2) +
00656                 irow;
00657         const cxdouble* _fraw = cpl_image_get_data_double_const(fraw) +
00658                 irow;
00659 
00660 
00661         memset(buffer, 0, ncols * sizeof(cxdouble));
00662         memset(flags, 0, ncols * sizeof(cxint));
00663 
00664 #if 1
00665         for (n = 0; n < ncols; ++n) {
00666 
00667 //            if ((_vt2[n] > 0.) || (n <= margin) || (n >= ncols - margin)) {
00668 //                buffer[n] = 0.;
00669 //            }
00670 //            if (_vt2[n] > 0.) {
00671 //                buffer[n] = 0.;
00672 //            }
00673             if (_vt2[n] <= 0.) {
00674                 buffer[n] = _vt1[n];
00675                 if ((n - 1 >= 0) && (_vt2[n - 1] > 0.)) {
00676                     buffer[n - 1] = _vt1[n - 1];
00677                 }
00678                 if ((n + 1 < ncols) && (_vt2[n + 1] > 0.)) {
00679                     buffer[n + 1] = _vt1[n + 1];
00680                 }
00681             }
00682         }
00683 #endif
00684 
00685         while (iteration < ncols) {
00686 
00687             cxint pos = -1;
00688 
00689             cxdouble dx = 3. * 2. * noise;
00690 
00691 
00692             for (n = 0; n < ncols; ++n) {
00693 
00694                 if (!flags[n] && (buffer[n] > dx)) {
00695                     dx = buffer[n];
00696                     pos = n;
00697                 }
00698 
00699             }
00700 
00701 
00702             if (pos >= 0) {
00703 
00704                 register cxint k = 0;
00705 
00706                 cxint start  = pos;
00707                 cxint end    = pos;
00708                 cxint width  = 0;
00709 
00710                 cxdouble sigma  = 0.;
00711                 cxdouble signal = 0.;
00712 
00713 
00714                 flags[pos] = 1;
00715 
00716                 k = pos - 1;
00717                 while ((k >= 0) && (buffer[k] > 0.)) {
00718                     flags[k] = 1;
00719                     start = k;
00720                     --k;
00721                 }
00722 
00723                 k = pos + 1;
00724                 while ((k < ncols) && (buffer[k] > 0.)) {
00725                     flags[k] = 1;
00726                     ++k;
00727                 }
00728                 pos = k - 1;
00729 
00730                 while ((k < ncols) && (buffer[k] < 0.)) {
00731                     flags[k] = 1;
00732                     end = k;
00733                     ++k;
00734                 }
00735                 width = end - start + 1;
00736 
00737 
00738                 /*
00739                  * Compute signal to noise ratio at the expected central
00740                  * position.
00741                  */
00742 
00743                 signal = (_fraw[pos] > 0.) ? _fraw[pos] : 0.;
00744                 sigma = sqrt((noise * noise + signal) / config->xbin);
00745 
00746                 if ((signal / sigma > 10.) && (width > 1)) {
00747 
00748                     start = (start == pos) ? start - 1 : start;
00749                     end   = (end == pos) ? end + 1 : end;
00750 
00751                     _center[pos] += 1;
00752                     _center[start] += -1;
00753                     _center[end] += -2;
00754 
00755                 }
00756 
00757             }
00758 
00759             ++iteration;
00760 
00761         }
00762 
00763         for (n = 0; n < ncols; ++n) {
00764 
00765             if (_center[n] == 1) {
00766                 ++scount;
00767             }
00768 
00769         }
00770 
00771         if (scount >= smax) {
00772             smax = scount;
00773             mmax = m;
00774         }
00775 
00776     }
00777 
00778     cx_free(buffer);
00779     buffer = NULL;
00780 
00781     cx_free(flags);
00782     flags = NULL;
00783 
00784     // FIXME: Test code only! Turn this experimental code into a final
00785     //        implementation.
00786 
00787     cx_print("scount: %d (%d) at %d\n", smax, nspectra, mmax);
00788 
00789 
00790     /*
00791      * Remove bad detections (incomplete fibers, missed spurious detections)
00792      */
00793 
00794     //const cxint limit = 0.95 * nrows;
00795     const cxint limit = 0.85 * nrows;
00796 
00797 
00798     /* Factor to scale the sigma of a Gaussian to its HWHM */
00799 
00800     const cxdouble hwf = sqrt(2. * log(2.));
00801 
00802     cxint* xtrace = cx_calloc(nrows, sizeof(cxint));
00803     cxint* ytrace = cx_calloc(nrows, sizeof(cxint));
00804 
00805     cpl_image* mask = cpl_image_new(ncols, nrows, CPL_TYPE_INT);
00806 
00807     for (m = 0; m < ncols; ++m) {
00808 
00809         const cxint* _center    = cpl_image_get_data_int(center);
00810         const cxint* _reference = _center + mmax * ncols;
00811 
00812         cxbool out_of_bounds = FALSE;
00813 
00814         cxint connected = 0;
00815 
00816 
00817         if (_reference[m] == 1) {
00818 
00819             register cxint j   = mmax;
00820             register cxint pos = m;
00821 
00822 
00823             ++itrace;
00824 
00825             xtrace[connected] = pos;
00826             ytrace[connected] = j;
00827 
00828             j = mmax + 1;
00829 
00830             while (j < nrows) {
00831 
00832                 register cxint k    = 0;
00833                 register cxint l    = j * ncols;
00834                 register cxint kmin = (pos - 1 >= 0) ? pos - 1 : 0;
00835                 register cxint kmax = (pos + 1 < ncols) ? pos + 1 : ncols - 1;
00836 
00837                 for (k = kmin; k <= kmax; ++k) {
00838 
00839                     if (_center[l + k] == 1) {
00840                         pos = k;
00841                         if ((pos <= margin) || (pos >= ncols - margin)) {
00842                             out_of_bounds = TRUE;
00843                         }
00844                         else {
00845                             ++connected;
00846                             xtrace[connected] = k;
00847                             ytrace[connected] = j;
00848                         }
00849                         break;
00850                     }
00851 
00852                 }
00853 
00854                 ++j;
00855 
00856             }
00857 
00858 
00859             j = mmax - 1;
00860             pos = m;
00861 
00862             while (j >= 0) {
00863 
00864                 register cxint k    = 0;
00865                 register cxint l    = j * ncols;
00866                 register cxint kmin = (pos - 1 >= 0)    ? pos - 1 : 0;
00867                 register cxint kmax = (pos + 1 < ncols) ? pos + 1 : ncols - 1;
00868 
00869                 for (k = kmin; k <= kmax; ++k) {
00870 
00871                     if (_center[l + k] == 1) {
00872                         pos = k;
00873                         if ((pos <= margin) || (pos >= ncols - margin)) {
00874                             out_of_bounds = TRUE;
00875                         }
00876                         else {
00877                             ++connected;
00878                             xtrace[connected] = k;
00879                             ytrace[connected] = j;
00880                         }
00881                         break;
00882                     }
00883 
00884                 }
00885 
00886                 --j;
00887 
00888             }
00889 
00890 
00891             if ((connected < limit) || (out_of_bounds == TRUE)) {
00892 
00893                 memset(xtrace, 0, nrows * sizeof(cxint));
00894                 memset(ytrace, 0, nrows * sizeof(cxint));
00895 
00896                 if (out_of_bounds == TRUE) {
00897                     cx_print("discarded candidate %d, going out of detector "
00898                              "boundaries.\n", itrace);
00899 
00900                 }
00901                 else {
00902                     cx_print("discarded candidate %d, not enough connected "
00903                              "centers (%d, required: %d)\n", itrace, connected,
00904                              limit);
00905                 }
00906 
00907             }
00908             else {
00909 
00910                 cxint* _mask = cpl_image_get_data_int(mask);
00911 
00912                 for (j = 0; j < connected; ++j) {
00913 
00914                     register cxint x  = xtrace[j];
00915                     register cxint y  = ytrace[j] * ncols;
00916                     register cxint ix = x;
00917 
00918                     _mask[y + x] = 1;
00919 
00920                     while ((_center[y + ix] != -1) && (ix > 0)) {
00921                         --ix;
00922                     }
00923                     _mask[y + ix] = -1;
00924 
00925                     ix = x;
00926                     while ((_center[y + ix] != -2) && (ix < ncols - 1)) {
00927                         ++ix;
00928                     }
00929                     _mask[y + ix] += -2;
00930 
00931                 }
00932 
00933                 ++ispectra;
00934 
00935             }
00936 
00937         }
00938 
00939     }
00940 
00941     cx_print("scount: %d (expected: %d)\n", ispectra, nspectra);
00942 
00943     cx_free(ytrace);
00944     ytrace = NULL;
00945 
00946     cx_free(xtrace);
00947     xtrace = NULL;
00948 
00949     for (m = 0; m < nrows; ++m) {
00950 
00951         register cxint j  = 0;
00952         register cxint ns = 0;
00953 
00954         const cxint* _mask   = cpl_image_get_data_int(mask) + m * ncols;
00955         const cxint* _center = cpl_image_get_data_int(center) + m * ncols;
00956 
00957 
00958         for (j = 0; j < ncols; ++j) {
00959 
00960             if (_mask[j] == 1) {
00961 
00962                 register cxint x  = j;
00963                 register cxint ix = x;
00964 
00965 
00966                 while ((_center[ix] != -1) && (ix > 0)) {
00967                     --ix;
00968                 }
00969                 cpl_matrix_set(mylo, naccepted, ns, x - hwf * fabs(x - ix));
00970 
00971                 ix = x;
00972                 while ((_center[ix] != -2) && (ix < ncols - 1)) {
00973                     ++ix;
00974                 }
00975                 cpl_matrix_set(myup, naccepted, ns, x + hwf * fabs(ix - x));
00976 
00977                 ++ns;
00978             }
00979 
00980         }
00981 
00982         if (ns == ispectra) {
00983             cpl_matrix_set(mxok, naccepted, 0, m);
00984             ++naccepted;
00985         }
00986 
00987     }
00988 
00989     *ndetect = ispectra;
00990 
00991 
00992     cpl_image_save(center, "center.fits", -32, 0, CPL_IO_DEFAULT);
00993     cpl_image_save(mask, "mask.fits", -32, 0, CPL_IO_DEFAULT);
00994 
00995     cpl_image_delete(mask);
00996     cpl_image_delete(center);
00997     cpl_image_delete(vertical2);
00998     cpl_image_delete(vertical1);
00999     cpl_image_delete(sraw);
01000     cpl_image_delete(fraw);
01001 
01002     return naccepted;
01003 }
01004 
01005 
01006 /*
01007  * @brief
01008  *   Computes initial raw localization borders.
01009  *
01010  * @param image     The image to process [nx,ny]
01011  * @param nspectra  Number of expected spectra
01012  * @param noise     Spectra/noise threshold
01013  * @param config    Mask parameters.
01014  * @param ndetect   Number of spectra detected.
01015  * @param mxok      Matrix[nx] of @em nxok good x bins.
01016  * @param myup      Matrix[nx,nspectra] of @em nxok upper Y borders.
01017  * @param mylo      Matrix[nx,nspectra] of @em nxok lower Y borders.
01018  *
01019  * @return The function returns the number of good X bins on success, or
01020  *   a negative value on failure.
01021  *
01022  * Starting from @em config.start bin of the CCD, the function tries to
01023  * detect spectrum pixels pattern:
01024  *
01025  *     '0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 0 0'
01026  *
01027  * for each X bin and sets @em mylo[nx,ns] and @em myup[nx,ns] to Y values
01028  * of first '1' and last '1' of each '1' group. '0' and '1' are determined
01029  * by a @em noise value.
01030  *
01031  * Each group of '1' is supposed to be a spectrum. @em nspectra is the
01032  * expected number of spectra defined by the current instrument setup.
01033  * if X bin is ok @em mxok, @em mylo and @em myup matrices are updated
01034  * otherwise go and try the next X bin until @em config.tries is reached.
01035  *
01036  * @em mxok[nx], @em mylo[nx,ns] and @em myup[nx,ns] are pre-allocated
01037  * matrices.
01038  */
01039 
01040 inline static cxint
01041 _giraffe_build_raw_mask(cpl_image *raw, cpl_image *bpixel, cxint nspectra,
01042                         cxdouble noise, GiMaskParameters *config,
01043                         cxint *ndetect, cpl_matrix *mxok, cpl_matrix *myup,
01044                         cpl_matrix *mylo)
01045 {
01046 
01047     register cxint x = 0;
01048     register cxint y = 0;
01049     register cxint xretry = 0;
01050     register cxint xok = 0;
01051 
01052     cxint ny = 0;
01053     cxint nrows = 0;
01054     cxint ncols = 0;
01055     cxint *yabove = NULL;
01056     cxint *ybelow = NULL;
01057     cxint *good_pixels = NULL;
01058     cxint ywidth = config->ywidth > 1 ? config->ywidth : 2;
01059     cxint ckwidth = config->ckdata.width;
01060     cxint ckheight = config->ckdata.height;
01061     cxint ckcount = config->ckdata.count;
01062 
01063 
01064     cxdouble* pixels = NULL;
01065 
01066     cpl_mask* med = NULL;
01067 
01068     cpl_image* img = raw;
01069 
01070 
01071     med = cpl_mask_new(1, 15);
01072 
01073     if (med != NULL) {
01074 
01075         cpl_mask_not(med);
01076 
01077         img = cpl_image_new(cpl_image_get_size_x(raw),
01078                             cpl_image_get_size_y(raw),
01079                             cpl_image_get_type(raw));
01080 
01081         cpl_image_filter_mask(img, raw, med, CPL_FILTER_MEDIAN,
01082                               CPL_BORDER_FILTER);
01083 
01084     }
01085 
01086     cpl_mask_delete(med);
01087     med = NULL;
01088 
01089     *ndetect = 0;
01090 
01091     GIDEBUG(gi_message("noise = %g start = %d tries = %d xbin = %d "
01092                        "ywidth = %d", noise, config->start, config->retry,
01093                        config->xbin, ywidth));
01094 
01095     pixels = cpl_image_get_data_double(img);
01096 
01097     nrows = cpl_image_get_size_y(img);
01098     ncols = cpl_image_get_size_x(img);
01099 
01100 
01101     if (config->xbin > 1) {
01102 
01103         cxint nx = nrows;
01104 
01105         cxdouble* _pixels = NULL;
01106 
01107 
01108         nrows = (cxint) ceil(nrows / config->xbin);
01109         config->start = (cxint) ceil(config->start / config->xbin);
01110 
01111         _pixels = cx_calloc(ncols * nrows, sizeof(cxdouble));
01112 
01113         for (y = 0; y < ncols; ++y) {
01114 
01115             for (x = 0; x < nrows; ++x) {
01116 
01117                 register cxint xx = 0;
01118                 register cxint zx = x * ncols;
01119                 register cxint xr = x * config->xbin;
01120                 register cxint zr = xr * ncols;
01121 
01122 
01123                 _pixels[zx + y] = 0.;
01124 
01125                 for (xx = 0; xx < config->xbin && xr < nx; ++xx) {
01126                     _pixels[zx + y] += pixels[zr + y];
01127                 }
01128 
01129                 _pixels[zx + y] /= config->xbin;
01130 
01131             }
01132 
01133         }
01134 
01135         pixels = _pixels;
01136 
01137     }
01138 
01139     good_pixels = cx_calloc(nrows * ncols, sizeof(cxint));
01140 
01141     switch (config->method) {
01142 
01143         case GILOCALIZE_THRESHOLD_LOCAL:
01144         {
01145 
01146             cxint ywidth2 = ywidth / 2;
01147             cxint sz = 2 * ywidth2 + 1;
01148 
01149             cpl_vector* ymins = cpl_vector_new(sz);
01150 
01151 
01152             /*
01153              * We define a window along y axis to compute a local minimum
01154              * and threshold. To handle variation of "background"
01155              * between spectra in subslits
01156              */
01157 
01158             for (x = 0; x < nrows; x++) {
01159 
01160                 cpl_vector_fill(ymins, 0.);
01161 
01162                 for (y = 0; y < ncols; y++) {
01163 
01164                     register cxint k  = 0;
01165                     register cxint kk = 0;
01166 
01167                     cxdouble value     = 0.;
01168                     cxdouble bkg       = 0.;
01169                     cxdouble threshold = 0.;
01170 
01171 
01172                     for (kk = 0, k = -ywidth2; k <= ywidth2; k++) {
01173 
01174                         register cxint ky = y + k;
01175 
01176                         if (ky < 0 || ky >= ncols) {
01177                             continue;
01178                         }
01179 
01180                         cpl_vector_set(ymins, kk, pixels[x * ncols + ky]);
01181                         ++kk;
01182                     }
01183 
01184                     if (kk == 0) {
01185                         continue;
01186                     }
01187 
01188                     if (config->threshold > 0.) {
01189 
01190                         const cxint count = 2;
01191 
01192                         cxint i = 0;
01193 
01194 
01195                         /* Note that ymins has, by construction, an odd number
01196                          * of elements which must be at least 3 at this point.
01197                          * Also kk must be at least ywidth2 + 1, since at most
01198                          * we loose ywidth2 pixels at the borders.
01199                          */
01200 
01201                         giraffe_array_sort(cpl_vector_get_data(ymins), kk);
01202 
01203                         bkg = 0.;
01204 
01205                         for (i = 0; i < count; i++) {
01206                             bkg += fabs(cpl_vector_get(ymins, i));
01207                         }
01208                         bkg /= (cxdouble)count;
01209 
01210                         threshold = sqrt((2. * noise * noise +
01211                                 fabs(pixels[x * ncols + y]) + bkg / count) / config->xbin);
01212 
01213                     }
01214                     else {
01215 
01216                         register cxint i;
01217                         register cxdouble mean = 0.;
01218 
01219 
01220                         for (i = 0; i < kk; i++) {
01221                             mean += cpl_vector_get(ymins, i);
01222                         }
01223                         mean /= kk;
01224 
01225                         giraffe_array_sort(cpl_vector_get_data(ymins), kk);
01226 
01227                         bkg = (cpl_vector_get(ymins, 0) +
01228                                cpl_vector_get(ymins, 1)) / 2.0;
01229                         threshold = mean - bkg;
01230 
01231                     }
01232 
01233 
01234                     /*
01235                      * Check background corrected pixel value
01236                      */
01237 
01238                     value = pixels[x * ncols + y] - bkg;
01239 
01240                     if (value < 0.) {
01241                         continue;
01242                     }
01243 
01244                     if (value > fabs(config->threshold) * threshold) {
01245                         good_pixels[x * ncols + y] = 1;
01246                     }
01247                 }
01248             }
01249 
01250             cpl_vector_delete(ymins);
01251             ymins = NULL;
01252 
01253             break;
01254 
01255         }
01256 
01257         case GILOCALIZE_THRESHOLD_ROW:
01258         {
01259 
01260             cpl_image* snr = cpl_image_abs_create(raw);
01261 
01262             cxint sx = cpl_image_get_size_x(snr);
01263 
01264 
01265             cpl_image_power(snr, 0.5);
01266 
01267             for (x = 0; x < nrows; ++x) {
01268 
01269                 const cxdouble* _snr = cpl_image_get_data_double_const(snr);
01270 
01271                 cxdouble avsnr = giraffe_array_median(_snr + x * sx, sx);
01272 
01273 
01274                 for (y = 0; y < ncols; ++y) {
01275 
01276                     if (pixels[x * ncols + y] <= 0.) {
01277                         continue;
01278                     }
01279 
01280                     if (_snr[x * ncols + y] > avsnr * fabs(config->threshold)) {
01281                         good_pixels[x * ncols + y] = 1;
01282                     }
01283 
01284                 }
01285 
01286             }
01287 
01288             cpl_image_delete(snr);
01289             snr = NULL;
01290 
01291             break;
01292 
01293         }
01294 
01295         default:
01296         {
01297 
01298             cxdouble threshold = 0.;
01299 
01300 
01301             /*
01302              * We use global background and threshold
01303              */
01304 
01305             if (config->threshold > 0.) {
01306                 threshold = config->threshold * noise;
01307             }
01308             else {
01309 
01310                 cxdouble mean = cpl_image_get_mean(raw);
01311 
01312                 threshold = -config->threshold * mean *
01313                         (nspectra * config->wavg / ncols);
01314 
01315             }
01316 
01317             for (x = 0; x < nrows; x++) {
01318 
01319                 for (y = 0; y < ncols; y++) {
01320 
01321                     if (pixels[x * ncols + y] > threshold) {
01322                         good_pixels[x * ncols + y] = 1;
01323                     }
01324 
01325                 }
01326 
01327             }
01328 
01329             break;
01330 
01331         }
01332 
01333     }
01334 
01335     GIDEBUG(cxint *data = cx_calloc(nrows * ncols, sizeof(cxint));
01336             memcpy(data, good_pixels, nrows * ncols * sizeof(cxint));
01337             cpl_image *gp = cpl_image_wrap_int(ncols, nrows, data);
01338             cpl_image_save(gp, "locmask.fits", 32, NULL, CPL_IO_DEFAULT);
01339             cpl_image_unwrap(gp);
01340             cx_free(data));
01341 
01342 
01343     /*
01344      * Buffers used to store the fiber boundaries.
01345      */
01346 
01347     yabove = cx_calloc(nspectra + 1, sizeof(cxint));
01348     ybelow = cx_calloc(nspectra + 1, sizeof(cxint));
01349 
01350 
01351     /*
01352      * Start from <config->start> of CCD to first pixel
01353      */
01354 
01355     ny = ncols - 1;
01356 
01357     xretry = 0;
01358     xok = 0;
01359 
01360     for (x = config->start; (x >= 0) && (xretry <= config->retry); x--) {
01361 
01362         register cxint zx = x * ncols;
01363         register cxint nborders = 0;
01364         register cxint nbelow = 0;
01365         register cxint nabove = 0;
01366         register cxint in_spectrum = 0;
01367 
01368 
01369         for (y = 1; y < ny; y++) {
01370 
01371             register cxint tmp = 2 * good_pixels[zx + y];
01372 
01373             /*
01374              * Number of spectra = max number of borders
01375              */
01376 
01377             nborders = CX_MAX(CX_MAX(nborders, nbelow), nabove);
01378 
01379             if (nborders > nspectra) {
01380                 break;   /* Error: too many spectrum borders detected */
01381             }
01382 
01383             /*
01384              * Try to detect spectrum pattern:
01385              *    0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1
01386              * we need at least two consecutive one to detect a spectrum.
01387              */
01388 
01389             if (good_pixels[zx + y + 1]) {
01390 
01391                 /*
01392                  * Next pixel is a spectrum pixel: it's a border if
01393                  * previous one is zero
01394                  */
01395 
01396                 if ((tmp - good_pixels[zx + y - 1]) == 2) {
01397 
01398                     /*
01399                      * 0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1
01400                      *       ^ so, here we are...
01401                      */
01402 
01403                     if (!in_spectrum) {
01404 
01405                         /*
01406                          * Could not be a below border if we are already
01407                          * into a spectrum
01408                          */
01409 
01410                         ybelow[nbelow++] = y;
01411                         in_spectrum = 1;    /* entering */
01412 
01413                     }
01414 
01415                 }
01416 
01417             }
01418 
01419             if (good_pixels[zx + y - 1]) {
01420 
01421                 /*
01422                  * Previous pixel is a spectrum pixel: it's a border if
01423                  * next one is zero
01424                  */
01425 
01426                 if ((tmp - good_pixels[zx + y + 1]) == 2) {
01427 
01428                     /*
01429                      * 0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1
01430                      *               ^ and now, there
01431                      */
01432 
01433                     if (in_spectrum) {
01434 
01435                         /*
01436                          * taken into account only if we already found a
01437                          * lower border, we really are into a spectrum
01438                          */
01439 
01440                         yabove[nabove++] = y;
01441                         in_spectrum = 0;    /* going out */
01442 
01443                     }
01444 
01445                 }
01446 
01447             }
01448 
01449 // FIXME: Just a try
01450 
01451             if (tmp &&
01452                 !good_pixels[zx + y - 1] && !good_pixels[zx + y + 1]) {
01453 
01454                 if (_giraffe_validate_pixel(good_pixels, ncols, nrows, y, x,
01455                                             ckwidth, ckheight, ckcount)) {
01456 
01457                     yabove[nabove++] = y;
01458                     ybelow[nbelow++] = y;
01459                 }
01460 
01461             }
01462 
01463         }   /* finished with this x bin */
01464 
01465         if (in_spectrum) {
01466             nborders--;
01467             nbelow--;
01468             in_spectrum = 0;
01469         }
01470 
01471         *ndetect = nborders;
01472 
01473         if (!in_spectrum && (nbelow == nspectra) && (nbelow == nabove)) {
01474 
01475             /*
01476              * Good number of upper and lower cuples found for all spectra:
01477              * xend will be the first good value and the updated xstart is
01478              * the current value. We also do not want last CCD clipped
01479              * spectrum
01480              */
01481 
01482             for (y = 0; y < nspectra; y++) {
01483                 cpl_matrix_set(mylo, xok, y, (cxdouble) ybelow[y]);
01484                 cpl_matrix_set(myup, xok, y, (cxdouble) yabove[y]);
01485                 cpl_matrix_set(mxok, xok, 0, (config->xbin > 1) ?
01486                                (cxdouble) (x + 0.5) * config->xbin :
01487                                (cxdouble) x);
01488             }
01489             xok++;
01490             xretry = 0; /* back on your feet */
01491         }
01492         else if (xretry++ < config->retry) {
01493 
01494             /*
01495              * Do not find good number of spectra but we still have some
01496              * credit for a next try
01497              */
01498 
01499             continue;
01500         }
01501         else {
01502 
01503             /*
01504              * This is the end of our rope
01505              */
01506 
01507             break;
01508         }
01509     } /* next x bin */
01510 
01511 
01512     /*
01513      * Second half: start from <config->start+1> of CCD to last pixel
01514      */
01515 
01516     /*
01517      * Oops we could have a 2 * xretry width hole around xstart!!!
01518      */
01519 
01520     xretry = 0;
01521 
01522     for (x = config->start + 1; (x < nrows) &&
01523              (xretry <= config->retry); x++) {
01524 
01525         register cxint zx = x * ncols;
01526         register cxint nborders = 0;
01527         register cxint nbelow = 0;
01528         register cxint nabove = 0;
01529         register cxint in_spectrum = 0;
01530 
01531 
01532         for (y = 1; y < ny; y++) {
01533 
01534             register cxint tmp = 2 * good_pixels[zx + y];
01535 
01536             nborders = CX_MAX(CX_MAX(nborders, nbelow), nabove);
01537 
01538             if (nborders > nspectra) {
01539                 break;
01540             }
01541 
01542             if (good_pixels[zx + y + 1]) {
01543                 if ((tmp - good_pixels[zx + y - 1]) == 2) {
01544                     if (!in_spectrum) {
01545                         ybelow[nbelow++] = y;
01546                         in_spectrum = 1;
01547                     }
01548                 }
01549             }
01550 
01551             if (good_pixels[zx + y - 1]) {
01552                 if ((tmp - good_pixels[zx + y + 1]) == 2) {
01553                     if (in_spectrum) {
01554                         yabove[nabove++] = y;
01555                         in_spectrum = 0;
01556                     }
01557                 }
01558             }
01559 
01560 // FIXME: Just a try
01561 
01562             if (tmp &&
01563                 !good_pixels[zx + y - 1] && !good_pixels[zx + y + 1]) {
01564 
01565                 if (_giraffe_validate_pixel(good_pixels, ncols, nrows, y, x,
01566                                             ckwidth, ckheight, ckcount)) {
01567 
01568                     yabove[nabove++] = y;
01569                     ybelow[nbelow++] = y;
01570                 }
01571 
01572             }
01573 
01574         } /* finished with this x bin */
01575 
01576         if (in_spectrum) {
01577             nborders--;
01578             nbelow--;
01579             in_spectrum = 0;
01580         }
01581 
01582         *ndetect = nborders;
01583 
01584         if (!in_spectrum && (nbelow == nspectra) && (nbelow == nabove)) {
01585 
01586             for (y = 0; y < nspectra; y++) {
01587                 cpl_matrix_set(mylo, xok, y, (cxdouble) ybelow[y]);
01588                 cpl_matrix_set(myup, xok, y, (cxdouble) yabove[y]);
01589                 cpl_matrix_set(mxok, xok, 0, (config->xbin > 1) ?
01590                                (cxdouble) (x + 0.5) * config->xbin :
01591                                (cxdouble) x);
01592             }
01593             xok++;
01594             xretry = 0;
01595         }
01596         else if (xretry++ < config->retry) {
01597             continue;
01598         }
01599         else {
01600             break;
01601         }
01602 
01603     } /* next x bin */
01604 
01605     cx_free(ybelow);
01606     cx_free(yabove);
01607     cx_free(good_pixels);
01608 
01609     if (pixels != cpl_image_get_data_double(img)) {
01610         cx_free(pixels);
01611         pixels = NULL;
01612     }
01613 
01614     if (img != raw) {
01615         cpl_image_delete(img);
01616         img = NULL;
01617     }
01618 
01619     if (xok == 0) {
01620         if (*ndetect < nspectra) {
01621             return -1;
01622         }
01623         else if (*ndetect > nspectra) {
01624             return -1;
01625         }
01626         else {
01627             return -2;
01628         }
01629     }
01630     else {
01631         *ndetect = nspectra;
01632     }
01633 
01634     return xok;
01635 
01636 }
01637 
01638 
01639 /*
01640  * @brief
01641  *   Computes fitted localization centroid and width.
01642  *
01643  * @param mxok      good X bins (all nspectra detected) [nxok]
01644  * @param myup      upper Y of spectra [nxok,nspectra]
01645  * @param mylo      lower Y of spectra [nxok,nspectra]
01646  * @param fibers    Table of spectra/fibers to localize [ns]
01647  * @param config    localization mask parameters
01648  * @param position  localization mask: locy[nx,ns] and locw[nx,ns]
01649  *
01650  * Computes Chebyshev polynomial fit of raw localization borders for each
01651  * spectrum specified in @em fibers.
01652  *
01653  * The @em myup[nxok,nspectra] and @em mylo[nxok,nspectra] border matrices
01654  * had been computed by @b _giraffe_build_raw_mask().
01655  *
01656  * The expected number of spectra to be localized is given by @em nspectra.
01657  * The computed results are stored in the pre-allocated matrices
01658  * @em position->my[nx,ns] and @em position->mw[nx,ns]. The fiber setup
01659  * for a particular observation is given by @em fibers, a table of all
01660  * fibers specifying the spectra to be processed where @em ns is number of
01661  * entries (fibers) in @em fibers defined by the current instrument setup.
01662  */
01663 
01664 inline static void
01665 _giraffe_fit_raw_mask(cpl_matrix *mxok, cpl_matrix *myup, cpl_matrix *mylo,
01666                       cpl_table *fibers, GiMaskParameters *config,
01667                       GiMaskPosition *position)
01668 {
01669 
01670     register cxint nn, x, nspectra;
01671     register cxint nx = cpl_matrix_get_nrow(position->my);
01672     register cxint ns = cpl_table_get_nrow(fibers);
01673 
01674     cpl_matrix *mxraw;
01675     cpl_matrix *base;
01676     cpl_matrix *mcoeff;
01677 
01678 
01679 
01680     mxraw  = cpl_matrix_new(nx, 1);
01681     mcoeff = cpl_matrix_new(config->ydeg + 1, 1);
01682 
01683 
01684     /*
01685      * Initialize with all abcissa
01686      */
01687 
01688     for (x = 0; x < nx; x++) {
01689         cpl_matrix_set(mxraw, x, 0, x);
01690     }
01691 
01692     /*
01693      * Compute Chebyshev base over all x bins
01694      */
01695 
01696     base = giraffe_chebyshev_base1d(0., nx, config->ydeg + 1, mxraw);
01697     cpl_matrix_delete(mxraw);
01698 
01699     nspectra = 0;
01700     for (nn = 0; nn < ns; nn++) {
01701         cpl_matrix *ylofit = NULL;
01702         cpl_matrix *yupfit = NULL;
01703 
01704         /* FIXME: The fiber selection changed the following piece of code
01705          *        should not be necessary but we have to check that the
01706          *        accessed to the matrix rows correspond to the selected
01707          *        fibers.
01708          */
01709 
01710         //if (mButton->m[nn] != LOC_OK_SPECTRUM) {
01711         //    continue;
01712         //}
01713 
01714         /* Fitting the lower border */
01715         ylofit = _giraffe_fit_border(mylo, base, mxok, nspectra,
01716                                      config->sigma, config->niter,
01717                                      config->mfrac, mcoeff);
01718         if (ylofit == NULL) {
01719             cpl_msg_warning(_task, "Could not compute low border for "
01720                             "spectrum %d", nn);
01721             nspectra++;
01722             continue;
01723         }
01724 
01725         /* Fitting the upper border */
01726         yupfit = _giraffe_fit_border(myup, base, mxok, nspectra,
01727                                      config->sigma, config->niter,
01728                                      config->mfrac, mcoeff);
01729         if (yupfit == NULL) {
01730             cpl_msg_warning(_task, "Could not compute up border for "
01731                             "spectrum %d", nn);
01732             nspectra++;
01733             continue;
01734         }
01735 
01736         /*
01737          * For each X bin the centroid and the half-width of the
01738          * corresponding mask is computed as the half-sum and the
01739          * half-difference of the fitted borders.
01740          */
01741 
01742         for (x = 0; x < nx; x++) {
01743 
01744             cpl_matrix_set(position->my, x, nn, 0.5 *
01745                            (cpl_matrix_get(yupfit, x, 0) +
01746                             cpl_matrix_get(ylofit, x, 0)));
01747 
01748             cpl_matrix_set(position->my, x, nn, 0.5 *
01749                            (cpl_matrix_get(yupfit, x, 0) -
01750                             cpl_matrix_get(ylofit, x, 0)) + config->ewid);
01751 
01752         }
01753         cpl_matrix_delete(ylofit);
01754         cpl_matrix_delete(yupfit);
01755         nspectra++;
01756 
01757     } /* each spectrum */
01758 
01759     cpl_msg_info(_task, "%03d spectrum positions fitted", nspectra);
01760 
01761     cpl_matrix_delete(base);
01762     cpl_matrix_delete(mcoeff);
01763 
01764     if (nspectra == 0) {
01765         cpl_msg_warning(_task, "could not fit any spectra, check number "
01766                         "of good wavelength bins");
01767         return;
01768     }
01769 
01770     return;
01771 
01772 }
01773 
01774 
01775 /*
01776  * @brief
01777  *   Computes fitted localization centroid and width.
01778  *
01779  * @param mz        Image[nx,ny] of pixels values
01780  * @param mxok      Good x bins (all nspectra detected) [nxok]
01781  * @param myup      Upper Y of spectra [nxok,nspectra]
01782  * @param mylo      Lower Y of spectra [nxok,nspectra]
01783  * @param fibers    Spectra used for localization [ns]
01784  * @param config    Localization mask parameters
01785  * @param position  Localization mask: my[nx, ns] and mw[nx, ns]
01786  * @param coeffs    Localization mask Chebyshev fit coefficients
01787  *
01788  * Computes Chebyshev polynomial fit of raw localization borders for each
01789  * spectrum specified in fibers[ns].
01790  *
01791  * The @em myup[nxok, nspectra] and @em mylo[nxok, nspectra] border matrices
01792  * had been computed by @b _giraffe_build_raw_mask(). The expected number
01793  * of spectra to be localized is given by @em nspectra. The matrix
01794  * @em position->my[nx, ns] is the fitted barycenter of Y values between raw
01795  * localization borders, while @em position->mw[nx,ns] is the 2D fit of
01796  * the half-width of the raw localization borders (+ extra width:
01797  * @em config->ewid).
01798  *
01799  * The matrices @em position->my[nx, ns], @em position->mw[nx, ns],
01800  * @em coeffs->my[ydeg + 1, ns] and @em coeffs->mw[(config->wdeg + 1)^2]
01801  * are pre-allocated matrices.
01802  *
01803  * The fiber setup for a particular observation is given by @em fibers,
01804  * a table of all fibers specifying the spectra to be processed where
01805  * @em ns is number of entries (fibers) in @em fibers defined by the
01806  * current instrument setup.
01807  */
01808 
01809 inline static void
01810 _giraffe_fit_raw_centroid(cpl_image* mz, cpl_matrix* mxok, cpl_matrix* myup,
01811                           cpl_matrix* mylo, cpl_table* fibers,
01812                           GiMaskParameters* config, GiMaskPosition* position,
01813                           GiMaskPosition* coeffs)
01814 {
01815 
01816     const cxchar* const fctid = "_giraffe_fit_raw_centroid";
01817 
01818     register cxint nn = 0;
01819     register cxint x = 0;
01820     register cxint y = 0;
01821     register cxint nspectra = 0;
01822     register cxint nx = cpl_image_get_size_y(mz);
01823     register cxint ny = cpl_image_get_size_x(mz);
01824     register cxint ns = cpl_table_get_nrow(fibers);
01825 
01826     cxint yorder = config->ydeg + 1;
01827     cxint worder = config->wdeg + 1;
01828 
01829     cpl_matrix* mxraw = NULL;
01830     cpl_matrix* base = NULL;
01831     cpl_matrix* mycenter = NULL;
01832     cpl_matrix* mywidth = NULL;
01833     cpl_matrix* mx = NULL;
01834     cpl_matrix* my = NULL;
01835     cpl_matrix* mw = NULL;
01836     cpl_matrix* chebcoeff = NULL;
01837     cpl_matrix* mfitlocw = NULL;
01838     cpl_matrix* ycenfit = NULL;
01839     cpl_matrix* ycencoeff = NULL;
01840 
01841 
01842 
01843     if (cpl_matrix_get_nrow(position->my) != nx ||
01844         cpl_matrix_get_ncol(position->my) != ns) {
01845         gi_error("%s: invalid size for position->my[%" CPL_SIZE_FORMAT ",%"
01846                  CPL_SIZE_FORMAT "], expected [%d,%d]", fctid,
01847                  cpl_matrix_get_nrow(position->my),
01848                  cpl_matrix_get_ncol(position->my), nx, ns);
01849         return;
01850     }
01851 
01852     if (cpl_matrix_get_nrow(position->mw) != nx ||
01853         cpl_matrix_get_ncol(position->mw) != ns) {
01854         gi_error("%s: invalid size for position->mw[%" CPL_SIZE_FORMAT ",%"
01855                  CPL_SIZE_FORMAT "], expected [%d,%d]", fctid,
01856                  cpl_matrix_get_nrow(position->my),
01857                  cpl_matrix_get_ncol(position->my), nx, ns);
01858         return;
01859     }
01860 
01861 
01862     /*
01863      * Initialize with all abcissa
01864      */
01865 
01866     mxraw = cpl_matrix_new(nx, 1);
01867 
01868     for (x = 0; x < nx; x++) {
01869         cpl_matrix_set(mxraw, x, 0, x);
01870     }
01871 
01872 
01873     /*
01874      * Compute Chebyshev base over all x bins
01875      */
01876 
01877     base = giraffe_chebyshev_base1d(0., nx, yorder, mxraw);
01878     cpl_matrix_delete(mxraw);
01879 
01880     mycenter = cpl_matrix_new(cpl_matrix_get_nrow(mxok), ns);
01881     mywidth = cpl_matrix_new(1, cpl_matrix_get_nrow(mxok) * ns);
01882 
01883     ycencoeff = cpl_matrix_new(yorder, 1);
01884 
01885     for (nn = 0; nn < ns; nn++) {
01886 
01887         /* FIXME: The fiber selection changed the following piece of code
01888          *        should not be necessary but we have to check that the
01889          *        accessed to the matrix rows correspond to the selected
01890          *        fibers.
01891          */
01892 
01893         //if (mButton->m[nn] != LOC_OK_SPECTRUM) {
01894         //    continue;
01895         //}
01896 
01897         /*
01898          * compute the barycenter and half-width of the corresponding mask
01899          * between raw borders.
01900          */
01901 
01902         cxdouble* pixels = cpl_image_get_data_double(mz);
01903 
01904         for (x = 0; x < cpl_matrix_get_nrow(mxok); x++) {
01905 
01906             register cxint zx = (cxint) cpl_matrix_get(mxok, x, 0);
01907 
01908             register cxdouble zz = 0.;
01909             register cxdouble yy = 0.;
01910 
01911             cxdouble lower = cpl_matrix_get(mylo, x, nspectra);
01912             cxdouble upper = cpl_matrix_get(myup, x, nspectra);
01913 
01914 
01915             for (y = (cxint) lower; y <= (cxint) upper; y++) {
01916                 yy += pixels[zx * ny + y] * y;
01917                 zz += pixels[zx * ny + y];
01918             }
01919 
01920             cpl_matrix_set(mycenter, x, nspectra, yy / zz);
01921             cpl_matrix_set(mywidth, 0, x * ns + nspectra, config->ewid +
01922                            (upper - lower) / 2.0);
01923 
01924         }   /* for each x bin */
01925 
01926         /*
01927          * The matrix ycenfit[nx] stores the fitted centroid
01928          */
01929 
01930         cpl_matrix_fill(ycencoeff, 0.);
01931         ycenfit = _giraffe_fit_border(mycenter, base, mxok, nspectra,
01932                                       config->sigma, config->niter,
01933                                       config->mfrac, ycencoeff);
01934         if (ycenfit == NULL) {
01935             cpl_msg_warning(_task, "Could not fit centroid for spectrum %d",
01936                             nn);
01937             nspectra++;
01938             continue;
01939         }
01940 
01941         /*
01942          * Save centroid Chebyshev fit coeffs
01943          */
01944 
01945         for (x = 0; x < yorder; x++) {
01946             cpl_matrix_set(coeffs->my, x, nn,
01947                            cpl_matrix_get(ycencoeff, x, 0));
01948         }
01949 
01950         /*
01951          * The localization centroid is a Chebyshev polynomial fit
01952          * of Y barycenters in raw mask
01953          */
01954 
01955         for (x = 0; x < nx; x++) {
01956             cpl_matrix_set(position->my, x, nn,
01957                            cpl_matrix_get(ycenfit, 0, x));
01958         }   /* for each x bin */
01959 
01960         cpl_matrix_delete(ycenfit);
01961         nspectra++;
01962 
01963     } /* each spectrum */
01964 
01965     GIDEBUG(cpl_image *lycenter = giraffe_matrix_create_image(mycenter);
01966             cpl_image_save(lycenter, "lycenter.fits", -32, NULL,
01967                            CPL_IO_DEFAULT);
01968             cpl_image_delete(lycenter);
01969 
01970             lycenter = giraffe_matrix_create_image(position->my);
01971             cpl_image_save(lycenter, "lycenterfit.fits", -32, NULL,
01972                            CPL_IO_DEFAULT);
01973             cpl_image_delete(lycenter);
01974 
01975             cpl_image *lyxok = giraffe_matrix_create_image(mxok);
01976             cpl_image_save(lyxok, "lyxok.fits", -32, NULL,
01977                            CPL_IO_DEFAULT);
01978             cpl_image_delete(lyxok));
01979 
01980 
01981     cpl_msg_info(_task, "%03d spectrum positions fitted", nspectra);
01982 
01983     cpl_matrix_delete(base);
01984     cpl_matrix_delete(mycenter);
01985     cpl_matrix_delete(ycencoeff);
01986 
01987     if (nspectra == 0) {
01988         cpl_msg_warning(_task, "Could not fit any spectra, check number of "
01989                         "good wavelength bins");
01990 
01991         cpl_matrix_delete(mywidth);
01992         return;
01993     }
01994 
01995     /*
01996      * 2D fit of mask width
01997      */
01998 
01999     cpl_msg_info(_task, "2D fit (order %dx%d) of mask width", worder,
02000                  worder);
02001 
02002     /*
02003      * Computes grid[nxok, nspectra]
02004      */
02005 
02006     mx = cpl_matrix_new(cpl_matrix_get_nrow(mxok) * nspectra, 1);
02007     my = cpl_matrix_new(cpl_matrix_get_nrow(mxok) * nspectra, 1);
02008     mw = cpl_matrix_new(1, cpl_matrix_get_nrow(mxok) * nspectra);
02009 
02010     for (y = 0, nn = 0; nn < nspectra; nn++) {
02011 
02012         /* FIXME: The fiber selection changed the following piece of code
02013          *        should not be necessary but we have to check that the
02014          *        accessed to the matrix rows correspond to the selected
02015          *        fibers.
02016          */
02017 
02018         //if (mButton->m[nn] != LOC_OK_SPECTRUM) {
02019         //    continue;
02020         //}
02021 
02022         for (x = 0; x < cpl_matrix_get_nrow(mxok); x++) {
02023 
02024             register cxint zx = (cxint) cpl_matrix_get(mxok, x, 0);
02025             register cxint lx = x * nspectra + y;
02026 
02027 
02028             cpl_matrix_set(mx, lx, 0, cpl_matrix_get(mxok, x, 0));
02029             cpl_matrix_set(my, lx, 0, cpl_matrix_get(position->my, zx, nn));
02030             cpl_matrix_set(mw, 0, lx, cpl_matrix_get(mywidth, 0, x * ns + y));
02031         }
02032         y++;
02033     }
02034 
02035     base = giraffe_chebyshev_base2d(0., 0., nx, ny, worder, worder, mx, my);
02036 
02037     cpl_matrix_delete(my);
02038     cpl_matrix_delete(mx);
02039 
02040     chebcoeff = giraffe_matrix_leastsq(base, mw);
02041     cpl_matrix_delete(base);
02042     cpl_matrix_delete(mw);
02043 
02044     cpl_matrix_delete(mywidth);
02045 
02046     if (chebcoeff == NULL) {
02047         gi_warning("%s: error in giraffe_matrix_leastsq() for width 2D fit",
02048                    fctid);
02049         return;
02050     }
02051 
02052     /*
02053      * Save half-width Chebyshev 2-D fit coeffs
02054      */
02055 
02056     for (nn = 0; nn < cpl_matrix_get_ncol(chebcoeff); nn++) {
02057         cpl_matrix_set(coeffs->mw, 0, nn, cpl_matrix_get(chebcoeff, 0, nn));
02058     }
02059 
02060     /*
02061      * Computes grid[nx, nspectra]
02062      */
02063 
02064     mx = cpl_matrix_new(nx * nspectra, 1);
02065     my = cpl_matrix_new(nx * nspectra, 1);
02066 
02067     for (y = 0, nn = 0; nn < nspectra; nn++) {
02068 
02069         /* FIXME: The fiber selection changed the following piece of code
02070          *        should not be necessary but we have to check that the
02071          *        accessed to the matrix rows correspond to the selected
02072          *        fibers.
02073          */
02074 
02075         //if (mButton->m[nn] != LOC_OK_SPECTRUM) {
02076         //    continue;
02077         //}
02078 
02079         for (x = 0; x < nx; x++) {
02080 
02081             register cxint lx = x * nspectra + y;
02082 
02083             cpl_matrix_set(mx, lx, 0, x);
02084             cpl_matrix_set(my, lx, 0, cpl_matrix_get(position->my, x, nn));
02085 
02086         }
02087         y++;
02088     }
02089 
02090     cpl_matrix_set_size(chebcoeff, worder, worder);
02091 
02092     mfitlocw = giraffe_chebyshev_fit2d(0., 0., nx, ny, chebcoeff, mx, my);
02093     cpl_matrix_delete(chebcoeff);
02094 
02095     cpl_matrix_delete(my);
02096     cpl_matrix_delete(mx);
02097 
02098     for (y = 0, nn = 0; nn < nspectra; nn++) {
02099 
02100         /* FIXME: The fiber selection changed the following piece of code
02101          *        should not be necessary but we have to check that the
02102          *        accessed to the matrix rows correspond to the selected
02103          *        fibers.
02104          */
02105 
02106         //if (mButton->m[nn] != LOC_OK_SPECTRUM) {
02107         //    continue;
02108         //}
02109 
02110         for (x = 0; x < nx; x++) {
02111 
02112             register cxint lx = x * nspectra + y;
02113 
02114             cpl_matrix_set(position->mw, x, nn,
02115                            cpl_matrix_get(mfitlocw, lx, 0));
02116 
02117         }
02118         y++;
02119     }
02120 
02121     cpl_matrix_delete(mfitlocw);
02122 
02123     return;
02124 
02125 }
02126 
02127 
02128 /*
02129  * @brief
02130  *   Computes fitted localization centroid and width on all spectra.
02131  *
02132  * @param mZraw      Matrix[nx,ny] of pixels values
02133  * @param mButton    Matrix[ns] of spectra used for localization
02134  * @param locMethod  Centroid computation method:
02135  *                   HALF_WIDTH, BARYCENTER, PSF_PROFIL
02136  * @param sNormalize Normalize spectra along dispersion axis
02137  * @param noithresh  Spectra/noise threshold
02138  * @param locPrms    Localization mask parameters
02139  * @param locPos     Localization mask: locY[nx,ns] and locW[nx,ns]
02140  * @param locCoeff   Localization mask Chebyshev fit coefficients
02141  *
02142  * @return The function returns 0 on success, or a negative value otherwise.
02143  *
02144  * Computes localization mask (centroid and half-width) for the given
02145  * image @a mZraw[nx,ny]. @a mButton[nspectra] is a matrix of 0/1 values
02146  * specifying spectra to be processed. @a *noithresh is the threshold value
02147  * use to select spectra or inter-spectra pixels. @a locMethod defines the
02148  * method used to compute localization mask centroid and half-width.
02149  * @a locPos.mY[nx,ns], @a locPos.mW[nx,ns], @a locCoeff.mY[ydeg+1,ns] and
02150  * @a locCoeff.mW[(wdeg+1)**2] are pre-allocated matrices.
02151  */
02152 
02153 inline static cxint
02154 _giraffe_localize_spectra(cpl_image *mzraw, cpl_image *bpixel,
02155                           cpl_table *fibers, GiLocalizeMethod method,
02156                           cxbool normalize, cxdouble noise,
02157                           GiMaskParameters *config, GiMaskPosition *position,
02158                           GiMaskPosition *coeffs)
02159 {
02160 
02161     cxint n, nn;
02162     cxint nx, ny, nxok;
02163     cxint ndetect, nspectra;
02164     cxint x, y;
02165 
02166     cxdouble uplost = 0.;
02167     cxdouble lolost = 0.;
02168     cxdouble avglost = 0.;
02169     cxdouble avgmask = 0.;
02170     cxdouble sigmask = 0.;
02171     cxdouble sigmean = 0.;
02172     cxdouble avgborders = 0.;
02173 
02174     cxdouble *_mzraw;
02175 
02176     cpl_matrix *mxok;         /* mylo[nx] abcissa og good x bins */
02177     cpl_matrix *myup;         /* myup[nx,ns] of upper Y for each spectrum */
02178     cpl_matrix *mylo;         /* mylo[nx,ns] of lower Y for each spectrum */
02179     cpl_matrix *mwid;
02180 
02181     cpl_image *mz = NULL;
02182     cpl_image *mznorm = NULL;
02183 
02184 
02185 
02186     nx = cpl_image_get_size_y(mzraw);
02187     ny = cpl_image_get_size_x(mzraw);
02188     _mzraw = cpl_image_get_data_double(mzraw);
02189 
02190 
02191     if (normalize == TRUE) {
02192 
02193         cxdouble zxmax = 0.0;
02194         cxdouble *_mzx = NULL;
02195         cxdouble *_mznorm = NULL;
02196 
02197         cpl_image *mzx = NULL;
02198 
02199 
02200         cpl_msg_info(_task, "Using normalized spectra for localization");
02201 
02202 
02203         /*
02204          * The matrix mznorm contains the pixel values from mz
02205          * normalized along X axis and the matrix mzx is the summ
02206          * of all spectra along X (dispersion) axis
02207          */
02208 
02209         mznorm = cpl_image_new(ny, nx, CPL_TYPE_DOUBLE);
02210         _mznorm = cpl_image_get_data_double(mznorm);
02211 
02212         mzx = cpl_image_new(1, nx, CPL_TYPE_DOUBLE);
02213         _mzx = cpl_image_get_data_double(mzx);
02214 
02215 
02216         /*
02217          * For each x bin, summ all y values
02218          */
02219 
02220         for (x = 0 ; x < nx; x++) {
02221             for (y = 0 ; y < ny; y++) {
02222                 _mzx[x] += _mzraw[x * ny + y];
02223             }
02224 
02225             /*
02226              * Maximum value of summ
02227              */
02228 
02229             if (_mzx[x] > zxmax) {
02230                 zxmax = _mzx[x];
02231             }
02232         }
02233 
02234         GIDEBUG(cpl_image_save(mzx, "mzx.fits", -32, NULL, CPL_IO_DEFAULT));
02235 
02236         for (x = 0 ; x < nx; x++) {
02237 
02238             register cxdouble zxnorm = zxmax / _mzx[x];
02239 
02240             for (y = 0 ; y < ny; y++) {
02241                 _mznorm[x * ny + y] = _mzraw[x * ny + y] * zxnorm;
02242             }
02243 
02244         }
02245 
02246         cpl_image_delete(mzx);
02247         mz = mznorm;
02248     }
02249     else {
02250 
02251         /*
02252          * Use pixel values as they are
02253          */
02254 
02255         cpl_msg_info(_task, "Using raw spectra for localization");
02256         mz = mzraw;
02257     }
02258 
02259 
02260     /*
02261      * Full localization: takes care of all spectra
02262      */
02263 
02264     nspectra = cpl_table_get_nrow(fibers);
02265 
02266     mxok = cpl_matrix_new(nx, 1);
02267     myup = cpl_matrix_new(nx, nspectra);
02268     mylo = cpl_matrix_new(nx, nspectra);
02269 
02270 
02271     /*
02272      *  Make the bin size an even value if it is larger than 1
02273      */
02274 
02275     config->xbin = (config->xbin > 1) ? 2 * (config->xbin / 2) : 1;
02276 
02277     GIDEBUG(cpl_image_save(mz, "mz.fits", -32, NULL, CPL_IO_DEFAULT));
02278 
02279 
02280     /*
02281      * Find spectrum borders
02282      */
02283 
02284     cpl_msg_info(_task, "Generating mask (%d spectra expected) ...",
02285                  nspectra);
02286 
02287     // FIXME: Finalize the implementation of this experimental method for
02288     //        detecting fibers
02289 #if 0
02290     nxok = _giraffe_build_edge_mask(mz, bpixel, nspectra, noise, config,
02291                                     &ndetect, mxok, myup, mylo);
02292 #endif
02293     // End of test code
02294 
02295 
02296     nxok = _giraffe_build_raw_mask(mz, bpixel, nspectra, noise, config,
02297                                    &ndetect, mxok, myup, mylo);
02298 
02299     if (nxok < 0) {
02300 
02301         switch (nxok) {
02302             case -1:
02303                 cpl_msg_warning(_task, "Invalid number of spectra detected: "
02304                                 "%d != %d", ndetect, nspectra);
02305                 break;
02306 
02307             case -2:
02308                 cpl_msg_warning(_task, "No abcissa with good number "
02309                                 "of spectra");
02310                 break;
02311 
02312             default:
02313                 cpl_msg_warning(_task, "Error while searching for spectra");
02314                 break;
02315         }
02316 
02317         return nxok;
02318 
02319     }
02320     else {
02321         cpl_msg_info(_task, "%d spectra detected in %d wavelength bins",
02322                      ndetect, nxok);
02323     }
02324 
02325 
02326     /*
02327      * Only takes care of good values
02328      */
02329 
02330     cpl_matrix_resize(mxok, 0, nxok - cpl_matrix_get_nrow(mxok), 0, 0);
02331     cpl_matrix_resize(myup, 0, nxok - cpl_matrix_get_nrow(myup), 0, 0);
02332     cpl_matrix_resize(mylo, 0, nxok - cpl_matrix_get_nrow(mylo), 0, 0);
02333 
02334     GIDEBUG(gi_message("%s: mxok[0-%d]=[%g-%g]", __func__,
02335                        cpl_matrix_get_nrow(mxok) - 1,
02336                        cpl_matrix_get_min(mxok),
02337                        cpl_matrix_get_max(mxok)));
02338 
02339 
02340     cpl_msg_info(_task, "Computing spectrum positions and widths in "
02341                  "pixel range [%g,%g]", cpl_matrix_get_min(mxok),
02342                  cpl_matrix_get_max(mxok));
02343 
02344     if (cpl_matrix_get_nrow(mxok) <= config->ydeg) {
02345         cpl_msg_info(_task, "Not enough data points %" CPL_SIZE_FORMAT
02346                      " for %d order fit", cpl_matrix_get_nrow(mxok),
02347                      config->ydeg);
02348 
02349         return -1;
02350     }
02351 
02352     switch (method) {
02353         case GILOCALIZE_HALF_WIDTH:
02354             cpl_msg_info(_task, "Using half-width for localization");
02355             _giraffe_fit_raw_mask(mxok, myup, mylo, fibers, config,
02356                                   position);
02357             break;
02358 
02359         case GILOCALIZE_BARYCENTER:
02360         default:
02361             cpl_msg_info(_task, "Using barycenter for localization");
02362             _giraffe_fit_raw_centroid(mz, mxok, myup, mylo, fibers, config,
02363                                       position, coeffs);
02364             break;
02365     }
02366 
02367     if (normalize == 1) {
02368         cpl_image_delete(mznorm);
02369     }
02370 
02371     /*
02372      * Compute the number of pixels rejected by the fit
02373      */
02374 
02375 
02376     /* FIXME: Here again nspectra equals cpl_table_get_nrow(fibers),
02377      *        where OGL used mButtons->nr. We have to check the
02378      *        correctness carefully here !!
02379      */
02380 
02381     mwid = cpl_matrix_new(nxok, nspectra);
02382 
02383     for (n = 0, nn = 0; nn < cpl_table_get_nrow(fibers); nn++) {
02384 
02385         for (x = 0; x < nxok; x++) {
02386             register cxint lx = (cxint) cpl_matrix_get(mxok, x, 0);
02387 
02388             cxdouble lower = cpl_matrix_get(mylo, x, n);
02389             cxdouble upper = cpl_matrix_get(myup, x, n);
02390             cxdouble width = cpl_matrix_get(position->mw, lx, nn);
02391 
02392             uplost += cpl_matrix_get(position->my, lx, nn) + width - upper;
02393             lolost += cpl_matrix_get(position->my, lx, nn) - width - lower;
02394 
02395             avgborders += upper - lower;
02396             avgmask += width;
02397 
02398             cpl_matrix_set(mwid, x, n, 2. * width);
02399         }
02400         n++;
02401     }
02402 
02403     sigmean = cpl_matrix_get_mean(mwid);
02404     sigmask = giraffe_matrix_sigma_mean(mwid, sigmean);
02405     avglost = (lolost + uplost) / (nspectra * nxok);
02406     avgmask = 2.0 * avgmask / nspectra;
02407 
02408     cpl_msg_info(_task, "Mask was computed using %d of %d wavelength bins",
02409                  nxok, nx);
02410     cpl_msg_info(_task, "Average # of pixels per spectra: %.4g",
02411                  avgmask);
02412     cpl_msg_info(_task, "Average # of in-borders pixels per spectra: %.4g",
02413                  avgborders / nspectra);
02414     cpl_msg_info(_task, "Average lost pixels per spectra: %.4g",
02415                  avglost);
02416     cpl_msg_info(_task, "Average lost pixels at upper border: %.4g",
02417                  uplost / (nspectra * nxok));
02418     cpl_msg_info(_task, "Average lost pixels at lower border: %.4g",
02419                  lolost / (nspectra * nxok));
02420     cpl_msg_info(_task, "Average spectrum width: %.4g +/- %.4g, "
02421                  "(min, max) = (%.4g, %.4g)", sigmean, sigmask,
02422                  cpl_matrix_get_min(mwid), cpl_matrix_get_max(mwid));
02423 
02424     cpl_matrix_delete(mwid);
02425 
02426     cpl_matrix_delete(mylo);
02427     cpl_matrix_delete(myup);
02428     cpl_matrix_delete(mxok);
02429 
02430     return 0;
02431 
02432 }
02433 
02434 
02435 inline static cxint
02436 _giraffe_finalize_fibers(cpl_table *fibers, cpl_matrix *locy, GiImage *mlocy,
02437                          cxdouble maxoffset, cxdouble* maxshift)
02438 {
02439 
02440     cxint i = 0;
02441     cxint j = 0;
02442     cxint nx = 0;
02443     cxint ny = 0;
02444     cxint _nx = 0;
02445     cxint _ny = 0;
02446     cxint nfibers = 0;
02447     cxint irow = 0;
02448 
02449     cxdouble max_shift = 0.;
02450     cxdouble *positions = NULL;
02451 
02452     cpl_image *_mlocy = NULL;
02453 
02454 
02455     if (fibers == NULL || locy == NULL || mlocy == NULL) {
02456         return -1;
02457     }
02458 
02459     if (cpl_table_has_column(fibers, "RINDEX") == FALSE) {
02460         return -1;
02461     }
02462 
02463     nx = cpl_matrix_get_ncol(locy);
02464     ny = cpl_matrix_get_nrow(locy);
02465 
02466     nfibers = cpl_table_get_nrow(fibers);
02467 
02468     _mlocy = giraffe_image_get(mlocy);
02469     _nx = cpl_image_get_size_x(_mlocy);
02470     _ny = cpl_image_get_size_y(_mlocy);
02471 
02472     if (ny != _ny) {
02473         return -2;
02474     }
02475 
02476     if (nfibers > _nx) {
02477         return -3;
02478     }
02479 
02480     cpl_table_select_all(fibers);
02481 
02482 
02483     /*
02484      * Get pointer to the central scan line.
02485      */
02486 
02487     irow = (_ny - 1) / 2;
02488     positions = (cxdouble *)cpl_image_get_data(_mlocy) + irow * _nx;
02489 
02490 
02491     /*
02492      * Compare the detected fiber positions with the positions
02493      * from the reference localization. Select only those fibers
02494      * whose distance from the reference positions is less than
02495      * a given offset. All other fibers are removed from the
02496      * fiber table.
02497      */
02498 
02499     for (i = 0; i < nfibers; i++) {
02500 
02501         if (j < nx) {
02502 
02503             cxint pos = cpl_table_get_int(fibers, "RINDEX", i, NULL) - 1;
02504 
02505             cxdouble yc = cpl_matrix_get(locy, irow, j);
02506             cxdouble shift = fabs(yc - positions[pos]);
02507 
02508             if (shift <= maxoffset) {
02509                 cpl_table_unselect_row(fibers, i);
02510                 ++j;
02511             }
02512             else {
02513                 max_shift = CX_MAX(max_shift, shift);
02514             }
02515 
02516         }
02517     }
02518 
02519     cpl_table_erase_selected(fibers);
02520 
02521     if (maxshift != NULL) {
02522         *maxshift = max_shift;
02523     }
02524 
02525     return 0;
02526 
02527 }
02528 
02529 
02558 cxint
02559 giraffe_localize_spectra(GiLocalization *result, GiImage *image,
02560                          GiTable *fibers, GiLocalization *master,
02561                          GiImage *badpixels, GiLocalizeConfig *config)
02562 {
02563 
02564     const cxchar *fctid = "giraffe_localize_spectra";
02565 
02566     cxint i;
02567     cxint status;
02568     cxint nrows;
02569     cxint nfibers;
02570     cxint nframes = 1;
02571     cxint ckwidth;
02572     cxint ckheight;
02573     cxint ckcount;
02574 
02575     cxdouble mwidth;
02576     cxdouble conad      = 0.;
02577     cxdouble bias_ron   = 0.;
02578     cxdouble mask_sigma = 0.;
02579 
02580     cx_string *pname;
02581 
02582     cpl_propertylist *properties;
02583 
02584     cpl_image *_image = giraffe_image_get(image);
02585     cpl_image *_bpixel = giraffe_image_get(badpixels);
02586     cpl_image *_result = NULL;
02587 
02588     cpl_matrix *_my;
02589 
02590     cpl_table *_fibers = NULL;
02591     cpl_table *fiber_setup = NULL;
02592     cpl_table *locc;
02593 
02594     GiLocalizeMethod method;
02595 
02596     GiInstrumentMode mode;
02597 
02598     GiMaskParameters mask_config;
02599 
02600     GiMaskPosition mask_position;
02601     GiMaskPosition mask_coeffs;
02602 
02603 
02604 
02605     /*
02606      * Preprocessing
02607      */
02608 
02609     if (result == NULL || image == NULL || fibers == NULL || config == NULL) {
02610         cpl_error_set(fctid, CPL_ERROR_NULL_INPUT);
02611         return 1;
02612     }
02613 
02614     if (badpixels != NULL) {
02615         cpl_msg_debug(fctid,"Bad pixel correction is not available. Bad "
02616                       "pixel map will be ignored.");
02617     }
02618 
02619     _fibers = giraffe_table_get(fibers);
02620 
02621     if (_fibers == NULL) {
02622         cpl_error_set(fctid, CPL_ERROR_DATA_NOT_FOUND);
02623         return 1;
02624     }
02625     else {
02626         fiber_setup = _fibers;
02627     }
02628 
02629     properties = giraffe_image_get_properties(image);
02630 
02631 
02632     /*
02633      * Add the number of fibers to the image properties.
02634      */
02635 
02636     nfibers = cpl_table_get_nrow(_fibers);
02637 
02638     cpl_msg_info(fctid, "Setting number of fibers (%s) to %d",
02639                  GIALIAS_NFIBERS, nfibers);
02640 
02641     cpl_propertylist_update_int(properties, GIALIAS_NFIBERS, nfibers);
02642     cpl_propertylist_set_comment(properties, GIALIAS_NFIBERS,
02643                                  "Number of fibres");
02644 
02645 
02646     if (!cpl_propertylist_has(properties, GIALIAS_CONAD)) {
02647         cpl_msg_error(fctid, "Missing detector gain property (%s)! ",
02648                       GIALIAS_CONAD);
02649         return 1;
02650     }
02651     else {
02652         conad = cpl_propertylist_get_double(properties, GIALIAS_CONAD);
02653     }
02654 
02655 
02656     /*
02657      * If a ron value is provided write it to the image properties.
02658      */
02659 
02660     if (config->ron > 0.) {
02661         cpl_msg_info(fctid, "Setting bias sigma value (%s) to %.5g",
02662                      GIALIAS_BIASSIGMA, config->ron);
02663         cpl_propertylist_update_double(properties, GIALIAS_BIASSIGMA,
02664                                        config->ron);
02665     }
02666 
02667     bias_ron = giraffe_propertylist_get_ron(properties);
02668     cpl_msg_info(fctid, "Bias sigma value: %.3g e-", bias_ron);
02669 
02670 
02671     if (cpl_propertylist_has(properties, GIALIAS_DATANCOM)) {
02672         nframes = cpl_propertylist_get_int(properties, GIALIAS_DATANCOM);
02673     }
02674 
02675 
02676     if (config->noise > 0.) {
02677         cpl_msg_info(fctid, "Noise multiplier: %.3g",
02678                      config->noise);
02679     }
02680     else {
02681         cpl_msg_info(fctid, "Threshold multiplier: %.3g",
02682                      fabs(config->noise));
02683     }
02684 
02685 
02686     /*
02687      * Setup localization start position in the dispersion direction.
02688      */
02689 
02690     nrows = cpl_image_get_size_y(_image);
02691 
02692     if (config->start < 0) {
02693         config->start = nrows / 2;
02694     }
02695 
02696     /*
02697      * Set instrument mode specific parameters like the width of a spectrum
02698      * and the width of the intra-spectrum gap.
02699      */
02700 
02701     mode = giraffe_get_mode(properties);
02702 
02703     if (config->ywidth < 1) {
02704 
02705         cpl_msg_info(fctid, "Configuring equilizing filter width from "
02706                      "instrument mode");
02707 
02708         switch (mode) {
02709             case GIMODE_MEDUSA:
02710                 config->ywidth = 16;
02711                 break;
02712 
02713             case GIMODE_IFU:
02714                 config->ywidth = 6;
02715                 break;
02716 
02717             case GIMODE_ARGUS:
02718                 config->ywidth = 6;
02719                 break;
02720 
02721             default:
02722                 cpl_msg_error(fctid, "Invalid instrument mode!");
02723                 return 1;
02724                 break;
02725         }
02726 
02727 
02728         if (!cpl_propertylist_has(properties, GIALIAS_SLITNAME)) {
02729             cpl_msg_error(fctid, "Property (%s) not found in raw image",
02730                           GIALIAS_SLITNAME);
02731             return 1;
02732         }
02733         else {
02734             const cxchar *slit =
02735                 cpl_propertylist_get_string(properties, GIALIAS_SLITNAME);
02736 
02737             cpl_msg_info(fctid, "Setting equilizing filter to %d [pxl] "
02738                          "for slit configuration `%s'", config->ywidth,
02739                          slit);
02740         }
02741 
02742     }
02743 
02744 
02745     /*
02746      * Set mean spectrum width according to the instrument mode
02747      */
02748 
02749     switch (mode) {
02750         case GIMODE_MEDUSA:
02751             mwidth = GISPECTRUM_MWIDTH_MEDUSA;
02752 
02753             ckwidth = 1;
02754             ckheight = 3;
02755             ckcount = 8;
02756 
02757             break;
02758 
02759         case GIMODE_IFU:
02760             mwidth = GISPECTRUM_MWIDTH_IFU;
02761 
02762             ckwidth = 0;
02763             ckheight = 3;
02764             ckcount = 4;
02765 
02766             break;
02767 
02768         case GIMODE_ARGUS:
02769             mwidth = GISPECTRUM_MWIDTH_IFU;
02770 
02771             ckwidth = 0;
02772             ckheight = 3;
02773             ckcount = 4;
02774 
02775             break;
02776 
02777         default:
02778             cpl_msg_error(fctid, "Invalid instrument mode!");
02779             return 1;
02780             break;
02781     }
02782 
02783 
02784     /*
02785      * Setup localization method
02786      */
02787 
02788     if (config->centroid == TRUE) {
02789         method = GILOCALIZE_BARYCENTER;
02790     }
02791     else {
02792         method = GILOCALIZE_HALF_WIDTH;
02793     }
02794 
02795 
02796     /*
02797      * Fill the parameter structure for the localization mask computation
02798      * with the actual values.
02799      */
02800 
02801     mask_config.ywidth = config->ywidth;
02802     mask_config.method = config->threshold;
02803     mask_config.threshold = config->noise;
02804     mask_config.ydeg = config->yorder;
02805     mask_config.wdeg = config->worder;
02806     mask_config.ewid = config->ewidth;
02807     mask_config.wavg = mwidth;
02808     mask_config.ckdata.width = ckwidth;
02809     mask_config.ckdata.height = ckheight;
02810     mask_config.ckdata.count = ckcount;
02811     mask_config.sigma = config->sigma;
02812     mask_config.niter = config->iterations;
02813     mask_config.mfrac = config->fraction;
02814     mask_config.start = config->start;
02815     mask_config.retry = config->retries;
02816     mask_config.xbin = config->binsize;
02817 
02818 
02819     /*
02820      * If config->noise is larger than 0. it is basically a signal-to-noise
02821      * ratio, given in ADU for a single/average frame. Since the fiber
02822      * detection is carried out on a summed frame of electrons, the
02823      * signal-to-noise limit has to be re-scaled properly.
02824      */
02825 
02826     if (config->noise > 0.) {
02827         mask_config.threshold *= sqrt(nframes * conad);
02828     }
02829 
02830 
02831     /*
02832      * Processing
02833      */
02834 
02835     /*
02836      * Localize spectra. Depending on the setup we either do a full
02837      * localization or we just localize the simultaneous calibration
02838      * fibers using an existing, full master localization.
02839      */
02840 
02841     if (config->full != TRUE) {
02842 
02843         cpl_msg_info(fctid, "Computing spectrum localization using SIWC "
02844                      "spectra");
02845 
02846         if (!master || !master->locy || !master->locy) {
02847             cpl_msg_error(fctid, "Required full master localization is "
02848                           "missing!");
02849             return 1;
02850         }
02851 
02852 
02853         /*
02854          * Select SIWC fibers from the fiber table. The simultaneous
02855          * calibration spectra are indicated by a -1 as retractor position.
02856          */
02857 
02858         cpl_table_unselect_all(_fibers);
02859         cpl_table_or_selected_int(_fibers, "RP", CPL_EQUAL_TO, -1);
02860 
02861         fiber_setup = cpl_table_extract_selected(_fibers);
02862         nfibers = cpl_table_get_nrow(fiber_setup);
02863 
02864     }
02865 
02866 
02867     /*
02868      * Allocate required output matrices and hook them into the appropriate
02869      * structures.
02870      */
02871 
02872     mask_position.type = GIMASK_FITTED_DATA;
02873     mask_position.my = cpl_matrix_new(nrows, nfibers);
02874     mask_position.mw = cpl_matrix_new(nrows, nfibers);
02875 
02876     mask_coeffs.type = GIMASK_FIT_COEFFS;
02877     mask_coeffs.my = cpl_matrix_new(mask_config.ydeg + 1, nfibers);
02878     mask_coeffs.mw = cpl_matrix_new(1, (mask_config.wdeg + 1) *
02879                                     (mask_config.wdeg + 1));
02880 
02881     /*
02882      * Convert raw image from ADU to electrons, and adjust the readout
02883      * noise to match the number of images that were used to create the
02884      * raw image.
02885      */
02886 
02887     _image = cpl_image_multiply_scalar_create(_image, nframes * conad);
02888 
02889     mask_sigma = sqrt(nframes) * bias_ron;
02890 
02891 
02892     /*
02893      * Compute the position of the spectra on the CCD
02894      */
02895 
02896     status = _giraffe_localize_spectra(_image, _bpixel, fiber_setup,
02897                                        method, config->normalize,
02898                                        mask_sigma,
02899                                        &mask_config, &mask_position,
02900                                        &mask_coeffs);
02901 
02902     cpl_image_delete(_image);
02903     _image = NULL;
02904 
02905     if (status) {
02906         result->locy = NULL;
02907         result->locw = NULL;
02908         result->locc = NULL;
02909         result->psf = NULL;
02910 
02911         cpl_matrix_delete(mask_position.my);
02912         cpl_matrix_delete(mask_position.mw);
02913 
02914         cpl_matrix_delete(mask_coeffs.my);
02915         cpl_matrix_delete(mask_coeffs.mw);
02916 
02917         if (config->full != TRUE) {
02918             cpl_table_delete(fiber_setup);
02919         }
02920 
02921         cpl_msg_error(fctid, "Spectrum localization computation failed!");
02922 
02923         return 1;
02924     }
02925 
02926 
02927     /*
02928      * Post processing
02929      */
02930 
02931     if (config->full != TRUE) {
02932 
02933         /*
02934          * TBD: Postprocessing of localization. Compare computed spectrum
02935          *      locations with master, i.e. calculate differences.
02936          */
02937 
02938         cpl_table_delete(fiber_setup);
02939 
02940     }
02941     else {
02942 
02943         if (master != NULL && master->locy != NULL) {
02944 
02945             cxint nf = cpl_table_get_nrow(_fibers);
02946 
02947             cxdouble maxoffset = 0.5 * mask_config.wavg;
02948             cxdouble maxshift = 0.;
02949 
02950 
02951             cpl_msg_info(fctid, "Comparing detected and expected fiber "
02952                          "positions.");
02953 
02954             status = _giraffe_finalize_fibers(_fibers, mask_position.my,
02955                                               master->locy, maxoffset,
02956                                               &maxshift);
02957 
02958             if (status != 0) {
02959 
02960                 if (status == -3) {
02961 
02962                     const cpl_image* mlocy = giraffe_image_get(master->locy);
02963                     cxint _nf = cpl_image_get_size_x(mlocy);
02964 
02965                     cpl_msg_error(fctid, "More fibers (%d) than expected "
02966                             "(%d) were found!", nf, _nf);
02967 
02968                 }
02969 
02970                 result->locy = NULL;
02971                 result->locw = NULL;
02972                 result->locc = NULL;
02973                 result->psf = NULL;
02974 
02975                 cpl_matrix_delete(mask_position.my);
02976                 cpl_matrix_delete(mask_position.mw);
02977 
02978                 cpl_matrix_delete(mask_coeffs.my);
02979                 cpl_matrix_delete(mask_coeffs.mw);
02980 
02981                 if (config->full != TRUE) {
02982                     cpl_table_delete(fiber_setup);
02983                 }
02984 
02985                 cpl_msg_error(fctid, "Comparison of fiber positions "
02986                               "failed!");
02987 
02988                 return 1;
02989             }
02990 
02991             cx_assert(cpl_table_get_nrow(_fibers) <= nf);
02992 
02993             cpl_msg_info(fctid, "%" CPL_SIZE_FORMAT " of %d expected fibers "
02994                          "were detected.", cpl_table_get_nrow(_fibers), nf);
02995 
02996             if (cpl_table_get_nrow(_fibers) < nf) {
02997                 cpl_msg_debug(fctid, "Maximum offset from the expected "
02998                         "position is %.2f, maximum allowed offset is %.2f",
02999                         maxshift, maxoffset);
03000                 cpl_msg_warning(fctid, "%" CPL_SIZE_FORMAT " fibers are "
03001                                 "missing!", nf - cpl_table_get_nrow(_fibers));
03002             }
03003 
03004         }
03005 
03006     }
03007 
03008 
03009     /*
03010      * Convert matrices into images and tables and add the necessary
03011      * properties.
03012      */
03013 
03014     /* Spectrum center position */
03015 
03016     result->locy =
03017         giraffe_image_create(CPL_TYPE_DOUBLE,
03018                              cpl_matrix_get_ncol(mask_position.my),
03019                              cpl_matrix_get_nrow(mask_position.my));
03020 
03021     giraffe_image_copy_matrix(result->locy, mask_position.my);
03022     cpl_matrix_delete(mask_position.my);
03023 
03024     giraffe_image_set_properties(result->locy, properties);
03025     properties = giraffe_image_get_properties(result->locy);
03026 
03027     _result = giraffe_image_get(result->locy);
03028 
03029     cpl_propertylist_set_int(properties, GIALIAS_NAXIS1,
03030                              cpl_image_get_size_x(_result));
03031     cpl_propertylist_set_int(properties, GIALIAS_NAXIS2,
03032                              cpl_image_get_size_y(_result));
03033     cpl_propertylist_set_int(properties, GIALIAS_BITPIX, -32);
03034     cpl_propertylist_set_double(properties, GIALIAS_BZERO, 0.);
03035     cpl_propertylist_set_double(properties, GIALIAS_BSCALE, 1.);
03036 
03037     cpl_propertylist_append_int(properties, GIALIAS_LOCNX,
03038                                 cpl_image_get_size_y(_result));
03039     cpl_propertylist_append_int(properties, GIALIAS_LOCNS,
03040                                 cpl_image_get_size_x(_result));
03041 
03042     if (config->centroid) {
03043         cpl_propertylist_append_string(properties, GIALIAS_LMETHOD,
03044                                        "BARYCENTER");
03045     }
03046     else {
03047         cpl_propertylist_append_string(properties, GIALIAS_LMETHOD,
03048                                        "HALF_WIDTH");
03049     }
03050 
03051     if (config->normalize) {
03052         cpl_propertylist_append_int(properties, GIALIAS_LNORMALIZE,
03053                                     config->ywidth);
03054     }
03055     else {
03056         cpl_propertylist_append_int(properties, GIALIAS_LNORMALIZE,
03057                                     -config->ywidth);
03058     }
03059 
03060     cpl_propertylist_append_bool(properties, GIALIAS_LFULLLOC, config->full);
03061     cpl_propertylist_append_int(properties, GIALIAS_LOCYDEG, config->yorder);
03062     cpl_propertylist_append_int(properties, GIALIAS_LOCWDEG, config->worder);
03063     cpl_propertylist_append_double(properties, GIALIAS_LEXTRAWID,
03064                                    config->ewidth);
03065     cpl_propertylist_append_double(properties, GIALIAS_LNOISEMULT,
03066                                    config->noise);
03067 
03068     cpl_propertylist_append_double(properties, GIALIAS_LCLIPSIGMA,
03069                                    config->sigma);
03070     cpl_propertylist_append_int(properties, GIALIAS_LCLIPNITER,
03071                                 config->iterations);
03072     cpl_propertylist_append_double(properties, GIALIAS_LCLIPMFRAC,
03073                                    config->fraction);
03074 
03075 
03076     if (cpl_propertylist_has(properties, GIALIAS_GIRFTYPE)) {
03077         cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE, "LOCY");
03078     }
03079     else {
03080         cpl_propertylist_append_string(properties, GIALIAS_GIRFTYPE, "LOCY");
03081     }
03082     cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE, "GIRAFFE "
03083                                  "localization centroid");
03084 
03085 
03086     /* Spectrum width */
03087 
03088     result->locw =
03089         giraffe_image_create(CPL_TYPE_DOUBLE,
03090                              cpl_matrix_get_ncol(mask_position.mw),
03091                              cpl_matrix_get_nrow(mask_position.mw));
03092 
03093     giraffe_image_copy_matrix(result->locw, mask_position.mw);
03094     cpl_matrix_delete(mask_position.mw);
03095 
03096     giraffe_image_set_properties(result->locw, properties);
03097     properties = giraffe_image_get_properties(result->locw);
03098 
03099     _result = giraffe_image_get(result->locw);
03100 
03101     cpl_propertylist_set_int(properties, GIALIAS_NAXIS1,
03102                              cpl_image_get_size_x(_result));
03103     cpl_propertylist_set_int(properties, GIALIAS_NAXIS2,
03104                              cpl_image_get_size_y(_result));
03105 
03106     if (cpl_propertylist_has(properties, GIALIAS_GIRFTYPE)) {
03107         cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE,
03108                                     "LOCWY");
03109     }
03110     else {
03111         cpl_propertylist_append_string(properties, GIALIAS_GIRFTYPE,
03112                                        "LOCWY");
03113     }
03114     cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE, "GIRAFFE "
03115                                  "localization half-width");
03116 
03117 
03118     /* Coefficients table */
03119 
03120     locc = cpl_table_new(cpl_matrix_get_ncol(mask_coeffs.my));
03121 
03122     cpl_table_new_column(locc, "BUTTON", CPL_TYPE_INT);
03123     for (i = 0; i < cpl_table_get_nrow(locc); i++) {
03124         cpl_table_set_int(locc, "BUTTON", i, i);
03125     }
03126 
03127     for (i = 0; i < cpl_matrix_get_nrow(mask_coeffs.my); i++) {
03128         cxchar *label = NULL;
03129 
03130         cx_asprintf(&label, "YC%d", i);
03131         cpl_table_new_column(locc, label, CPL_TYPE_DOUBLE);
03132         cx_free(label);
03133     }
03134 
03135 
03136     result->locc = giraffe_table_create(locc, properties);
03137     cpl_table_delete(locc);
03138 
03139     _my = cpl_matrix_transpose_create(mask_coeffs.my);
03140     giraffe_table_copy_matrix(result->locc, "YC0", _my);
03141     cpl_matrix_delete(_my);
03142     cpl_matrix_delete(mask_coeffs.my);
03143 
03144     properties = giraffe_table_get_properties(result->locc);
03145 
03146 
03147     /* Add coefficients of the 2D fit to the table properties */
03148 
03149     pname = cx_string_new();
03150 
03151     for (i = 0; i < cpl_matrix_get_ncol(mask_coeffs.mw); i++) {
03152         cx_string_sprintf(pname, "%s%d", GIALIAS_LOCWIDCOEF, i);
03153         cpl_propertylist_append_double(properties, cx_string_get(pname),
03154                                        cpl_matrix_get(mask_coeffs.mw, 0, i));
03155     }
03156 
03157     cx_string_delete(pname);
03158     cpl_matrix_delete(mask_coeffs.mw);
03159 
03160     cpl_propertylist_update_string(properties, GIALIAS_GIRFTYPE,
03161                                    "LOCYWCHEB");
03162     cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE, "GIRAFFE "
03163                                  "localization fit coefficients");
03164 
03165 
03166     /* Not used */
03167 
03168     result->psf = NULL;
03169 
03170     return 0;
03171 
03172 }
03173 
03174 
03185 GiLocalizeConfig *
03186 giraffe_localize_config_create(cpl_parameterlist *list)
03187 {
03188 
03189     const cxchar *s;
03190     cpl_parameter *p;
03191 
03192     GiLocalizeConfig *config = NULL;
03193 
03194 
03195     if (list == NULL) {
03196         return NULL;
03197     }
03198 
03199     config = cx_calloc(1, sizeof *config);
03200 
03201 
03202     /*
03203      * Some defaults
03204      */
03205 
03206     config->full = TRUE;
03207     config->centroid = TRUE;
03208     config->threshold = GILOCALIZE_THRESHOLD_LOCAL;
03209 
03210 
03211     p = cpl_parameterlist_find(list, "giraffe.localization.mode");
03212     s = cpl_parameter_get_string(p);
03213     if (strcmp(s, "siwc") == 0) {
03214         config->full = FALSE;
03215     }
03216 
03217     p = cpl_parameterlist_find(list, "giraffe.localization.start");
03218     config->start = cpl_parameter_get_int(p);
03219 
03220     p = cpl_parameterlist_find(list, "giraffe.localization.retries");
03221     config->retries = cpl_parameter_get_int(p);
03222 
03223     p = cpl_parameterlist_find(list, "giraffe.localization.binsize");
03224     config->binsize = cpl_parameter_get_int(p);
03225 
03226     p = cpl_parameterlist_find(list, "giraffe.localization.ewidth");
03227     config->ewidth = cpl_parameter_get_double(p);
03228 
03229     p = cpl_parameterlist_find(list, "giraffe.localization.ywidth");
03230     config->ywidth = cpl_parameter_get_int(p);
03231 
03232     p = cpl_parameterlist_find(list, "giraffe.localization.center");
03233     s = cpl_parameter_get_string(p);
03234     if (!strcmp(s, "hwidth")) {
03235         config->centroid = FALSE;
03236     }
03237 
03238     p = cpl_parameterlist_find(list, "giraffe.localization.normalize");
03239     config->normalize = cpl_parameter_get_bool(p);
03240 
03241     p = cpl_parameterlist_find(list, "giraffe.localization.threshold");
03242     s = cpl_parameter_get_string(p);
03243 
03244     if (strncmp(s, "global", 6) == 0) {
03245         config->threshold = GILOCALIZE_THRESHOLD_GLOBAL;
03246     }
03247     else if (strncmp(s, "row", 3) == 0) {
03248         config->threshold = GILOCALIZE_THRESHOLD_ROW;
03249     }
03250     else {
03251         config->threshold = GILOCALIZE_THRESHOLD_LOCAL;
03252     }
03253 
03254     p = cpl_parameterlist_find(list, "giraffe.localization.noise");
03255     config->noise = cpl_parameter_get_double(p);
03256 
03257     p = cpl_parameterlist_find(list, "giraffe.localization.ron");
03258     config->ron = cpl_parameter_get_double(p);
03259 
03260     p = cpl_parameterlist_find(list, "giraffe.localization.yorder");
03261     config->yorder = cpl_parameter_get_int(p);
03262 
03263     p = cpl_parameterlist_find(list, "giraffe.localization.worder");
03264     config->worder = cpl_parameter_get_int(p);
03265 
03266     p = cpl_parameterlist_find(list, "giraffe.localization.sigma");
03267     config->sigma = cpl_parameter_get_double(p);
03268 
03269     p = cpl_parameterlist_find(list, "giraffe.localization.iterations");
03270     config->iterations = cpl_parameter_get_int(p);
03271 
03272     p = cpl_parameterlist_find(list, "giraffe.localization.fraction");
03273     config->fraction = cpl_parameter_get_double(p);
03274 
03275     return config;
03276 
03277 }
03278 
03279 
03292 void
03293 giraffe_localize_config_destroy(GiLocalizeConfig *config)
03294 {
03295 
03296     if (config) {
03297         cx_free(config);
03298     }
03299 
03300     return;
03301 
03302 }
03303 
03304 
03316 void
03317 giraffe_localize_config_add(cpl_parameterlist *list)
03318 {
03319 
03320     cpl_parameter *p;
03321 
03322 
03323     if (list == NULL) {
03324         return;
03325     }
03326 
03327     p = cpl_parameter_new_enum("giraffe.localization.mode",
03328                                CPL_TYPE_STRING,
03329                                "Localization mode: Use all spectra "
03330                                "or the 5 SIWC spectra",
03331                                "giraffe.localization",
03332                                "all", 2, "all", "siwc");
03333     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-mode");
03334     cpl_parameterlist_append(list, p);
03335 
03336 
03337     p = cpl_parameter_new_value("giraffe.localization.start",
03338                                 CPL_TYPE_INT,
03339                                 "Bin along x-axis",
03340                                 "giraffe.localization",
03341                                 -1);
03342     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-start");
03343     cpl_parameterlist_append(list, p);
03344 
03345 
03346     p = cpl_parameter_new_value("giraffe.localization.retries",
03347                                 CPL_TYPE_INT,
03348                                 "Initial localization detection "
03349                                 "xbin retries.",
03350                                 "giraffe.localization",
03351                                 10);
03352     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-retries");
03353     cpl_parameterlist_append(list, p);
03354 
03355 
03356     p = cpl_parameter_new_value("giraffe.localization.binsize",
03357                                 CPL_TYPE_INT,
03358                                 "Initial localization detection "
03359                                 "xbin size.",
03360                                 "giraffe.localization",
03361                                 -1);
03362     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-binsize");
03363     cpl_parameterlist_append(list, p);
03364 
03365 
03366     p = cpl_parameter_new_value("giraffe.localization.ewidth",
03367                                 CPL_TYPE_DOUBLE,
03368                                 "Localization detection extra width.",
03369                                 "giraffe.localization",
03370                                 1.0);
03371     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-ewidth");
03372     cpl_parameterlist_append(list, p);
03373 
03374 
03375     p = cpl_parameter_new_value("giraffe.localization.ywidth",
03376                                 CPL_TYPE_INT,
03377                                 "Full width [pxl] of the equilizing "
03378                                 "filter (distance between two "
03379                                 "adjacent fibers).",
03380                                 "giraffe.localization",
03381                                 -1);
03382     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-ywidth");
03383     cpl_parameterlist_append(list, p);
03384 
03385 
03386     p = cpl_parameter_new_enum("giraffe.localization.center",
03387                                CPL_TYPE_STRING,
03388                                "Method used for mask center "
03389                                "computation.",
03390                                "giraffe.localization",
03391                                "centroid", 2, "centroid",
03392                                "hwidth");
03393     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-center");
03394     cpl_parameterlist_append(list, p);
03395 
03396 
03397     p = cpl_parameter_new_value("giraffe.localization.normalize",
03398                                 CPL_TYPE_BOOL,
03399                                 "Enable spectrum normalization along "
03400                                 "the dispersion axis.",
03401                                 "giraffe.localization",
03402                                 FALSE);
03403     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-norm");
03404     cpl_parameterlist_append(list, p);
03405 
03406 
03407     p = cpl_parameter_new_value("giraffe.localization.noise",
03408                                 CPL_TYPE_DOUBLE,
03409                                 "Threshold multiplier.",
03410                                 "giraffe.localization",
03411                                 7.0);
03412     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-noise");
03413     cpl_parameterlist_append(list, p);
03414 
03415 
03416     p = cpl_parameter_new_enum("giraffe.localization.threshold",
03417                                CPL_TYPE_STRING,
03418                                "Selects thresholding algorithm: local, "
03419                                "row or global",
03420                                "giraffe.localization",
03421                                "local", 3, "local", "row", "global");
03422     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-threshold");
03423     cpl_parameterlist_append(list, p);
03424 
03425 
03426     p = cpl_parameter_new_value("giraffe.localization.ron",
03427                                 CPL_TYPE_DOUBLE,
03428                                 "New bias sigma (RON) value for dark "
03429                                 "subtraction",
03430                                 "giraffe.localization",
03431                                 -1.);
03432     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-ron");
03433     cpl_parameterlist_append(list, p);
03434 
03435 
03436     p = cpl_parameter_new_value("giraffe.localization.yorder",
03437                                 CPL_TYPE_INT,
03438                                 "Order of Chebyshev polynomial fit.",
03439                                 "giraffe.localization",
03440                                 4);
03441     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-yorder");
03442     cpl_parameterlist_append(list, p);
03443 
03444 
03445     p = cpl_parameter_new_value("giraffe.localization.worder",
03446                                 CPL_TYPE_INT,
03447                                 "Order of Chebyshev 2D polynomial fit.",
03448                                 "giraffe.localization",
03449                                 2);
03450     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-worder");
03451     cpl_parameterlist_append(list, p);
03452 
03453 
03454     p = cpl_parameter_new_value("giraffe.localization.sigma",
03455                                 CPL_TYPE_DOUBLE,
03456                                 "Localization clipping: sigma threshold "
03457                                 "factor",
03458                                 "giraffe.localization",
03459                                 2.5);
03460     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-sigma");
03461     cpl_parameterlist_append(list, p);
03462 
03463 
03464     p = cpl_parameter_new_value("giraffe.localization.iterations",
03465                                 CPL_TYPE_INT,
03466                                 "Localization clipping: number of "
03467                                 "iterations",
03468                                 "giraffe.localization",
03469                                 5);
03470     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-niter");
03471     cpl_parameterlist_append(list, p);
03472 
03473 
03474     p = cpl_parameter_new_range("giraffe.localization.fraction",
03475                                 CPL_TYPE_DOUBLE,
03476                                 "Localization clipping: minimum fraction "
03477                                 "of points accepted/total.",
03478                                 "giraffe.localization",
03479                                 0.9, 0.0, 1.0);
03480     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-mfrac");
03481     cpl_parameterlist_append(list, p);
03482 
03483     return;
03484 
03485 }

This file is part of the GIRAFFE Pipeline Reference Manual 2.10.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Thu Mar 7 14:11:02 2013 by doxygen 1.4.7 written by Dimitri van Heesch, © 1997-2004