00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031
00032
00033
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
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
00051 #define SPEC_MAXWIDTH 48
00052
00053
00054
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
00114 if (in == NULL) return -1 ;
00115 if (orient!=0 && orient!=1) return -1 ;
00116
00117
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
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
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
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
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
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
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
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
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
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
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
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
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
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
00273 if (in == NULL) return NULL ;
00274
00275
00276 nb_samples = cpl_vector_get_size(in) ;
00277 filt_size = 5 ;
00278
00279
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
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
00297 cpl_msg_info(__func__, "Spectrum convolution") ;
00298
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
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
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
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
00328 pspec_clean[0] = pspec_clean[nb_samples-1] = 0.0 ;
00329
00330
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
00336 nb_det = 0 ;
00337 while (max > med + stdev * sigma) {
00338
00339 i=0 ;
00340 while (pspec_clean[i] < max) i++ ;
00341 if (i<=0 || i>=nb_samples-1) break ;
00342
00343
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
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
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
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
00370 pspec_clean[i] = 0.0 ;
00371
00372
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
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
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
00423 *valid_specs = NULL ;
00424 nb_aperts = cpl_apertures_get_size(aperts) ;
00425 *n_valid_specs = 0 ;
00426
00427
00428 if (nb_aperts < 1) return -1 ;
00429
00430
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
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
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
00483 if (cpl_apertures_get_npix(aperts, objnum) < 2) return 0 ;
00484
00485
00486 if (shadows == NO_SHADOW) return 1 ;
00487
00488
00489 valcenter = cpl_apertures_get_median(aperts, objnum) ;
00490
00491
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 }