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 "xsh_spectrum.h"
00041 #include "xsh_utils_wrappers.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 xsh_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 = xsh_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
00253
00254 cpl_vector * xsh_spectrum_detect_peaks(
00255 const cpl_vector * in,
00256 int fwhm,
00257 double kappa,
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 cpl_plot_vector(
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 = cpl_wlcalib_xc_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 (cpl_wlcalib_xc_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 cpl_plot_vector(
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 * kappa) {
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
00351
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 cpl_msg_info(__func__, "%d lines detected", nb_det) ;
00380
00381
00382 if (nb_det == 0) {
00383 detected = NULL ;
00384 } else {
00385 detected = cpl_vector_new(nb_det) ;
00386 pdetected = cpl_vector_get_data(detected) ;
00387 pbig_detected = cpl_vector_get_data(big_detected) ;
00388 for (i=0 ; i<nb_det ; i++) pdetected[i] = pbig_detected[i] ;
00389 }
00390 cpl_vector_delete(big_detected) ;
00391
00392
00393 return detected ;
00394 }
00395
00398
00410
00411 static int select_valid_spectra(
00412 cpl_image * in,
00413 cpl_apertures * aperts,
00414 int offset,
00415 spec_shadows shadows,
00416 int max_spec_width,
00417 int * n_valid_specs,
00418 int ** valid_specs)
00419 {
00420 int nb_aperts ;
00421 int i, j ;
00422
00423
00424 *valid_specs = NULL ;
00425 nb_aperts = cpl_apertures_get_size(aperts) ;
00426 *n_valid_specs = 0 ;
00427
00428
00429 if (nb_aperts < 1) return -1 ;
00430
00431
00432 j = 0 ;
00433 for (i=0 ; i<nb_aperts ; i++)
00434 if (valid_spectrum(in, aperts, offset, shadows, max_spec_width,
00435 i+1)) (*n_valid_specs)++ ;
00436
00437
00438 if (*n_valid_specs) {
00439 *valid_specs = cpl_calloc(*n_valid_specs, sizeof(int)) ;
00440 j = 0 ;
00441 for (i=0 ; i<nb_aperts ; i++)
00442 if (valid_spectrum(in, aperts, offset, shadows, max_spec_width,
00443 i+1)) {
00444 (*valid_specs)[j] = i ;
00445 j++ ;
00446 }
00447 } else return -1 ;
00448
00449 return 0 ;
00450 }
00451
00452
00463
00464 static int valid_spectrum(
00465 cpl_image * in,
00466 cpl_apertures * aperts,
00467 int offset,
00468 spec_shadows shadows,
00469 int max_spec_width,
00470 int objnum)
00471 {
00472 int objwidth ;
00473 double valover, valunder, valcenter ;
00474
00475
00476 objwidth = cpl_apertures_get_top(aperts, objnum) -
00477 cpl_apertures_get_bottom(aperts, objnum) + 1 ;
00478 if (objwidth > max_spec_width) {
00479 cpl_msg_error(cpl_func, "object is too wide") ;
00480 return 0 ;
00481 }
00482
00483
00484 if (cpl_apertures_get_npix(aperts, objnum) < 2) return 0 ;
00485
00486
00487 if (shadows == NO_SHADOW) return 1 ;
00488
00489
00490 valcenter = cpl_apertures_get_median(aperts, objnum) ;
00491
00492
00493 if (cpl_apertures_get_bottom(aperts, objnum) - offset < 1) valunder = 0.0 ;
00494 else valunder = cpl_image_get_median_window(in, 1,
00495 cpl_apertures_get_bottom(aperts, objnum) - offset, 1,
00496 cpl_apertures_get_top(aperts, objnum) - offset) ;
00497
00498 if (cpl_apertures_get_top(aperts, objnum) + offset > 1024) valover = 0.0 ;
00499 else valover = cpl_image_get_median_window(in, 1,
00500 cpl_apertures_get_bottom(aperts, objnum) + offset, 1,
00501 cpl_apertures_get_top(aperts, objnum) + offset) ;
00502
00503 switch (shadows) {
00504 case TWO_SHADOWS:
00505 if ((valunder < -fabs(valcenter/SPEC_SHADOW_FACT)) &&
00506 (valover < -fabs(valcenter/SPEC_SHADOW_FACT)) &&
00507 (valunder/valover > 0.5) &&
00508 (valunder/valover < 2.0)) return 1 ;
00509 else return 0 ;
00510
00511 case ONE_SHADOW:
00512 if ((valunder < -fabs(valcenter/SPEC_SHADOW_FACT)) ||
00513 (valover < -fabs(valcenter/SPEC_SHADOW_FACT))) return 1 ;
00514 else return 0 ;
00515
00516 case NO_SHADOW:
00517 return 1 ;
00518
00519 default:
00520 cpl_msg_error(cpl_func, "unknown spec_detect_mode") ;
00521 break ;
00522 }
00523
00524 return 0 ;
00525 }