GIRAFFE Pipeline Reference Manual

gilocalize.c

00001 /* $Id: gilocalize.c,v 1.43.2.2 2008/06/09 15:00:12 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: 2008/06/09 15:00:12 $
00024  * $Revision: 1.43.2.2 $
00025  * $Name: giraffe-2_5_3 $
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_parameterlist.h>
00042 #include <cpl_msg.h>
00043 
00044 #include "gimacros.h"
00045 #include "gialias.h"
00046 #include "giarray.h"
00047 #include "giimage.h"
00048 #include "gitable.h"
00049 #include "gimatrix.h"
00050 #include "giarray.h"
00051 #include "gimask.h"
00052 #include "gimath.h"
00053 #include "gimessages.h"
00054 #include "giutils.h"
00055 #include "gilocalize.h"
00056 #include "gidebug.h"
00057 
00058 
00059 
00068 /* FIXME: Is this really needed? It really seems questionable! (RP) */
00069 
00070 enum GiLocalizeMethod {
00071     GILOCALIZE_HALF_WIDTH,
00072     GILOCALIZE_BARYCENTER
00073 };
00074 
00075 typedef enum GiLocalizeMethod GiLocalizeMethod;
00076 
00077 
00078 /*
00079  * Main task identifier. Used for terminal output from internal functions.
00080  */
00081 
00082 static const cxchar *_task = "giraffe_localize_spectra";
00083 
00084 
00085 /*
00086  * @brief
00087  *   Check whether a pixel in a detection mask belongs to a spectrum.
00088  *
00089  * @param pixels  The pixel buffer.
00090  * @param xsize   The size of the pixel buffer along x.
00091  * @param ysize   The size of the pixel buffer along y.
00092  * @param xpos    x-position of the pixel to check.
00093  * @param ypos    y-position of the pixel to check.
00094  * @param xwidth  Half width of the pixel neighbourhood along x.
00095  * @param ywidth  Half width of the pixel neighbourhood along y.
00096  * @param count   The number of required, non-zero mask pixels.
00097  *
00098  * @return The function returns 1 if the pixel at (@em xpos, @em ypos) is
00099  *   found to be valid, or 0 otherwise.
00100  *
00101  * The function checks whether the pixel at position (@em xpos, @em ypos) in
00102  * the detection mask's pixel buffer @em pixels belongs to a spectrum. It
00103  * expects in input a pixel buffer of a detection mask, i.e. the pixel values
00104  * must be non-zero only at pixel positions associated to a positive
00105  * detection.
00106  *
00107  * A pixel is considered to be valid if, at least, @em count non-zero pixels
00108  * are found in the neighborhood of the pixel position (@em xpos, @em ypos).
00109  * The neighborhood is specified by @em xsize and @em ysize the number of
00110  * pixels to be checked on both sides of the pixel along the x and y axis.
00111  * The pixel row given by @em ypos which contains the pixel to check is
00112  * not considered when the pixel neighbourhood is checked. Assuming that
00113  * the spectra extend along the y-axis only the neighbours along the
00114  * dispersion axis are taken into account.
00115  */
00116 
00117 inline static cxbool
00118 _giraffe_validate_pixel(cxint *pixels, cxint xsize, cxint ysize,
00119                         cxint xpos, cxint ypos, cxint xwidth, cxint ywidth,
00120                         cxsize count)
00121 {
00122 
00123     cxint i;
00124     cxint xstart = xpos - xwidth;
00125     cxint ystart = ypos - ywidth;
00126     cxint xend = xpos + xwidth;
00127     cxint yend = ypos + ywidth;
00128 
00129     cxsize _count = 0;
00130 
00131 
00132 
00133     /*
00134      * Clip start and end positions to pixel buffer boundaries
00135      */
00136 
00137     xstart = CX_MAX(0, xstart);
00138     ystart = CX_MAX(0, ystart);
00139 
00140     xend = CX_MIN(xsize - 1, xend);
00141     yend = CX_MIN(ysize - 1, yend);
00142 
00143     xwidth = CX_MAX(xwidth,1 );
00144     ywidth = CX_MAX(ywidth,1 );
00145 
00146 
00147     /*
00148      * Search for count non-zero pixel values in the pixel region
00149      * defined by the rectangle (xstart, ystart, xend, yend).
00150      */
00151 
00152     for (i = ystart; i <= yend; i++) {
00153 
00154         cxint j;
00155         cxint row;
00156 
00157 
00158         /*
00159          * Skip the pixel row containing the pixel to check. Since the pixel
00160          * should be checked whether it belongs to a spectrum (extending
00161          * along the y-axis) we only check the adjacent pixel rows on
00162          * both sides.
00163          */
00164 
00165         if (i == ypos) {
00166             continue;
00167         }
00168 
00169         row = i * xsize;
00170 
00171         for (j = xstart; j <= xend; j++) {
00172             if (pixels[row + j]) {
00173                 ++_count;
00174             }
00175 
00176             if (_count >= count) {
00177                 return 1;
00178             }
00179         }
00180 
00181     }
00182 
00183     return 0;
00184 
00185 }
00186 
00187 
00188 /*
00189  * @brief
00190  *   Polynomial fit of raw spectrum region border.
00191  *
00192  * @param mborder   Y of detected borders
00193  * @param mbase     Full Chebyshev base
00194  * @param mxok      Good abcissa
00195  * @param nspectra  Spectrum number
00196  * @param sigma     Sigma clipping: sigma threshold level
00197  * @param niter     Sigma clipping: number of iterations
00198  * @param mfrac     Sigma clipping: minimum fraction of points accepted/total
00199  * @param mcoeff    Computed Chebyshev coefficients
00200  *
00201  * @return Matrix with the polynomial fit of @em mborder.
00202  *
00203  * Computes Chebyshev polynomial fit of @em mborder[:,nspectra].
00204  * The order of the polynomial fit is given by the Chebyshev base
00205  * @em mbase 1st dimension. The matrices @em mxtmp and @em mcoeff
00206  * are pre-allocated. The returned matrix @em mfit must be freed
00207  * using @b cpl_matrix_delete().
00208  *
00209  * @code
00210  *   mfit = _giraffe_fit_border(mborder, mbase, mxtmp, mxok, nspectra,
00211  *                               sigma, niter, mfrac, mcoeff);
00212  * @endcode
00213  */
00214 
00215 inline static cpl_matrix*
00216 _giraffe_fit_border(cpl_matrix* mborder, cpl_matrix* mbase,
00217                     cpl_matrix* mxok, cxint nspectra, cxdouble sigma,
00218                     cxint niter, cxdouble mfrac, cpl_matrix* mcoeff)
00219 {
00220 
00221     const cxchar* const fctid = "_giraffe_fit_border";
00222 
00223     register cxint x = 0;
00224     register cxint naccept = 0;
00225     register cxint ntotal = 0;
00226     register cxint iteration = 0;
00227     register cxint nx = cpl_matrix_get_ncol(mbase);
00228     register cxint yorder = cpl_matrix_get_nrow(mbase);
00229     register cxint nxok = cpl_matrix_get_nrow(mxok);
00230 
00231     register cxdouble ratio = 1.0;
00232 
00233     cpl_matrix* mtmp = NULL;
00234     cpl_matrix* yraw = NULL;
00235     cpl_matrix* ydiff = NULL;
00236     cpl_matrix* mfit = NULL;
00237     cpl_matrix* coeffs = NULL;
00238 
00239 
00240 
00241     if (nxok < yorder) {
00242         cpl_error_set(fctid, CPL_ERROR_INCOMPATIBLE_INPUT);
00243 
00244         GIDEBUG(gi_warning("%s: not enough points mxok[%d] for %d order fit",
00245                            fctid, nxok, yorder));
00246 
00247         return NULL;
00248     }
00249 
00250 
00251     /*
00252      * Initialize X,Y to be fit
00253      */
00254 
00255     yraw = cpl_matrix_new(1, nxok);
00256     ydiff = cpl_matrix_new(nxok, 1);
00257 
00258     mtmp = cpl_matrix_duplicate(mxok);
00259 
00260     /*
00261      * For each good x bin
00262      */
00263 
00264     for (x = 0; x < nxok; x++) {
00265         cxdouble data = cpl_matrix_get(mborder, x, nspectra);
00266         cpl_matrix_set(yraw, 0, x, data);
00267     }
00268 
00269 
00270     /*
00271      * Here comes the sigma clipping
00272      */
00273 
00274     ntotal = nxok;
00275     naccept = ntotal;
00276 
00277     while (naccept > 0 && iteration < niter && ratio > mfrac) {
00278 
00279         register cxint k = 0;
00280         register cxint l = 0;
00281 
00282         register cxdouble ysigma = 0.;
00283 
00284         cpl_matrix* rawbase = giraffe_chebyshev_base1d(0., nx, yorder, mtmp);
00285         cx_assert(rawbase != NULL);
00286 
00287         if (coeffs != NULL) {
00288             cpl_matrix_delete(coeffs);
00289         }
00290 
00291         coeffs = giraffe_matrix_leastsq(rawbase, yraw);
00292         if (coeffs == NULL) {
00293             gi_warning("%s: error in giraffe_matrix_leastsq(), spectrum %d",
00294                        fctid, nspectra);
00295             break;
00296         }
00297 
00298         cpl_matrix_delete(rawbase);
00299         rawbase = NULL;
00300 
00301         if (mfit != NULL) {
00302             cpl_matrix_delete(mfit);
00303         }
00304 
00305         mfit = cpl_matrix_product_create(coeffs, mbase);
00306 
00307 //1        for (x = 0; x < nxok; x++) {
00308 //1
00309 //1            cxint xok = (cxint) cpl_matrix_get(mxok, x, 0);
00310 //1            cxdouble diff;
00311 //1
00312 //1            diff = cpl_matrix_get(mborder, x, nspectra) -
00313 //1                cpl_matrix_get(mfit, 0, xok);
00314 //1            cpl_matrix_set(ydiff, x , 0, diff);
00315 //1
00316 //1        }
00317 
00318         for (x = 0; x < cpl_matrix_get_nrow(ydiff); x++) {
00319 
00320             cxint xok = (cxint) cpl_matrix_get(mtmp, x, 0);
00321 
00322             cxdouble diff =
00323                 cpl_matrix_get(yraw, 0, x) - cpl_matrix_get(mfit, 0, xok);
00324 
00325 
00326             cpl_matrix_set(ydiff, x , 0, diff);
00327 
00328         }
00329 
00330         ysigma = sigma * giraffe_matrix_sigma_mean(ydiff, 0.);
00331 
00332         /*
00333          * Reset sizes
00334          */
00335 
00336 //1        cpl_matrix_resize(mtmp, 0, nxok - naccept, 0, 0);
00337 //1        cpl_matrix_resize(yraw, 0, 0, 0, nxok - naccept);
00338 
00339 
00340         k = 0;
00341         for (l = 0; l < cpl_matrix_get_nrow(ydiff); l++) {
00342 
00343             if (fabs(cpl_matrix_get(ydiff, l, 0)) <= ysigma) {
00344 
00345 //1                cxint xok = cpl_matrix_get(mxok, l, 0);
00346 //1                cxdouble data = cpl_matrix_get(mborder, l, nspectra);
00347 
00348                 cxint xok = cpl_matrix_get(mtmp, l, 0);
00349                 cxdouble data = cpl_matrix_get(yraw, 0, l);
00350 
00351                 cpl_matrix_set(mtmp, k, 0, xok);
00352                 cpl_matrix_set(yraw, 0, k, data);
00353 
00354                 ++k;
00355             }
00356 
00357         }
00358 
00359         /*
00360          * No new points rejected, no more iterations
00361          * That was the last turn, boy
00362          */
00363 
00364         if (k == naccept) {
00365             break;
00366         }
00367 
00368         /*
00369          * Merry-go-round once more
00370          */
00371 
00372         naccept = k;
00373         ratio = (cxdouble) naccept / (cxdouble) ntotal;
00374 
00375         GIDEBUG(gi_message("Iteration %d: Sigma %f, accepted bins: %d, "
00376                            "rejected %d\n", iteration, ysigma, naccept,
00377                            ntotal - naccept));
00378 
00379         /*
00380          * Extract the new clipped matrices
00381          */
00382 
00383 //1        cpl_matrix_resize(mtmp, 0, naccept - nxok, 0, 0);
00384 //1        cpl_matrix_resize(yraw, 0, 0, 0, naccept - nxok);
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 /*
00420  * @brief
00421  *   Computes initial raw localization borders.
00422  *
00423  * @param image     The image to process [nx,ny]
00424  * @param nspectra  Number of expected spectra
00425  * @param noise     Spectra/noise threshold
00426  * @param config    Mask parameters.
00427  * @param ndetect   Number of spectra detected.
00428  * @param mxok      Matrix[nx] of @em nxok good x bins.
00429  * @param myup      Matrix[nx,nspectra] of @em nxok upper Y borders.
00430  * @param mylo      Matrix[nx,nspectra] of @em nxok lower Y borders.
00431  *
00432  * @return The function returns the number of good X bins on success, or
00433  *   a negative value on failure.
00434  *
00435  * Starting from @em config.start bin of the CCD, the function tries to
00436  * detect spectrum pixels pattern:
00437  *
00438  *     '0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 0 0'
00439  *
00440  * for each X bin and sets @em mylo[nx,ns] and @em myup[nx,ns] to Y values
00441  * of first '1' and last '1' of each '1' group. '0' and '1' are determined
00442  * by a @em noise value.
00443  *
00444  * Each group of '1' is supposed to be a spectrum. @em nspectra is the
00445  * expected number of spectra defined by the current instrument setup.
00446  * if X bin is ok @em mxok, @em mylo and @em myup matrices are updated
00447  * otherwise go and try the next X bin until @em config.tries is reached.
00448  *
00449  * @em mxok[nx], @em mylo[nx,ns] and @em myup[nx,ns] are pre-allocated
00450  * matrices.
00451  */
00452 
00453 inline static cxint
00454 _giraffe_build_raw_mask(cpl_image *raw, cpl_image *bpixel, cxint nspectra,
00455                         cxdouble noise, GiMaskParameters *config,
00456                         cxint *ndetect, cpl_matrix *mxok, cpl_matrix *myup,
00457                         cpl_matrix *mylo)
00458 {
00459 
00460     register cxint x, y;
00461     register cxint xretry, xok;
00462     register cxint ny;
00463 
00464     cxint nrows, ncols;
00465     cxint *yabove = NULL;
00466     cxint *ybelow = NULL;
00467     cxint *good_pixels = NULL;
00468     cxint ywidth = config->ywidth > 1 ? config->ywidth : 2;
00469     cxint ckwidth = config->ckdata.width;
00470     cxint ckheight = config->ckdata.height;
00471     cxint ckcount = config->ckdata.count;
00472 
00473 
00474     cxdouble *pixels = NULL;
00475 
00476     cpl_image *img = raw;
00477 
00478 
00479     *ndetect = 0;
00480 
00481     cpl_msg_info(_task, "Generating mask (%d spectra expected) ...",
00482                  nspectra);
00483 
00484     GIDEBUG(gi_message("noise = %g start = %d tries = %d xbin = %d "
00485                        "ywidth = %d", noise, config->start, config->retry,
00486                        config->xbin, ywidth));
00487 
00488     pixels = cpl_image_get_data_double(raw);
00489 
00490     nrows = cpl_image_get_size_y(raw);
00491     ncols = cpl_image_get_size_x(raw);
00492 
00493     if (config->xbin > 1) {
00494 
00495         cxdouble *_pixels = NULL;
00496 
00497         nrows = (cxint) ceil(nrows / config->xbin);
00498         config->start = (cxint) ceil(config->start / config->xbin);
00499 
00500         img = cpl_image_new(ncols, nrows, CPL_TYPE_DOUBLE);
00501         _pixels = cpl_image_get_data_double(img);
00502 
00503         for (y = 0; y < ncols; y++) {
00504             for (x = 0; x < nrows; x++) {
00505 
00506                 register cxint xx, xraw;
00507                 register cxint zx = x * config->xbin;
00508 
00509 
00510                 _pixels[x * ncols + y] = 0.;
00511 
00512                 for (xx = 0, xraw = zx; xx < config->xbin && xraw < nrows;
00513                      xx++, xraw++) {
00514                     _pixels[x * ncols + y] += pixels[xraw * ncols + y];
00515                 }
00516             }
00517         }
00518 
00519         pixels = _pixels;
00520 
00521     }
00522 
00523     good_pixels = cx_calloc(nrows * ncols, sizeof(cxint));
00524 
00525     if (config->local) {
00526 
00527         cxint ywidth2 = ywidth >> 1;
00528         cxint sz = 2 * ywidth2 + 1;
00529 
00530         cpl_vector *ymins = cpl_vector_new(sz);
00531 
00532 
00533         /*
00534          * We define a window along y axis to compute a local minimum
00535          * and threshold. To handle variation of "background"
00536          * between spectra in subslits
00537          */
00538 
00539         for (x = 0; x < nrows; x++) {
00540 
00541             cpl_vector_fill(ymins, 0.);
00542 
00543             for (y = 0; y < ncols; y++) {
00544 
00545                 register cxint k, kk;
00546 
00547                 cxdouble value = 0.;
00548                 cxdouble bkg = 0.;
00549                 cxdouble threshold = 0.;
00550 
00551 
00552                 for (kk = 0, k = -ywidth2; k <= ywidth2; k++) {
00553 
00554                     register cxint ky = y + k;
00555 
00556                     if (ky < 0 || ky >= ncols) {
00557                         continue;
00558                     }
00559 
00560                     cpl_vector_set(ymins, kk, pixels[x * ncols + ky]);
00561                     ++kk;
00562                 }
00563 
00564                 if (kk == 0) {
00565                     continue;
00566                 }
00567 
00568                 if (config->threshold > 0.) {
00569 
00570                     const cxint count = 2;
00571 
00572                     cxint i = 0;
00573 
00574                     /* Note that ymins has, by construction, an odd number
00575                      * of elements which must be at least 3 at this point.
00576                      * Also kk must be at least ywidth2 + 1, since at most we
00577                      * loose ywidth2 pixels at the borders.
00578                      */
00579 
00580                     giraffe_array_sort(cpl_vector_get_data(ymins), kk);
00581 
00582                     bkg = 0.;
00583 
00584                     for (i = 0; i < count; i++) {
00585                         bkg += cpl_vector_get(ymins, i);
00586                     }
00587                     bkg /= (cxdouble)count;
00588                     threshold = sqrt(noise * noise +
00589                                      fabs(pixels[x * ncols + y]));
00590 
00591                 }
00592                 else {
00593 
00594                     register cxint i;
00595                     register cxdouble mean = 0.;
00596 
00597 
00598                     for (i = 0; i < kk; i++) {
00599                         mean += cpl_vector_get(ymins, i);
00600                     }
00601                     mean /= kk;
00602 
00603                     giraffe_array_sort(cpl_vector_get_data(ymins), kk);
00604 
00605                     bkg = (cpl_vector_get(ymins, 0) +
00606                            cpl_vector_get(ymins, 1)) / 2.0;
00607                     threshold = mean - bkg;
00608 
00609                 }
00610 
00611 
00612                 /*
00613                  * Check background corrected pixel value
00614                  */
00615 
00616                 value = pixels[x * ncols + y] - bkg;
00617 
00618                 if (value < 0.) {
00619                     continue;
00620                 }
00621 
00622                 if (value > fabs(config->threshold) * threshold) {
00623                     good_pixels[x * ncols + y] = 1;
00624                 }
00625             }
00626         }
00627 
00628         cpl_vector_delete(ymins);
00629         ymins = NULL;
00630 
00631     }
00632     else {
00633 
00634         cxdouble threshold = 0.;
00635 
00636 
00637         /*
00638          * We use global background and threshold
00639          */
00640 
00641         if (config->threshold > 0.) {
00642             threshold = config->threshold * noise;
00643         }
00644         else {
00645             cxdouble mean = cpl_image_get_mean(raw);
00646 
00647             threshold = -config->threshold * mean * nspectra *
00648                 config->wavg / ncols;
00649         }
00650 
00651         for (x = 0; x < nrows; x++) {
00652             for (y = 0; y < ncols; y++) {
00653 
00654                 if (pixels[x * ncols + y] > threshold) {
00655                     good_pixels[x * ncols + y] = 1;
00656                 }
00657 
00658             }
00659         }
00660 
00661     }
00662 
00663     GIDEBUG(cxint *data = cx_calloc(nrows * ncols, sizeof(cxint));
00664 
00665             memcpy(data, good_pixels, nrows * ncols * sizeof(cxint));
00666             cpl_image *gp = cpl_image_wrap_int(ncols, nrows, data);
00667             cpl_image_save(gp, "locmask.fits", 32, NULL, CPL_IO_DEFAULT);
00668             cpl_image_unwrap(gp);
00669             cx_free(data));
00670 
00671 
00672     /*
00673      * Buffers used to store the fiber boundaries.
00674      */
00675 
00676     yabove = cx_calloc(nspectra + 1, sizeof(cxint));
00677     ybelow = cx_calloc(nspectra + 1, sizeof(cxint));
00678 
00679 
00680     /*
00681      * Start from <config->start> of CCD to first pixel
00682      */
00683 
00684     ny = ncols - 1;
00685 
00686     xretry = 0;
00687     xok = 0;
00688 
00689     for (x = config->start; (x >= 0) && (xretry <= config->retry); x--) {
00690 
00691         register cxint zx = x * ncols;
00692         register cxint nborders = 0;
00693         register cxint nbelow = 0;
00694         register cxint nabove = 0;
00695         register cxint in_spectrum = 0;
00696 
00697 
00698         for (y = 1; y < ny; y++) {
00699 
00700             register cxint tmp = 2 * good_pixels[zx + y];
00701 
00702             /*
00703              * Number of spectra = max number of borders
00704              */
00705 
00706             nborders = CX_MAX(CX_MAX(nborders, nbelow), nabove);
00707 
00708             if (nborders > nspectra) {
00709                 break;   /* Error: too many spectrum borders detected */
00710             }
00711 
00712             /*
00713              * Try to detect spectrum pattern:
00714              *    0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1
00715              * we need at least two consecutive one to detect a spectrum.
00716              */
00717 
00718             if (good_pixels[zx + y + 1]) {
00719 
00720                 /*
00721                  * Next pixel is a spectrum pixel: it's a border if
00722                  * previous one is zero
00723                  */
00724 
00725                 if ((tmp - good_pixels[zx + y - 1]) == 2) {
00726 
00727                     /*
00728                      * 0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1
00729                      *       ^ so, here we are...
00730                      */
00731 
00732                     if (!in_spectrum) {
00733 
00734                         /*
00735                          * Could not be a below border if we are already
00736                          * into a spectrum
00737                          */
00738 
00739                         ybelow[nbelow++] = y;
00740                         in_spectrum = 1;    /* entering */
00741 
00742                     }
00743 
00744                 }
00745 
00746             }
00747 
00748             if (good_pixels[zx + y - 1]) {
00749 
00750                 /*
00751                  * Previous pixel is a spectrum pixel: it's a border if
00752                  * next one is zero
00753                  */
00754 
00755                 if ((tmp - good_pixels[zx + y + 1]) == 2) {
00756 
00757                     /*
00758                      * 0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1
00759                      *               ^ and now, there
00760                      */
00761 
00762                     if (in_spectrum) {
00763 
00764                         /*
00765                          * taken into account only if we already found a
00766                          * lower border, we really are into a spectrum
00767                          */
00768 
00769                         yabove[nabove++] = y;
00770                         in_spectrum = 0;    /* going out */
00771 
00772                     }
00773 
00774                 }
00775 
00776             }
00777 
00778 // FIXME: Just a try
00779 
00780             if (tmp &&
00781                 !good_pixels[zx + y - 1] && !good_pixels[zx + y + 1]) {
00782 
00783                 if (_giraffe_validate_pixel(good_pixels, ncols, nrows, y, x,
00784                                             ckwidth, ckheight, ckcount)) {
00785 
00786                     yabove[nabove++] = y;
00787                     ybelow[nbelow++] = y;
00788                 }
00789 
00790             }
00791 
00792         }   /* finished with this x bin */
00793 
00794         if (in_spectrum) {
00795             nborders--;
00796             nbelow--;
00797             in_spectrum = 0;
00798         }
00799 
00800         *ndetect = nborders;
00801 
00802         if (!in_spectrum && (nbelow == nspectra) && (nbelow == nabove)) {
00803 
00804             /*
00805              * Good number of upper and lower cuples found for all spectra:
00806              * xend will be the first good value and the updated xstart is
00807              * the current value. We also do not want last CCD clipped
00808              * spectrum
00809              */
00810 
00811             for (y = 0; y < nspectra; y++) {
00812                 cpl_matrix_set(mylo, xok, y, (cxdouble) ybelow[y]);
00813                 cpl_matrix_set(myup, xok, y, (cxdouble) yabove[y]);
00814                 cpl_matrix_set(mxok, xok, 0, (config->xbin > 1) ?
00815                                (cxdouble) (x + 0.5) * config->xbin :
00816                                (cxdouble) x);
00817             }
00818             xok++;
00819             xretry = 0; /* back on your feet */
00820         }
00821         else if (xretry++ < config->retry) {
00822 
00823             /*
00824              * Do not find good number of spectra but we still have some
00825              * credit for a next try
00826              */
00827 
00828             continue;
00829         }
00830         else {
00831 
00832             /*
00833              * This is the end of our rope
00834              */
00835 
00836             break;
00837         }
00838     } /* next x bin */
00839 
00840 
00841     /*
00842      * Second half: start from <config->start+1> of CCD to last pixel
00843      */
00844 
00845     /*
00846      * Oops we could have a 2 * xretry width hole around xstart!!!
00847      */
00848 
00849     xretry = 0;
00850 
00851     for (x = config->start + 1; (x < nrows) &&
00852              (xretry <= config->retry); x++) {
00853 
00854         register cxint zx = x * ncols;
00855         register cxint nborders = 0;
00856         register cxint nbelow = 0;
00857         register cxint nabove = 0;
00858         register cxint in_spectrum = 0;
00859 
00860 
00861         for (y = 1; y < ny; y++) {
00862 
00863             register cxint tmp = 2 * good_pixels[zx + y];
00864 
00865             nborders = CX_MAX(CX_MAX(nborders, nbelow), nabove);
00866 
00867             if (nborders > nspectra) {
00868                 break;
00869             }
00870 
00871             if (good_pixels[zx + y + 1]) {
00872                 if ((tmp - good_pixels[zx + y - 1]) == 2) {
00873                     if (!in_spectrum) {
00874                         ybelow[nbelow++] = y;
00875                         in_spectrum = 1;
00876                     }
00877                 }
00878             }
00879 
00880             if (good_pixels[zx + y - 1]) {
00881                 if ((tmp - good_pixels[zx + y + 1]) == 2) {
00882                     if (in_spectrum) {
00883                         yabove[nabove++] = y;
00884                         in_spectrum = 0;
00885                     }
00886                 }
00887             }
00888 
00889 // FIXME: Just a try
00890 
00891             if (tmp &&
00892                 !good_pixels[zx + y - 1] && !good_pixels[zx + y + 1]) {
00893 
00894                 if (_giraffe_validate_pixel(good_pixels, ncols, nrows, y, x,
00895                                             ckwidth, ckheight, ckcount)) {
00896 
00897                     yabove[nabove++] = y;
00898                     ybelow[nbelow++] = y;
00899                 }
00900 
00901             }
00902 
00903         } /* finished with this x bin */
00904 
00905         if (in_spectrum) {
00906             nborders--;
00907             nbelow--;
00908             in_spectrum = 0;
00909         }
00910 
00911         *ndetect = nborders;
00912 
00913         if (!in_spectrum && (nbelow == nspectra) && (nbelow == nabove)) {
00914 
00915             for (y = 0; y < nspectra; y++) {
00916                 cpl_matrix_set(mylo, xok, y, (cxdouble) ybelow[y]);
00917                 cpl_matrix_set(myup, xok, y, (cxdouble) yabove[y]);
00918                 cpl_matrix_set(mxok, xok, 0, (config->xbin > 1) ?
00919                                (cxdouble) (x + 0.5) * config->xbin :
00920                                (cxdouble) x);
00921             }
00922             xok++;
00923             xretry = 0;
00924         }
00925         else if (xretry++ < config->retry) {
00926             continue;
00927         }
00928         else {
00929             break;
00930         }
00931 
00932     } /* next x bin */
00933 
00934     cx_free(ybelow);
00935     cx_free(yabove);
00936     cx_free(good_pixels);
00937 
00938     if (config->xbin > 1) {
00939         cpl_image_delete(img);
00940     }
00941 
00942     if (xok == 0) {
00943         if (*ndetect < nspectra) {
00944             return -1;
00945         }
00946         else if (*ndetect > nspectra) {
00947             return -1;
00948         }
00949         else {
00950             return -2;
00951         }
00952     }
00953     else {
00954         *ndetect = nspectra;
00955         cpl_msg_info(_task, "%d spectra detected in %d wavelength bins",
00956                      *ndetect, xok);
00957     }
00958 
00959     return xok;
00960 
00961 }
00962 
00963 
00964 /*
00965  * @brief
00966  *   Computes fitted localization centroid and width.
00967  *
00968  * @param mxok      good X bins (all nspectra detected) [nxok]
00969  * @param myup      upper Y of spectra [nxok,nspectra]
00970  * @param mylo      lower Y of spectra [nxok,nspectra]
00971  * @param fibers    Table of spectra/fibers to localize [ns]
00972  * @param config    localization mask parameters
00973  * @param position  localization mask: locy[nx,ns] and locw[nx,ns]
00974  *
00975  * Computes Chebyshev polynomial fit of raw localization borders for each
00976  * spectrum specified in @em fibers.
00977  *
00978  * The @em myup[nxok,nspectra] and @em mylo[nxok,nspectra] border matrices
00979  * had been computed by @b _giraffe_build_raw_mask().
00980  *
00981  * The expected number of spectra to be localized is given by @em nspectra.
00982  * The computed results are stored in the pre-allocated matrices
00983  * @em position->my[nx,ns] and @em position->mw[nx,ns]. The fiber setup
00984  * for a particular observation is given by @em fibers, a table of all
00985  * fibers specifying the spectra to be processed where @em ns is number of
00986  * entries (fibers) in @em fibers defined by the current instrument setup.
00987  */
00988 
00989 inline static void
00990 _giraffe_fit_raw_mask(cpl_matrix *mxok, cpl_matrix *myup, cpl_matrix *mylo,
00991                       cpl_table *fibers, GiMaskParameters *config,
00992                       GiMaskPosition *position)
00993 {
00994 
00995     register cxint nn, x, nspectra;
00996     register cxint nx = cpl_matrix_get_nrow(position->my);
00997     register cxint ns = cpl_table_get_nrow(fibers);
00998 
00999     cpl_matrix *mxraw;
01000     cpl_matrix *base;
01001     cpl_matrix *mcoeff;
01002 
01003 
01004 
01005     mxraw  = cpl_matrix_new(nx, 1);
01006     mcoeff = cpl_matrix_new(config->ydeg + 1, 1);
01007 
01008 
01009     /*
01010      * Initialize with all abcissa
01011      */
01012 
01013     for (x = 0; x < nx; x++) {
01014         cpl_matrix_set(mxraw, x, 0, x);
01015     }
01016 
01017     /*
01018      * Compute Chebyshev base over all x bins
01019      */
01020 
01021     base = giraffe_chebyshev_base1d(0., nx, config->ydeg + 1, mxraw);
01022     cpl_matrix_delete(mxraw);
01023 
01024     nspectra = 0;
01025     for (nn = 0; nn < ns; nn++) {
01026         cpl_matrix *ylofit = NULL;
01027         cpl_matrix *yupfit = NULL;
01028 
01029         /* FIXME: The fiber selection changed the following piece of code
01030          *        should not be necessary but we have to check that the
01031          *        accessed to the matrix rows correspond to the selected
01032          *        fibers.
01033          */
01034 
01035         //if (mButton->m[nn] != LOC_OK_SPECTRUM) {
01036         //    continue;
01037         //}
01038 
01039         /* ylofit[nx] =  fit of lower border */
01040         ylofit = _giraffe_fit_border(mylo, base, mxok, nspectra,
01041                                      config->sigma, config->niter,
01042                                      config->mfrac, mcoeff);
01043         if (ylofit == NULL) {
01044             cpl_msg_warning(_task, "Could not compute low border for "
01045                             "spectrum %d", nn);
01046             nspectra++;
01047             continue;
01048         }
01049 
01050         /* yupfit[nx] =  fit of upper border */
01051         yupfit = _giraffe_fit_border(myup, base, mxok, nspectra,
01052                                      config->sigma, config->niter,
01053                                      config->mfrac, mcoeff);
01054         if (yupfit == NULL) {
01055             cpl_msg_warning(_task, "Could not compute up border for "
01056                             "spectrum %d", nn);
01057             nspectra++;
01058             continue;
01059         }
01060 
01061         /*
01062          * For each X bin the centroid and the half-width of the
01063          * corresponding mask is computed as the half-sum and the
01064          * half-difference of the fitted borders.
01065          */
01066 
01067         for (x = 0; x < nx; x++) {
01068 
01069             cpl_matrix_set(position->my, x, nn, 0.5 *
01070                            (cpl_matrix_get(yupfit, x, 0) +
01071                             cpl_matrix_get(ylofit, x, 0)));
01072 
01073             cpl_matrix_set(position->my, x, nn, 0.5 *
01074                            (cpl_matrix_get(yupfit, x, 0) -
01075                             cpl_matrix_get(ylofit, x, 0)) + config->ewid);
01076 
01077         }
01078         cpl_matrix_delete(ylofit);
01079         cpl_matrix_delete(yupfit);
01080         nspectra++;
01081 
01082     } /* each spectrum */
01083 
01084     cpl_msg_info(_task, "%03d spectrum positions fitted", nspectra);
01085 
01086     cpl_matrix_delete(base);
01087     cpl_matrix_delete(mcoeff);
01088 
01089     if (nspectra == 0) {
01090         cpl_msg_warning(_task, "could not fit any spectra, check number "
01091                         "of good wavelength bins");
01092         return;
01093     }
01094 
01095     return;
01096 
01097 }
01098 
01099 
01100 /*
01101  * @brief
01102  *   Computes fitted localization centroid and width.
01103  *
01104  * @param mz        Image[nx,ny] of pixels values
01105  * @param mxok      Good x bins (all nspectra detected) [nxok]
01106  * @param myup      Upper Y of spectra [nxok,nspectra]
01107  * @param mylo      Lower Y of spectra [nxok,nspectra]
01108  * @param fibers    Spectra used for localization [ns]
01109  * @param config    Localization mask parameters
01110  * @param position  Localization mask: my[nx, ns] and mw[nx, ns]
01111  * @param coeffs    Localization mask Chebyshev fit coefficients
01112  *
01113  * Computes Chebyshev polynomial fit of raw localization borders for each
01114  * spectrum specified in fibers[ns].
01115  *
01116  * The @em myup[nxok, nspectra] and @em mylo[nxok, nspectra] border matrices
01117  * had been computed by @b _giraffe_build_raw_mask(). The expected number
01118  * of spectra to be localized is given by @em nspectra. The matrix
01119  * @em position->my[nx, ns] is the fitted barycenter of Y values between raw
01120  * localization borders, while @em position->mw[nx,ns] is the 2D fit of
01121  * the half-width of the raw localization borders (+ extra width:
01122  * @em config->ewid).
01123  *
01124  * The matrices @em position->my[nx, ns], @em position->mw[nx, ns],
01125  * @em coeffs->my[ydeg + 1, ns] and @em coeffs->mw[(config->wdeg + 1)^2]
01126  * are pre-allocated matrices.
01127  *
01128  * The fiber setup for a particular observation is given by @em fibers,
01129  * a table of all fibers specifying the spectra to be processed where
01130  * @em ns is number of entries (fibers) in @em fibers defined by the
01131  * current instrument setup.
01132  */
01133 
01134 inline static void
01135 _giraffe_fit_raw_centroid(cpl_image* mz, cpl_matrix* mxok, cpl_matrix* myup,
01136                           cpl_matrix* mylo, cpl_table* fibers,
01137                           GiMaskParameters* config, GiMaskPosition* position,
01138                           GiMaskPosition* coeffs)
01139 {
01140 
01141     const cxchar* const fctid = "_giraffe_fit_raw_centroid";
01142 
01143     register cxint nn = 0;
01144     register cxint x = 0;
01145     register cxint y = 0;
01146     register cxint nspectra = 0;
01147     register cxint nx = cpl_image_get_size_y(mz);
01148     register cxint ny = cpl_image_get_size_x(mz);
01149     register cxint ns = cpl_table_get_nrow(fibers);
01150 
01151     cxint yorder = config->ydeg + 1;
01152     cxint worder = config->wdeg + 1;
01153 
01154     cpl_matrix* mxraw = NULL;
01155     cpl_matrix* base = NULL;
01156     cpl_matrix* mycenter = NULL;
01157     cpl_matrix* mywidth = NULL;
01158     cpl_matrix* mx = NULL;
01159     cpl_matrix* my = NULL;
01160     cpl_matrix* mw = NULL;
01161     cpl_matrix* chebcoeff = NULL;
01162     cpl_matrix* mfitlocw = NULL;
01163     cpl_matrix* ycenfit = NULL;
01164     cpl_matrix* ycencoeff = NULL;
01165 
01166 
01167 
01168     if (cpl_matrix_get_nrow(position->my) != nx ||
01169         cpl_matrix_get_ncol(position->my) != ns) {
01170         gi_error("%s: invalid size for position->my[%d,%d], expected "
01171                  "[%d,%d]", fctid, cpl_matrix_get_nrow(position->my),
01172                  cpl_matrix_get_ncol(position->my), nx, ns);
01173         return;
01174     }
01175 
01176     if (cpl_matrix_get_nrow(position->mw) != nx ||
01177         cpl_matrix_get_ncol(position->mw) != ns) {
01178         gi_error("%s: invalid size for position->mw[%d,%d], expected "
01179                  "[%d,%d]", fctid, cpl_matrix_get_nrow(position->my),
01180                  cpl_matrix_get_ncol(position->my), nx, ns);
01181         return;
01182     }
01183 
01184 
01185     /*
01186      * Initialize with all abcissa
01187      */
01188 
01189     mxraw = cpl_matrix_new(nx, 1);
01190 
01191     for (x = 0; x < nx; x++) {
01192         cpl_matrix_set(mxraw, x, 0, x);
01193     }
01194 
01195 
01196     /*
01197      * Compute Chebyshev base over all x bins
01198      */
01199 
01200     base = giraffe_chebyshev_base1d(0., nx, yorder, mxraw);
01201     cpl_matrix_delete(mxraw);
01202 
01203     mycenter = cpl_matrix_new(cpl_matrix_get_nrow(mxok), ns);
01204     mywidth = cpl_matrix_new(1, cpl_matrix_get_nrow(mxok) * ns);
01205 
01206     ycencoeff = cpl_matrix_new(yorder, 1);
01207 
01208     for (nn = 0; nn < ns; nn++) {
01209 
01210         /* FIXME: The fiber selection changed the following piece of code
01211          *        should not be necessary but we have to check that the
01212          *        accessed to the matrix rows correspond to the selected
01213          *        fibers.
01214          */
01215 
01216         //if (mButton->m[nn] != LOC_OK_SPECTRUM) {
01217         //    continue;
01218         //}
01219 
01220         /*
01221          * compute the barycenter and half-width of the corresponding mask
01222          * between raw borders.
01223          */
01224 
01225         cxdouble* pixels = cpl_image_get_data_double(mz);
01226 
01227         for (x = 0; x < cpl_matrix_get_nrow(mxok); x++) {
01228 
01229             register cxint zx = (cxint) cpl_matrix_get(mxok, x, 0);
01230 
01231             register cxdouble zz = 0.;
01232             register cxdouble yy = 0.;
01233 
01234             cxdouble lower = cpl_matrix_get(mylo, x, nspectra);
01235             cxdouble upper = cpl_matrix_get(myup, x, nspectra);
01236 
01237 
01238             for (y = (cxint) lower; y <= (cxint) upper; y++) {
01239                 yy += pixels[zx * ny + y] * y;
01240                 zz += pixels[zx * ny + y];
01241             }
01242 
01243             cpl_matrix_set(mycenter, x, nspectra, yy / zz);
01244             cpl_matrix_set(mywidth, 0, x * ns + nspectra, config->ewid +
01245                            (upper - lower) / 2.0);
01246 
01247         }   /* for each x bin */
01248 
01249         /*
01250          * The matrix ycenfit[nx] stores the fitted centroid
01251          */
01252 
01253         cpl_matrix_fill(ycencoeff, 0.);
01254         ycenfit = _giraffe_fit_border(mycenter, base, mxok, nspectra,
01255                                       config->sigma, config->niter,
01256                                       config->mfrac, ycencoeff);
01257         if (ycenfit == NULL) {
01258             cpl_msg_warning(_task, "Could not fit centroid for spectrum %d",
01259                             nn);
01260             nspectra++;
01261             continue;
01262         }
01263 
01264         /*
01265          * Save centroid Chebyshev fit coeffs
01266          */
01267 
01268         for (x = 0; x < yorder; x++) {
01269             cpl_matrix_set(coeffs->my, x, nn,
01270                            cpl_matrix_get(ycencoeff, x, 0));
01271         }
01272 
01273         /*
01274          * The localization centroid is a Chebyshev polynomial fit
01275          * of Y barycenters in raw mask
01276          */
01277 
01278         for (x = 0; x < nx; x++) {
01279             cpl_matrix_set(position->my, x, nn,
01280                            cpl_matrix_get(ycenfit, 0, x));
01281         }   /* for each x bin */
01282 
01283         cpl_matrix_delete(ycenfit);
01284         nspectra++;
01285 
01286     } /* each spectrum */
01287 
01288     GIDEBUG(cpl_image *lycenter = giraffe_matrix_create_image(mycenter);
01289             cpl_image_save(lycenter, "lycenter.fits", -32, NULL,
01290                            CPL_IO_DEFAULT);
01291             cpl_image_delete(lycenter);
01292 
01293             lycenter = giraffe_matrix_create_image(position->my);
01294             cpl_image_save(lycenter, "lycenterfit.fits", -32, NULL,
01295                            CPL_IO_DEFAULT);
01296             cpl_image_delete(lycenter);
01297 
01298             cpl_image *lyxok = giraffe_matrix_create_image(mxok);
01299             cpl_image_save(lyxok, "lyxok.fits", -32, NULL,
01300                            CPL_IO_DEFAULT);
01301             cpl_image_delete(lyxok));
01302 
01303 
01304     cpl_msg_info(_task, "%03d spectrum positions fitted", nspectra);
01305 
01306     cpl_matrix_delete(base);
01307     cpl_matrix_delete(mycenter);
01308     cpl_matrix_delete(ycencoeff);
01309 
01310     if (nspectra == 0) {
01311         cpl_msg_warning(_task, "Could not fit any spectra, check number of "
01312                         "good wavelength bins");
01313 
01314         cpl_matrix_delete(mywidth);
01315         return;
01316     }
01317 
01318     /*
01319      * 2D fit of mask width
01320      */
01321 
01322     cpl_msg_info(_task, "2D fit (order %dx%d) of mask width", worder,
01323                  worder);
01324 
01325     /*
01326      * Computes grid[nxok, nspectra]
01327      */
01328 
01329     mx = cpl_matrix_new(cpl_matrix_get_nrow(mxok) * nspectra, 1);
01330     my = cpl_matrix_new(cpl_matrix_get_nrow(mxok) * nspectra, 1);
01331     mw = cpl_matrix_new(1, cpl_matrix_get_nrow(mxok) * nspectra);
01332 
01333     for (y = 0, nn = 0; nn < nspectra; nn++) {
01334 
01335         /* FIXME: The fiber selection changed the following piece of code
01336          *        should not be necessary but we have to check that the
01337          *        accessed to the matrix rows correspond to the selected
01338          *        fibers.
01339          */
01340 
01341         //if (mButton->m[nn] != LOC_OK_SPECTRUM) {
01342         //    continue;
01343         //}
01344 
01345         for (x = 0; x < cpl_matrix_get_nrow(mxok); x++) {
01346 
01347             register cxint zx = (cxint) cpl_matrix_get(mxok, x, 0);
01348             register cxint lx = x * nspectra + y;
01349 
01350 
01351             cpl_matrix_set(mx, lx, 0, cpl_matrix_get(mxok, x, 0));
01352             cpl_matrix_set(my, lx, 0, cpl_matrix_get(position->my, zx, nn));
01353             cpl_matrix_set(mw, 0, lx, cpl_matrix_get(mywidth, 0, x * ns + y));
01354         }
01355         y++;
01356     }
01357 
01358     base = giraffe_chebyshev_base2d(0., 0., nx, ny, worder, worder, mx, my);
01359 
01360     cpl_matrix_delete(my);
01361     cpl_matrix_delete(mx);
01362 
01363     chebcoeff = giraffe_matrix_leastsq(base, mw);
01364     cpl_matrix_delete(base);
01365     cpl_matrix_delete(mw);
01366 
01367     cpl_matrix_delete(mywidth);
01368 
01369     if (chebcoeff == NULL) {
01370         gi_warning("%s: error in giraffe_matrix_leastsq() for width 2D fit",
01371                    fctid);
01372         return;
01373     }
01374 
01375     /*
01376      * Save half-width Chebyshev 2-D fit coeffs
01377      */
01378 
01379     for (nn = 0; nn < cpl_matrix_get_ncol(chebcoeff); nn++) {
01380         cpl_matrix_set(coeffs->mw, 0, nn, cpl_matrix_get(chebcoeff, 0, nn));
01381     }
01382 
01383     /*
01384      * Computes grid[nx, nspectra]
01385      */
01386 
01387     mx = cpl_matrix_new(nx * nspectra, 1);
01388     my = cpl_matrix_new(nx * nspectra, 1);
01389 
01390     for (y = 0, nn = 0; nn < nspectra; nn++) {
01391 
01392         /* FIXME: The fiber selection changed the following piece of code
01393          *        should not be necessary but we have to check that the
01394          *        accessed to the matrix rows correspond to the selected
01395          *        fibers.
01396          */
01397 
01398         //if (mButton->m[nn] != LOC_OK_SPECTRUM) {
01399         //    continue;
01400         //}
01401 
01402         for (x = 0; x < nx; x++) {
01403 
01404             register cxint lx = x * nspectra + y;
01405 
01406             cpl_matrix_set(mx, lx, 0, x);
01407             cpl_matrix_set(my, lx, 0, cpl_matrix_get(position->my, x, nn));
01408 
01409         }
01410         y++;
01411     }
01412 
01413     cpl_matrix_set_size(chebcoeff, worder, worder);
01414 
01415     mfitlocw = giraffe_chebyshev_fit2d(0., 0., nx, ny, chebcoeff, mx, my);
01416     cpl_matrix_delete(chebcoeff);
01417 
01418     cpl_matrix_delete(my);
01419     cpl_matrix_delete(mx);
01420 
01421     for (y = 0, nn = 0; nn < nspectra; nn++) {
01422 
01423         /* FIXME: The fiber selection changed the following piece of code
01424          *        should not be necessary but we have to check that the
01425          *        accessed to the matrix rows correspond to the selected
01426          *        fibers.
01427          */
01428 
01429         //if (mButton->m[nn] != LOC_OK_SPECTRUM) {
01430         //    continue;
01431         //}
01432 
01433         for (x = 0; x < nx; x++) {
01434 
01435             register cxint lx = x * nspectra + y;
01436 
01437             cpl_matrix_set(position->mw, x, nn,
01438                            cpl_matrix_get(mfitlocw, lx, 0));
01439 
01440         }
01441         y++;
01442     }
01443 
01444     cpl_matrix_delete(mfitlocw);
01445 
01446     return;
01447 
01448 }
01449 
01450 
01451 /*
01452  * @brief
01453  *   Computes fitted localization centroid and width on all spectra.
01454  *
01455  * @param mZraw      Matrix[nx,ny] of pixels values
01456  * @param mButton    Matrix[ns] of spectra used for localization
01457  * @param locMethod  Centroid computation method:
01458  *                   HALF_WIDTH, BARYCENTER, PSF_PROFIL
01459  * @param sNormalize Normalize spectra along dispersion axis
01460  * @param noithresh  Spectra/noise threshold
01461  * @param locPrms    Localization mask parameters
01462  * @param locPos     Localization mask: locY[nx,ns] and locW[nx,ns]
01463  * @param locCoeff   Localization mask Chebyshev fit coefficients
01464  *
01465  * @return The function returns 0 on success, or a negative value otherwise.
01466  *
01467  * Computes localization mask (centroid and half-width) for the given
01468  * image @a mZraw[nx,ny]. @a mButton[nspectra] is a matrix of 0/1 values
01469  * specifying spectra to be processed. @a *noithresh is the threshold value
01470  * use to select spectra or inter-spectra pixels. @a locMethod defines the
01471  * method used to compute localization mask centroid and half-width.
01472  * @a locPos.mY[nx,ns], @a locPos.mW[nx,ns], @a locCoeff.mY[ydeg+1,ns] and
01473  * @a locCoeff.mW[(wdeg+1)**2] are pre-allocated matrices.
01474  */
01475 
01476 inline static cxint
01477 _giraffe_localize_spectra(cpl_image *mzraw, cpl_image *bpixel,
01478                           cpl_table *fibers, GiLocalizeMethod method,
01479                           cxbool normalize, cxdouble noise,
01480                           GiMaskParameters *config, GiMaskPosition *position,
01481                           GiMaskPosition *coeffs)
01482 {
01483 
01484     cxint n, nn;
01485     cxint nx, ny, nxok;
01486     cxint ndetect, nspectra;
01487     cxint x, y;
01488 
01489     cxdouble uplost = 0.;
01490     cxdouble lolost = 0.;
01491     cxdouble avglost = 0.;
01492     cxdouble avgmask = 0.;
01493     cxdouble sigmask = 0.;
01494     cxdouble sigmean = 0.;
01495     cxdouble avgborders = 0.;
01496 
01497     cxdouble *_mzraw;
01498 
01499     cpl_matrix *mxok;         /* mylo[nx] abcissa og good x bins */
01500     cpl_matrix *myup;         /* myup[nx,ns] of upper Y for each spectrum */
01501     cpl_matrix *mylo;         /* mylo[nx,ns] of lower Y for each spectrum */
01502     cpl_matrix *mwid;
01503 
01504     cpl_image *mz = NULL;
01505     cpl_image *mznorm = NULL;
01506 
01507 
01508 
01509     nx = cpl_image_get_size_y(mzraw);
01510     ny = cpl_image_get_size_x(mzraw);
01511     _mzraw = cpl_image_get_data_double(mzraw);
01512 
01513 
01514     if (normalize == TRUE) {
01515 
01516         cxdouble zxmax = 0.0;
01517         cxdouble *_mzx = NULL;
01518         cxdouble *_mznorm = NULL;
01519 
01520         cpl_image *mzx = NULL;
01521 
01522 
01523         cpl_msg_info(_task, "Using normalized spectra for localization");
01524 
01525 
01526         /*
01527          * The matrix mznorm contains the pixel values from mz
01528          * normalized along X axis and the matrix mzx is the summ
01529          * of all spectra along X (dispersion) axis
01530          */
01531 
01532         mznorm = cpl_image_new(ny, nx, CPL_TYPE_DOUBLE);
01533         _mznorm = cpl_image_get_data_double(mznorm);
01534 
01535         mzx = cpl_image_new(1, nx, CPL_TYPE_DOUBLE);
01536         _mzx = cpl_image_get_data_double(mzx);
01537 
01538 
01539         /*
01540          * For each x bin, summ all y values
01541          */
01542 
01543         for (x = 0 ; x < nx; x++) {
01544             for (y = 0 ; y < ny; y++) {
01545                 _mzx[x] += _mzraw[x * ny + y];
01546             }
01547 
01548             /*
01549              * Maximum value of summ
01550              */
01551 
01552             if (_mzx[x] > zxmax) {
01553                 zxmax = _mzx[x];
01554             }
01555         }
01556 
01557         GIDEBUG(cpl_image_save(mzx, "mzx.fits", -32, NULL, CPL_IO_DEFAULT));
01558 
01559         for (x = 0 ; x < nx; x++) {
01560 
01561             register cxdouble zxnorm = zxmax / _mzx[x];
01562 
01563             for (y = 0 ; y < ny; y++) {
01564                 _mznorm[x * ny + y] = _mzraw[x * ny + y] * zxnorm;
01565             }
01566 
01567         }
01568 
01569         cpl_image_delete(mzx);
01570         mz = mznorm;
01571     }
01572     else {
01573 
01574         /*
01575          * Use pixel values as they are
01576          */
01577 
01578         cpl_msg_info(_task, "Using raw spectra for localization");
01579         mz = mzraw;
01580     }
01581 
01582 
01583     /*
01584      * Full localization: takes care of all spectra
01585      */
01586 
01587     nspectra = cpl_table_get_nrow(fibers);
01588 
01589     mxok = cpl_matrix_new(nx, 1);
01590     myup = cpl_matrix_new(nx, nspectra);
01591     mylo = cpl_matrix_new(nx, nspectra);
01592 
01593     config->xbin = CX_MAX(config->xbin, 0);
01594     config->xbin = 2 * (config->xbin / 2);  /* make it an even value */
01595 
01596     GIDEBUG(cpl_image_save(mz, "mz.fits", -32, NULL, CPL_IO_DEFAULT));
01597 
01598     nxok = _giraffe_build_raw_mask(mz, bpixel, nspectra, noise, config,
01599                                    &ndetect, mxok, myup, mylo);
01600 
01601     if (nxok < 0) {
01602         switch (nxok) {
01603             case -1:
01604                 cpl_msg_warning(_task, "Invalid number of spectra detected: "
01605                                 "%d != %d", ndetect, nspectra);
01606                 break;
01607 
01608             case -2:
01609                 cpl_msg_warning(_task, "No abcissa with good number "
01610                                 "of spectra");
01611                 break;
01612 
01613             default:
01614                 cpl_msg_warning(_task, "Error while searching for spectra");
01615         }
01616 
01617         return nxok;
01618     }
01619 
01620 
01621     /*
01622      * Only takes care of good values
01623      */
01624 
01625     cpl_matrix_resize(mxok, 0, nxok - cpl_matrix_get_nrow(mxok), 0, 0);
01626     cpl_matrix_resize(myup, 0, nxok - cpl_matrix_get_nrow(myup), 0, 0);
01627     cpl_matrix_resize(mylo, 0, nxok - cpl_matrix_get_nrow(mylo), 0, 0);
01628 
01629     GIDEBUG(gi_message("%s: mxok[0-%d]=[%g-%g]", __func__,
01630                        cpl_matrix_get_nrow(mxok) - 1,
01631                        cpl_matrix_get_min(mxok),
01632                        cpl_matrix_get_max(mxok)));
01633 
01634 
01635     cpl_msg_info(_task, "Computing spectrum positions and widths in "
01636                  "pixel range [%g,%g]", cpl_matrix_get_min(mxok),
01637                  cpl_matrix_get_max(mxok));
01638 
01639     if (cpl_matrix_get_nrow(mxok) <= config->ydeg) {
01640         cpl_msg_info(_task, "Not enough data points %d for %d order fit",
01641                      cpl_matrix_get_nrow(mxok), config->ydeg);
01642 
01643         return -1;
01644     }
01645 
01646     switch (method) {
01647         case GILOCALIZE_HALF_WIDTH:
01648             cpl_msg_info(_task, "Using half-width for localization");
01649             _giraffe_fit_raw_mask(mxok, myup, mylo, fibers, config,
01650                                   position);
01651             break;
01652 
01653         case GILOCALIZE_BARYCENTER:
01654         default:
01655             cpl_msg_info(_task, "Using barycenter for localization");
01656             _giraffe_fit_raw_centroid(mz, mxok, myup, mylo, fibers, config,
01657                                       position, coeffs);
01658             break;
01659     }
01660 
01661     if (normalize == 1) {
01662         cpl_image_delete(mznorm);
01663     }
01664 
01665     /*
01666      * Compute the number of pixels rejected by the fit
01667      */
01668 
01669 
01670     /* FIXME: Here again nspectra equals cpl_table_get_nrow(fibers),
01671      *        where OGL used mButtons->nr. We have to check the
01672      *        correctness carefully here !!
01673      */
01674 
01675     mwid = cpl_matrix_new(nx, nspectra);
01676 
01677     for (n = 0, nn = 0; nn < cpl_table_get_nrow(fibers); nn++) {
01678 
01679         for (x = 0; x < nxok; x++) {
01680             register cxint lx = (cxint) cpl_matrix_get(mxok, x, 0);
01681 
01682             cxdouble lower = cpl_matrix_get(mylo, x, n);
01683             cxdouble upper = cpl_matrix_get(myup, x, n);
01684             cxdouble width = cpl_matrix_get(position->mw, lx, nn);
01685 
01686             uplost += cpl_matrix_get(position->my, lx, nn) + width - upper;
01687             lolost += cpl_matrix_get(position->my, lx, nn) - width - lower;
01688 
01689             avgborders += upper - lower;
01690             avgmask += width;
01691 
01692             cpl_matrix_set(mwid, x, n, 2. * width);
01693         }
01694         n++;
01695     }
01696 
01697     sigmean = cpl_matrix_get_mean(mwid);
01698     sigmask = giraffe_matrix_sigma_mean(mwid, sigmean);
01699     avglost = (lolost + uplost) / (nspectra * nxok);
01700     avgmask = 2.0 * avgmask / nspectra;
01701 
01702     cpl_msg_info(_task, "Mask was computed using %d of %d wavelength bins",
01703                  nxok, nx);
01704     cpl_msg_info(_task, "Average # of pixels per spectra: %.4g",
01705                  avgmask);
01706     cpl_msg_info(_task, "Average # of in-borders pixels per spectra: %.4g",
01707                  avgborders / nspectra);
01708     cpl_msg_info(_task, "Average lost pixels per spectra: %.4g",
01709                  avglost);
01710     cpl_msg_info(_task, "Average lost pixels at upper border: %.4g",
01711                  uplost / (nspectra * nxok));
01712     cpl_msg_info(_task, "Average lost pixels at lower border: %.4g",
01713                  lolost / (nspectra * nxok));
01714     cpl_msg_info(_task, "Average spectrum width: %.4g +/- %.4g, "
01715                  "(min, max) = (%.4g, %.4g)", sigmean, sigmask,
01716                  cpl_matrix_get_min(mwid), cpl_matrix_get_max(mwid));
01717 
01718     cpl_matrix_delete(mwid);
01719 
01720     cpl_matrix_delete(mylo);
01721     cpl_matrix_delete(myup);
01722     cpl_matrix_delete(mxok);
01723 
01724     return 0;
01725 
01726 }
01727 
01728 
01729 inline static cxint
01730 _giraffe_finalize_fibers(cpl_table *fibers, cpl_matrix *locy, GiImage *mlocy,
01731                          cxdouble maxoffset, cxdouble* maxshift)
01732 {
01733 
01734     cxint i = 0;
01735     cxint j = 0;
01736     cxint nx = 0;
01737     cxint ny = 0;
01738     cxint _nx = 0;
01739     cxint _ny = 0;
01740     cxint nfibers = 0;
01741     cxint irow = 0;
01742 
01743     cxdouble max_shift = 0.;
01744     cxdouble *positions = NULL;
01745 
01746     cpl_image *_mlocy = NULL;
01747 
01748 
01749     if (fibers == NULL || locy == NULL || mlocy == NULL) {
01750         return -1;
01751     }
01752 
01753     if (cpl_table_has_column(fibers, "RINDEX") == FALSE) {
01754         return -1;
01755     }
01756 
01757     nx = cpl_matrix_get_ncol(locy);
01758     ny = cpl_matrix_get_nrow(locy);
01759 
01760     nfibers = cpl_table_get_nrow(fibers);
01761 
01762     _mlocy = giraffe_image_get(mlocy);
01763     _nx = cpl_image_get_size_x(_mlocy);
01764     _ny = cpl_image_get_size_y(_mlocy);
01765 
01766     if (ny != _ny) {
01767         return -2;
01768     }
01769     
01770     if (nfibers > _nx) {
01771         return -3;
01772     }
01773 
01774     cpl_table_select_all(fibers);
01775 
01776 
01777     /*
01778      * Get pointer to the central scan line.
01779      */
01780 
01781     irow = (_ny - 1) / 2;
01782     positions = (cxdouble *)cpl_image_get_data(_mlocy) + irow * _nx;
01783 
01784 
01785     /*
01786      * Compare the detected fiber positions with the positions
01787      * from the reference localization. Select only those fibers
01788      * whose distance from the reference positions is less than
01789      * a given offset. All other fibers are removed from the
01790      * fiber table.
01791      */
01792 
01793     for (i = 0; i < nfibers; i++) {
01794 
01795         if (j < nx) {
01796 
01797             cxint pos = cpl_table_get_int(fibers, "RINDEX", i, NULL) - 1;
01798 
01799             cxdouble yc = cpl_matrix_get(locy, irow, j);
01800             cxdouble shift = fabs(yc - positions[pos]); 
01801 
01802             if (shift <= maxoffset) {
01803                 cpl_table_unselect_row(fibers, i);
01804                 ++j;
01805             }
01806             else {
01807                 max_shift = CX_MAX(max_shift, shift);
01808             }
01809 
01810         }
01811     }
01812 
01813     cpl_table_erase_selected(fibers);
01814 
01815     if (maxshift != NULL) {
01816         *maxshift = max_shift;
01817     }
01818     
01819     return 0;
01820 
01821 }
01822 
01823 
01852 cxint
01853 giraffe_localize_spectra(GiLocalization *result, GiImage *image,
01854                          GiTable *fibers, GiLocalization *master,
01855                          GiImage *badpixels, GiLocalizeConfig *config)
01856 {
01857 
01858     const cxchar *fctid = "giraffe_localize_spectra";
01859 
01860     cxint i;
01861     cxint status;
01862     cxint nrows;
01863     cxint nfibers;
01864     cxint ckwidth;
01865     cxint ckheight;
01866     cxint ckcount;
01867 
01868     cxdouble mwidth;
01869     cxdouble sigma;
01870 
01871     cx_string *pname;
01872 
01873     cpl_propertylist *properties;
01874 
01875     cpl_image *_image = giraffe_image_get(image);
01876     cpl_image *_bpixel = giraffe_image_get(badpixels);
01877     cpl_image *_result = NULL;
01878 
01879     cpl_matrix *_my;
01880 
01881     cpl_table *_fibers = NULL;
01882     cpl_table *fiber_setup = NULL;
01883     cpl_table *locc;
01884 
01885     GiLocalizeMethod method;
01886 
01887     GiInstrumentMode mode;
01888 
01889     GiMaskParameters mask_config;
01890 
01891     GiMaskPosition mask_position;
01892     GiMaskPosition mask_coeffs;
01893 
01894 
01895 
01896     /*
01897      * Preprocessing
01898      */
01899 
01900     if (result == NULL || image == NULL || fibers == NULL || config == NULL) {
01901         cpl_error_set(fctid, CPL_ERROR_NULL_INPUT);
01902         return 1;
01903     }
01904 
01905     if (badpixels != NULL) {
01906 #if 1
01907         cpl_msg_debug(fctid,"Bad pixel correction is not available. Bad "
01908                       "pixel map will be ignored.");
01909 #else
01910         gi_warning("%s: Bad pixel correction is not available. Bad "
01911                    "pixel map at %p will be ignored.", fctid, badpixels);
01912 #endif
01913     }
01914 
01915     _fibers = giraffe_table_get(fibers);
01916 
01917     if (_fibers == NULL) {
01918         cpl_error_set(fctid, CPL_ERROR_DATA_NOT_FOUND);
01919         return 1;
01920     }
01921     else {
01922         fiber_setup = _fibers;
01923     }
01924 
01925     properties = giraffe_image_get_properties(image);
01926 
01927 
01928     /*
01929      * Add the number of fibers to the image properties.
01930      */
01931 
01932     nfibers = cpl_table_get_nrow(_fibers);
01933 
01934     cpl_msg_info(fctid, "Setting number of fibers (%s) to %d",
01935                  GIALIAS_NFIBERS, nfibers);
01936 
01937     cpl_propertylist_update_int(properties, GIALIAS_NFIBERS, nfibers);
01938     cpl_propertylist_set_comment(properties, GIALIAS_NFIBERS,
01939                                  "Number of fibres");
01940 
01941 
01942     /*
01943      * If a ron value is provided write it to the image properties.
01944      */
01945 
01946     if (config->ron > 0.) {
01947         cpl_msg_info(fctid, "Setting bias sigma value (%s) to %.5g",
01948                      GIALIAS_BIASSIGMA, config->ron);
01949         cpl_propertylist_update_double(properties, GIALIAS_BIASSIGMA,
01950                                        config->ron);
01951     }
01952 
01953 
01954     sigma = cpl_propertylist_get_double(properties, GIALIAS_BIASSIGMA);
01955 
01956     if (cpl_propertylist_has(properties, GIALIAS_CONAD)) {
01957 
01958         cxdouble conad = cpl_propertylist_get_double(properties,
01959                                                      GIALIAS_CONAD);
01960 
01961 
01962         if (conad > 0.) {
01963             sigma *= conad;
01964         }
01965         else {
01966             cpl_msg_error(fctid, "Invalid conversion factor "
01967                           "(ADU to e-): %.2g", conad);
01968             return 1;
01969         }
01970 
01971     }
01972 
01973     cpl_msg_info(fctid, "Bias sigma value: %.3g e-", sigma);
01974 
01975     if (config->noise > 0.) {
01976         cpl_msg_info(fctid, "Noise multiplier: %.3g",
01977                      config->noise);
01978     }
01979     else {
01980         cpl_msg_info(fctid, "Threshold multiplier: %.3g",
01981                      fabs(config->noise));
01982     }
01983 
01984 
01985     /*
01986      * Setup localization start position in the dispersion direction.
01987      */
01988 
01989     nrows = cpl_image_get_size_y(_image);
01990 
01991     if (config->start < 0) {
01992         config->start = nrows / 2;
01993     }
01994 
01995     /*
01996      * Set instrument mode specific parameters like the width of a spectrum
01997      * and the width of the intra-spectrum gap.
01998      */
01999 
02000     mode = giraffe_get_mode(properties);
02001 
02002     if (config->ywidth < 1) {
02003 
02004         cpl_msg_info(fctid, "Configuring equilizing filter width from "
02005                      "instrument mode");
02006 
02007         switch (mode) {
02008             case GIMODE_MEDUSA:
02009                 config->ywidth = 16;
02010                 break;
02011 
02012             case GIMODE_IFU:
02013                 config->ywidth = 6;
02014                 break;
02015 
02016             case GIMODE_ARGUS:
02017                 config->ywidth = 6;
02018                 break;
02019 
02020             default:
02021                 cpl_msg_error(fctid, "Invalid instrument mode!");
02022                 return 1;
02023                 break;
02024         }
02025 
02026 
02027         if (!cpl_propertylist_has(properties, GIALIAS_SLITNAME)) {
02028             cpl_msg_error(fctid, "Property (%s) not found in raw image",
02029                           GIALIAS_SLITNAME);
02030             return 1;
02031         }
02032         else {
02033             const cxchar *slit =
02034                 cpl_propertylist_get_string(properties, GIALIAS_SLITNAME);
02035 
02036             cpl_msg_info(fctid, "Setting equilizing filter to %d [pxl] "
02037                          "for slit configuration `%s'", config->ywidth,
02038                          slit);
02039         }
02040 
02041     }
02042 
02043 
02044     /*
02045      * Set mean spectrum width according to the instrument mode
02046      */
02047 
02048     switch (mode) {
02049         case GIMODE_MEDUSA:
02050             mwidth = GISPECTRUM_MWIDTH_MEDUSA;
02051 
02052             ckwidth = 1;
02053             ckheight = 3;
02054             ckcount = 8;
02055 
02056             break;
02057 
02058         case GIMODE_IFU:
02059             mwidth = GISPECTRUM_MWIDTH_IFU;
02060 
02061             ckwidth = 0;
02062             ckheight = 3;
02063             ckcount = 4;
02064 
02065             break;
02066 
02067         case GIMODE_ARGUS:
02068             mwidth = GISPECTRUM_MWIDTH_IFU;
02069 
02070             ckwidth = 0;
02071             ckheight = 3;
02072             ckcount = 4;
02073 
02074             break;
02075 
02076         default:
02077             cpl_msg_error(fctid, "Invalid instrument mode!");
02078             return 1;
02079             break;
02080     }
02081 
02082 
02083     /*
02084      * Setup localization method
02085      */
02086 
02087     if (config->centroid == TRUE) {
02088         method = GILOCALIZE_BARYCENTER;
02089     }
02090     else {
02091         method = GILOCALIZE_HALF_WIDTH;
02092     }
02093 
02094 
02095     /*
02096      * Fill the parameter structure for the localization mask computation
02097      * with the actual values.
02098      */
02099 
02100     mask_config.ywidth = config->ywidth;
02101     mask_config.local = config->local;
02102     mask_config.threshold = config->noise;
02103     mask_config.ydeg = config->yorder;
02104     mask_config.wdeg = config->worder;
02105     mask_config.ewid = config->ewidth;
02106     mask_config.wavg = mwidth;
02107     mask_config.ckdata.width = ckwidth;
02108     mask_config.ckdata.height = ckheight;
02109     mask_config.ckdata.count = ckcount;
02110     mask_config.sigma = config->sigma;
02111     mask_config.niter = config->iterations;
02112     mask_config.mfrac = config->fraction;
02113     mask_config.start = config->start;
02114     mask_config.retry = config->retries;
02115     mask_config.xbin = config->binsize;
02116 
02117 
02118     /*
02119      * Processing
02120      */
02121 
02122     /*
02123      * Localize spectra. Depending on the setup we either do a full
02124      * localization or we just localize the simultaneous calibration
02125      * fibers using an existing, full master localization.
02126      */
02127 
02128     if (config->full != TRUE) {
02129 
02130         cpl_msg_info(fctid, "Computing spectrum localization using SIWC "
02131                      "spectra");
02132 
02133         if (!master || !master->locy || !master->locy) {
02134             cpl_msg_error(fctid, "Required full master localization is "
02135                           "missing!");
02136             return 1;
02137         }
02138 
02139 
02140         /*
02141          * Select SIWC fibers from the fiber table. The simultaneous
02142          * calibration spectra are indicated by a -1 as retractor position.
02143          */
02144 
02145         cpl_table_unselect_all(_fibers);
02146         cpl_table_or_selected_int(_fibers, "RP", CPL_EQUAL_TO, -1);
02147 
02148         fiber_setup = cpl_table_extract_selected(_fibers);
02149         nfibers = cpl_table_get_nrow(fiber_setup);
02150 
02151     }
02152 
02153 
02154     /*
02155      * Allocate required output matrices and hook them into the appropriate
02156      * structures.
02157      */
02158 
02159     mask_position.type = GIMASK_FITTED_DATA;
02160     mask_position.my = cpl_matrix_new(nrows, nfibers);
02161     mask_position.mw = cpl_matrix_new(nrows, nfibers);
02162 
02163     mask_coeffs.type = GIMASK_FIT_COEFFS;
02164     mask_coeffs.my = cpl_matrix_new(mask_config.ydeg + 1, nfibers);
02165     mask_coeffs.mw = cpl_matrix_new(1, (mask_config.wdeg + 1) *
02166                                     (mask_config.wdeg + 1));
02167 
02168 
02169     /*
02170      * Compute the position of the spectra on the CCD
02171      */
02172 
02173     status = _giraffe_localize_spectra(_image, _bpixel, fiber_setup,
02174                                        method, config->normalize, sigma,
02175                                        &mask_config, &mask_position,
02176                                        &mask_coeffs);
02177 
02178     if (status) {
02179         result->locy = NULL;
02180         result->locw = NULL;
02181         result->locc = NULL;
02182         result->psf = NULL;
02183 
02184         cpl_matrix_delete(mask_position.my);
02185         cpl_matrix_delete(mask_position.mw);
02186 
02187         cpl_matrix_delete(mask_coeffs.my);
02188         cpl_matrix_delete(mask_coeffs.mw);
02189 
02190         if (config->full != TRUE) {
02191             cpl_table_delete(fiber_setup);
02192         }
02193 
02194         cpl_msg_error(fctid, "Spectrum localization computation failed!");
02195 
02196         return 1;
02197     }
02198 
02199 
02200     /*
02201      * Post processing
02202      */
02203 
02204     if (config->full != TRUE) {
02205 
02206         /*
02207          * TBD: Postprocessing of localization. Compare computed spectrum
02208          *      locations with master, i.e. calculate differences.
02209          */
02210 
02211         cpl_table_delete(fiber_setup);
02212 
02213     }
02214     else {
02215 
02216         if (master != NULL && master->locy != NULL) {
02217 
02218             cxint nf = cpl_table_get_nrow(_fibers);
02219 
02220             cxdouble maxoffset = 0.5 * mask_config.wavg;
02221             cxdouble maxshift = 0.;
02222 
02223 
02224             cpl_msg_info(fctid, "Comparing detected and expected fiber "
02225                          "positions.");
02226 
02227             status = _giraffe_finalize_fibers(_fibers, mask_position.my,
02228                                               master->locy, maxoffset,
02229                                               &maxshift);
02230 
02231             if (status != 0) {
02232                 
02233                 if (status == -3) {
02234 
02235                     const cpl_image* mlocy = giraffe_image_get(master->locy);
02236                     cxint _nf = cpl_image_get_size_x(mlocy);
02237                     
02238                     cpl_msg_error(fctid, "More fibers (%d) than expected "
02239                             "(%d) were found!", nf, _nf);
02240                     
02241                 }
02242                 
02243                 result->locy = NULL;
02244                 result->locw = NULL;
02245                 result->locc = NULL;
02246                 result->psf = NULL;
02247 
02248                 cpl_matrix_delete(mask_position.my);
02249                 cpl_matrix_delete(mask_position.mw);
02250 
02251                 cpl_matrix_delete(mask_coeffs.my);
02252                 cpl_matrix_delete(mask_coeffs.mw);
02253 
02254                 if (config->full != TRUE) {
02255                     cpl_table_delete(fiber_setup);
02256                 }
02257 
02258                 cpl_msg_error(fctid, "Comparison of fiber positions "
02259                               "failed!");
02260 
02261                 return 1;
02262             }
02263 
02264             cx_assert(cpl_table_get_nrow(_fibers) <= nf);
02265 
02266             cpl_msg_info(fctid, "%d of %d expected fibers were detected.",
02267                          cpl_table_get_nrow(_fibers), nf);
02268 
02269             if (cpl_table_get_nrow(_fibers) < nf) {
02270                 cpl_msg_debug(fctid, "Maximum offset from the expected "
02271                         "position is %.2f, maximum allowed offset is %.2f",
02272                         maxshift, maxoffset);
02273                 cpl_msg_warning(fctid, "%d fibers are missing!",
02274                                 nf - cpl_table_get_nrow(_fibers));
02275             }
02276 
02277         }
02278 
02279     }
02280 
02281 
02282     /*
02283      * Convert matrices into images and tables and add the necessary
02284      * properties.
02285      */
02286 
02287     /* Spectrum center position */
02288 
02289     result->locy =
02290         giraffe_image_create(CPL_TYPE_DOUBLE,
02291                              cpl_matrix_get_ncol(mask_position.my),
02292                              cpl_matrix_get_nrow(mask_position.my));
02293 
02294     giraffe_image_copy_matrix(result->locy, mask_position.my);
02295     cpl_matrix_delete(mask_position.my);
02296 
02297     giraffe_image_set_properties(result->locy, properties);
02298     properties = giraffe_image_get_properties(result->locy);
02299 
02300     _result = giraffe_image_get(result->locy);
02301 
02302     cpl_propertylist_set_int(properties, GIALIAS_NAXIS1,
02303                              cpl_image_get_size_x(_result));
02304     cpl_propertylist_set_int(properties, GIALIAS_NAXIS2,
02305                              cpl_image_get_size_y(_result));
02306     cpl_propertylist_set_int(properties, GIALIAS_BITPIX, -32);
02307     cpl_propertylist_set_double(properties, GIALIAS_BZERO, 0.);
02308     cpl_propertylist_set_double(properties, GIALIAS_BSCALE, 1.);
02309 
02310     cpl_propertylist_append_int(properties, GIALIAS_LOCNX,
02311                                 cpl_image_get_size_y(_result));
02312     cpl_propertylist_append_int(properties, GIALIAS_LOCNS,
02313                                 cpl_image_get_size_x(_result));
02314 
02315     if (config->centroid) {
02316         cpl_propertylist_append_string(properties, GIALIAS_LMETHOD,
02317                                        "BARYCENTER");
02318     }
02319     else {
02320         cpl_propertylist_append_string(properties, GIALIAS_LMETHOD,
02321                                        "HALF_WIDTH");
02322     }
02323 
02324     if (config->normalize) {
02325         cpl_propertylist_append_int(properties, GIALIAS_LNORMALIZE,
02326                                     config->ywidth);
02327     }
02328     else {
02329         cpl_propertylist_append_int(properties, GIALIAS_LNORMALIZE,
02330                                     -config->ywidth);
02331     }
02332 
02333     cpl_propertylist_append_bool(properties, GIALIAS_LFULLLOC, config->full);
02334     cpl_propertylist_append_int(properties, GIALIAS_LOCYDEG, config->yorder);
02335     cpl_propertylist_append_int(properties, GIALIAS_LOCWDEG, config->worder);
02336     cpl_propertylist_append_double(properties, GIALIAS_LEXTRAWID,
02337                                    config->ewidth);
02338     cpl_propertylist_append_double(properties, GIALIAS_LNOISEMULT,
02339                                    config->noise);
02340 
02341     cpl_propertylist_append_double(properties, GIALIAS_LCLIPSIGMA,
02342                                    config->sigma);
02343     cpl_propertylist_append_int(properties, GIALIAS_LCLIPNITER,
02344                                 config->iterations);
02345     cpl_propertylist_append_double(properties, GIALIAS_LCLIPMFRAC,
02346                                    config->fraction);
02347 
02348 
02349     if (cpl_propertylist_has(properties, GIALIAS_GIRFTYPE)) {
02350         cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE, "LOCY");
02351     }
02352     else {
02353         cpl_propertylist_append_string(properties, GIALIAS_GIRFTYPE, "LOCY");
02354     }
02355     cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE, "GIRAFFE "
02356                                  "localization centroid");
02357 
02358 
02359     /* Spectrum width */
02360 
02361     result->locw =
02362         giraffe_image_create(CPL_TYPE_DOUBLE,
02363                              cpl_matrix_get_ncol(mask_position.mw),
02364                              cpl_matrix_get_nrow(mask_position.mw));
02365 
02366     giraffe_image_copy_matrix(result->locw, mask_position.mw);
02367     cpl_matrix_delete(mask_position.mw);
02368 
02369     giraffe_image_set_properties(result->locw, properties);
02370     properties = giraffe_image_get_properties(result->locw);
02371 
02372     _result = giraffe_image_get(result->locw);
02373 
02374     cpl_propertylist_set_int(properties, GIALIAS_NAXIS1,
02375                              cpl_image_get_size_x(_result));
02376     cpl_propertylist_set_int(properties, GIALIAS_NAXIS2,
02377                              cpl_image_get_size_y(_result));
02378 
02379     if (cpl_propertylist_has(properties, GIALIAS_GIRFTYPE)) {
02380         cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE,
02381                                     "LOCWY");
02382     }
02383     else {
02384         cpl_propertylist_append_string(properties, GIALIAS_GIRFTYPE,
02385                                        "LOCWY");
02386     }
02387     cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE, "GIRAFFE "
02388                                  "localization half-width");
02389 
02390 
02391     /* Coefficients table */
02392 
02393     locc = cpl_table_new(cpl_matrix_get_ncol(mask_coeffs.my));
02394 
02395     cpl_table_new_column(locc, "BUTTON", CPL_TYPE_INT);
02396     for (i = 0; i < cpl_table_get_nrow(locc); i++) {
02397         cpl_table_set_int(locc, "BUTTON", i, i);
02398     }
02399 
02400     for (i = 0; i < cpl_matrix_get_nrow(mask_coeffs.my); i++) {
02401         cxchar *label = NULL;
02402 
02403         cx_asprintf(&label, "YC%d", i);
02404         cpl_table_new_column(locc, label, CPL_TYPE_DOUBLE);
02405         cx_free(label);
02406     }
02407 
02408 
02409     result->locc = giraffe_table_create(locc, properties);
02410     cpl_table_delete(locc);
02411 
02412     _my = cpl_matrix_transpose_create(mask_coeffs.my);
02413     giraffe_table_copy_matrix(result->locc, "YC0", _my);
02414     cpl_matrix_delete(_my);
02415     cpl_matrix_delete(mask_coeffs.my);
02416 
02417     properties = giraffe_table_get_properties(result->locc);
02418 
02419 
02420     /* Add coefficients of the 2D fit to the table properties */
02421 
02422     pname = cx_string_new();
02423 
02424     for (i = 0; i < cpl_matrix_get_ncol(mask_coeffs.mw); i++) {
02425         cx_string_sprintf(pname, "%s%d", GIALIAS_LOCWIDCOEF, i);
02426         cpl_propertylist_append_double(properties, cx_string_get(pname),
02427                                        cpl_matrix_get(mask_coeffs.mw, 0, i));
02428     }
02429 
02430     cx_string_delete(pname);
02431     cpl_matrix_delete(mask_coeffs.mw);
02432 
02433     cpl_propertylist_update_string(properties, GIALIAS_GIRFTYPE,
02434                                    "LOCYWCHEB");
02435     cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE, "GIRAFFE "
02436                                  "localization fit coefficients");
02437 
02438 
02439     /* Not used */
02440 
02441     result->psf = NULL;
02442 
02443     return 0;
02444 
02445 }
02446 
02447 
02458 GiLocalizeConfig *
02459 giraffe_localize_config_create(cpl_parameterlist *list)
02460 {
02461 
02462     const cxchar *s;
02463     cpl_parameter *p;
02464 
02465     GiLocalizeConfig *config = NULL;
02466 
02467 
02468     if (list == NULL) {
02469         return NULL;
02470     }
02471 
02472     config = cx_calloc(1, sizeof *config);
02473 
02474 
02475     /*
02476      * Some defaults
02477      */
02478 
02479     config->full = TRUE;
02480     config->centroid = TRUE;
02481     config->local = TRUE;
02482 
02483 
02484     p = cpl_parameterlist_find(list, "giraffe.localization.mode");
02485     s = cpl_parameter_get_string(p);
02486     if (strcmp(s, "siwc") == 0) {
02487         config->full = FALSE;
02488     }
02489 
02490     p = cpl_parameterlist_find(list, "giraffe.localization.start");
02491     config->start = cpl_parameter_get_int(p);
02492 
02493     p = cpl_parameterlist_find(list, "giraffe.localization.retries");
02494     config->retries = cpl_parameter_get_int(p);
02495 
02496     p = cpl_parameterlist_find(list, "giraffe.localization.binsize");
02497     config->binsize = cpl_parameter_get_int(p);
02498 
02499     p = cpl_parameterlist_find(list, "giraffe.localization.ewidth");
02500     config->ewidth = cpl_parameter_get_double(p);
02501 
02502     p = cpl_parameterlist_find(list, "giraffe.localization.ywidth");
02503     config->ywidth = cpl_parameter_get_int(p);
02504 
02505     p = cpl_parameterlist_find(list, "giraffe.localization.center");
02506     s = cpl_parameter_get_string(p);
02507     if (!strcmp(s, "hwidth")) {
02508         config->centroid = FALSE;
02509     }
02510 
02511     p = cpl_parameterlist_find(list, "giraffe.localization.normalize");
02512     config->normalize = cpl_parameter_get_bool(p);
02513 
02514     p = cpl_parameterlist_find(list, "giraffe.localization.threshold");
02515     s = cpl_parameter_get_string(p);
02516     if (!strcmp(s, "global")) {
02517         config->local = FALSE;
02518     }
02519 
02520     p = cpl_parameterlist_find(list, "giraffe.localization.noise");
02521     config->noise = cpl_parameter_get_double(p);
02522 
02523     p = cpl_parameterlist_find(list, "giraffe.localization.ron");
02524     config->ron = cpl_parameter_get_double(p);
02525 
02526     p = cpl_parameterlist_find(list, "giraffe.localization.yorder");
02527     config->yorder = cpl_parameter_get_int(p);
02528 
02529     p = cpl_parameterlist_find(list, "giraffe.localization.worder");
02530     config->worder = cpl_parameter_get_int(p);
02531 
02532     p = cpl_parameterlist_find(list, "giraffe.localization.sigma");
02533     config->sigma = cpl_parameter_get_double(p);
02534 
02535     p = cpl_parameterlist_find(list, "giraffe.localization.iterations");
02536     config->iterations = cpl_parameter_get_int(p);
02537 
02538     p = cpl_parameterlist_find(list, "giraffe.localization.fraction");
02539     config->fraction = cpl_parameter_get_double(p);
02540 
02541     return config;
02542 
02543 }
02544 
02545 
02558 void
02559 giraffe_localize_config_destroy(GiLocalizeConfig *config)
02560 {
02561 
02562     if (config) {
02563         cx_free(config);
02564     }
02565 
02566     return;
02567 
02568 }
02569 
02570 
02582 void
02583 giraffe_localize_config_add(cpl_parameterlist *list)
02584 {
02585 
02586     cpl_parameter *p;
02587 
02588 
02589     if (list == NULL) {
02590         return;
02591     }
02592 
02593     p = cpl_parameter_new_enum("giraffe.localization.mode",
02594                                CPL_TYPE_STRING,
02595                                "Localization mode: Use all spectra "
02596                                "or the 5 SIWC spectra",
02597                                "giraffe.localization",
02598                                "all", 2, "all", "siwc");
02599     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-mode");
02600     cpl_parameterlist_append(list, p);
02601 
02602 
02603     p = cpl_parameter_new_value("giraffe.localization.start",
02604                                 CPL_TYPE_INT,
02605                                 "Bin along x-axis",
02606                                 "giraffe.localization",
02607                                 -1);
02608     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-start");
02609     cpl_parameterlist_append(list, p);
02610 
02611 
02612     p = cpl_parameter_new_value("giraffe.localization.retries",
02613                                 CPL_TYPE_INT,
02614                                 "Initial localization detection "
02615                                 "xbin retries.",
02616                                 "giraffe.localization",
02617                                 10);
02618     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-retries");
02619     cpl_parameterlist_append(list, p);
02620 
02621 
02622     p = cpl_parameter_new_value("giraffe.localization.binsize",
02623                                 CPL_TYPE_INT,
02624                                 "Initial localization detection "
02625                                 "xbin size.",
02626                                 "giraffe.localization",
02627                                 -1);
02628     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-binsize");
02629     cpl_parameterlist_append(list, p);
02630 
02631 
02632     p = cpl_parameter_new_value("giraffe.localization.ewidth",
02633                                 CPL_TYPE_DOUBLE,
02634                                 "Localization detection extra width.",
02635                                 "giraffe.localization",
02636                                 1.0);
02637     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-ewidth");
02638     cpl_parameterlist_append(list, p);
02639 
02640 
02641     p = cpl_parameter_new_value("giraffe.localization.ywidth",
02642                                 CPL_TYPE_INT,
02643                                 "Full width [pxl] of the equilizing "
02644                                 "filter (distance between two "
02645                                 "adjacent fibers).",
02646                                 "giraffe.localization",
02647                                 -1);
02648     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-ywidth");
02649     cpl_parameterlist_append(list, p);
02650 
02651 
02652     p = cpl_parameter_new_enum("giraffe.localization.center",
02653                                CPL_TYPE_STRING,
02654                                "Method used for mask center "
02655                                "computation.",
02656                                "giraffe.localization",
02657                                "centroid", 2, "centroid",
02658                                "hwidth");
02659     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-center");
02660     cpl_parameterlist_append(list, p);
02661 
02662 
02663     p = cpl_parameter_new_value("giraffe.localization.normalize",
02664                                 CPL_TYPE_BOOL,
02665                                 "Enable spectrum normalization along "
02666                                 "the dispersion axis.",
02667                                 "giraffe.localization",
02668                                 FALSE);
02669     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-norm");
02670     cpl_parameterlist_append(list, p);
02671 
02672 
02673     p = cpl_parameter_new_value("giraffe.localization.noise",
02674                                 CPL_TYPE_DOUBLE,
02675                                 "Threshold multiplier.",
02676                                 "giraffe.localization",
02677                                 5.0);
02678     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-noise");
02679     cpl_parameterlist_append(list, p);
02680 
02681 
02682     p = cpl_parameter_new_enum("giraffe.localization.threshold",
02683                                CPL_TYPE_STRING,
02684                                "Selects thresholding algorithm: local or "
02685                                "global",
02686                                "giraffe.localization",
02687                                "local", 2, "local", "global");
02688     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-threshold");
02689     cpl_parameterlist_append(list, p);
02690 
02691 
02692     p = cpl_parameter_new_value("giraffe.localization.ron",
02693                                 CPL_TYPE_DOUBLE,
02694                                 "New bias sigma (RON) value for dark "
02695                                 "subtraction",
02696                                 "giraffe.localization",
02697                                 -1.);
02698     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-ron");
02699     cpl_parameterlist_append(list, p);
02700 
02701 
02702     p = cpl_parameter_new_value("giraffe.localization.yorder",
02703                                 CPL_TYPE_INT,
02704                                 "Order of Chebyshev polynomial fit.",
02705                                 "giraffe.localization",
02706                                 4);
02707     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-yorder");
02708     cpl_parameterlist_append(list, p);
02709 
02710 
02711     p = cpl_parameter_new_value("giraffe.localization.worder",
02712                                 CPL_TYPE_INT,
02713                                 "Order of Chebyshev 2D polynomial fit.",
02714                                 "giraffe.localization",
02715                                 2);
02716     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-worder");
02717     cpl_parameterlist_append(list, p);
02718 
02719 
02720     p = cpl_parameter_new_value("giraffe.localization.sigma",
02721                                 CPL_TYPE_DOUBLE,
02722                                 "Localization clipping: sigma threshold "
02723                                 "factor",
02724                                 "giraffe.localization",
02725                                 2.5);
02726     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-sigma");
02727     cpl_parameterlist_append(list, p);
02728 
02729 
02730     p = cpl_parameter_new_value("giraffe.localization.iterations",
02731                                 CPL_TYPE_INT,
02732                                 "Localization clipping: number of "
02733                                 "iterations",
02734                                 "giraffe.localization",
02735                                 5);
02736     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-niter");
02737     cpl_parameterlist_append(list, p);
02738 
02739 
02740     p = cpl_parameter_new_range("giraffe.localization.fraction",
02741                                 CPL_TYPE_DOUBLE,
02742                                 "Localization clipping: minimum fraction "
02743                                 "of points accepted/total.",
02744                                 "giraffe.localization",
02745                                 0.9, 0.0, 1.0);
02746     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sloc-mfrac");
02747     cpl_parameterlist_append(list, p);
02748 
02749     return;
02750 
02751 }

This file is part of the GIRAFFE Pipeline Reference Manual 2.5.3.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Fri Jul 18 09:49:46 2008 by doxygen 1.4.6 written by Dimitri van Heesch, © 1997-2004