irplib_spectrum.c

00001 /* $Id: irplib_spectrum.c,v 1.28 2011/11/23 14:46:22 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 14:46:22 $
00024  * $Revision: 1.28 $
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 /* 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, brightest ;
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     brightest = valid_specs[0] ;
00240     brightness = cpl_apertures_get_flux(aperts, valid_specs[0]+1) ;
00241     for (i=0 ; i<n_valid_specs ; i++) {
00242         if (cpl_apertures_get_flux(aperts, valid_specs[i]+1) > brightness) {
00243             *pos = cpl_apertures_get_centroid_y(aperts, valid_specs[i]+1) ;
00244             brightest = valid_specs[i] ;
00245             brightness = cpl_apertures_get_flux(aperts, valid_specs[i]+1) ;
00246         }
00247     }
00248     cpl_apertures_delete(aperts) ;
00249     cpl_free(valid_specs) ;
00250 
00251     /* Minimum brightness required */
00252     if (brightness < min_bright) {
00253         cpl_msg_error(cpl_func, "brightness %f too low <%f", brightness,
00254                 min_bright) ;
00255         return -1 ;
00256     }
00257 
00258     /* Return */
00259     return 0 ;
00260 }
00261 
00262 /*----------------------------------------------------------------------------*/
00274 /*----------------------------------------------------------------------------*/
00275 cpl_vector * irplib_spectrum_detect_peaks(
00276         const cpl_vector    *   in,
00277         int                     fwhm,
00278         double                  sigma,
00279         int                     display,
00280         cpl_vector          **  fwhms_out,
00281         cpl_vector          **  areas_out)
00282 {
00283     cpl_vector      *   filtered ;
00284     cpl_vector      *   spec_clean ;
00285     cpl_vector      *   spec_convolved ;
00286     double          *   pspec_convolved ;
00287     int                 filt_size ;
00288     cpl_vector      *   conv_kernel ;
00289     cpl_vector      *   extract ;
00290     cpl_vector      *   extract_x ;
00291     cpl_vector      *   big_detected ;
00292     cpl_vector      *   big_fwhms ;
00293     cpl_vector      *   big_area ;
00294     double          *   pbig_detected ;
00295     double          *   pbig_fwhms ;
00296     double          *   pbig_area ;
00297     cpl_vector      *   detected ;
00298     double          *   pdetected ;
00299     cpl_vector      *   fwhms ;
00300     double          *   pfwhms ;
00301     cpl_vector      *   area ;
00302     double          *   parea ;
00303     double              max, med, stdev, cur_val ;
00304     double              x0, sig, norm, offset ;
00305     int                 nb_det, nb_samples, hwidth, start, stop ;
00306     int                 i, j ;
00307 
00308     /* Test entries */
00309     if (in == NULL) return NULL ;
00310 
00311     /* Initialise */
00312     nb_samples = cpl_vector_get_size(in) ;
00313     filt_size = 5 ;
00314     hwidth = 5 ;
00315 
00316     /* Subtract the low frequency part */
00317     cpl_msg_info(__func__, "Low Frequency signal removal") ;
00318     if ((filtered=cpl_vector_filter_median_create(in, filt_size))==NULL){
00319         cpl_msg_error(__func__, "Cannot filter the spectrum") ;
00320         return NULL ;
00321     }
00322     spec_clean = cpl_vector_duplicate(in) ;
00323     cpl_vector_subtract(spec_clean, filtered) ;
00324     cpl_vector_delete(filtered) ;
00325 
00326     /* Display if requested */
00327     if (display) {
00328         cpl_plot_vector(
00329     "set grid;set xlabel 'Position (pixels)';set ylabel 'Intensity (ADU)';",
00330         "t 'Filtered extracted spectrum' w lines", "", spec_clean);
00331     }
00332 
00333     /* Convolve */
00334     spec_convolved = cpl_vector_duplicate(spec_clean) ;
00335     if (fwhm > 0) {
00336         cpl_msg_info(__func__, "Spectrum convolution") ;
00337         /* Create convolution kernel */
00338         if ((conv_kernel = irplib_wlxcorr_convolve_create_kernel(fwhm,
00339                         fwhm)) == NULL) {
00340             cpl_msg_error(cpl_func, "Cannot create convolution kernel") ;
00341             cpl_vector_delete(spec_clean) ;
00342             cpl_vector_delete(spec_convolved) ;
00343             return NULL ;
00344         }
00345 
00346         /* Smooth the instrument resolution */
00347         if (irplib_wlxcorr_convolve(spec_convolved, conv_kernel)) {
00348             cpl_msg_error(cpl_func, "Cannot smoothe the signal");
00349             cpl_vector_delete(spec_clean) ;
00350             cpl_vector_delete(spec_convolved) ;
00351             cpl_vector_delete(conv_kernel) ;
00352             return NULL ;
00353         }
00354         cpl_vector_delete(conv_kernel) ;
00355 
00356         /* Display if requested */
00357         if (display) {
00358             cpl_plot_vector(
00359         "set grid;set xlabel 'Position (pixels)';set ylabel 'Intensity (ADU)';",
00360             "t 'Convolved extracted spectrum' w lines", "", spec_convolved);
00361         }
00362     }
00363 
00364     /* Apply the detection */
00365     big_detected = cpl_vector_duplicate(spec_convolved) ;
00366     big_fwhms = cpl_vector_duplicate(spec_convolved) ;
00367     big_area = cpl_vector_duplicate(spec_convolved) ;
00368     pbig_detected = cpl_vector_get_data(big_detected) ;
00369     pbig_fwhms = cpl_vector_get_data(big_fwhms) ;
00370     pbig_area = cpl_vector_get_data(big_area) ;
00371     
00372     pspec_convolved = cpl_vector_get_data(spec_convolved) ;
00373 
00374     /* To avoid detection on the side */
00375     pspec_convolved[0] = pspec_convolved[nb_samples-1] = 0.0 ;
00376 
00377     /* Compute stats */
00378     max     =   cpl_vector_get_max(spec_convolved) ;
00379     stdev   =   cpl_vector_get_stdev(spec_convolved) ;
00380     med     =   cpl_vector_get_median_const(spec_convolved) ;
00381 
00382     /* Loop on the detected lines */
00383     nb_det = 0 ;
00384     while (max > med + stdev * sigma) {
00385         /* Compute the position */
00386         i=0 ;
00387         while (pspec_convolved[i] < max) i++ ;
00388         if (i<=0 || i>=nb_samples-1) break ;
00389 
00390         /* Extract the line */
00391         if (i - hwidth >= 0)                start = i - hwidth ;
00392         else                                start = 0 ;
00393         if (i + hwidth <= nb_samples-1)     stop = i + hwidth ;
00394         else                                stop = nb_samples-1 ;
00395         extract = cpl_vector_extract(spec_clean, start, stop, 1) ;
00396         extract_x = cpl_vector_duplicate(extract) ;
00397         for (j=0 ; j<cpl_vector_get_size(extract_x) ; j++) {
00398             cpl_vector_set(extract_x, j, (double)j+1) ;
00399         }
00400         /* Fit the gaussian */
00401         if (cpl_vector_fit_gaussian(extract_x, NULL, extract, NULL, 
00402                     CPL_FIT_ALL, &x0, &sig, &norm, &offset, NULL, NULL, 
00403                     NULL) != CPL_ERROR_NONE) {
00404             cpl_msg_warning(__func__, 
00405                     "Cannot fit a gaussian at [%d, %d]",
00406                     start, stop) ;
00407             cpl_error_reset() ;
00408         } else {
00409             pbig_detected[nb_det] = x0+start ;
00410             pbig_area[nb_det] = norm ;
00411             pbig_fwhms[nb_det] = 2*sig*sqrt(2*log(2)) ;
00412             cpl_msg_debug(__func__, "Line nb %d at position %g",
00413                     nb_det+1, pbig_detected[nb_det]) ;
00414             nb_det ++ ;
00415         }
00416         cpl_vector_delete(extract) ;
00417         cpl_vector_delete(extract_x) ;
00418 
00419         /* Cancel out the line on the left */
00420         j = i-1 ;
00421         cur_val = pspec_convolved[i] ;
00422         while (j>=0 && pspec_convolved[j] < cur_val) {
00423             cur_val = pspec_convolved[j] ;
00424             pspec_convolved[j] = 0.0 ;
00425             j-- ;
00426         }
00427         /* Cancel out the line on the right */
00428         j = i+1 ;
00429         cur_val = pspec_convolved[i] ;
00430         while (j<=nb_samples-1 && pspec_convolved[j] < cur_val) {
00431             cur_val = pspec_convolved[j] ;
00432             pspec_convolved[j] = 0.0 ;
00433             j++ ;
00434         }
00435         /* Cancel out the line on center */
00436         pspec_convolved[i] = 0.0 ;
00437 
00438         /* Recompute the stats */
00439         max     =   cpl_vector_get_max(spec_convolved) ;
00440         stdev   =   cpl_vector_get_stdev(spec_convolved) ;
00441         med     =   cpl_vector_get_median_const(spec_convolved) ;
00442     }
00443     cpl_vector_delete(spec_convolved) ;
00444     cpl_vector_delete(spec_clean) ;
00445 
00446     /* Create the output vector */
00447     if (nb_det == 0) {
00448         detected = NULL ;
00449         area = NULL ;
00450         fwhms = NULL ;
00451     } else {
00452         detected = cpl_vector_new(nb_det) ;
00453         area = cpl_vector_new(nb_det) ;
00454         fwhms = cpl_vector_new(nb_det) ;
00455         pdetected = cpl_vector_get_data(detected) ;
00456         parea = cpl_vector_get_data(area) ;
00457         pfwhms = cpl_vector_get_data(fwhms) ;
00458         for (i=0 ; i<nb_det ; i++) {
00459             pdetected[i] = pbig_detected[i] ;
00460             parea[i] = pbig_area[i] ;
00461             pfwhms[i] = pbig_fwhms[i] ;
00462         }
00463     }
00464     cpl_vector_delete(big_detected) ;
00465     cpl_vector_delete(big_area) ;
00466     cpl_vector_delete(big_fwhms) ;
00467 
00468     /* Return  */
00469     if (fwhms_out == NULL)  cpl_vector_delete(fwhms) ;
00470     else                    *fwhms_out = fwhms ;
00471     if (areas_out == NULL)  cpl_vector_delete(area) ;
00472     else                    *areas_out = area ;
00473     return detected ;
00474 }
00475 
00478 /*----------------------------------------------------------------------------*/
00490 /*----------------------------------------------------------------------------*/
00491 static int select_valid_spectra(
00492         cpl_image       *   in,
00493         cpl_apertures   *   aperts,
00494         int                 offset,
00495         spec_shadows        shadows,
00496         int                 max_spec_width,
00497         int             *   n_valid_specs,
00498         int             **  valid_specs)
00499 {
00500     int                 nb_aperts ;
00501     int                 i, j ;
00502 
00503     /* Initialise */
00504     *valid_specs = NULL ;
00505     nb_aperts = cpl_apertures_get_size(aperts) ;
00506     *n_valid_specs = 0 ;
00507 
00508     /* Test entries */
00509     if (nb_aperts < 1) return -1 ;
00510 
00511     /* Count nb of valid specs */
00512     j = 0 ;
00513     for (i=0 ; i<nb_aperts ; i++)
00514         if (valid_spectrum(in, aperts, offset, shadows, max_spec_width,
00515                     i+1)) (*n_valid_specs)++ ;
00516 
00517     /* Associate to each spectrum, its object number */
00518     if (*n_valid_specs) {
00519         *valid_specs = cpl_calloc(*n_valid_specs, sizeof(int)) ;
00520         j = 0 ;
00521         for (i=0 ; i<nb_aperts ; i++)
00522             if (valid_spectrum(in, aperts, offset, shadows, max_spec_width,
00523                         i+1)) {
00524                 (*valid_specs)[j] = i ;
00525                 j++ ;
00526             }
00527     } else return -1 ;
00528 
00529     return 0 ;
00530 }
00531 
00532 /*---------------------------------------------------------------------------*/
00543 /*----------------------------------------------------------------------------*/
00544 static int valid_spectrum(
00545         cpl_image       *   in,
00546         cpl_apertures   *   aperts,
00547         int                 offset,
00548         spec_shadows        shadows,
00549         int                 max_spec_width,
00550         int                 objnum)
00551 {
00552     int                 objwidth ;
00553     double              valover, valunder, valcenter ;
00554 
00555     /* Find objwidth */
00556     objwidth = cpl_apertures_get_top(aperts, objnum) -
00557         cpl_apertures_get_bottom(aperts, objnum) + 1 ;
00558     if (objwidth > max_spec_width) {
00559         cpl_msg_error(cpl_func, "object is too wide") ;
00560         return 0 ;
00561     }
00562 
00563     /* Object is too small */
00564     if (cpl_apertures_get_npix(aperts, objnum) < 2) return 0 ;
00565 
00566     /* no shadow required */
00567     if (shadows == NO_SHADOW) return 1 ;
00568 
00569     /* Get the median of the object (valcenter) */
00570     valcenter = cpl_apertures_get_median(aperts, objnum) ;
00571 
00572     /* Get the black shadows medians (valunder and valover) */
00573     if (cpl_apertures_get_bottom(aperts, objnum) - offset < 1) valunder = 0.0 ;
00574     else valunder = cpl_image_get_median_window(in, 1,
00575             cpl_apertures_get_bottom(aperts, objnum) - offset, 1, 
00576             cpl_apertures_get_top(aperts, objnum) - offset) ;
00577     
00578     if (cpl_apertures_get_top(aperts, objnum) + offset > 1024) valover = 0.0 ;
00579     else valover = cpl_image_get_median_window(in, 1,
00580             cpl_apertures_get_bottom(aperts, objnum) + offset, 1, 
00581             cpl_apertures_get_top(aperts, objnum) + offset) ;
00582 
00583     switch (shadows) {
00584         case TWO_SHADOWS:
00585         if ((valunder < -fabs(valcenter/SPEC_SHADOW_FACT)) &&
00586             (valover < -fabs(valcenter/SPEC_SHADOW_FACT))    &&
00587             (valunder/valover > 0.5) &&
00588             (valunder/valover < 2.0)) return 1 ;
00589         break;
00590 
00591         case ONE_SHADOW:
00592         if ((valunder < -fabs(valcenter/SPEC_SHADOW_FACT)) ||
00593             (valover < -fabs(valcenter/SPEC_SHADOW_FACT))) return 1 ;
00594         break;
00595 
00596         case NO_SHADOW:
00597         return 1 ;
00598 
00599         default:
00600         cpl_msg_error(cpl_func, "unknown spec_detect_mode") ;
00601         break ;
00602     }
00603 
00604     cpl_msg_debug(cpl_func, "No spectrum(%d): under=%g, center=%g, over=%g",
00605                   shadows, valunder, valcenter, valover);
00606 
00607     return 0 ;
00608 }

Generated on 9 Jan 2012 for DETMON Pipeline Reference Manual by  doxygen 1.6.1