irplib_spectrum.c

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

Generated on Mon Feb 6 15:23:49 2012 for VISIR Pipeline Reference Manual by  doxygen 1.5.8