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, brightest ;
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 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
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
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
00309 if (in == NULL) return NULL ;
00310
00311
00312 nb_samples = cpl_vector_get_size(in) ;
00313 filt_size = 5 ;
00314 hwidth = 5 ;
00315
00316
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
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
00334 spec_convolved = cpl_vector_duplicate(spec_clean) ;
00335 if (fwhm > 0) {
00336 cpl_msg_info(__func__, "Spectrum convolution") ;
00337
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
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
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
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
00375 pspec_convolved[0] = pspec_convolved[nb_samples-1] = 0.0 ;
00376
00377
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
00383 nb_det = 0 ;
00384 while (max > med + stdev * sigma) {
00385
00386 i=0 ;
00387 while (pspec_convolved[i] < max) i++ ;
00388 if (i<=0 || i>=nb_samples-1) break ;
00389
00390
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
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
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
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
00436 pspec_convolved[i] = 0.0 ;
00437
00438
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
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
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
00504 *valid_specs = NULL ;
00505 nb_aperts = cpl_apertures_get_size(aperts) ;
00506 *n_valid_specs = 0 ;
00507
00508
00509 if (nb_aperts < 1) return -1 ;
00510
00511
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
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
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
00564 if (cpl_apertures_get_npix(aperts, objnum) < 2) return 0 ;
00565
00566
00567 if (shadows == NO_SHADOW) return 1 ;
00568
00569
00570 valcenter = cpl_apertures_get_median(aperts, objnum) ;
00571
00572
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 }