irplib_spectrum.c

00001 /* $Id: irplib_spectrum.c,v 1.15 2008/04/08 10:56:51 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: 2008/04/08 10:56:51 $
00024  * $Revision: 1.15 $
00025  * $Name: uves-4_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_spectrum.h"
00041 #include "irplib_plot.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_matrix      *   kernel ;
00097     cpl_image       *   collapsed ;
00098     float           *   pcollapsed ;
00099     cpl_vector      *   line ;
00100     double          *   pline ;
00101     cpl_vector      *   line_filt ;
00102     double              threshold ;
00103     double              median, stdev, max, mean ;
00104     cpl_mask        *   mask ;
00105     cpl_image       *   labels ;
00106     int                 nlabels ;
00107     cpl_apertures   *   aperts ;
00108     int                 n_valid_specs ;
00109     int             *   valid_specs ;
00110     double              brightness, brightest ;
00111     int                 i ;
00112 
00113     /* Test entries */
00114     if (in == NULL) return -1 ;
00115     if (orient!=0 && orient!=1) return -1 ;
00116 
00117     /* Flip the image if necessary */
00118     if (orient == 1) {
00119         loc_ima = cpl_image_duplicate(in) ;
00120         cpl_image_flip(loc_ima, 1) ;
00121     } else {
00122         loc_ima = cpl_image_duplicate(in) ;
00123     }
00124     
00125     /* Get rid of very high frequencies  */
00126     kernel = cpl_matrix_new(3, 3) ;
00127     cpl_matrix_fill(kernel, 1.0) ;
00128     if ((filt_image = cpl_image_filter_median(loc_ima, kernel)) == NULL) {
00129         cpl_matrix_delete(kernel) ;
00130         cpl_image_delete(loc_ima) ;
00131         cpl_msg_error(cpl_func, "cannot filter the image") ;
00132         return -1 ;
00133     }
00134     cpl_image_delete(loc_ima) ;
00135     cpl_matrix_delete(kernel) ;
00136 
00137     /* Collapse the image */
00138     if ((collapsed = cpl_image_collapse_median_create(filt_image, 1, 0,
00139                     0)) == NULL) {
00140         cpl_msg_error(cpl_func, "collapsing image: aborting spectrum detection");
00141         cpl_image_delete(filt_image) ;
00142         return -1 ;
00143     }
00144     cpl_image_delete(filt_image) ;
00145 
00146     /* Subtract low frequency signal */
00147     line = cpl_vector_new_from_image_column(collapsed, 1) ;
00148     cpl_image_delete(collapsed) ;
00149     line_filt = cpl_vector_filter_median_create(line, SPECTRUM_HW) ;
00150     cpl_vector_subtract(line, line_filt) ;
00151     cpl_vector_delete(line_filt) ;
00152 
00153     /* Get relevant stats for thresholding */
00154     median = cpl_vector_get_median_const(line) ;
00155     stdev = cpl_vector_get_stdev(line) ;
00156     max = cpl_vector_get_max(line) ;
00157     mean = cpl_vector_get_mean(line) ;
00158 
00159     /* Set the threshold */
00160     threshold = median + stdev ;
00161     if (threshold > MIN_THRESH_FACT * max)  threshold = MIN_THRESH_FACT * max ;
00162     if (threshold < MAX_THRESH_FACT * mean) threshold = MAX_THRESH_FACT * mean;
00163 
00164     /* Recreate the image */
00165     collapsed = cpl_image_new(1, cpl_vector_get_size(line), CPL_TYPE_FLOAT) ;
00166     pcollapsed = cpl_image_get_data_float(collapsed) ;
00167     pline = cpl_vector_get_data(line) ;
00168     for (i=0 ; i<cpl_vector_get_size(line) ; i++)
00169         pcollapsed[i] = (float)pline[i] ;
00170     cpl_vector_delete(line) ;
00171 
00172     /* Binarise the image */
00173     if ((mask = cpl_mask_threshold_image_create(collapsed, threshold,
00174             DBL_MAX)) == NULL) {
00175         cpl_msg_error(cpl_func, "cannot binarise") ;
00176         cpl_image_delete(collapsed) ;
00177         return -1 ;
00178     }
00179     if (cpl_mask_count(mask) < 1) {
00180         cpl_msg_error(cpl_func, "not enough signal to detect spectra") ;
00181         cpl_image_delete(collapsed) ;
00182         cpl_mask_delete(mask) ;
00183         return -1 ;
00184     }
00185     /* Labelise the different detected apertures */
00186     if ((labels = cpl_image_labelise_mask_create(mask, &nlabels))==NULL) {
00187         cpl_msg_error(cpl_func, "cannot labelise") ;
00188         cpl_image_delete(collapsed) ;
00189         cpl_mask_delete(mask) ;
00190         return -1 ;
00191     }
00192     cpl_mask_delete(mask) ;
00193 
00194     /* Create the detected apertures list */
00195     if ((aperts = cpl_apertures_new_from_image(collapsed, labels)) == NULL) {
00196         cpl_msg_error(cpl_func, "cannot compute apertures") ;
00197         cpl_image_delete(collapsed) ;
00198         cpl_image_delete(labels) ;
00199         return -1 ;
00200     }
00201     cpl_image_delete(labels) ;
00202 
00203     /* Select only relevant specs, create corresponding LUT's */
00204     if (select_valid_spectra(collapsed, aperts, offset, shadows, SPEC_MAXWIDTH,
00205                 &n_valid_specs, &valid_specs) == -1) {
00206         cpl_msg_debug(cpl_func, "cannot select valid spectra") ;
00207         cpl_image_delete(collapsed) ;
00208         cpl_apertures_delete(aperts) ;
00209         return -1 ;
00210     }
00211     cpl_image_delete(collapsed) ;
00212     if (n_valid_specs < 1) {
00213         cpl_msg_error(cpl_func, "no valid spectrum detected") ;
00214         cpl_free(valid_specs) ;
00215         cpl_apertures_delete(aperts) ;
00216         return -1 ;
00217     }
00218 
00219     /* Look for the brightest, among the detected spectra */
00220     *pos = cpl_apertures_get_centroid_y(aperts, valid_specs[0]+1) ;
00221     brightest = valid_specs[0] ;
00222     brightness = cpl_apertures_get_flux(aperts, valid_specs[0]+1) ;
00223     for (i=0 ; i<n_valid_specs ; i++) {
00224         if (cpl_apertures_get_flux(aperts, valid_specs[i]+1) > brightness) {
00225             *pos = cpl_apertures_get_centroid_y(aperts, valid_specs[i]+1) ;
00226             brightest = valid_specs[i] ;
00227             brightness = cpl_apertures_get_flux(aperts, valid_specs[i]+1) ;
00228         }
00229     }
00230     cpl_apertures_delete(aperts) ;
00231     cpl_free(valid_specs) ;
00232 
00233     /* Minimum brightness required */
00234     if (brightness < min_bright) {
00235         cpl_msg_error(cpl_func, "brightness %f too low <%f", brightness,
00236                 min_bright) ;
00237         return -1 ;
00238     }
00239 
00240     /* Return */
00241     return 0 ;
00242 }
00243 
00244 /*----------------------------------------------------------------------------*/
00252 /*----------------------------------------------------------------------------*/
00253 cpl_vector * irplib_spectrum_detect_peaks(
00254         const cpl_vector    *   in,
00255         int                     fwhm,
00256         double                  sigma,
00257         int                     display)
00258 {
00259     cpl_vector      *   filtered ;
00260     cpl_vector      *   spec_clean ;
00261     double          *   pspec_clean ;
00262     int                 filt_size ;
00263     cpl_vector      *   conv_kernel ;
00264     cpl_vector      *   big_detected ;
00265     double          *   pbig_detected ;
00266     cpl_vector      *   detected ;
00267     double          *   pdetected ;
00268     double              max, med, stdev, cur_val ;
00269     int                 nb_det, nb_samples ;
00270     int                 i, j ;
00271 
00272     /* Test entries */
00273     if (in == NULL) return NULL ;
00274 
00275     /* Initialise */
00276     nb_samples = cpl_vector_get_size(in) ;
00277     filt_size = 5 ;
00278 
00279     /* Subrtract the low frequency part */
00280     cpl_msg_info(__func__, "Low Frequency signal removal") ;
00281     if ((filtered=cpl_vector_filter_median_create(in, filt_size))==NULL){
00282         cpl_msg_error(__func__, "Cannot filter the spectrum") ;
00283         return NULL ;
00284     }
00285     spec_clean = cpl_vector_duplicate(in) ;
00286     cpl_vector_subtract(spec_clean, filtered) ;
00287     cpl_vector_delete(filtered) ;
00288 
00289     /* Display if requested */
00290     if (display) {
00291         irplib_vector_plot(
00292     "set grid;set xlabel 'Position (pixels)';set ylabel 'Intensity (ADU)';",
00293         "t 'Filtered extracted spectrum' w lines", "", spec_clean);
00294     }
00295 
00296     /* Convolve */
00297     cpl_msg_info(__func__, "Spectrum convolution") ;
00298     /* Create convolution kernel */
00299     if ((conv_kernel = cpl_wlcalib_xc_convolve_create_kernel(fwhm,
00300                     fwhm)) == NULL) {
00301         cpl_msg_error(cpl_func, "Cannot create convolution kernel") ;
00302         cpl_vector_delete(spec_clean) ;
00303         return NULL ;
00304     }
00305 
00306     /* Smooth the instrument resolution */
00307     if (cpl_wlcalib_xc_convolve(spec_clean, conv_kernel)) {
00308         cpl_msg_error(cpl_func, "Cannot smoothe the signal");
00309         cpl_vector_delete(spec_clean) ;
00310         cpl_vector_delete(conv_kernel) ;
00311         return NULL ;
00312     }
00313     cpl_vector_delete(conv_kernel) ;
00314 
00315     /* Display if requested */
00316     if (display) {
00317         irplib_vector_plot(
00318         "set grid;set xlabel 'Position (pixels)';set ylabel 'Intensity (ADU)';",
00319         "t 'Convolved extracted spectrum' w lines", "", spec_clean);
00320     }
00321 
00322     /* Apply the detection */
00323     big_detected = cpl_vector_duplicate(spec_clean) ;
00324     pbig_detected = cpl_vector_get_data(big_detected) ;
00325     pspec_clean = cpl_vector_get_data(spec_clean) ;
00326 
00327     /* To avoid detection on the side */
00328     pspec_clean[0] = pspec_clean[nb_samples-1] = 0.0 ;
00329 
00330     /* Compute stats */
00331     max     =   cpl_vector_get_max(spec_clean) ;
00332     stdev   =   cpl_vector_get_stdev(spec_clean) ;
00333     med     =   cpl_vector_get_median_const(spec_clean) ;
00334 
00335     /* Loop on the detected lines */
00336     nb_det = 0 ;
00337     while (max > med + stdev * sigma) {
00338         /* Compute the position */
00339         i=0 ;
00340         while (pspec_clean[i] < max) i++ ;
00341         if (i<=0 || i>=nb_samples-1) break ;
00342 
00343         /* Store the detected line */
00344         pbig_detected[nb_det] = (pspec_clean[i]*i +
00345                 pspec_clean[i-1]*(i-1) + pspec_clean[i+1]*(i+1)) /
00346             (pspec_clean[i]+pspec_clean[i-1]+pspec_clean[i+1]);
00347         /* Position = index + 1 */
00348         pbig_detected[nb_det] ++ ;
00349         cpl_msg_info(__func__, "Line nb %d at position %g",
00350                 nb_det+1, pbig_detected[nb_det]) ;
00351         nb_det ++ ;
00352 
00353         /* Cancel out the line on the left */
00354         j = i-1 ;
00355         cur_val = pspec_clean[i] ;
00356         while (j>=0 && pspec_clean[j] < cur_val) {
00357             cur_val = pspec_clean[j] ;
00358             pspec_clean[j] = 0.0 ;
00359             j-- ;
00360         }
00361         /* Cancel out the line on the right */
00362         j = i+1 ;
00363         cur_val = pspec_clean[i] ;
00364         while (j<=nb_samples-1 && pspec_clean[j] < cur_val) {
00365             cur_val = pspec_clean[j] ;
00366             pspec_clean[j] = 0.0 ;
00367             j++ ;
00368         }
00369         /* Cancel out the line on center */
00370         pspec_clean[i] = 0.0 ;
00371 
00372         /* Recompute the stats */
00373         max     =   cpl_vector_get_max(spec_clean) ;
00374         stdev   =   cpl_vector_get_stdev(spec_clean) ;
00375         med     =   cpl_vector_get_median_const(spec_clean) ;
00376     }
00377     cpl_vector_delete(spec_clean) ;
00378     cpl_msg_info(__func__, "%d lines detected", nb_det) ;
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 Mon Apr 21 10:56:54 2008 for UVES Pipeline Reference Manual by  doxygen 1.5.1