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