irplib_slitpos.c

00001 /* $Id: irplib_slitpos.c,v 1.30 2011/11/23 13:58:45 yjung Exp $
00002  *
00003  * This file is part of the irplib package
00004  * Copyright (C) 2002,2003 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  02111-1307  USA
00019  */
00020 
00021 /*
00022  * $Author: yjung $
00023  * $Date: 2011/11/23 13:58:45 $
00024  * $Revision: 1.30 $
00025  * $Name: HEAD $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 /* The IRPLIB-based application may have checked for the availability of
00033    memrchr() in which case the macro HAVE_DECL_MEMRCHR is defined as either
00034    0 or 1. Without checks it is assumed that the function is not available.
00035    With a suitable version of autoconf the macro can be defined with this
00036    entry in configure.ac:
00037    AC_CHECK_DECLS([memrchr])
00038 */
00039 
00040 /*-----------------------------------------------------------------------------
00041                                 Includes
00042  -----------------------------------------------------------------------------*/
00043 
00044 #include <string.h>
00045 #include <math.h>
00046 #include <assert.h>
00047 #include <cpl.h>
00048 
00049 #include "irplib_slitpos.h"
00050 #include "irplib_flat.h"
00051 
00052 /*-----------------------------------------------------------------------------
00053                                 Defines
00054  -----------------------------------------------------------------------------*/
00055 
00056 /* TEMPORARY SUPPORT OF CPL 5.x */
00057 #ifndef CPL_SIZE_FORMAT
00058 #define CPL_SIZE_FORMAT "d"
00059 #define cpl_size int
00060 #endif
00061 /* END TEMPORARY SUPPORT OF CPL 5.x */
00062 
00063 #ifndef IRPLIB_SLITPOS_KERNEL_SIZE_Y
00064 #define IRPLIB_SLITPOS_KERNEL_SIZE_Y      5
00065 #endif
00066 
00067 #ifndef IRPLIB_SLITPOS_MAX_EROSION
00068 #define IRPLIB_SLITPOS_MAX_EROSION     1024
00069 #endif
00070 
00071 /*-----------------------------------------------------------------------------
00072                             Functions prototypes
00073  -----------------------------------------------------------------------------*/
00074 
00075 static cpl_error_code irplib_slitpos_find_edges_one_line(const cpl_image *,
00076                                                          int, int *, int *);
00077 static cpl_error_code irplib_slitpos_find_vert_slit_ends(const cpl_image *,
00078                                                          int, int *, int *);
00079 static cpl_error_code irplib_slitpos_find_vert_pos(const cpl_image *, int,
00080                                                    cpl_size *);
00081 static cpl_error_code irplib_image_filter_background_line(cpl_image *,
00082                                                    const cpl_image *,
00083                                                    int, cpl_boolean) ;
00084 
00085 /*----------------------------------------------------------------------------*/
00089 /*----------------------------------------------------------------------------*/
00090 
00092 /*----------------------------------------------------------------------------*/
00118 /*----------------------------------------------------------------------------*/
00119 cpl_table * irplib_slitpos_analysis(const cpl_image * imslit,
00120                                     int               slit_max_width,
00121                                     double          * slit_flux)
00122 {
00123     const int       size_x = cpl_image_get_size_x(imslit);
00124     const int       size_y = cpl_image_get_size_y(imslit);
00125     int             slit_length;
00126     cpl_size        slit_pos;
00127     cpl_image   *   filtered;
00128     cpl_mask    *   mask;
00129     cpl_image   *   thin_im;
00130     int             slit_top_y = 0; /* Avoid (false) uninit warning */
00131     int             slit_bot_y = 0; /* Avoid (false) uninit warning */
00132     cpl_table   *   self;
00133     double      *   slit_y,
00134                 *   slit_x_l,
00135                 *   slit_x_r;
00136     double      *   coeff_r;
00137     double      *   coeff_l;
00138     int             i;
00139     cpl_error_code error = CPL_ERROR_NONE;
00140 
00141     /* Initialize */
00142     if (slit_flux != NULL) *slit_flux = 0.0 ;
00143 
00144     /* Median vertical filtering 3x3 */
00145     mask = cpl_mask_new(3, 3) ;
00146     cpl_mask_not(mask) ;
00147     filtered = cpl_image_new(size_x, size_y, cpl_image_get_type(imslit));
00148     error = cpl_image_filter_mask(filtered, imslit, mask,
00149                                   CPL_FILTER_MEDIAN, CPL_BORDER_FILTER);
00150     cpl_mask_delete(mask);
00151 
00152     if (error) {
00153         cpl_image_delete(filtered);
00154         cpl_ensure(0, cpl_error_get_code(), NULL);
00155     }
00156 
00157     /* The background may vary strongly along the vertical line. */
00158     /* Detect and remove background with a 1+2*Slit_max x 1 median filter */
00159     error = irplib_image_filter_background_line(filtered, NULL, slit_max_width,
00160                                                 CPL_TRUE);
00161 
00162     if (error) {
00163         cpl_image_delete(filtered) ;
00164         cpl_ensure(0, cpl_error_get_code(), NULL);
00165     }
00166 
00167     /* Find the position of the slit */
00168     if (irplib_slitpos_find_vert_pos(filtered, slit_max_width/2, &slit_pos)) {
00169         cpl_image_delete(filtered);
00170         cpl_msg_error(cpl_func, "Could not find the slit position");
00171         cpl_ensure(0, cpl_error_get_code(), NULL);
00172     }
00173 
00174     /* Extract a thin image containing the slit */
00175     thin_im = cpl_image_extract(filtered, slit_pos-slit_max_width/2, 1,
00176                                 slit_pos+slit_max_width/2, size_y);
00177     if (thin_im == NULL) {
00178         cpl_msg_error(cpl_func, "Could not extract the %d pixel thin image "
00179                       "around position %"CPL_SIZE_FORMAT, 
00180                       slit_max_width, slit_pos);
00181         cpl_image_delete(filtered);
00182         cpl_ensure(0, cpl_error_get_code(), NULL);
00183     }
00184 
00185     /* Find the ends of the slit */
00186     error = irplib_slitpos_find_vert_slit_ends(thin_im,
00187                                                IRPLIB_SLITPOS_KERNEL_SIZE_Y,
00188                                                &slit_bot_y,
00189                                                &slit_top_y);
00190     cpl_image_delete(thin_im);
00191     if (error) {
00192         cpl_image_delete(filtered);
00193         cpl_ensure(0, cpl_error_get_code(), NULL);
00194     }
00195 
00196     /* Extract an image with exactly the slit */
00197     thin_im = cpl_image_extract(filtered,
00198                                 slit_pos-slit_max_width/2,
00199                                 slit_bot_y,
00200                                 slit_pos+slit_max_width/2,
00201                                 slit_top_y);
00202     cpl_image_delete(filtered);
00203 
00204     cpl_ensure(thin_im != NULL, cpl_error_get_code(), NULL);
00205 
00206     slit_length = 1 + slit_top_y - slit_bot_y;
00207 
00208     /* Allocate some arrays */
00209     slit_y = cpl_malloc(slit_length * sizeof(double));
00210     slit_x_l = cpl_malloc(slit_length * sizeof(double));
00211     slit_x_r = cpl_malloc(slit_length * sizeof(double));
00212     
00213     /* Find the edges of the slit */
00214     for (i=0 ; i<slit_length ; i++) {
00215         int right_pos = 0; /* Avoid (false) uninit warning */
00216         int left_pos  = 0; /* Avoid (false) uninit warning */
00217 
00218         if (irplib_slitpos_find_edges_one_line(thin_im,
00219                                                 i,
00220                                                 &left_pos,
00221                                                 &right_pos)) {
00222             cpl_msg_error(cpl_func, "cannot find the edges of the [%d]th line", 
00223                     i+1);
00224             cpl_image_delete(thin_im);
00225             return NULL;
00226         }
00227 
00228         /* Update the slit_flux */
00229         if (slit_flux != NULL) {
00230             *slit_flux += cpl_image_get_flux_window(thin_im, left_pos+1,
00231                     i+1, right_pos+1, i+1) ;
00232         }
00233         
00234         /* Store the edges for the fit */
00235         slit_x_l[i] = (double)left_pos;
00236         slit_x_r[i] = (double)right_pos;
00237         slit_y[i]   = (double)(i+slit_bot_y-1);
00238     }
00239     cpl_image_delete(thin_im);
00240 
00241     /* Linear regression to find the edges */
00242     coeff_l = irplib_flat_fit_slope_robust(slit_y, slit_x_l, slit_length);
00243     coeff_r = irplib_flat_fit_slope_robust(slit_y, slit_x_r, slit_length);
00244     cpl_free(slit_y);
00245     cpl_free(slit_x_l);
00246     cpl_free(slit_x_r);
00247 
00248     /* Allocate the table containing the results */
00249     self = cpl_table_new(slit_length);
00250     error |= cpl_table_new_column(self, "SLIT_Y",      CPL_TYPE_INT);
00251     error |= cpl_table_new_column(self, "SLIT_LEFT",   CPL_TYPE_DOUBLE);
00252     error |= cpl_table_new_column(self, "SLIT_CENTER", CPL_TYPE_DOUBLE);
00253     error |= cpl_table_new_column(self, "SLIT_RIGHT",  CPL_TYPE_DOUBLE);
00254 
00255     error |= cpl_table_set_column_unit(self, "SLIT_Y", "pixel");
00256     error |= cpl_table_set_column_unit(self, "SLIT_LEFT", "pixel");
00257     error |= cpl_table_set_column_unit(self, "SLIT_CENTER", "pixel");
00258     error |= cpl_table_set_column_unit(self, "SLIT_RIGHT", "pixel");
00259 
00260     cpl_ensure(!error, cpl_error_get_code(), NULL);
00261 
00262     /* Rewrite the edges in the out table, and write the center */
00263     for (i=0 ; i < slit_length ; i++) {
00264         const int    islity = i + slit_bot_y;
00265         const double dslit  = slit_pos - slit_max_width / 2.0;
00266         const double dleft  = coeff_l[0] + coeff_l[1] * (double)islity + dslit;
00267         const double dright = coeff_r[0] + coeff_r[1] * (double)islity + dslit;
00268         const double dcent  = 0.5 * (dleft + dright);
00269 
00270         if (cpl_table_set_int(self,    "SLIT_Y",      i, islity)) break;
00271         if (cpl_table_set_double(self, "SLIT_LEFT",   i, dleft))  break;
00272         if (cpl_table_set_double(self, "SLIT_RIGHT",  i, dright)) break;
00273         if (cpl_table_set_double(self, "SLIT_CENTER", i, dcent))  break;
00274     }
00275 
00276     cpl_free(coeff_r);
00277     cpl_free(coeff_l);
00278 
00279     if (i != slit_length) {
00280         cpl_table_delete(self);
00281         cpl_ensure(0, cpl_error_get_code(), NULL);
00282     }
00283 
00284     return self;
00285 }
00286 
00289 /*----------------------------------------------------------------------------*/
00301 /*----------------------------------------------------------------------------*/
00302 static cpl_error_code irplib_slitpos_find_edges_one_line(const cpl_image * self,
00303                                                          int          line_pos,
00304                                                          int        * left_pos,
00305                                                          int        * right_pos)
00306 {
00307     const int     size_x = cpl_image_get_size_x(self);
00308     const float * pself;
00309     double        threshold;
00310     int           i;
00311 
00312     cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
00313     cpl_ensure_code(cpl_image_get_type(self) == CPL_TYPE_FLOAT,
00314                     CPL_ERROR_INVALID_TYPE);
00315 
00316     pself = cpl_image_get_data_float_const(self);
00317 
00318     /* Find the threshold */
00319     threshold = cpl_image_get_mean_window(self, 1, line_pos+1, size_x,
00320                                           line_pos+1);
00321 
00322     /* Detect the left edge */
00323     i = 0;
00324     while (i < size_x && pself[line_pos*size_x+i] < threshold) i++;
00325     *left_pos = i;
00326 
00327     /* Detect the right edge */
00328     i = size_x - 1;
00329     while (i >= 0 && pself[line_pos*size_x+i] < threshold) i--;
00330     *right_pos = i;
00331 
00332     return CPL_ERROR_NONE;
00333 }
00334 
00335 /*----------------------------------------------------------------------------*/
00346 /*----------------------------------------------------------------------------*/
00347 static
00348 cpl_error_code irplib_slitpos_find_vert_slit_ends(const cpl_image * self,
00349                                                   int        kernel_size,
00350                                                   int      * bot_slit_y,
00351                                                   int      * top_slit_y)
00352 {
00353     cpl_mask         * binary;
00354     cpl_mask         * copy = NULL;
00355     cpl_mask         * kernel;
00356     cpl_image        * label_image;
00357     int                erosions_nb;
00358     cpl_size           nobj ;
00359     const int          size_x = cpl_image_get_size_x(self);
00360     const int          size_y = cpl_image_get_size_y(self);
00361     const int          npix = size_x * size_y;
00362     const cpl_binary * pbinary;
00363     const cpl_binary * pfind;
00364     int                i, itop, ibot;
00365 
00366 
00367     cpl_ensure_code(size_x      > 0, cpl_error_get_code());
00368     cpl_ensure_code(kernel_size > 0, cpl_error_get_code());
00369 
00370     /* Threshold to have a binary image */
00371     binary = cpl_mask_threshold_image_create(self, cpl_image_get_mean(self),
00372                                              cpl_image_get_max(self));
00373     cpl_ensure_code(binary != NULL, cpl_error_get_code());
00374 
00375     /* Erode until there is 1 object left in the image */
00376     label_image = cpl_image_labelise_mask_create(binary, &nobj);
00377     cpl_image_delete(label_image);
00378 
00379     if (label_image == NULL) {
00380         cpl_mask_delete(binary);
00381         cpl_ensure_code(0, cpl_error_get_code());
00382     }
00383 
00384     /* Define the kernel for morpho operations */
00385     kernel = cpl_mask_new(kernel_size, 1);
00386     cpl_mask_not(kernel);
00387     copy = cpl_mask_wrap(size_x, size_y, cpl_malloc(size_x * size_y *
00388                                                     sizeof(cpl_binary)));
00389     for (erosions_nb = 0; erosions_nb < IRPLIB_SLITPOS_MAX_EROSION && nobj > 1;
00390          erosions_nb++) {
00391         /* Should not be possible to break from this loop */
00392         cpl_mask_copy(copy, binary, 1, 1);
00393         if (cpl_mask_filter(binary, copy, kernel, CPL_FILTER_EROSION,
00394                             CPL_BORDER_ZERO)) break;
00395 
00396         label_image = cpl_image_labelise_mask_create(binary, &nobj);
00397         if (label_image == NULL) break; /* Assuming nobj was not set to 1 */
00398         cpl_image_delete(label_image);
00399     }
00400 
00401     if (nobj > 1) {
00402         cpl_mask_delete(binary);
00403         cpl_mask_delete(copy);
00404         cpl_mask_delete(kernel);
00405         if (erosions_nb >= IRPLIB_SLITPOS_MAX_EROSION) {
00406             cpl_msg_error(cpl_func, "Number of erosions reached a limit of %d "
00407                           "with %"CPL_SIZE_FORMAT" possible slits left",
00408                           IRPLIB_SLITPOS_MAX_EROSION, nobj);
00409             cpl_ensure_code(0, CPL_ERROR_CONTINUE);
00410         }
00411         cpl_ensure_code(0, cpl_error_get_code());
00412     } else if (nobj < 1) {
00413         cpl_mask_delete(binary);
00414         cpl_mask_delete(copy);
00415         cpl_mask_delete(kernel);
00416         if (erosions_nb == 0)
00417             cpl_msg_error(cpl_func, "No slit could be detected across %d "
00418                           "pixels", size_x);
00419         else 
00420             cpl_msg_error(cpl_func, "The last of %d erosions removed all the "
00421                           "possible slits", erosions_nb);
00422         cpl_ensure_code(0, CPL_ERROR_DATA_NOT_FOUND);
00423     }
00424 
00425     /* Reconstruct the slit with dilations */
00426     for (i=0 ; i < erosions_nb ; i++) {
00427         cpl_mask_copy(copy, binary, 1, 1);
00428         if (cpl_mask_filter(binary, copy, kernel, CPL_FILTER_DILATION,
00429                             CPL_BORDER_ZERO)) break;
00430     }
00431     cpl_mask_delete(copy);
00432     cpl_mask_delete(kernel);
00433 
00434     if (i != erosions_nb) {
00435         cpl_msg_error(cpl_func, "Dilation number %d out of %d failed",
00436                       i, erosions_nb);
00437         cpl_mask_delete(binary);
00438         cpl_ensure_code(0, cpl_error_get_code());
00439     }
00440 
00441     /* Find the ends of the slit */
00442     pbinary = cpl_mask_get_data(binary);
00443     assert( pbinary != NULL );
00444 
00445     pfind = memchr(pbinary, CPL_BINARY_1, (size_t)npix);
00446     assert( pfind != NULL );
00447 
00448     ibot = (int)(pfind - pbinary);
00449 
00450 #if defined HAVE_DECL_MEMRCHR && HAVE_DECL_MEMRCHR == 1
00451     /* FIXME: Not tested */
00452     pfind = memrchr(pfind, CPL_BINARY_1, (size_t)(npix - ibot));
00453     assert( pfind != NULL );
00454 
00455     itop = (int)(pfind - pbinary);
00456 #else
00457 
00458     itop = npix - 1;
00459     while (itop > ibot && pbinary[itop] != CPL_BINARY_1) itop--;
00460 
00461 #endif
00462 
00463     *bot_slit_y = 1 + ibot / size_x;
00464     *top_slit_y = 1 + itop / size_x;
00465 
00466     cpl_msg_info(cpl_func, 
00467             "Detected %"CPL_SIZE_FORMAT"-pixel slit from pixel %d to %d "
00468             "using %d erosions/dilations", cpl_mask_count(binary),
00469             *bot_slit_y, *top_slit_y, erosions_nb);
00470 
00471     cpl_mask_delete(binary);
00472 
00473     /* Should really be an assert() */
00474     cpl_ensure_code(ibot <= itop, CPL_ERROR_DATA_NOT_FOUND);
00475 
00476     return CPL_ERROR_NONE;
00477 }
00478 
00479 /*----------------------------------------------------------------------------*/
00489 /*----------------------------------------------------------------------------*/
00490 static cpl_error_code irplib_slitpos_find_vert_pos(const cpl_image * self,
00491                                                    int               xwidth,
00492                                                    cpl_size        * slit_pos)
00493 {
00494     const int       size_x = cpl_image_get_size_x(self);
00495     cpl_image   *   image1D;
00496     cpl_size        yone;
00497     cpl_error_code  error;
00498 
00499 
00500     /* Collapse the image to a horizontal 1D image */
00501     image1D = cpl_image_collapse_create(self, 0);
00502 
00503     cpl_ensure_code(image1D != NULL, cpl_error_get_code());
00504 
00505     /* Search the max of the 1D image to identify the slit position */
00506     error = cpl_image_get_maxpos_window(image1D, 1+xwidth, 1, size_x-xwidth,
00507                                         1, slit_pos, &yone);
00508 
00509     cpl_image_delete(image1D);
00510 
00511     cpl_ensure_code(!error, error);
00512 
00513     return CPL_ERROR_NONE;
00514 }
00515 
00516 /*----------------------------------------------------------------------------*/
00530 /*----------------------------------------------------------------------------*/
00531 static cpl_error_code irplib_image_filter_background_line(cpl_image * self,
00532                                                    const cpl_image * other,
00533                                                    int hsize,
00534                                                    cpl_boolean vertical)
00535 {
00536     const int      nx = cpl_image_get_size_x(self);
00537     const int      ny = cpl_image_get_size_y(self);
00538     const int      msize = 1 + 2 * hsize;
00539     cpl_mask     * mask;
00540     cpl_image    * background;
00541     cpl_error_code error = CPL_ERROR_NONE;
00542 
00543     cpl_ensure_code(self  != NULL, CPL_ERROR_NULL_INPUT);
00544     cpl_ensure_code(hsize >= 0,    CPL_ERROR_ILLEGAL_INPUT);
00545 
00546     if (other == NULL) other = self;
00547 
00548     mask = vertical ? cpl_mask_new(msize, 1) : cpl_mask_new(1, msize);
00549 
00550     error |= cpl_mask_not(mask);
00551 
00552     background = cpl_image_new(nx, ny, cpl_image_get_type(other));
00553 
00554     error |= cpl_image_filter_mask(background, other, mask, CPL_FILTER_MEDIAN,
00555                                    CPL_BORDER_FILTER);
00556     cpl_mask_delete(mask);
00557 
00558     if (self != other) {
00559         error |= cpl_image_copy(self, other, 1, 1);
00560     }
00561 
00562     error |= cpl_image_subtract(self, background);
00563     cpl_image_delete(background);
00564 
00565     return error ? cpl_error_set_where(cpl_func) : CPL_ERROR_NONE;
00566 }
00567 
00568 

Generated on 3 Mar 2013 for DETMON Pipeline Reference Manual by  doxygen 1.6.1