irplib_spectrum.c

00001 /* $Id: irplib_spectrum.c,v 1.18 2008/11/06 12:55:07 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: 2008/11/06 12:55:07 $
00024  * $Revision: 1.18 $
00025  * $Name: visir-3_2_2 $
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 #include "irplib_plot.h"
00043 
00044 /*-----------------------------------------------------------------------------
00045                                    Define
00046  -----------------------------------------------------------------------------*/
00047 
00048 #define SPECTRUM_HW                     16
00049 #define MIN_THRESH_FACT                 0.9
00050 #define MAX_THRESH_FACT                 1.1
00051 #define SPEC_SHADOW_FACT                30.0 /* Negative spectrum intensity*/
00052 #define SPEC_MAXWIDTH                   48
00053 
00054 /*-----------------------------------------------------------------------------
00055                             Functions prototypes
00056  -----------------------------------------------------------------------------*/
00057 
00058 static int select_valid_spectra(cpl_image *, cpl_apertures *, int,
00059         spec_shadows, int, int *, int **) ;
00060 static int valid_spectrum(cpl_image *, cpl_apertures *, int, spec_shadows, int,
00061         int) ;
00062 
00063 /*----------------------------------------------------------------------------*/
00067 /*----------------------------------------------------------------------------*/
00068 
00071 /*----------------------------------------------------------------------------*/
00086 /*----------------------------------------------------------------------------*/
00087 int irplib_spectrum_find_brightest(
00088         const cpl_image     *   in,
00089         int                     offset,
00090         spec_shadows            shadows,
00091         double                  min_bright,
00092         int                     orient,
00093         double              *   pos)
00094 {
00095     cpl_image       *   loc_ima ;
00096     cpl_image       *   filt_image ;
00097     cpl_matrix      *   kernel ;
00098     cpl_image       *   collapsed ;
00099     float           *   pcollapsed ;
00100     cpl_vector      *   line ;
00101     double          *   pline ;
00102     cpl_vector      *   line_filt ;
00103     double              threshold ;
00104     double              median, stdev, max, mean ;
00105     cpl_mask        *   mask ;
00106     cpl_image       *   labels ;
00107     int                 nlabels ;
00108     cpl_apertures   *   aperts ;
00109     int                 n_valid_specs ;
00110     int             *   valid_specs ;
00111     double              brightness, brightest ;
00112     int                 i ;
00113 
00114     /* Test entries */
00115     if (in == NULL) return -1 ;
00116     if (orient!=0 && orient!=1) return -1 ;
00117 
00118     /* Flip the image if necessary */
00119     if (orient == 1) {
00120         loc_ima = cpl_image_duplicate(in) ;
00121         cpl_image_flip(loc_ima, 1) ;
00122     } else {
00123         loc_ima = cpl_image_duplicate(in) ;
00124     }
00125     
00126     /* Get rid of very high frequencies  */
00127     kernel = cpl_matrix_new(3, 3) ;
00128     cpl_matrix_fill(kernel, 1.0) ;
00129     if ((filt_image = cpl_image_filter_median(loc_ima, kernel)) == NULL) {
00130         cpl_matrix_delete(kernel) ;
00131         cpl_image_delete(loc_ima) ;
00132         cpl_msg_error(cpl_func, "cannot filter the image") ;
00133         return -1 ;
00134     }
00135     cpl_image_delete(loc_ima) ;
00136     cpl_matrix_delete(kernel) ;
00137 
00138     /* Collapse the image */
00139     if ((collapsed = cpl_image_collapse_median_create(filt_image, 1, 0,
00140                     0)) == NULL) {
00141         cpl_msg_error(cpl_func, "collapsing image: aborting spectrum detection");
00142         cpl_image_delete(filt_image) ;
00143         return -1 ;
00144     }
00145     cpl_image_delete(filt_image) ;
00146 
00147     /* Subtract low frequency signal */
00148     line = cpl_vector_new_from_image_column(collapsed, 1) ;
00149     cpl_image_delete(collapsed) ;
00150     line_filt = cpl_vector_filter_median_create(line, SPECTRUM_HW) ;
00151     cpl_vector_subtract(line, line_filt) ;
00152     cpl_vector_delete(line_filt) ;
00153 
00154     /* Get relevant stats for thresholding */
00155     median = cpl_vector_get_median_const(line) ;
00156     stdev = cpl_vector_get_stdev(line) ;
00157     max = cpl_vector_get_max(line) ;
00158     mean = cpl_vector_get_mean(line) ;
00159 
00160     /* Set the threshold */
00161     threshold = median + stdev ;
00162     if (threshold > MIN_THRESH_FACT * max)  threshold = MIN_THRESH_FACT * max ;
00163     if (threshold < MAX_THRESH_FACT * mean) threshold = MAX_THRESH_FACT * mean;
00164 
00165     /* Recreate the image */
00166     collapsed = cpl_image_new(1, cpl_vector_get_size(line), CPL_TYPE_FLOAT) ;
00167     pcollapsed = cpl_image_get_data_float(collapsed) ;
00168     pline = cpl_vector_get_data(line) ;
00169     for (i=0 ; i<cpl_vector_get_size(line) ; i++)
00170         pcollapsed[i] = (float)pline[i] ;
00171     cpl_vector_delete(line) ;
00172 
00173     /* Binarise the image */
00174     if ((mask = cpl_mask_threshold_image_create(collapsed, threshold,
00175             DBL_MAX)) == NULL) {
00176         cpl_msg_error(cpl_func, "cannot binarise") ;
00177         cpl_image_delete(collapsed) ;
00178         return -1 ;
00179     }
00180     if (cpl_mask_count(mask) < 1) {
00181         cpl_msg_error(cpl_func, "not enough signal to detect spectra") ;
00182         cpl_image_delete(collapsed) ;
00183         cpl_mask_delete(mask) ;
00184         return -1 ;
00185     }
00186     /* Labelise the different detected apertures */
00187     if ((labels = cpl_image_labelise_mask_create(mask, &nlabels))==NULL) {
00188         cpl_msg_error(cpl_func, "cannot labelise") ;
00189         cpl_image_delete(collapsed) ;
00190         cpl_mask_delete(mask) ;
00191         return -1 ;
00192     }
00193     cpl_mask_delete(mask) ;
00194 
00195     /* Create the detected apertures list */
00196     if ((aperts = cpl_apertures_new_from_image(collapsed, labels)) == NULL) {
00197         cpl_msg_error(cpl_func, "cannot compute apertures") ;
00198         cpl_image_delete(collapsed) ;
00199         cpl_image_delete(labels) ;
00200         return -1 ;
00201     }
00202     cpl_image_delete(labels) ;
00203 
00204     /* Select only relevant specs, create corresponding LUT's */
00205     if (select_valid_spectra(collapsed, aperts, offset, shadows, SPEC_MAXWIDTH,
00206                 &n_valid_specs, &valid_specs) == -1) {
00207         cpl_msg_debug(cpl_func, "cannot select valid spectra") ;
00208         cpl_image_delete(collapsed) ;
00209         cpl_apertures_delete(aperts) ;
00210         return -1 ;
00211     }
00212     cpl_image_delete(collapsed) ;
00213     if (n_valid_specs < 1) {
00214         cpl_msg_error(cpl_func, "no valid spectrum detected") ;
00215         cpl_free(valid_specs) ;
00216         cpl_apertures_delete(aperts) ;
00217         return -1 ;
00218     }
00219 
00220     /* Look for the brightest, among the detected spectra */
00221     *pos = cpl_apertures_get_centroid_y(aperts, valid_specs[0]+1) ;
00222     brightest = valid_specs[0] ;
00223     brightness = cpl_apertures_get_flux(aperts, valid_specs[0]+1) ;
00224     for (i=0 ; i<n_valid_specs ; i++) {
00225         if (cpl_apertures_get_flux(aperts, valid_specs[i]+1) > brightness) {
00226             *pos = cpl_apertures_get_centroid_y(aperts, valid_specs[i]+1) ;
00227             brightest = valid_specs[i] ;
00228             brightness = cpl_apertures_get_flux(aperts, valid_specs[i]+1) ;
00229         }
00230     }
00231     cpl_apertures_delete(aperts) ;
00232     cpl_free(valid_specs) ;
00233 
00234     /* Minimum brightness required */
00235     if (brightness < min_bright) {
00236         cpl_msg_error(cpl_func, "brightness %f too low <%f", brightness,
00237                 min_bright) ;
00238         return -1 ;
00239     }
00240 
00241     /* Return */
00242     return 0 ;
00243 }
00244 
00245 /*----------------------------------------------------------------------------*/
00253 /*----------------------------------------------------------------------------*/
00254 cpl_vector * irplib_spectrum_detect_peaks(
00255         const cpl_vector    *   in,
00256         int                     fwhm,
00257         double                  sigma,
00258         int                     display)
00259 {
00260     cpl_vector      *   filtered ;
00261     cpl_vector      *   spec_clean ;
00262     double          *   pspec_clean ;
00263     int                 filt_size ;
00264     cpl_vector      *   conv_kernel ;
00265     cpl_vector      *   big_detected ;
00266     double          *   pbig_detected ;
00267     cpl_vector      *   detected ;
00268     double          *   pdetected ;
00269     double              max, med, stdev, cur_val ;
00270     int                 nb_det, nb_samples ;
00271     int                 i, j ;
00272 
00273     /* Test entries */
00274     if (in == NULL) return NULL ;
00275 
00276     /* Initialise */
00277     nb_samples = cpl_vector_get_size(in) ;
00278     filt_size = 5 ;
00279 
00280     /* Subrtract the low frequency part */
00281     cpl_msg_info(__func__, "Low Frequency signal removal") ;
00282     if ((filtered=cpl_vector_filter_median_create(in, filt_size))==NULL){
00283         cpl_msg_error(__func__, "Cannot filter the spectrum") ;
00284         return NULL ;
00285     }
00286     spec_clean = cpl_vector_duplicate(in) ;
00287     cpl_vector_subtract(spec_clean, filtered) ;
00288     cpl_vector_delete(filtered) ;
00289 
00290     /* Display if requested */
00291     if (display) {
00292         irplib_vector_plot(
00293     "set grid;set xlabel 'Position (pixels)';set ylabel 'Intensity (ADU)';",
00294         "t 'Filtered extracted spectrum' w lines", "", spec_clean);
00295     }
00296 
00297     /* Convolve */
00298     cpl_msg_info(__func__, "Spectrum convolution") ;
00299     /* Create convolution kernel */
00300     if ((conv_kernel = irplib_wlxcorr_convolve_create_kernel(fwhm,
00301                     fwhm)) == NULL) {
00302         cpl_msg_error(cpl_func, "Cannot create convolution kernel") ;
00303         cpl_vector_delete(spec_clean) ;
00304         return NULL ;
00305     }
00306 
00307     /* Smooth the instrument resolution */
00308     if (irplib_wlxcorr_convolve(spec_clean, conv_kernel)) {
00309         cpl_msg_error(cpl_func, "Cannot smoothe the signal");
00310         cpl_vector_delete(spec_clean) ;
00311         cpl_vector_delete(conv_kernel) ;
00312         return NULL ;
00313     }
00314     cpl_vector_delete(conv_kernel) ;
00315 
00316     /* Display if requested */
00317     if (display) {
00318         irplib_vector_plot(
00319         "set grid;set xlabel 'Position (pixels)';set ylabel 'Intensity (ADU)';",
00320         "t 'Convolved extracted spectrum' w lines", "", spec_clean);
00321     }
00322 
00323     /* Apply the detection */
00324     big_detected = cpl_vector_duplicate(spec_clean) ;
00325     pbig_detected = cpl_vector_get_data(big_detected) ;
00326     pspec_clean = cpl_vector_get_data(spec_clean) ;
00327 
00328     /* To avoid detection on the side */
00329     pspec_clean[0] = pspec_clean[nb_samples-1] = 0.0 ;
00330 
00331     /* Compute stats */
00332     max     =   cpl_vector_get_max(spec_clean) ;
00333     stdev   =   cpl_vector_get_stdev(spec_clean) ;
00334     med     =   cpl_vector_get_median_const(spec_clean) ;
00335 
00336     /* Loop on the detected lines */
00337     nb_det = 0 ;
00338     while (max > med + stdev * sigma) {
00339         /* Compute the position */
00340         i=0 ;
00341         while (pspec_clean[i] < max) i++ ;
00342         if (i<=0 || i>=nb_samples-1) break ;
00343 
00344         /* Store the detected line */
00345         pbig_detected[nb_det] = (pspec_clean[i]*i +
00346                 pspec_clean[i-1]*(i-1) + pspec_clean[i+1]*(i+1)) /
00347             (pspec_clean[i]+pspec_clean[i-1]+pspec_clean[i+1]);
00348         /* Position = index + 1 */
00349         pbig_detected[nb_det] ++ ;
00350         cpl_msg_debug(__func__, "Line nb %d at position %g",
00351                 nb_det+1, pbig_detected[nb_det]) ;
00352         nb_det ++ ;
00353 
00354         /* Cancel out the line on the left */
00355         j = i-1 ;
00356         cur_val = pspec_clean[i] ;
00357         while (j>=0 && pspec_clean[j] < cur_val) {
00358             cur_val = pspec_clean[j] ;
00359             pspec_clean[j] = 0.0 ;
00360             j-- ;
00361         }
00362         /* Cancel out the line on the right */
00363         j = i+1 ;
00364         cur_val = pspec_clean[i] ;
00365         while (j<=nb_samples-1 && pspec_clean[j] < cur_val) {
00366             cur_val = pspec_clean[j] ;
00367             pspec_clean[j] = 0.0 ;
00368             j++ ;
00369         }
00370         /* Cancel out the line on center */
00371         pspec_clean[i] = 0.0 ;
00372 
00373         /* Recompute the stats */
00374         max     =   cpl_vector_get_max(spec_clean) ;
00375         stdev   =   cpl_vector_get_stdev(spec_clean) ;
00376         med     =   cpl_vector_get_median_const(spec_clean) ;
00377     }
00378     cpl_vector_delete(spec_clean) ;
00379 
00380     /* Create the output vector */
00381     if (nb_det == 0) {
00382         detected = NULL ;
00383     } else {
00384         detected = cpl_vector_new(nb_det) ;
00385         pdetected = cpl_vector_get_data(detected) ;
00386         pbig_detected = cpl_vector_get_data(big_detected) ;
00387         for (i=0 ; i<nb_det ; i++) pdetected[i] = pbig_detected[i] ;
00388     }
00389     cpl_vector_delete(big_detected) ;
00390 
00391     /* Return  */
00392     return detected ;
00393 }
00394 
00397 /*----------------------------------------------------------------------------*/
00409 /*----------------------------------------------------------------------------*/
00410 static int select_valid_spectra(
00411         cpl_image       *   in,
00412         cpl_apertures   *   aperts,
00413         int                 offset,
00414         spec_shadows        shadows,
00415         int                 max_spec_width,
00416         int             *   n_valid_specs,
00417         int             **  valid_specs)
00418 {
00419     int                 nb_aperts ;
00420     int                 i, j ;
00421 
00422     /* Initialise */
00423     *valid_specs = NULL ;
00424     nb_aperts = cpl_apertures_get_size(aperts) ;
00425     *n_valid_specs = 0 ;
00426 
00427     /* Test entries */
00428     if (nb_aperts < 1) return -1 ;
00429 
00430     /* Count nb of valid specs */
00431     j = 0 ;
00432     for (i=0 ; i<nb_aperts ; i++)
00433         if (valid_spectrum(in, aperts, offset, shadows, max_spec_width,
00434                     i+1)) (*n_valid_specs)++ ;
00435 
00436     /* Associate to each spectrum, its object number */
00437     if (*n_valid_specs) {
00438         *valid_specs = cpl_calloc(*n_valid_specs, sizeof(int)) ;
00439         j = 0 ;
00440         for (i=0 ; i<nb_aperts ; i++)
00441             if (valid_spectrum(in, aperts, offset, shadows, max_spec_width,
00442                         i+1)) {
00443                 (*valid_specs)[j] = i ;
00444                 j++ ;
00445             }
00446     } else return -1 ;
00447 
00448     return 0 ;
00449 }
00450 
00451 /*---------------------------------------------------------------------------*/
00462 /*----------------------------------------------------------------------------*/
00463 static int valid_spectrum(
00464         cpl_image       *   in,
00465         cpl_apertures   *   aperts,
00466         int                 offset,
00467         spec_shadows        shadows,
00468         int                 max_spec_width,
00469         int                 objnum)
00470 {
00471     int                 objwidth ;
00472     double              valover, valunder, valcenter ;
00473 
00474     /* Find objwidth */
00475     objwidth = cpl_apertures_get_top(aperts, objnum) -
00476         cpl_apertures_get_bottom(aperts, objnum) + 1 ;
00477     if (objwidth > max_spec_width) {
00478         cpl_msg_error(cpl_func, "object is too wide") ;
00479         return 0 ;
00480     }
00481 
00482     /* Object is too small */
00483     if (cpl_apertures_get_npix(aperts, objnum) < 2) return 0 ;
00484 
00485     /* no shadow required */
00486     if (shadows == NO_SHADOW) return 1 ;
00487 
00488     /* Get the median of the object (valcenter) */
00489     valcenter = cpl_apertures_get_median(aperts, objnum) ;
00490 
00491     /* Get the black shadows medians (valunder and valover) */
00492     if (cpl_apertures_get_bottom(aperts, objnum) - offset < 1) valunder = 0.0 ;
00493     else valunder = cpl_image_get_median_window(in, 1,
00494             cpl_apertures_get_bottom(aperts, objnum) - offset, 1, 
00495             cpl_apertures_get_top(aperts, objnum) - offset) ;
00496     
00497     if (cpl_apertures_get_top(aperts, objnum) + offset > 1024) valover = 0.0 ;
00498     else valover = cpl_image_get_median_window(in, 1,
00499             cpl_apertures_get_bottom(aperts, objnum) + offset, 1, 
00500             cpl_apertures_get_top(aperts, objnum) + offset) ;
00501     
00502     switch (shadows) {
00503         case TWO_SHADOWS:
00504         if ((valunder < -fabs(valcenter/SPEC_SHADOW_FACT)) &&
00505             (valover < -fabs(valcenter/SPEC_SHADOW_FACT))    &&
00506             (valunder/valover > 0.5) &&
00507             (valunder/valover < 2.0)) return 1 ;
00508         else return 0 ;
00509 
00510         case ONE_SHADOW:
00511         if ((valunder < -fabs(valcenter/SPEC_SHADOW_FACT)) ||
00512             (valover < -fabs(valcenter/SPEC_SHADOW_FACT))) return 1 ;
00513         else return 0 ;
00514 
00515         case NO_SHADOW:
00516         return 1 ;
00517 
00518         default:
00519         cpl_msg_error(cpl_func, "unknown spec_detect_mode") ;
00520         break ;
00521     }
00522 
00523     return 0 ;
00524 }

Generated on Fri Jul 3 11:15:23 2009 for VISIR Pipeline Reference Manual by  doxygen 1.5.8