irplib_spectrum.c

00001 /* $Id: irplib_spectrum.c,v 1.24 2009/09/11 08:23:38 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: 2009/09/11 08:23:38 $
00024  * $Revision: 1.24 $
00025  * $Name: HEAD $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 /*-----------------------------------------------------------------------------
00033                                 Includes
00034  -----------------------------------------------------------------------------*/
00035 
00036 #include <math.h>
00037 #include <float.h>
00038 #include <cpl.h>
00039 
00040 #include "irplib_wlxcorr.h"
00041 #include "irplib_spectrum.h"
00042 
00043 /*-----------------------------------------------------------------------------
00044                                    Define
00045  -----------------------------------------------------------------------------*/
00046 
00047 #define SPECTRUM_HW                     16
00048 #define MIN_THRESH_FACT                 0.9
00049 #define MAX_THRESH_FACT                 1.1
00050 #define SPEC_SHADOW_FACT                30.0 /* Negative spectrum intensity*/
00051 #define SPEC_MAXWIDTH                   48
00052 
00053 /*-----------------------------------------------------------------------------
00054                             Functions prototypes
00055  -----------------------------------------------------------------------------*/
00056 
00057 static int select_valid_spectra(cpl_image *, cpl_apertures *, int,
00058         spec_shadows, int, int *, int **) ;
00059 static int valid_spectrum(cpl_image *, cpl_apertures *, int, spec_shadows, int,
00060         int) ;
00061 
00062 /*----------------------------------------------------------------------------*/
00066 /*----------------------------------------------------------------------------*/
00067 
00070 /*----------------------------------------------------------------------------*/
00085 /*----------------------------------------------------------------------------*/
00086 int irplib_spectrum_find_brightest(
00087         const cpl_image     *   in,
00088         int                     offset,
00089         spec_shadows            shadows,
00090         double                  min_bright,
00091         int                     orient,
00092         double              *   pos)
00093 {
00094     cpl_image       *   loc_ima ;
00095     cpl_image       *   filt_image ;
00096     cpl_image       *   collapsed ;
00097     float           *   pcollapsed ;
00098     cpl_vector      *   line ;
00099     double          *   pline ;
00100     cpl_vector      *   line_filt ;
00101     double              threshold ;
00102     double              median, stdev, max, mean ;
00103     cpl_mask        *   mask ;
00104     cpl_image       *   labels ;
00105     int                 nlabels ;
00106     cpl_apertures   *   aperts ;
00107     int                 n_valid_specs ;
00108     int             *   valid_specs ;
00109     double              brightness, brightest ;
00110     int                 i ;
00111 
00112     /* Test entries */
00113     if (in == NULL) return -1 ;
00114     if (orient!=0 && orient!=1) return -1 ;
00115 
00116     /* Flip the image if necessary */
00117     if (orient == 1) {
00118         loc_ima = cpl_image_duplicate(in) ;
00119         cpl_image_flip(loc_ima, 1) ;
00120     } else {
00121         loc_ima = cpl_image_duplicate(in) ;
00122     }
00123 
00124     /* Median vertical filtering 3x3 */
00125     mask = cpl_mask_new(3, 3) ;
00126     cpl_mask_not(mask) ;
00127     filt_image = cpl_image_new(
00128             cpl_image_get_size_x(loc_ima),
00129             cpl_image_get_size_y(loc_ima),
00130             cpl_image_get_type(loc_ima)) ;
00131     if (cpl_image_filter_mask(filt_image, loc_ima, mask,
00132                 CPL_FILTER_MEDIAN, CPL_BORDER_FILTER) != CPL_ERROR_NONE) {
00133         cpl_msg_error(__func__, "Cannot filter the image") ;
00134         cpl_mask_delete(mask) ;
00135         cpl_image_delete(filt_image) ;
00136         return -1 ;
00137     }
00138     cpl_mask_delete(mask) ;
00139     cpl_image_delete(loc_ima) ;
00140 
00141     /* Collapse the image */
00142     if ((collapsed = cpl_image_collapse_median_create(filt_image, 1, 0,
00143                     0)) == NULL) {
00144         cpl_msg_error(cpl_func, "collapsing image: aborting spectrum detection");
00145         cpl_image_delete(filt_image) ;
00146         return -1 ;
00147     }
00148     cpl_image_delete(filt_image) ;
00149 
00150     /* Subtract low frequency signal */
00151     line = cpl_vector_new_from_image_column(collapsed, 1) ;
00152     cpl_image_delete(collapsed) ;
00153     line_filt = cpl_vector_filter_median_create(line, SPECTRUM_HW) ;
00154     cpl_vector_subtract(line, line_filt) ;
00155     cpl_vector_delete(line_filt) ;
00156 
00157     /* Get relevant stats for thresholding */
00158     median = cpl_vector_get_median_const(line) ;
00159     stdev = cpl_vector_get_stdev(line) ;
00160     max = cpl_vector_get_max(line) ;
00161     mean = cpl_vector_get_mean(line) ;
00162 
00163     /* Set the threshold */
00164     threshold = median + stdev ;
00165     if (threshold > MIN_THRESH_FACT * max)  threshold = MIN_THRESH_FACT * max ;
00166     if (threshold < MAX_THRESH_FACT * mean) threshold = MAX_THRESH_FACT * mean;
00167 
00168     /* Recreate the image */
00169     collapsed = cpl_image_new(1, cpl_vector_get_size(line), CPL_TYPE_FLOAT) ;
00170     pcollapsed = cpl_image_get_data_float(collapsed) ;
00171     pline = cpl_vector_get_data(line) ;
00172     for (i=0 ; i<cpl_vector_get_size(line) ; i++)
00173         pcollapsed[i] = (float)pline[i] ;
00174     cpl_vector_delete(line) ;
00175 
00176     /* Binarise the image */
00177     if ((mask = cpl_mask_threshold_image_create(collapsed, threshold,
00178             DBL_MAX)) == NULL) {
00179         cpl_msg_error(cpl_func, "cannot binarise") ;
00180         cpl_image_delete(collapsed) ;
00181         return -1 ;
00182     }
00183     if (cpl_mask_count(mask) < 1) {
00184         cpl_msg_error(cpl_func, "not enough signal to detect spectra") ;
00185         cpl_image_delete(collapsed) ;
00186         cpl_mask_delete(mask) ;
00187         return -1 ;
00188     }
00189     /* Labelise the different detected apertures */
00190     if ((labels = cpl_image_labelise_mask_create(mask, &nlabels))==NULL) {
00191         cpl_msg_error(cpl_func, "cannot labelise") ;
00192         cpl_image_delete(collapsed) ;
00193         cpl_mask_delete(mask) ;
00194         return -1 ;
00195     }
00196     cpl_mask_delete(mask) ;
00197 
00198     /* Create the detected apertures list */
00199     if ((aperts = cpl_apertures_new_from_image(collapsed, labels)) == NULL) {
00200         cpl_msg_error(cpl_func, "cannot compute apertures") ;
00201         cpl_image_delete(collapsed) ;
00202         cpl_image_delete(labels) ;
00203         return -1 ;
00204     }
00205     cpl_image_delete(labels) ;
00206 
00207     /* Select only relevant specs, create corresponding LUT's */
00208     if (select_valid_spectra(collapsed, aperts, offset, shadows, SPEC_MAXWIDTH,
00209                 &n_valid_specs, &valid_specs) == -1) {
00210         cpl_msg_debug(cpl_func, "Could not select valid spectra from the %d "
00211                       "apertures in %d-col 1D-image, offset=%d, min_bright=%d",
00212                       cpl_apertures_get_size(aperts),
00213                       cpl_image_get_size_y(collapsed), offset, SPEC_MAXWIDTH);
00214         if (cpl_msg_get_level() <= CPL_MSG_DEBUG)
00215             cpl_apertures_dump(aperts, stderr);
00216         cpl_image_delete(collapsed);
00217         cpl_apertures_delete(aperts);
00218         return -1;
00219     }
00220     cpl_image_delete(collapsed) ;
00221     if (n_valid_specs < 1) {
00222         cpl_msg_error(cpl_func, "no valid spectrum detected") ;
00223         cpl_free(valid_specs) ;
00224         cpl_apertures_delete(aperts) ;
00225         return -1 ;
00226     }
00227 
00228     /* Look for the brightest, among the detected spectra */
00229     *pos = cpl_apertures_get_centroid_y(aperts, valid_specs[0]+1) ;
00230     brightest = valid_specs[0] ;
00231     brightness = cpl_apertures_get_flux(aperts, valid_specs[0]+1) ;
00232     for (i=0 ; i<n_valid_specs ; i++) {
00233         if (cpl_apertures_get_flux(aperts, valid_specs[i]+1) > brightness) {
00234             *pos = cpl_apertures_get_centroid_y(aperts, valid_specs[i]+1) ;
00235             brightest = valid_specs[i] ;
00236             brightness = cpl_apertures_get_flux(aperts, valid_specs[i]+1) ;
00237         }
00238     }
00239     cpl_apertures_delete(aperts) ;
00240     cpl_free(valid_specs) ;
00241 
00242     /* Minimum brightness required */
00243     if (brightness < min_bright) {
00244         cpl_msg_error(cpl_func, "brightness %f too low <%f", brightness,
00245                 min_bright) ;
00246         return -1 ;
00247     }
00248 
00249     /* Return */
00250     return 0 ;
00251 }
00252 
00253 /*----------------------------------------------------------------------------*/
00265 /*----------------------------------------------------------------------------*/
00266 cpl_vector * irplib_spectrum_detect_peaks(
00267         const cpl_vector    *   in,
00268         int                     fwhm,
00269         double                  sigma,
00270         int                     display,
00271         cpl_vector          **  fwhms_out,
00272         cpl_vector          **  areas_out)
00273 {
00274     cpl_vector      *   filtered ;
00275     cpl_vector      *   spec_clean ;
00276     cpl_vector      *   spec_convolved ;
00277     double          *   pspec_convolved ;
00278     int                 filt_size ;
00279     cpl_vector      *   conv_kernel ;
00280     cpl_vector      *   extract ;
00281     cpl_vector      *   extract_x ;
00282     cpl_vector      *   big_detected ;
00283     cpl_vector      *   big_fwhms ;
00284     cpl_vector      *   big_area ;
00285     double          *   pbig_detected ;
00286     double          *   pbig_fwhms ;
00287     double          *   pbig_area ;
00288     cpl_vector      *   detected ;
00289     double          *   pdetected ;
00290     cpl_vector      *   fwhms ;
00291     double          *   pfwhms ;
00292     cpl_vector      *   area ;
00293     double          *   parea ;
00294     double              max, med, stdev, cur_val ;
00295     double              x0, sig, norm, offset ;
00296     int                 nb_det, nb_samples, hwidth, start, stop ;
00297     int                 i, j ;
00298 
00299     /* Test entries */
00300     if (in == NULL) return NULL ;
00301 
00302     /* Initialise */
00303     nb_samples = cpl_vector_get_size(in) ;
00304     filt_size = 5 ;
00305     hwidth = 5 ;
00306 
00307     /* Subtract the low frequency part */
00308     cpl_msg_info(__func__, "Low Frequency signal removal") ;
00309     if ((filtered=cpl_vector_filter_median_create(in, filt_size))==NULL){
00310         cpl_msg_error(__func__, "Cannot filter the spectrum") ;
00311         return NULL ;
00312     }
00313     spec_clean = cpl_vector_duplicate(in) ;
00314     cpl_vector_subtract(spec_clean, filtered) ;
00315     cpl_vector_delete(filtered) ;
00316 
00317     /* Display if requested */
00318     if (display) {
00319         cpl_plot_vector(
00320     "set grid;set xlabel 'Position (pixels)';set ylabel 'Intensity (ADU)';",
00321         "t 'Filtered extracted spectrum' w lines", "", spec_clean);
00322     }
00323 
00324     /* Convolve */
00325     spec_convolved = cpl_vector_duplicate(spec_clean) ;
00326     if (fwhm > 0) {
00327         cpl_msg_info(__func__, "Spectrum convolution") ;
00328         /* Create convolution kernel */
00329         if ((conv_kernel = irplib_wlxcorr_convolve_create_kernel(fwhm,
00330                         fwhm)) == NULL) {
00331             cpl_msg_error(cpl_func, "Cannot create convolution kernel") ;
00332             cpl_vector_delete(spec_clean) ;
00333             cpl_vector_delete(spec_convolved) ;
00334             return NULL ;
00335         }
00336 
00337         /* Smooth the instrument resolution */
00338         if (irplib_wlxcorr_convolve(spec_convolved, conv_kernel)) {
00339             cpl_msg_error(cpl_func, "Cannot smoothe the signal");
00340             cpl_vector_delete(spec_clean) ;
00341             cpl_vector_delete(spec_convolved) ;
00342             cpl_vector_delete(conv_kernel) ;
00343             return NULL ;
00344         }
00345         cpl_vector_delete(conv_kernel) ;
00346 
00347         /* Display if requested */
00348         if (display) {
00349             cpl_plot_vector(
00350         "set grid;set xlabel 'Position (pixels)';set ylabel 'Intensity (ADU)';",
00351             "t 'Convolved extracted spectrum' w lines", "", spec_convolved);
00352         }
00353     }
00354 
00355     /* Apply the detection */
00356     big_detected = cpl_vector_duplicate(spec_convolved) ;
00357     big_fwhms = cpl_vector_duplicate(spec_convolved) ;
00358     big_area = cpl_vector_duplicate(spec_convolved) ;
00359     pbig_detected = cpl_vector_get_data(big_detected) ;
00360     pbig_fwhms = cpl_vector_get_data(big_fwhms) ;
00361     pbig_area = cpl_vector_get_data(big_area) ;
00362     
00363     pspec_convolved = cpl_vector_get_data(spec_convolved) ;
00364 
00365     /* To avoid detection on the side */
00366     pspec_convolved[0] = pspec_convolved[nb_samples-1] = 0.0 ;
00367 
00368     /* Compute stats */
00369     max     =   cpl_vector_get_max(spec_convolved) ;
00370     stdev   =   cpl_vector_get_stdev(spec_convolved) ;
00371     med     =   cpl_vector_get_median_const(spec_convolved) ;
00372 
00373     /* Loop on the detected lines */
00374     nb_det = 0 ;
00375     while (max > med + stdev * sigma) {
00376         /* Compute the position */
00377         i=0 ;
00378         while (pspec_convolved[i] < max) i++ ;
00379         if (i<=0 || i>=nb_samples-1) break ;
00380 
00381         /* Extract the line */
00382         if (i - hwidth >= 0)                start = i - hwidth ;
00383         else                                start = 0 ;
00384         if (i + hwidth <= nb_samples-1)     stop = i + hwidth ;
00385         else                                stop = nb_samples-1 ;
00386         extract = cpl_vector_extract(spec_clean, start, stop, 1) ;
00387         extract_x = cpl_vector_duplicate(extract) ;
00388         for (j=0 ; j<cpl_vector_get_size(extract_x) ; j++) {
00389             cpl_vector_set(extract_x, j, (double)j+1) ;
00390         }
00391         /* Fit the gaussian */
00392         if (cpl_vector_fit_gaussian(extract_x, NULL, extract, NULL, 
00393                     CPL_FIT_ALL, &x0, &sig, &norm, &offset, NULL, NULL, 
00394                     NULL) != CPL_ERROR_NONE) {
00395             cpl_msg_warning(__func__, 
00396                     "Cannot fit a gaussian at [%d, %d]",
00397                     start, stop) ;
00398             cpl_error_reset() ;
00399         } else {
00400             pbig_detected[nb_det] = x0+start ;
00401             pbig_area[nb_det] = norm ;
00402             pbig_fwhms[nb_det] = 2*sig*sqrt(2*log(2)) ;
00403             cpl_msg_debug(__func__, "Line nb %d at position %g",
00404                     nb_det+1, pbig_detected[nb_det]) ;
00405             nb_det ++ ;
00406         }
00407         cpl_vector_delete(extract) ;
00408         cpl_vector_delete(extract_x) ;
00409 
00410         /* Cancel out the line on the left */
00411         j = i-1 ;
00412         cur_val = pspec_convolved[i] ;
00413         while (j>=0 && pspec_convolved[j] < cur_val) {
00414             cur_val = pspec_convolved[j] ;
00415             pspec_convolved[j] = 0.0 ;
00416             j-- ;
00417         }
00418         /* Cancel out the line on the right */
00419         j = i+1 ;
00420         cur_val = pspec_convolved[i] ;
00421         while (j<=nb_samples-1 && pspec_convolved[j] < cur_val) {
00422             cur_val = pspec_convolved[j] ;
00423             pspec_convolved[j] = 0.0 ;
00424             j++ ;
00425         }
00426         /* Cancel out the line on center */
00427         pspec_convolved[i] = 0.0 ;
00428 
00429         /* Recompute the stats */
00430         max     =   cpl_vector_get_max(spec_convolved) ;
00431         stdev   =   cpl_vector_get_stdev(spec_convolved) ;
00432         med     =   cpl_vector_get_median_const(spec_convolved) ;
00433     }
00434     cpl_vector_delete(spec_convolved) ;
00435     cpl_vector_delete(spec_clean) ;
00436 
00437     /* Create the output vector */
00438     if (nb_det == 0) {
00439         detected = NULL ;
00440         area = NULL ;
00441         fwhms = NULL ;
00442     } else {
00443         detected = cpl_vector_new(nb_det) ;
00444         area = cpl_vector_new(nb_det) ;
00445         fwhms = cpl_vector_new(nb_det) ;
00446         pdetected = cpl_vector_get_data(detected) ;
00447         parea = cpl_vector_get_data(area) ;
00448         pfwhms = cpl_vector_get_data(fwhms) ;
00449         for (i=0 ; i<nb_det ; i++) {
00450             pdetected[i] = pbig_detected[i] ;
00451             parea[i] = pbig_area[i] ;
00452             pfwhms[i] = pbig_fwhms[i] ;
00453         }
00454     }
00455     cpl_vector_delete(big_detected) ;
00456     cpl_vector_delete(big_area) ;
00457     cpl_vector_delete(big_fwhms) ;
00458 
00459     /* Return  */
00460     if (fwhms_out == NULL)  cpl_vector_delete(fwhms) ;
00461     else                    *fwhms_out = fwhms ;
00462     if (areas_out == NULL)  cpl_vector_delete(area) ;
00463     else                    *areas_out = area ;
00464     return detected ;
00465 }
00466 
00469 /*----------------------------------------------------------------------------*/
00481 /*----------------------------------------------------------------------------*/
00482 static int select_valid_spectra(
00483         cpl_image       *   in,
00484         cpl_apertures   *   aperts,
00485         int                 offset,
00486         spec_shadows        shadows,
00487         int                 max_spec_width,
00488         int             *   n_valid_specs,
00489         int             **  valid_specs)
00490 {
00491     int                 nb_aperts ;
00492     int                 i, j ;
00493 
00494     /* Initialise */
00495     *valid_specs = NULL ;
00496     nb_aperts = cpl_apertures_get_size(aperts) ;
00497     *n_valid_specs = 0 ;
00498 
00499     /* Test entries */
00500     if (nb_aperts < 1) return -1 ;
00501 
00502     /* Count nb of valid specs */
00503     j = 0 ;
00504     for (i=0 ; i<nb_aperts ; i++)
00505         if (valid_spectrum(in, aperts, offset, shadows, max_spec_width,
00506                     i+1)) (*n_valid_specs)++ ;
00507 
00508     /* Associate to each spectrum, its object number */
00509     if (*n_valid_specs) {
00510         *valid_specs = cpl_calloc(*n_valid_specs, sizeof(int)) ;
00511         j = 0 ;
00512         for (i=0 ; i<nb_aperts ; i++)
00513             if (valid_spectrum(in, aperts, offset, shadows, max_spec_width,
00514                         i+1)) {
00515                 (*valid_specs)[j] = i ;
00516                 j++ ;
00517             }
00518     } else return -1 ;
00519 
00520     return 0 ;
00521 }
00522 
00523 /*---------------------------------------------------------------------------*/
00534 /*----------------------------------------------------------------------------*/
00535 static int valid_spectrum(
00536         cpl_image       *   in,
00537         cpl_apertures   *   aperts,
00538         int                 offset,
00539         spec_shadows        shadows,
00540         int                 max_spec_width,
00541         int                 objnum)
00542 {
00543     int                 objwidth ;
00544     double              valover, valunder, valcenter ;
00545 
00546     /* Find objwidth */
00547     objwidth = cpl_apertures_get_top(aperts, objnum) -
00548         cpl_apertures_get_bottom(aperts, objnum) + 1 ;
00549     if (objwidth > max_spec_width) {
00550         cpl_msg_error(cpl_func, "object is too wide") ;
00551         return 0 ;
00552     }
00553 
00554     /* Object is too small */
00555     if (cpl_apertures_get_npix(aperts, objnum) < 2) return 0 ;
00556 
00557     /* no shadow required */
00558     if (shadows == NO_SHADOW) return 1 ;
00559 
00560     /* Get the median of the object (valcenter) */
00561     valcenter = cpl_apertures_get_median(aperts, objnum) ;
00562 
00563     /* Get the black shadows medians (valunder and valover) */
00564     if (cpl_apertures_get_bottom(aperts, objnum) - offset < 1) valunder = 0.0 ;
00565     else valunder = cpl_image_get_median_window(in, 1,
00566             cpl_apertures_get_bottom(aperts, objnum) - offset, 1, 
00567             cpl_apertures_get_top(aperts, objnum) - offset) ;
00568     
00569     if (cpl_apertures_get_top(aperts, objnum) + offset > 1024) valover = 0.0 ;
00570     else valover = cpl_image_get_median_window(in, 1,
00571             cpl_apertures_get_bottom(aperts, objnum) + offset, 1, 
00572             cpl_apertures_get_top(aperts, objnum) + offset) ;
00573 
00574     switch (shadows) {
00575         case TWO_SHADOWS:
00576         if ((valunder < -fabs(valcenter/SPEC_SHADOW_FACT)) &&
00577             (valover < -fabs(valcenter/SPEC_SHADOW_FACT))    &&
00578             (valunder/valover > 0.5) &&
00579             (valunder/valover < 2.0)) return 1 ;
00580         break;
00581 
00582         case ONE_SHADOW:
00583         if ((valunder < -fabs(valcenter/SPEC_SHADOW_FACT)) ||
00584             (valover < -fabs(valcenter/SPEC_SHADOW_FACT))) return 1 ;
00585         break;
00586 
00587         case NO_SHADOW:
00588         return 1 ;
00589 
00590         default:
00591         cpl_msg_error(cpl_func, "unknown spec_detect_mode") ;
00592         break ;
00593     }
00594 
00595     cpl_msg_debug(cpl_func, "No spectrum(%d): under=%g, center=%g, over=%g",
00596                   shadows, valunder, valcenter, valover);
00597 
00598     return 0 ;
00599 }

Generated on Wed Mar 9 15:46:17 2011 for NACO Pipeline Reference Manual by  doxygen 1.5.8