GIRAFFE Pipeline Reference Manual

gilocalize.c

00001 /* $Id: gilocalize.c,v 1.51 2010/06/21 09:27:22 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: 2010/06/21 09:27:22 $
00024  * $Revision: 1.51 $
00025  * $Name: giraffe-2_8_8 $
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[%d,%d], expected "
01846                  "[%d,%d]", fctid, cpl_matrix_get_nrow(position->my),
01847                  cpl_matrix_get_ncol(position->my), nx, ns);
01848         return;
01849     }
01850 
01851     if (cpl_matrix_get_nrow(position->mw) != nx ||
01852         cpl_matrix_get_ncol(position->mw) != ns) {
01853         gi_error("%s: invalid size for position->mw[%d,%d], expected "
01854                  "[%d,%d]", fctid, cpl_matrix_get_nrow(position->my),
01855                  cpl_matrix_get_ncol(position->my), nx, ns);
01856         return;
01857     }
01858 
01859 
01860     /*
01861      * Initialize with all abcissa
01862      */
01863 
01864     mxraw = cpl_matrix_new(nx, 1);
01865 
01866     for (x = 0; x < nx; x++) {
01867         cpl_matrix_set(mxraw, x, 0, x);
01868     }
01869 
01870 
01871     /*
01872      * Compute Chebyshev base over all x bins
01873      */
01874 
01875     base = giraffe_chebyshev_base1d(0., nx, yorder, mxraw);
01876     cpl_matrix_delete(mxraw);
01877 
01878     mycenter = cpl_matrix_new(cpl_matrix_get_nrow(mxok), ns);
01879     mywidth = cpl_matrix_new(1, cpl_matrix_get_nrow(mxok) * ns);
01880 
01881     ycencoeff = cpl_matrix_new(yorder, 1);
01882 
01883     for (nn = 0; nn < ns; nn++) {
01884 
01885         /* FIXME: The fiber selection changed the following piece of code
01886          *        should not be necessary but we have to check that the
01887          *        accessed to the matrix rows correspond to the selected
01888          *        fibers.
01889          */
01890 
01891         //if (mButton->m[nn] != LOC_OK_SPECTRUM) {
01892         //    continue;
01893         //}
01894 
01895         /*
01896          * compute the barycenter and half-width of the corresponding mask
01897          * between raw borders.
01898          */
01899 
01900         cxdouble* pixels = cpl_image_get_data_double(mz);
01901 
01902         for (x = 0; x < cpl_matrix_get_nrow(mxok); x++) {
01903 
01904             register cxint zx = (cxint) cpl_matrix_get(mxok, x, 0);
01905 
01906             register cxdouble zz = 0.;
01907             register cxdouble yy = 0.;
01908 
01909             cxdouble lower = cpl_matrix_get(mylo, x, nspectra);
01910             cxdouble upper = cpl_matrix_get(myup, x, nspectra);
01911 
01912 
01913             for (y = (cxint) lower; y <= (cxint) upper; y++) {
01914                 yy += pixels[zx * ny + y] * y;
01915                 zz += pixels[zx * ny + y];
01916             }
01917 
01918             cpl_matrix_set(mycenter, x, nspectra, yy / zz);
01919             cpl_matrix_set(mywidth, 0, x * ns + nspectra, config->ewid +
01920                            (upper - lower) / 2.0);
01921 
01922         }   /* for each x bin */
01923 
01924         /*
01925          * The matrix ycenfit[nx] stores the fitted centroid
01926          */
01927 
01928         cpl_matrix_fill(ycencoeff, 0.);
01929         ycenfit = _giraffe_fit_border(mycenter, base, mxok, nspectra,
01930                                       config->sigma, config->niter,
01931                                       config->mfrac, ycencoeff);
01932         if (ycenfit == NULL) {
01933             cpl_msg_warning(_task, "Could not fit centroid for spectrum %d",
01934                             nn);
01935             nspectra++;
01936             continue;
01937         }
01938 
01939         /*
01940          * Save centroid Chebyshev fit coeffs
01941          */
01942 
01943         for (x = 0; x < yorder; x++) {
01944             cpl_matrix_set(coeffs->my, x, nn,
01945                            cpl_matrix_get(ycencoeff, x, 0));
01946         }
01947 
01948         /*
01949          * The localization centroid is a Chebyshev polynomial fit
01950          * of Y barycenters in raw mask
01951          */
01952 
01953         for (x = 0; x < nx; x++) {
01954             cpl_matrix_set(position->my, x, nn,
01955                            cpl_matrix_get(ycenfit, 0, x));
01956         }   /* for each x bin */
01957 
01958         cpl_matrix_delete(ycenfit);
01959         nspectra++;
01960 
01961     } /* each spectrum */
01962 
01963     GIDEBUG(cpl_image *lycenter = giraffe_matrix_create_image(mycenter);
01964             cpl_image_save(lycenter, "lycenter.fits", -32, NULL,
01965                            CPL_IO_DEFAULT);
01966             cpl_image_delete(lycenter);
01967 
01968             lycenter = giraffe_matrix_create_image(position->my);
01969             cpl_image_save(lycenter, "lycenterfit.fits", -32, NULL,
01970                            CPL_IO_DEFAULT);
01971             cpl_image_delete(lycenter);
01972 
01973             cpl_image *lyxok = giraffe_matrix_create_image(mxok);
01974             cpl_image_save(lyxok, "lyxok.fits", -32, NULL,
01975                            CPL_IO_DEFAULT);
01976             cpl_image_delete(lyxok));
01977 
01978 
01979     cpl_msg_info(_task, "%03d spectrum positions fitted", nspectra);
01980 
01981     cpl_matrix_delete(base);
01982     cpl_matrix_delete(mycenter);
01983     cpl_matrix_delete(ycencoeff);
01984 
01985     if (nspectra == 0) {
01986         cpl_msg_warning(_task, "Could not fit any spectra, check number of "
01987                         "good wavelength bins");
01988 
01989         cpl_matrix_delete(mywidth);
01990         return;
01991     }
01992 
01993     /*
01994      * 2D fit of mask width
01995      */
01996 
01997     cpl_msg_info(_task, "2D fit (order %dx%d) of mask width", worder,
01998                  worder);
01999 
02000     /*
02001      * Computes grid[nxok, nspectra]
02002      */
02003 
02004     mx = cpl_matrix_new(cpl_matrix_get_nrow(mxok) * nspectra, 1);
02005     my = cpl_matrix_new(cpl_matrix_get_nrow(mxok) * nspectra, 1);
02006     mw = cpl_matrix_new(1, cpl_matrix_get_nrow(mxok) * nspectra);
02007 
02008     for (y = 0, nn = 0; nn < nspectra; nn++) {
02009 
02010         /* FIXME: The fiber selection changed the following piece of code
02011          *        should not be necessary but we have to check that the
02012          *        accessed to the matrix rows correspond to the selected
02013          *        fibers.
02014          */
02015 
02016         //if (mButton->m[nn] != LOC_OK_SPECTRUM) {
02017         //    continue;
02018         //}
02019 
02020         for (x = 0; x < cpl_matrix_get_nrow(mxok); x++) {
02021 
02022             register cxint zx = (cxint) cpl_matrix_get(mxok, x, 0);
02023             register cxint lx = x * nspectra + y;
02024 
02025 
02026             cpl_matrix_set(mx, lx, 0, cpl_matrix_get(mxok, x, 0));
02027             cpl_matrix_set(my, lx, 0, cpl_matrix_get(position->my, zx, nn));
02028             cpl_matrix_set(mw, 0, lx, cpl_matrix_get(mywidth, 0, x * ns + y));
02029         }
02030         y++;
02031     }
02032 
02033     base = giraffe_chebyshev_base2d(0., 0., nx, ny, worder, worder, mx, my);
02034 
02035     cpl_matrix_delete(my);
02036     cpl_matrix_delete(mx);
02037 
02038     chebcoeff = giraffe_matrix_leastsq(base, mw);
02039     cpl_matrix_delete(base);
02040     cpl_matrix_delete(mw);
02041 
02042     cpl_matrix_delete(mywidth);
02043 
02044     if (chebcoeff == NULL) {
02045         gi_warning("%s: error in giraffe_matrix_leastsq() for width 2D fit",
02046                    fctid);
02047         return;
02048     }
02049 
02050     /*
02051      * Save half-width Chebyshev 2-D fit coeffs
02052      */
02053 
02054     for (nn = 0; nn < cpl_matrix_get_ncol(chebcoeff); nn++) {
02055         cpl_matrix_set(coeffs->mw, 0, nn, cpl_matrix_get(chebcoeff, 0, nn));
02056     }
02057 
02058     /*
02059      * Computes grid[nx, nspectra]
02060      */
02061 
02062     mx = cpl_matrix_new(nx * nspectra, 1);
02063     my = cpl_matrix_new(nx * nspectra, 1);
02064 
02065     for (y = 0, nn = 0; nn < nspectra; nn++) {
02066 
02067         /* FIXME: The fiber selection changed the following piece of code
02068          *        should not be necessary but we have to check that the
02069          *        accessed to the matrix rows correspond to the selected
02070          *        fibers.
02071          */
02072 
02073         //if (mButton->m[nn] != LOC_OK_SPECTRUM) {
02074         //    continue;
02075         //}
02076 
02077         for (x = 0; x < nx; x++) {
02078 
02079             register cxint lx = x * nspectra + y;
02080 
02081             cpl_matrix_set(mx, lx, 0, x);
02082             cpl_matrix_set(my, lx, 0, cpl_matrix_get(position->my, x, nn));
02083 
02084         }
02085         y++;
02086     }
02087 
02088     cpl_matrix_set_size(chebcoeff, worder, worder);
02089 
02090     mfitlocw = giraffe_chebyshev_fit2d(0., 0., nx, ny, chebcoeff, mx, my);
02091     cpl_matrix_delete(chebcoeff);
02092 
02093     cpl_matrix_delete(my);
02094     cpl_matrix_delete(mx);
02095 
02096     for (y = 0, nn = 0; nn < nspectra; nn++) {
02097 
02098         /* FIXME: The fiber selection changed the following piece of code
02099          *        should not be necessary but we have to check that the
02100          *        accessed to the matrix rows correspond to the selected
02101          *        fibers.
02102          */
02103 
02104         //if (mButton->m[nn] != LOC_OK_SPECTRUM) {
02105         //    continue;
02106         //}
02107 
02108         for (x = 0; x < nx; x++) {
02109 
02110             register cxint lx = x * nspectra + y;
02111 
02112             cpl_matrix_set(position->mw, x, nn,
02113                            cpl_matrix_get(mfitlocw, lx, 0));
02114 
02115         }
02116         y++;
02117     }
02118 
02119     cpl_matrix_delete(mfitlocw);
02120 
02121     return;
02122 
02123 }
02124 
02125 
02126 /*
02127  * @brief
02128  *   Computes fitted localization centroid and width on all spectra.
02129  *
02130  * @param mZraw      Matrix[nx,ny] of pixels values
02131  * @param mButton    Matrix[ns] of spectra used for localization
02132  * @param locMethod  Centroid computation method:
02133  *                   HALF_WIDTH, BARYCENTER, PSF_PROFIL
02134  * @param sNormalize Normalize spectra along dispersion axis
02135  * @param noithresh  Spectra/noise threshold
02136  * @param locPrms    Localization mask parameters
02137  * @param locPos     Localization mask: locY[nx,ns] and locW[nx,ns]
02138  * @param locCoeff   Localization mask Chebyshev fit coefficients
02139  *
02140  * @return The function returns 0 on success, or a negative value otherwise.
02141  *
02142  * Computes localization mask (centroid and half-width) for the given
02143  * image @a mZraw[nx,ny]. @a mButton[nspectra] is a matrix of 0/1 values
02144  * specifying spectra to be processed. @a *noithresh is the threshold value
02145  * use to select spectra or inter-spectra pixels. @a locMethod defines the
02146  * method used to compute localization mask centroid and half-width.
02147  * @a locPos.mY[nx,ns], @a locPos.mW[nx,ns], @a locCoeff.mY[ydeg+1,ns] and
02148  * @a locCoeff.mW[(wdeg+1)**2] are pre-allocated matrices.
02149  */
02150 
02151 inline static cxint
02152 _giraffe_localize_spectra(cpl_image *mzraw, cpl_image *bpixel,
02153                           cpl_table *fibers, GiLocalizeMethod method,
02154                           cxbool normalize, cxdouble noise,
02155                           GiMaskParameters *config, GiMaskPosition *position,
02156                           GiMaskPosition *coeffs)
02157 {
02158 
02159     cxint n, nn;
02160     cxint nx, ny, nxok;
02161     cxint ndetect, nspectra;
02162     cxint x, y;
02163 
02164     cxdouble uplost = 0.;
02165     cxdouble lolost = 0.;
02166     cxdouble avglost = 0.;
02167     cxdouble avgmask = 0.;
02168     cxdouble sigmask = 0.;
02169     cxdouble sigmean = 0.;
02170     cxdouble avgborders = 0.;
02171 
02172     cxdouble *_mzraw;
02173 
02174     cpl_matrix *mxok;         /* mylo[nx] abcissa og good x bins */
02175     cpl_matrix *myup;         /* myup[nx,ns] of upper Y for each spectrum */
02176     cpl_matrix *mylo;         /* mylo[nx,ns] of lower Y for each spectrum */
02177     cpl_matrix *mwid;
02178 
02179     cpl_image *mz = NULL;
02180     cpl_image *mznorm = NULL;
02181 
02182 
02183 
02184     nx = cpl_image_get_size_y(mzraw);
02185     ny = cpl_image_get_size_x(mzraw);
02186     _mzraw = cpl_image_get_data_double(mzraw);
02187 
02188 
02189     if (normalize == TRUE) {
02190 
02191         cxdouble zxmax = 0.0;
02192         cxdouble *_mzx = NULL;
02193         cxdouble *_mznorm = NULL;
02194 
02195         cpl_image *mzx = NULL;
02196 
02197 
02198         cpl_msg_info(_task, "Using normalized spectra for localization");
02199 
02200 
02201         /*
02202          * The matrix mznorm contains the pixel values from mz
02203          * normalized along X axis and the matrix mzx is the summ
02204          * of all spectra along X (dispersion) axis
02205          */
02206 
02207         mznorm = cpl_image_new(ny, nx, CPL_TYPE_DOUBLE);
02208         _mznorm = cpl_image_get_data_double(mznorm);
02209 
02210         mzx = cpl_image_new(1, nx, CPL_TYPE_DOUBLE);
02211         _mzx = cpl_image_get_data_double(mzx);
02212 
02213 
02214         /*
02215          * For each x bin, summ all y values
02216          */
02217 
02218         for (x = 0 ; x < nx; x++) {
02219             for (y = 0 ; y < ny; y++) {
02220                 _mzx[x] += _mzraw[x * ny + y];
02221             }
02222 
02223             /*
02224              * Maximum value of summ
02225              */
02226 
02227             if (_mzx[x] > zxmax) {
02228                 zxmax = _mzx[x];
02229             }
02230         }
02231 
02232         GIDEBUG(cpl_image_save(mzx, "mzx.fits", -32, NULL, CPL_IO_DEFAULT));
02233 
02234         for (x = 0 ; x < nx; x++) {
02235 
02236             register cxdouble zxnorm = zxmax / _mzx[x];
02237 
02238             for (y = 0 ; y < ny; y++) {
02239                 _mznorm[x * ny + y] = _mzraw[x * ny + y] * zxnorm;
02240             }
02241 
02242         }
02243 
02244         cpl_image_delete(mzx);
02245         mz = mznorm;
02246     }
02247     else {
02248 
02249         /*
02250          * Use pixel values as they are
02251          */
02252 
02253         cpl_msg_info(_task, "Using raw spectra for localization");
02254         mz = mzraw;
02255     }
02256 
02257 
02258     /*
02259      * Full localization: takes care of all spectra
02260      */
02261 
02262     nspectra = cpl_table_get_nrow(fibers);
02263 
02264     mxok = cpl_matrix_new(nx, 1);
02265     myup = cpl_matrix_new(nx, nspectra);
02266     mylo = cpl_matrix_new(nx, nspectra);
02267 
02268 
02269     /*
02270      *  Make the bin size an even value if it is larger than 1
02271      */
02272 
02273     config->xbin = (config->xbin > 1) ? 2 * (config->xbin / 2) : 1;
02274 
02275     GIDEBUG(cpl_image_save(mz, "mz.fits", -32, NULL, CPL_IO_DEFAULT));
02276 
02277 
02278     /*
02279      * Find spectrum borders
02280      */
02281 
02282     cpl_msg_info(_task, "Generating mask (%d spectra expected) ...",
02283                  nspectra);
02284 
02285     // FIXME: Finalize the implementation of this experimental method for
02286     //        detecting fibers
02287 #if 0
02288     nxok = _giraffe_build_edge_mask(mz, bpixel, nspectra, noise, config,
02289                                     &ndetect, mxok, myup, mylo);
02290 #endif
02291     // End of test code
02292 
02293 
02294     nxok = _giraffe_build_raw_mask(mz, bpixel, nspectra, noise, config,
02295                                    &ndetect, mxok, myup, mylo);
02296 
02297     if (nxok < 0) {
02298 
02299         switch (nxok) {
02300             case -1:
02301                 cpl_msg_warning(_task, "Invalid number of spectra detected: "
02302                                 "%d != %d", ndetect, nspectra);
02303                 break;
02304 
02305             case -2:
02306                 cpl_msg_warning(_task, "No abcissa with good number "
02307                                 "of spectra");
02308                 break;
02309 
02310             default:
02311                 cpl_msg_warning(_task, "Error while searching for spectra");
02312         }
02313 
02314         return nxok;
02315 
02316     }
02317     else {
02318         cpl_msg_info(_task, "%d spectra detected in %d wavelength bins",
02319                      ndetect, nxok);
02320     }
02321 
02322 
02323     /*
02324      * Only takes care of good values
02325      */
02326 
02327     cpl_matrix_resize(mxok, 0, nxok - cpl_matrix_get_nrow(mxok), 0, 0);
02328     cpl_matrix_resize(myup, 0, nxok - cpl_matrix_get_nrow(myup), 0, 0);
02329     cpl_matrix_resize(mylo, 0, nxok - cpl_matrix_get_nrow(mylo), 0, 0);
02330 
02331     GIDEBUG(gi_message("%s: mxok[0-%d]=[%g-%g]", __func__,
02332                        cpl_matrix_get_nrow(mxok) - 1,
02333                        cpl_matrix_get_min(mxok),
02334                        cpl_matrix_get_max(mxok)));
02335 
02336 
02337     cpl_msg_info(_task, "Computing spectrum positions and widths in "
02338                  "pixel range [%g,%g]", cpl_matrix_get_min(mxok),
02339                  cpl_matrix_get_max(mxok));
02340 
02341     if (cpl_matrix_get_nrow(mxok) <= config->ydeg) {
02342         cpl_msg_info(_task, "Not enough data points %d for %d order fit",
02343                      cpl_matrix_get_nrow(mxok), config->ydeg);
02344 
02345         return -1;
02346     }
02347 
02348     switch (method) {
02349         case GILOCALIZE_HALF_WIDTH:
02350             cpl_msg_info(_task, "Using half-width for localization");
02351             _giraffe_fit_raw_mask(mxok, myup, mylo, fibers, config,
02352                                   position);
02353             break;
02354 
02355         case GILOCALIZE_BARYCENTER:
02356         default:
02357             cpl_msg_info(_task, "Using barycenter for localization");
02358             _giraffe_fit_raw_centroid(mz, mxok, myup, mylo, fibers, config,
02359                                       position, coeffs);
02360             break;
02361     }
02362 
02363     if (normalize == 1) {
02364         cpl_image_delete(mznorm);
02365     }
02366 
02367     /*
02368      * Compute the number of pixels rejected by the fit
02369      */
02370 
02371 
02372     /* FIXME: Here again nspectra equals cpl_table_get_nrow(fibers),
02373      *        where OGL used mButtons->nr. We have to check the
02374      *        correctness carefully here !!
02375      */
02376 
02377     mwid = cpl_matrix_new(nxok, nspectra);
02378 
02379     for (n = 0, nn = 0; nn < cpl_table_get_nrow(fibers); nn++) {
02380 
02381         for (x = 0; x < nxok; x++) {
02382             register cxint lx = (cxint) cpl_matrix_get(mxok, x, 0);
02383 
02384             cxdouble lower = cpl_matrix_get(mylo, x, n);
02385             cxdouble upper = cpl_matrix_get(myup, x, n);
02386             cxdouble width = cpl_matrix_get(position->mw, lx, nn);
02387 
02388             uplost += cpl_matrix_get(position->my, lx, nn) + width - upper;
02389             lolost += cpl_matrix_get(position->my, lx, nn) - width - lower;
02390 
02391             avgborders += upper - lower;
02392             avgmask += width;
02393 
02394             cpl_matrix_set(mwid, x, n, 2. * width);
02395         }
02396         n++;
02397     }
02398 
02399     sigmean = cpl_matrix_get_mean(mwid);
02400     sigmask = giraffe_matrix_sigma_mean(mwid, sigmean);
02401     avglost = (lolost + uplost) / (nspectra * nxok);
02402     avgmask = 2.0 * avgmask / nspectra;
02403 
02404     cpl_msg_info(_task, "Mask was computed using %d of %d wavelength bins",
02405                  nxok, nx);
02406     cpl_msg_info(_task, "Average # of pixels per spectra: %.4g",
02407                  avgmask);
02408     cpl_msg_info(_task, "Average # of in-borders pixels per spectra: %.4g",
02409                  avgborders / nspectra);
02410     cpl_msg_info(_task, "Average lost pixels per spectra: %.4g",
02411                  avglost);
02412     cpl_msg_info(_task, "Average lost pixels at upper border: %.4g",
02413                  uplost / (nspectra * nxok));
02414     cpl_msg_info(_task, "Average lost pixels at lower border: %.4g",
02415                  lolost / (nspectra * nxok));
02416     cpl_msg_info(_task, "Average spectrum width: %.4g +/- %.4g, "
02417                  "(min, max) = (%.4g, %.4g)", sigmean, sigmask,
02418                  cpl_matrix_get_min(mwid), cpl_matrix_get_max(mwid));
02419 
02420     cpl_matrix_delete(mwid);
02421 
02422     cpl_matrix_delete(mylo);
02423     cpl_matrix_delete(myup);
02424     cpl_matrix_delete(mxok);
02425 
02426     return 0;
02427 
02428 }
02429 
02430 
02431 inline static cxint
02432 _giraffe_finalize_fibers(cpl_table *fibers, cpl_matrix *locy, GiImage *mlocy,
02433                          cxdouble maxoffset, cxdouble* maxshift)
02434 {
02435 
02436     cxint i = 0;
02437     cxint j = 0;
02438     cxint nx = 0;
02439     cxint ny = 0;
02440     cxint _nx = 0;
02441     cxint _ny = 0;
02442     cxint nfibers = 0;
02443     cxint irow = 0;
02444 
02445     cxdouble max_shift = 0.;
02446     cxdouble *positions = NULL;
02447 
02448     cpl_image *_mlocy = NULL;
02449 
02450 
02451     if (fibers == NULL || locy == NULL || mlocy == NULL) {
02452         return -1;
02453     }
02454 
02455     if (cpl_table_has_column(fibers, "RINDEX") == FALSE) {
02456         return -1;
02457     }
02458 
02459     nx = cpl_matrix_get_ncol(locy);
02460     ny = cpl_matrix_get_nrow(locy);
02461 
02462     nfibers = cpl_table_get_nrow(fibers);
02463 
02464     _mlocy = giraffe_image_get(mlocy);
02465     _nx = cpl_image_get_size_x(_mlocy);
02466     _ny = cpl_image_get_size_y(_mlocy);
02467 
02468     if (ny != _ny) {
02469         return -2;
02470     }
02471 
02472     if (nfibers > _nx) {
02473         return -3;
02474     }
02475 
02476     cpl_table_select_all(fibers);
02477 
02478 
02479     /*
02480      * Get pointer to the central scan line.
02481      */
02482 
02483     irow = (_ny - 1) / 2;
02484     positions = (cxdouble *)cpl_image_get_data(_mlocy) + irow * _nx;
02485 
02486 
02487     /*
02488      * Compare the detected fiber positions with the positions
02489      * from the reference localization. Select only those fibers
02490      * whose distance from the reference positions is less than
02491      * a given offset. All other fibers are removed from the
02492      * fiber table.
02493      */
02494 
02495     for (i = 0; i < nfibers; i++) {
02496 
02497         if (j < nx) {
02498 
02499             cxint pos = cpl_table_get_int(fibers, "RINDEX", i, NULL) - 1;
02500 
02501             cxdouble yc = cpl_matrix_get(locy, irow, j);
02502             cxdouble shift = fabs(yc - positions[pos]);
02503 
02504             if (shift <= maxoffset) {
02505                 cpl_table_unselect_row(fibers, i);
02506                 ++j;
02507             }
02508             else {
02509                 max_shift = CX_MAX(max_shift, shift);
02510             }
02511 
02512         }
02513     }
02514 
02515     cpl_table_erase_selected(fibers);
02516 
02517     if (maxshift != NULL) {
02518         *maxshift = max_shift;
02519     }
02520 
02521     return 0;
02522 
02523 }
02524 
02525 
02554 cxint
02555 giraffe_localize_spectra(GiLocalization *result, GiImage *image,
02556                          GiTable *fibers, GiLocalization *master,
02557                          GiImage *badpixels, GiLocalizeConfig *config)
02558 {
02559 
02560     const cxchar *fctid = "giraffe_localize_spectra";
02561 
02562     cxint i;
02563     cxint status;
02564     cxint nrows;
02565     cxint nfibers;
02566     cxint nframes = 1;
02567     cxint ckwidth;
02568     cxint ckheight;
02569     cxint ckcount;
02570 
02571     cxdouble mwidth;
02572     cxdouble conad      = 0.;
02573     cxdouble bias_ron   = 0.;
02574     cxdouble mask_sigma = 0.;
02575 
02576     cx_string *pname;
02577 
02578     cpl_propertylist *properties;
02579 
02580     cpl_image *_image = giraffe_image_get(image);
02581     cpl_image *_bpixel = giraffe_image_get(badpixels);
02582     cpl_image *_result = NULL;
02583 
02584     cpl_matrix *_my;
02585 
02586     cpl_table *_fibers = NULL;
02587     cpl_table *fiber_setup = NULL;
02588     cpl_table *locc;
02589 
02590     GiLocalizeMethod method;
02591 
02592     GiInstrumentMode mode;
02593 
02594     GiMaskParameters mask_config;
02595 
02596     GiMaskPosition mask_position;
02597     GiMaskPosition mask_coeffs;
02598 
02599 
02600 
02601     /*
02602      * Preprocessing
02603      */
02604 
02605     if (result == NULL || image == NULL || fibers == NULL || config == NULL) {
02606         cpl_error_set(fctid, CPL_ERROR_NULL_INPUT);
02607         return 1;
02608     }
02609 
02610     if (badpixels != NULL) {
02611         cpl_msg_debug(fctid,"Bad pixel correction is not available. Bad "
02612                       "pixel map will be ignored.");
02613     }
02614 
02615     _fibers = giraffe_table_get(fibers);
02616 
02617     if (_fibers == NULL) {
02618         cpl_error_set(fctid, CPL_ERROR_DATA_NOT_FOUND);
02619         return 1;
02620     }
02621     else {
02622         fiber_setup = _fibers;
02623     }
02624 
02625     properties = giraffe_image_get_properties(image);
02626 
02627 
02628     /*
02629      * Add the number of fibers to the image properties.
02630      */
02631 
02632     nfibers = cpl_table_get_nrow(_fibers);
02633 
02634     cpl_msg_info(fctid, "Setting number of fibers (%s) to %d",
02635                  GIALIAS_NFIBERS, nfibers);
02636 
02637     cpl_propertylist_update_int(properties, GIALIAS_NFIBERS, nfibers);
02638     cpl_propertylist_set_comment(properties, GIALIAS_NFIBERS,
02639                                  "Number of fibres");
02640 
02641 
02642     if (!cpl_propertylist_has(properties, GIALIAS_CONAD)) {
02643         cpl_msg_error(fctid, "Missing detector gain property (%s)! ",
02644                       GIALIAS_CONAD);
02645         return 1;
02646     }
02647     else {
02648         conad = cpl_propertylist_get_double(properties, GIALIAS_CONAD);
02649     }
02650 
02651 
02652     /*
02653      * If a ron value is provided write it to the image properties.
02654      */
02655 
02656     if (config->ron > 0.) {
02657         cpl_msg_info(fctid, "Setting bias sigma value (%s) to %.5g",
02658                      GIALIAS_BIASSIGMA, config->ron);
02659         cpl_propertylist_update_double(properties, GIALIAS_BIASSIGMA,
02660                                        config->ron);
02661     }
02662 
02663     bias_ron = giraffe_propertylist_get_ron(properties);
02664     cpl_msg_info(fctid, "Bias sigma value: %.3g e-", bias_ron);
02665 
02666 
02667     if (cpl_propertylist_has(properties, GIALIAS_DATANCOM)) {
02668         nframes = cpl_propertylist_get_int(properties, GIALIAS_DATANCOM);
02669     }
02670 
02671 
02672     if (config->noise > 0.) {
02673         cpl_msg_info(fctid, "Noise multiplier: %.3g",
02674                      config->noise);
02675     }
02676     else {
02677         cpl_msg_info(fctid, "Threshold multiplier: %.3g",
02678                      fabs(config->noise));
02679     }
02680 
02681 
02682     /*
02683      * Setup localization start position in the dispersion direction.
02684      */
02685 
02686     nrows = cpl_image_get_size_y(_image);
02687 
02688     if (config->start < 0) {
02689         config->start = nrows / 2;
02690     }
02691 
02692     /*
02693      * Set instrument mode specific parameters like the width of a spectrum
02694      * and the width of the intra-spectrum gap.
02695      */
02696 
02697     mode = giraffe_get_mode(properties);
02698 
02699     if (config->ywidth < 1) {
02700 
02701         cpl_msg_info(fctid, "Configuring equilizing filter width from "
02702                      "instrument mode");
02703 
02704         switch (mode) {
02705             case GIMODE_MEDUSA:
02706                 config->ywidth = 16;
02707                 break;
02708 
02709             case GIMODE_IFU:
02710                 config->ywidth = 6;
02711                 break;
02712 
02713             case GIMODE_ARGUS:
02714                 config->ywidth = 6;
02715                 break;
02716 
02717             default:
02718                 cpl_msg_error(fctid, "Invalid instrument mode!");
02719                 return 1;
02720                 break;
02721         }
02722 
02723 
02724         if (!cpl_propertylist_has(properties, GIALIAS_SLITNAME)) {
02725             cpl_msg_error(fctid, "Property (%s) not found in raw image",
02726                           GIALIAS_SLITNAME);
02727             return 1;
02728         }
02729         else {
02730             const cxchar *slit =
02731                 cpl_propertylist_get_string(properties, GIALIAS_SLITNAME);
02732 
02733             cpl_msg_info(fctid, "Setting equilizing filter to %d [pxl] "
02734                          "for slit configuration `%s'", config->ywidth,
02735                          slit);
02736         }
02737 
02738     }
02739 
02740 
02741     /*
02742      * Set mean spectrum width according to the instrument mode
02743      */
02744 
02745     switch (mode) {
02746         case GIMODE_MEDUSA:
02747             mwidth = GISPECTRUM_MWIDTH_MEDUSA;
02748 
02749             ckwidth = 1;
02750             ckheight = 3;
02751             ckcount = 8;
02752 
02753             break;
02754 
02755         case GIMODE_IFU:
02756             mwidth = GISPECTRUM_MWIDTH_IFU;
02757 
02758             ckwidth = 0;
02759             ckheight = 3;
02760             ckcount = 4;
02761 
02762             break;
02763 
02764         case GIMODE_ARGUS:
02765             mwidth = GISPECTRUM_MWIDTH_IFU;
02766 
02767             ckwidth = 0;
02768             ckheight = 3;
02769             ckcount = 4;
02770 
02771             break;
02772 
02773         default:
02774             cpl_msg_error(fctid, "Invalid instrument mode!");
02775             return 1;
02776             break;
02777     }
02778 
02779 
02780     /*
02781      * Setup localization method
02782      */
02783 
02784     if (config->centroid == TRUE) {
02785         method = GILOCALIZE_BARYCENTER;
02786     }
02787     else {
02788         method = GILOCALIZE_HALF_WIDTH;
02789     }
02790 
02791 
02792     /*
02793      * Fill the parameter structure for the localization mask computation
02794      * with the actual values.
02795      */
02796 
02797     mask_config.ywidth = config->ywidth;
02798     mask_config.method = config->threshold;
02799     mask_config.threshold = config->noise;
02800     mask_config.ydeg = config->yorder;
02801     mask_config.wdeg = config->worder;
02802     mask_config.ewid = config->ewidth;
02803     mask_config.wavg = mwidth;
02804     mask_config.ckdata.width = ckwidth;
02805     mask_config.ckdata.height = ckheight;
02806     mask_config.ckdata.count = ckcount;
02807     mask_config.sigma = config->sigma;
02808     mask_config.niter = config->iterations;
02809     mask_config.mfrac = config->fraction;
02810     mask_config.start = config->start;
02811     mask_config.retry = config->retries;
02812     mask_config.xbin = config->binsize;
02813 
02814 
02815     /*
02816      * If config->noise is larger than 0. it is basically a signal-to-noise
02817      * ratio, given in ADU for a single/average frame. Since the fiber
02818      * detection is carried out on a summed frame of electrons, the
02819      * signal-to-noise limit has to be re-scaled properly.
02820      */
02821 
02822     if (config->noise > 0.) {
02823         mask_config.threshold *= sqrt(nframes * conad);
02824     }
02825 
02826 
02827     /*
02828      * Processing
02829      */
02830 
02831     /*
02832      * Localize spectra. Depending on the setup we either do a full
02833      * localization or we just localize the simultaneous calibration
02834      * fibers using an existing, full master localization.
02835      */
02836 
02837     if (config->full != TRUE) {
02838 
02839         cpl_msg_info(fctid, "Computing spectrum localization using SIWC "
02840                      "spectra");
02841 
02842         if (!master || !master->locy || !master->locy) {
02843             cpl_msg_error(fctid, "Required full master localization is "
02844                           "missing!");
02845             return 1;
02846         }
02847 
02848 
02849         /*
02850          * Select SIWC fibers from the fiber table. The simultaneous
02851          * calibration spectra are indicated by a -1 as retractor position.
02852          */
02853 
02854         cpl_table_unselect_all(_fibers);
02855         cpl_table_or_selected_int(_fibers, "RP", CPL_EQUAL_TO, -1);
02856 
02857         fiber_setup = cpl_table_extract_selected(_fibers);
02858         nfibers = cpl_table_get_nrow(fiber_setup);
02859 
02860     }
02861 
02862 
02863     /*
02864      * Allocate required output matrices and hook them into the appropriate
02865      * structures.
02866      */
02867 
02868     mask_position.type = GIMASK_FITTED_DATA;
02869     mask_position.my = cpl_matrix_new(nrows, nfibers);
02870     mask_position.mw = cpl_matrix_new(nrows, nfibers);
02871 
02872     mask_coeffs.type = GIMASK_FIT_COEFFS;
02873     mask_coeffs.my = cpl_matrix_new(mask_config.ydeg + 1, nfibers);
02874     mask_coeffs.mw = cpl_matrix_new(1, (mask_config.wdeg + 1) *
02875                                     (mask_config.wdeg + 1));
02876 
02877     /*
02878      * Convert raw image from ADU to electrons, and adjust the readout
02879      * noise to match the number of images that were used to create the
02880      * raw image.
02881      */
02882 
02883     _image = cpl_image_multiply_scalar_create(_image, nframes * conad);
02884 
02885     mask_sigma = sqrt(nframes) * bias_ron;
02886 
02887 
02888     /*
02889      * Compute the position of the spectra on the CCD
02890      */
02891 
02892     status = _giraffe_localize_spectra(_image, _bpixel, fiber_setup,
02893                                        method, config->normalize,
02894                                        mask_sigma,
02895                                        &mask_config, &mask_position,
02896                                        &mask_coeffs);
02897 
02898     cpl_image_delete(_image);
02899     _image = NULL;
02900 
02901     if (status) {
02902         result->locy = NULL;
02903         result->locw = NULL;
02904         result->locc = NULL;
02905         result->psf = NULL;
02906 
02907         cpl_matrix_delete(mask_position.my);
02908         cpl_matrix_delete(mask_position.mw);
02909 
02910         cpl_matrix_delete(mask_coeffs.my);
02911         cpl_matrix_delete(mask_coeffs.mw);
02912 
02913         if (config->full != TRUE) {
02914             cpl_table_delete(fiber_setup);
02915         }
02916 
02917         cpl_msg_error(fctid, "Spectrum localization computation failed!");
02918 
02919         return 1;
02920     }
02921 
02922 
02923     /*
02924      * Post processing
02925      */
02926 
02927     if (config->full != TRUE) {
02928 
02929         /*
02930          * TBD: Postprocessing of localization. Compare computed spectrum
02931          *      locations with master, i.e. calculate differences.
02932          */
02933 
02934         cpl_table_delete(fiber_setup);
02935 
02936     }
02937     else {
02938 
02939         if (master != NULL && master->locy != NULL) {
02940 
02941             cxint nf = cpl_table_get_nrow(_fibers);
02942 
02943             cxdouble maxoffset = 0.5 * mask_config.wavg;
02944             cxdouble maxshift = 0.;
02945 
02946 
02947             cpl_msg_info(fctid, "Comparing detected and expected fiber "
02948                          "positions.");
02949 
02950             status = _giraffe_finalize_fibers(_fibers, mask_position.my,
02951                                               master->locy, maxoffset,
02952                                               &maxshift);
02953 
02954             if (status != 0) {
02955 
02956                 if (status == -3) {
02957 
02958                     const cpl_image* mlocy = giraffe_image_get(master->locy);
02959                     cxint _nf = cpl_image_get_size_x(mlocy);
02960 
02961                     cpl_msg_error(fctid, "More fibers (%d) than expected "
02962                             "(%d) were found!", nf, _nf);
02963 
02964                 }
02965 
02966                 result->locy = NULL;
02967                 result->locw = NULL;
02968                 result->locc = NULL;
02969                 result->psf = NULL;
02970 
02971                 cpl_matrix_delete(mask_position.my);
02972                 cpl_matrix_delete(mask_position.mw);
02973 
02974                 cpl_matrix_delete(mask_coeffs.my);
02975                 cpl_matrix_delete(mask_coeffs.mw);
02976 
02977                 if (config->full != TRUE) {
02978                     cpl_table_delete(fiber_setup);
02979                 }
02980 
02981                 cpl_msg_error(fctid, "Comparison of fiber positions "
02982                               "failed!");
02983 
02984                 return 1;
02985             }
02986 
02987             cx_assert(cpl_table_get_nrow(_fibers) <= nf);
02988 
02989             cpl_msg_info(fctid, "%d of %d expected fibers were detected.",
02990                          cpl_table_get_nrow(_fibers), nf);
02991 
02992             if (cpl_table_get_nrow(_fibers) < nf) {
02993                 cpl_msg_debug(fctid, "Maximum offset from the expected "
02994                         "position is %.2f, maximum allowed offset is %.2f",
02995                         maxshift, maxoffset);
02996                 cpl_msg_warning(fctid, "%d fibers are missing!",
02997                                 nf - cpl_table_get_nrow(_fibers));
02998             }
02999 
03000         }
03001 
03002     }
03003 
03004 
03005     /*
03006      * Convert matrices into images and tables and add the necessary
03007      * properties.
03008      */
03009 
03010     /* Spectrum center position */
03011 
03012     result->locy =
03013         giraffe_image_create(CPL_TYPE_DOUBLE,
03014                              cpl_matrix_get_ncol(mask_position.my),
03015                              cpl_matrix_get_nrow(mask_position.my));
03016 
03017     giraffe_image_copy_matrix(result->locy, mask_position.my);
03018     cpl_matrix_delete(mask_position.my);
03019 
03020     giraffe_image_set_properties(result->locy, properties);
03021     properties = giraffe_image_get_properties(result->locy);
03022 
03023     _result = giraffe_image_get(result->locy);
03024 
03025     cpl_propertylist_set_int(properties, GIALIAS_NAXIS1,
03026                              cpl_image_get_size_x(_result));
03027     cpl_propertylist_set_int(properties, GIALIAS_NAXIS2,
03028                              cpl_image_get_size_y(_result));
03029     cpl_propertylist_set_int(properties, GIALIAS_BITPIX, -32);
03030     cpl_propertylist_set_double(properties, GIALIAS_BZERO, 0.);
03031     cpl_propertylist_set_double(properties, GIALIAS_BSCALE, 1.);
03032 
03033     cpl_propertylist_append_int(properties, GIALIAS_LOCNX,
03034                                 cpl_image_get_size_y(_result));
03035     cpl_propertylist_append_int(properties, GIALIAS_LOCNS,
03036                                 cpl_image_get_size_x(_result));
03037 
03038     if (config->centroid) {
03039         cpl_propertylist_append_string(properties, GIALIAS_LMETHOD,
03040                                        "BARYCENTER");
03041     }
03042     else {
03043         cpl_propertylist_append_string(properties, GIALIAS_LMETHOD,
03044                                        "HALF_WIDTH");
03045     }
03046 
03047     if (config->normalize) {
03048         cpl_propertylist_append_int(properties, GIALIAS_LNORMALIZE,
03049                                     config->ywidth);
03050     }
03051     else {
03052         cpl_propertylist_append_int(properties, GIALIAS_LNORMALIZE,
03053                                     -config->ywidth);
03054     }
03055 
03056     cpl_propertylist_append_bool(properties, GIALIAS_LFULLLOC, config->full);
03057     cpl_propertylist_append_int(properties, GIALIAS_LOCYDEG, config->yorder);
03058     cpl_propertylist_append_int(properties, GIALIAS_LOCWDEG, config->worder);
03059     cpl_propertylist_append_double(properties, GIALIAS_LEXTRAWID,
03060                                    config->ewidth);
03061     cpl_propertylist_append_double(properties, GIALIAS_LNOISEMULT,
03062                                    config->noise);
03063 
03064     cpl_propertylist_append_double(properties, GIALIAS_LCLIPSIGMA,
03065                                    config->sigma);
03066     cpl_propertylist_append_int(properties, GIALIAS_LCLIPNITER,
03067                                 config->iterations);
03068     cpl_propertylist_append_double(properties, GIALIAS_LCLIPMFRAC,
03069                                    config->fraction);
03070 
03071 
03072     if (cpl_propertylist_has(properties, GIALIAS_GIRFTYPE)) {
03073         cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE, "LOCY");
03074     }
03075     else {
03076         cpl_propertylist_append_string(properties, GIALIAS_GIRFTYPE, "LOCY");
03077     }
03078     cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE, "GIRAFFE "
03079                                  "localization centroid");
03080 
03081 
03082     /* Spectrum width */
03083 
03084     result->locw =
03085         giraffe_image_create(CPL_TYPE_DOUBLE,
03086                              cpl_matrix_get_ncol(mask_position.mw),
03087                              cpl_matrix_get_nrow(mask_position.mw));
03088 
03089     giraffe_image_copy_matrix(result->locw, mask_position.mw);
03090     cpl_matrix_delete(mask_position.mw);
03091 
03092     giraffe_image_set_properties(result->locw, properties);
03093     properties = giraffe_image_get_properties(result->locw);
03094 
03095     _result = giraffe_image_get(result->locw);
03096 
03097     cpl_propertylist_set_int(properties, GIALIAS_NAXIS1,
03098                              cpl_image_get_size_x(_result));
03099     cpl_propertylist_set_int(properties, GIALIAS_NAXIS2,
03100                              cpl_image_get_size_y(_result));
03101 
03102     if (cpl_propertylist_has(properties, GIALIAS_GIRFTYPE)) {
03103         cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE,
03104                                     "LOCWY");
03105     }
03106     else {
03107         cpl_propertylist_append_string(properties, GIALIAS_GIRFTYPE,
03108                                        "LOCWY");
03109     }
03110     cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE, "GIRAFFE "
03111                                  "localization half-width");
03112 
03113 
03114     /* Coefficients table */
03115 
03116     locc = cpl_table_new(cpl_matrix_get_ncol(mask_coeffs.my));
03117 
03118     cpl_table_new_column(locc, "BUTTON", CPL_TYPE_INT);
03119     for (i = 0; i < cpl_table_get_nrow(locc); i++) {
03120         cpl_table_set_int(locc, "BUTTON", i, i);
03121     }
03122 
03123     for (i = 0; i < cpl_matrix_get_nrow(mask_coeffs.my); i++) {
03124         cxchar *label = NULL;
03125 
03126         cx_asprintf(&label, "YC%d", i);
03127         cpl_table_new_column(locc, label, CPL_TYPE_DOUBLE);
03128         cx_free(label);
03129     }
03130 
03131 
03132     result->locc = giraffe_table_create(locc, properties);
03133     cpl_table_delete(locc);
03134 
03135     _my = cpl_matrix_transpose_create(mask_coeffs.my);
03136     giraffe_table_copy_matrix(result->locc, "YC0", _my);
03137     cpl_matrix_delete(_my);
03138     cpl_matrix_delete(mask_coeffs.my);
03139 
03140     properties = giraffe_table_get_properties(result->locc);
03141 
03142 
03143     /* Add coefficients of the 2D fit to the table properties */
03144 
03145     pname = cx_string_new();
03146 
03147     for (i = 0; i < cpl_matrix_get_ncol(mask_coeffs.mw); i++) {
03148         cx_string_sprintf(pname, "%s%d", GIALIAS_LOCWIDCOEF, i);
03149         cpl_propertylist_append_double(properties, cx_string_get(pname),
03150                                        cpl_matrix_get(mask_coeffs.mw, 0, i));
03151     }
03152 
03153     cx_string_delete(pname);
03154     cpl_matrix_delete(mask_coeffs.mw);
03155 
03156     cpl_propertylist_update_string(properties, GIALIAS_GIRFTYPE,
03157                                    "LOCYWCHEB");
03158     cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE, "GIRAFFE "
03159                                  "localization fit coefficients");
03160 
03161 
03162     /* Not used */
03163 
03164     result->psf = NULL;
03165 
03166     return 0;
03167 
03168 }
03169 
03170 
03181 GiLocalizeConfig *
03182 giraffe_localize_config_create(cpl_parameterlist *list)
03183 {
03184 
03185     const cxchar *s;
03186     cpl_parameter *p;
03187 
03188     GiLocalizeConfig *config = NULL;
03189 
03190 
03191     if (list == NULL) {
03192         return NULL;
03193     }
03194 
03195     config = cx_calloc(1, sizeof *config);
03196 
03197 
03198     /*
03199      * Some defaults
03200      */
03201 
03202     config->full = TRUE;
03203     config->centroid = TRUE;
03204     config->threshold = GILOCALIZE_THRESHOLD_LOCAL;
03205 
03206 
03207     p = cpl_parameterlist_find(list, "giraffe.localization.mode");
03208     s = cpl_parameter_get_string(p);
03209     if (strcmp(s, "siwc") == 0) {
03210         config->full = FALSE;
03211     }
03212 
03213     p = cpl_parameterlist_find(list, "giraffe.localization.start");
03214     config->start = cpl_parameter_get_int(p);
03215 
03216     p = cpl_parameterlist_find(list, "giraffe.localization.retries");
03217     config->retries = cpl_parameter_get_int(p);
03218 
03219     p = cpl_parameterlist_find(list, "giraffe.localization.binsize");
03220     config->binsize = cpl_parameter_get_int(p);
03221 
03222     p = cpl_parameterlist_find(list, "giraffe.localization.ewidth");
03223     config->ewidth = cpl_parameter_get_double(p);
03224 
03225     p = cpl_parameterlist_find(list, "giraffe.localization.ywidth");
03226     config->ywidth = cpl_parameter_get_int(p);
03227 
03228     p = cpl_parameterlist_find(list, "giraffe.localization.center");
03229     s = cpl_parameter_get_string(p);
03230     if (!strcmp(s, "hwidth")) {
03231         config->centroid = FALSE;
03232     }
03233 
03234     p = cpl_parameterlist_find(list, "giraffe.localization.normalize");
03235     config->normalize = cpl_parameter_get_bool(p);
03236 
03237     p = cpl_parameterlist_find(list, "giraffe.localization.threshold");
03238     s = cpl_parameter_get_string(p);
03239 
03240     if (strncmp(s, "global", 6) == 0) {
03241         config->threshold = GILOCALIZE_THRESHOLD_GLOBAL;
03242     }
03243     else if (strncmp(s, "row", 3) == 0) {
03244         config->threshold = GILOCALIZE_THRESHOLD_ROW;
03245     }
03246     else {
03247         config->threshold = GILOCALIZE_THRESHOLD_LOCAL;
03248     }
03249 
03250     p = cpl_parameterlist_find(list, "giraffe.localization.noise");
03251     config->noise = cpl_parameter_get_double(p);
03252 
03253     p = cpl_parameterlist_find(list, "giraffe.localization.ron");
03254     config->ron = cpl_parameter_get_double(p);
03255 
03256     p = cpl_parameterlist_find(list, "giraffe.localization.yorder");
03257     config->yorder = cpl_parameter_get_int(p);
03258 
03259     p = cpl_parameterlist_find(list, "giraffe.localization.worder");
03260     config->worder = cpl_parameter_get_int(p);
03261 
03262     p = cpl_parameterlist_find(list, "giraffe.localization.sigma");
03263     config->sigma = cpl_parameter_get_double(p);
03264 
03265     p = cpl_parameterlist_find(list, "giraffe.localization.iterations");
03266     config->iterations = cpl_parameter_get_int(p);
03267 
03268     p = cpl_parameterlist_find(list, "giraffe.localization.fraction");
03269     config->fraction = cpl_parameter_get_double(p);
03270 
03271     return config;
03272 
03273 }
03274 
03275 
03288 void
03289 giraffe_localize_config_destroy(GiLocalizeConfig *config)
03290 {
03291 
03292     if (config) {
03293         cx_free(config);
03294     }
03295 
03296     return;
03297 
03298 }
03299 
03300 
03312 void
03313 giraffe_localize_config_add(cpl_parameterlist *list)
03314 {
03315 
03316     cpl_parameter *p;
03317 
03318 
03319     if (list == NULL) {
03320         return;
03321     }
03322 
03323     p = cpl_parameter_new_enum("giraffe.localization.mode",
03324                                CPL_TYPE_STRING,
03325                                "Localization mode: Use all spectra "
03326                                "or the 5 SIWC spectra",
03327                                "giraffe.localization",
03328                                "all", 2, "all", "siwc");
03329     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-mode");
03330     cpl_parameterlist_append(list, p);
03331 
03332 
03333     p = cpl_parameter_new_value("giraffe.localization.start",
03334                                 CPL_TYPE_INT,
03335                                 "Bin along x-axis",
03336                                 "giraffe.localization",
03337                                 -1);
03338     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-start");
03339     cpl_parameterlist_append(list, p);
03340 
03341 
03342     p = cpl_parameter_new_value("giraffe.localization.retries",
03343                                 CPL_TYPE_INT,
03344                                 "Initial localization detection "
03345                                 "xbin retries.",
03346                                 "giraffe.localization",
03347                                 10);
03348     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-retries");
03349     cpl_parameterlist_append(list, p);
03350 
03351 
03352     p = cpl_parameter_new_value("giraffe.localization.binsize",
03353                                 CPL_TYPE_INT,
03354                                 "Initial localization detection "
03355                                 "xbin size.",
03356                                 "giraffe.localization",
03357                                 -1);
03358     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-binsize");
03359     cpl_parameterlist_append(list, p);
03360 
03361 
03362     p = cpl_parameter_new_value("giraffe.localization.ewidth",
03363                                 CPL_TYPE_DOUBLE,
03364                                 "Localization detection extra width.",
03365                                 "giraffe.localization",
03366                                 1.0);
03367     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-ewidth");
03368     cpl_parameterlist_append(list, p);
03369 
03370 
03371     p = cpl_parameter_new_value("giraffe.localization.ywidth",
03372                                 CPL_TYPE_INT,
03373                                 "Full width [pxl] of the equilizing "
03374                                 "filter (distance between two "
03375                                 "adjacent fibers).",
03376                                 "giraffe.localization",
03377                                 -1);
03378     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-ywidth");
03379     cpl_parameterlist_append(list, p);
03380 
03381 
03382     p = cpl_parameter_new_enum("giraffe.localization.center",
03383                                CPL_TYPE_STRING,
03384                                "Method used for mask center "
03385                                "computation.",
03386                                "giraffe.localization",
03387                                "centroid", 2, "centroid",
03388                                "hwidth");
03389     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-center");
03390     cpl_parameterlist_append(list, p);
03391 
03392 
03393     p = cpl_parameter_new_value("giraffe.localization.normalize",
03394                                 CPL_TYPE_BOOL,
03395                                 "Enable spectrum normalization along "
03396                                 "the dispersion axis.",
03397                                 "giraffe.localization",
03398                                 FALSE);
03399     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-norm");
03400     cpl_parameterlist_append(list, p);
03401 
03402 
03403     p = cpl_parameter_new_value("giraffe.localization.noise",
03404                                 CPL_TYPE_DOUBLE,
03405                                 "Threshold multiplier.",
03406                                 "giraffe.localization",
03407                                 7.0);
03408     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-noise");
03409     cpl_parameterlist_append(list, p);
03410 
03411 
03412     p = cpl_parameter_new_enum("giraffe.localization.threshold",
03413                                CPL_TYPE_STRING,
03414                                "Selects thresholding algorithm: local, "
03415                                "row or global",
03416                                "giraffe.localization",
03417                                "local", 3, "local", "row", "global");
03418     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-threshold");
03419     cpl_parameterlist_append(list, p);
03420 
03421 
03422     p = cpl_parameter_new_value("giraffe.localization.ron",
03423                                 CPL_TYPE_DOUBLE,
03424                                 "New bias sigma (RON) value for dark "
03425                                 "subtraction",
03426                                 "giraffe.localization",
03427                                 -1.);
03428     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-ron");
03429     cpl_parameterlist_append(list, p);
03430 
03431 
03432     p = cpl_parameter_new_value("giraffe.localization.yorder",
03433                                 CPL_TYPE_INT,
03434                                 "Order of Chebyshev polynomial fit.",
03435                                 "giraffe.localization",
03436                                 4);
03437     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-yorder");
03438     cpl_parameterlist_append(list, p);
03439 
03440 
03441     p = cpl_parameter_new_value("giraffe.localization.worder",
03442                                 CPL_TYPE_INT,
03443                                 "Order of Chebyshev 2D polynomial fit.",
03444                                 "giraffe.localization",
03445                                 2);
03446     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-worder");
03447     cpl_parameterlist_append(list, p);
03448 
03449 
03450     p = cpl_parameter_new_value("giraffe.localization.sigma",
03451                                 CPL_TYPE_DOUBLE,
03452                                 "Localization clipping: sigma threshold "
03453                                 "factor",
03454                                 "giraffe.localization",
03455                                 2.5);
03456     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-sigma");
03457     cpl_parameterlist_append(list, p);
03458 
03459 
03460     p = cpl_parameter_new_value("giraffe.localization.iterations",
03461                                 CPL_TYPE_INT,
03462                                 "Localization clipping: number of "
03463                                 "iterations",
03464                                 "giraffe.localization",
03465                                 5);
03466     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-niter");
03467     cpl_parameterlist_append(list, p);
03468 
03469 
03470     p = cpl_parameter_new_range("giraffe.localization.fraction",
03471                                 CPL_TYPE_DOUBLE,
03472                                 "Localization clipping: minimum fraction "
03473                                 "of points accepted/total.",
03474                                 "giraffe.localization",
03475                                 0.9, 0.0, 1.0);
03476     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-mfrac");
03477     cpl_parameterlist_append(list, p);
03478 
03479     return;
03480 
03481 }

This file is part of the GIRAFFE Pipeline Reference Manual 2.8.8.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Fri Mar 4 10:50:27 2011 by doxygen 1.6.3 written by Dimitri van Heesch, © 1997-2004