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_wlxcorr.h"
00041 #include "irplib_spectrum.h"
00042 #include "irplib_plot.h"
00043
00044
00045
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
00052 #define SPEC_MAXWIDTH 48
00053
00054
00055
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
00115 if (in == NULL) return -1 ;
00116 if (orient!=0 && orient!=1) return -1 ;
00117
00118
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
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
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
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
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
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
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
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
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
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
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
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
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
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
00274 if (in == NULL) return NULL ;
00275
00276
00277 nb_samples = cpl_vector_get_size(in) ;
00278 filt_size = 5 ;
00279
00280
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
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
00298 cpl_msg_info(__func__, "Spectrum convolution") ;
00299
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
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
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
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
00329 pspec_clean[0] = pspec_clean[nb_samples-1] = 0.0 ;
00330
00331
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
00337 nb_det = 0 ;
00338 while (max > med + stdev * sigma) {
00339
00340 i=0 ;
00341 while (pspec_clean[i] < max) i++ ;
00342 if (i<=0 || i>=nb_samples-1) break ;
00343
00344
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
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
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
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
00371 pspec_clean[i] = 0.0 ;
00372
00373
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
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 }