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 #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_image * collapsed ;
00097 float * pcollapsed ;
00098 cpl_vector * line ;
00099 double * pline ;
00100 cpl_vector * line_filt ;
00101 double threshold ;
00102 double median, stdev, max, mean ;
00103 cpl_mask * mask ;
00104 cpl_image * labels ;
00105 int nlabels ;
00106 cpl_apertures * aperts ;
00107 int n_valid_specs ;
00108 int * valid_specs ;
00109 double brightness, brightest ;
00110 int i ;
00111
00112
00113 if (in == NULL) return -1 ;
00114 if (orient!=0 && orient!=1) return -1 ;
00115
00116
00117 if (orient == 1) {
00118 loc_ima = cpl_image_duplicate(in) ;
00119 cpl_image_flip(loc_ima, 1) ;
00120 } else {
00121 loc_ima = cpl_image_duplicate(in) ;
00122 }
00123
00124
00125 mask = cpl_mask_new(3, 3) ;
00126 cpl_mask_not(mask) ;
00127 filt_image = cpl_image_new(
00128 cpl_image_get_size_x(loc_ima),
00129 cpl_image_get_size_y(loc_ima),
00130 cpl_image_get_type(loc_ima)) ;
00131 if (cpl_image_filter_mask(filt_image, loc_ima, mask,
00132 CPL_FILTER_MEDIAN, CPL_BORDER_FILTER) != CPL_ERROR_NONE) {
00133 cpl_msg_error(__func__, "Cannot filter the image") ;
00134 cpl_mask_delete(mask) ;
00135 cpl_image_delete(filt_image) ;
00136 return -1 ;
00137 }
00138 cpl_mask_delete(mask) ;
00139 cpl_image_delete(loc_ima) ;
00140
00141
00142 if ((collapsed = cpl_image_collapse_median_create(filt_image, 1, 0,
00143 0)) == NULL) {
00144 cpl_msg_error(cpl_func, "collapsing image: aborting spectrum detection");
00145 cpl_image_delete(filt_image) ;
00146 return -1 ;
00147 }
00148 cpl_image_delete(filt_image) ;
00149
00150
00151 line = cpl_vector_new_from_image_column(collapsed, 1) ;
00152 cpl_image_delete(collapsed) ;
00153 line_filt = cpl_vector_filter_median_create(line, SPECTRUM_HW) ;
00154 cpl_vector_subtract(line, line_filt) ;
00155 cpl_vector_delete(line_filt) ;
00156
00157
00158 median = cpl_vector_get_median_const(line) ;
00159 stdev = cpl_vector_get_stdev(line) ;
00160 max = cpl_vector_get_max(line) ;
00161 mean = cpl_vector_get_mean(line) ;
00162
00163
00164 threshold = median + stdev ;
00165 if (threshold > MIN_THRESH_FACT * max) threshold = MIN_THRESH_FACT * max ;
00166 if (threshold < MAX_THRESH_FACT * mean) threshold = MAX_THRESH_FACT * mean;
00167
00168
00169 collapsed = cpl_image_new(1, cpl_vector_get_size(line), CPL_TYPE_FLOAT) ;
00170 pcollapsed = cpl_image_get_data_float(collapsed) ;
00171 pline = cpl_vector_get_data(line) ;
00172 for (i=0 ; i<cpl_vector_get_size(line) ; i++)
00173 pcollapsed[i] = (float)pline[i] ;
00174 cpl_vector_delete(line) ;
00175
00176
00177 if ((mask = cpl_mask_threshold_image_create(collapsed, threshold,
00178 DBL_MAX)) == NULL) {
00179 cpl_msg_error(cpl_func, "cannot binarise") ;
00180 cpl_image_delete(collapsed) ;
00181 return -1 ;
00182 }
00183 if (cpl_mask_count(mask) < 1) {
00184 cpl_msg_error(cpl_func, "not enough signal to detect spectra") ;
00185 cpl_image_delete(collapsed) ;
00186 cpl_mask_delete(mask) ;
00187 return -1 ;
00188 }
00189
00190 if ((labels = cpl_image_labelise_mask_create(mask, &nlabels))==NULL) {
00191 cpl_msg_error(cpl_func, "cannot labelise") ;
00192 cpl_image_delete(collapsed) ;
00193 cpl_mask_delete(mask) ;
00194 return -1 ;
00195 }
00196 cpl_mask_delete(mask) ;
00197
00198
00199 if ((aperts = cpl_apertures_new_from_image(collapsed, labels)) == NULL) {
00200 cpl_msg_error(cpl_func, "cannot compute apertures") ;
00201 cpl_image_delete(collapsed) ;
00202 cpl_image_delete(labels) ;
00203 return -1 ;
00204 }
00205 cpl_image_delete(labels) ;
00206
00207
00208 if (select_valid_spectra(collapsed, aperts, offset, shadows, SPEC_MAXWIDTH,
00209 &n_valid_specs, &valid_specs) == -1) {
00210 cpl_msg_debug(cpl_func, "Could not select valid spectra from the %d "
00211 "apertures in %d-col 1D-image, offset=%d, min_bright=%d",
00212 cpl_apertures_get_size(aperts),
00213 cpl_image_get_size_y(collapsed), offset, SPEC_MAXWIDTH);
00214 if (cpl_msg_get_level() <= CPL_MSG_DEBUG)
00215 cpl_apertures_dump(aperts, stderr);
00216 cpl_image_delete(collapsed);
00217 cpl_apertures_delete(aperts);
00218 return -1;
00219 }
00220 cpl_image_delete(collapsed) ;
00221 if (n_valid_specs < 1) {
00222 cpl_msg_error(cpl_func, "no valid spectrum detected") ;
00223 cpl_free(valid_specs) ;
00224 cpl_apertures_delete(aperts) ;
00225 return -1 ;
00226 }
00227
00228
00229 *pos = cpl_apertures_get_centroid_y(aperts, valid_specs[0]+1) ;
00230 brightest = valid_specs[0] ;
00231 brightness = cpl_apertures_get_flux(aperts, valid_specs[0]+1) ;
00232 for (i=0 ; i<n_valid_specs ; i++) {
00233 if (cpl_apertures_get_flux(aperts, valid_specs[i]+1) > brightness) {
00234 *pos = cpl_apertures_get_centroid_y(aperts, valid_specs[i]+1) ;
00235 brightest = valid_specs[i] ;
00236 brightness = cpl_apertures_get_flux(aperts, valid_specs[i]+1) ;
00237 }
00238 }
00239 cpl_apertures_delete(aperts) ;
00240 cpl_free(valid_specs) ;
00241
00242
00243 if (brightness < min_bright) {
00244 cpl_msg_error(cpl_func, "brightness %f too low <%f", brightness,
00245 min_bright) ;
00246 return -1 ;
00247 }
00248
00249
00250 return 0 ;
00251 }
00252
00253
00265
00266 cpl_vector * irplib_spectrum_detect_peaks(
00267 const cpl_vector * in,
00268 int fwhm,
00269 double sigma,
00270 int display,
00271 cpl_vector ** fwhms_out,
00272 cpl_vector ** areas_out)
00273 {
00274 cpl_vector * filtered ;
00275 cpl_vector * spec_clean ;
00276 cpl_vector * spec_convolved ;
00277 double * pspec_convolved ;
00278 int filt_size ;
00279 cpl_vector * conv_kernel ;
00280 cpl_vector * extract ;
00281 cpl_vector * extract_x ;
00282 cpl_vector * big_detected ;
00283 cpl_vector * big_fwhms ;
00284 cpl_vector * big_area ;
00285 double * pbig_detected ;
00286 double * pbig_fwhms ;
00287 double * pbig_area ;
00288 cpl_vector * detected ;
00289 double * pdetected ;
00290 cpl_vector * fwhms ;
00291 double * pfwhms ;
00292 cpl_vector * area ;
00293 double * parea ;
00294 double max, med, stdev, cur_val ;
00295 double x0, sig, norm, offset ;
00296 int nb_det, nb_samples, hwidth, start, stop ;
00297 int i, j ;
00298
00299
00300 if (in == NULL) return NULL ;
00301
00302
00303 nb_samples = cpl_vector_get_size(in) ;
00304 filt_size = 5 ;
00305 hwidth = 5 ;
00306
00307
00308 cpl_msg_info(__func__, "Low Frequency signal removal") ;
00309 if ((filtered=cpl_vector_filter_median_create(in, filt_size))==NULL){
00310 cpl_msg_error(__func__, "Cannot filter the spectrum") ;
00311 return NULL ;
00312 }
00313 spec_clean = cpl_vector_duplicate(in) ;
00314 cpl_vector_subtract(spec_clean, filtered) ;
00315 cpl_vector_delete(filtered) ;
00316
00317
00318 if (display) {
00319 cpl_plot_vector(
00320 "set grid;set xlabel 'Position (pixels)';set ylabel 'Intensity (ADU)';",
00321 "t 'Filtered extracted spectrum' w lines", "", spec_clean);
00322 }
00323
00324
00325 spec_convolved = cpl_vector_duplicate(spec_clean) ;
00326 if (fwhm > 0) {
00327 cpl_msg_info(__func__, "Spectrum convolution") ;
00328
00329 if ((conv_kernel = irplib_wlxcorr_convolve_create_kernel(fwhm,
00330 fwhm)) == NULL) {
00331 cpl_msg_error(cpl_func, "Cannot create convolution kernel") ;
00332 cpl_vector_delete(spec_clean) ;
00333 cpl_vector_delete(spec_convolved) ;
00334 return NULL ;
00335 }
00336
00337
00338 if (irplib_wlxcorr_convolve(spec_convolved, conv_kernel)) {
00339 cpl_msg_error(cpl_func, "Cannot smoothe the signal");
00340 cpl_vector_delete(spec_clean) ;
00341 cpl_vector_delete(spec_convolved) ;
00342 cpl_vector_delete(conv_kernel) ;
00343 return NULL ;
00344 }
00345 cpl_vector_delete(conv_kernel) ;
00346
00347
00348 if (display) {
00349 cpl_plot_vector(
00350 "set grid;set xlabel 'Position (pixels)';set ylabel 'Intensity (ADU)';",
00351 "t 'Convolved extracted spectrum' w lines", "", spec_convolved);
00352 }
00353 }
00354
00355
00356 big_detected = cpl_vector_duplicate(spec_convolved) ;
00357 big_fwhms = cpl_vector_duplicate(spec_convolved) ;
00358 big_area = cpl_vector_duplicate(spec_convolved) ;
00359 pbig_detected = cpl_vector_get_data(big_detected) ;
00360 pbig_fwhms = cpl_vector_get_data(big_fwhms) ;
00361 pbig_area = cpl_vector_get_data(big_area) ;
00362
00363 pspec_convolved = cpl_vector_get_data(spec_convolved) ;
00364
00365
00366 pspec_convolved[0] = pspec_convolved[nb_samples-1] = 0.0 ;
00367
00368
00369 max = cpl_vector_get_max(spec_convolved) ;
00370 stdev = cpl_vector_get_stdev(spec_convolved) ;
00371 med = cpl_vector_get_median_const(spec_convolved) ;
00372
00373
00374 nb_det = 0 ;
00375 while (max > med + stdev * sigma) {
00376
00377 i=0 ;
00378 while (pspec_convolved[i] < max) i++ ;
00379 if (i<=0 || i>=nb_samples-1) break ;
00380
00381
00382 if (i - hwidth >= 0) start = i - hwidth ;
00383 else start = 0 ;
00384 if (i + hwidth <= nb_samples-1) stop = i + hwidth ;
00385 else stop = nb_samples-1 ;
00386 extract = cpl_vector_extract(spec_clean, start, stop, 1) ;
00387 extract_x = cpl_vector_duplicate(extract) ;
00388 for (j=0 ; j<cpl_vector_get_size(extract_x) ; j++) {
00389 cpl_vector_set(extract_x, j, (double)j+1) ;
00390 }
00391
00392 if (cpl_vector_fit_gaussian(extract_x, NULL, extract, NULL,
00393 CPL_FIT_ALL, &x0, &sig, &norm, &offset, NULL, NULL,
00394 NULL) != CPL_ERROR_NONE) {
00395 cpl_msg_warning(__func__,
00396 "Cannot fit a gaussian at [%d, %d]",
00397 start, stop) ;
00398 cpl_error_reset() ;
00399 } else {
00400 pbig_detected[nb_det] = x0+start ;
00401 pbig_area[nb_det] = norm ;
00402 pbig_fwhms[nb_det] = 2*sig*sqrt(2*log(2)) ;
00403 cpl_msg_debug(__func__, "Line nb %d at position %g",
00404 nb_det+1, pbig_detected[nb_det]) ;
00405 nb_det ++ ;
00406 }
00407 cpl_vector_delete(extract) ;
00408 cpl_vector_delete(extract_x) ;
00409
00410
00411 j = i-1 ;
00412 cur_val = pspec_convolved[i] ;
00413 while (j>=0 && pspec_convolved[j] < cur_val) {
00414 cur_val = pspec_convolved[j] ;
00415 pspec_convolved[j] = 0.0 ;
00416 j-- ;
00417 }
00418
00419 j = i+1 ;
00420 cur_val = pspec_convolved[i] ;
00421 while (j<=nb_samples-1 && pspec_convolved[j] < cur_val) {
00422 cur_val = pspec_convolved[j] ;
00423 pspec_convolved[j] = 0.0 ;
00424 j++ ;
00425 }
00426
00427 pspec_convolved[i] = 0.0 ;
00428
00429
00430 max = cpl_vector_get_max(spec_convolved) ;
00431 stdev = cpl_vector_get_stdev(spec_convolved) ;
00432 med = cpl_vector_get_median_const(spec_convolved) ;
00433 }
00434 cpl_vector_delete(spec_convolved) ;
00435 cpl_vector_delete(spec_clean) ;
00436
00437
00438 if (nb_det == 0) {
00439 detected = NULL ;
00440 area = NULL ;
00441 fwhms = NULL ;
00442 } else {
00443 detected = cpl_vector_new(nb_det) ;
00444 area = cpl_vector_new(nb_det) ;
00445 fwhms = cpl_vector_new(nb_det) ;
00446 pdetected = cpl_vector_get_data(detected) ;
00447 parea = cpl_vector_get_data(area) ;
00448 pfwhms = cpl_vector_get_data(fwhms) ;
00449 for (i=0 ; i<nb_det ; i++) {
00450 pdetected[i] = pbig_detected[i] ;
00451 parea[i] = pbig_area[i] ;
00452 pfwhms[i] = pbig_fwhms[i] ;
00453 }
00454 }
00455 cpl_vector_delete(big_detected) ;
00456 cpl_vector_delete(big_area) ;
00457 cpl_vector_delete(big_fwhms) ;
00458
00459
00460 if (fwhms_out == NULL) cpl_vector_delete(fwhms) ;
00461 else *fwhms_out = fwhms ;
00462 if (areas_out == NULL) cpl_vector_delete(area) ;
00463 else *areas_out = area ;
00464 return detected ;
00465 }
00466
00469
00481
00482 static int select_valid_spectra(
00483 cpl_image * in,
00484 cpl_apertures * aperts,
00485 int offset,
00486 spec_shadows shadows,
00487 int max_spec_width,
00488 int * n_valid_specs,
00489 int ** valid_specs)
00490 {
00491 int nb_aperts ;
00492 int i, j ;
00493
00494
00495 *valid_specs = NULL ;
00496 nb_aperts = cpl_apertures_get_size(aperts) ;
00497 *n_valid_specs = 0 ;
00498
00499
00500 if (nb_aperts < 1) return -1 ;
00501
00502
00503 j = 0 ;
00504 for (i=0 ; i<nb_aperts ; i++)
00505 if (valid_spectrum(in, aperts, offset, shadows, max_spec_width,
00506 i+1)) (*n_valid_specs)++ ;
00507
00508
00509 if (*n_valid_specs) {
00510 *valid_specs = cpl_calloc(*n_valid_specs, sizeof(int)) ;
00511 j = 0 ;
00512 for (i=0 ; i<nb_aperts ; i++)
00513 if (valid_spectrum(in, aperts, offset, shadows, max_spec_width,
00514 i+1)) {
00515 (*valid_specs)[j] = i ;
00516 j++ ;
00517 }
00518 } else return -1 ;
00519
00520 return 0 ;
00521 }
00522
00523
00534
00535 static int valid_spectrum(
00536 cpl_image * in,
00537 cpl_apertures * aperts,
00538 int offset,
00539 spec_shadows shadows,
00540 int max_spec_width,
00541 int objnum)
00542 {
00543 int objwidth ;
00544 double valover, valunder, valcenter ;
00545
00546
00547 objwidth = cpl_apertures_get_top(aperts, objnum) -
00548 cpl_apertures_get_bottom(aperts, objnum) + 1 ;
00549 if (objwidth > max_spec_width) {
00550 cpl_msg_error(cpl_func, "object is too wide") ;
00551 return 0 ;
00552 }
00553
00554
00555 if (cpl_apertures_get_npix(aperts, objnum) < 2) return 0 ;
00556
00557
00558 if (shadows == NO_SHADOW) return 1 ;
00559
00560
00561 valcenter = cpl_apertures_get_median(aperts, objnum) ;
00562
00563
00564 if (cpl_apertures_get_bottom(aperts, objnum) - offset < 1) valunder = 0.0 ;
00565 else valunder = cpl_image_get_median_window(in, 1,
00566 cpl_apertures_get_bottom(aperts, objnum) - offset, 1,
00567 cpl_apertures_get_top(aperts, objnum) - offset) ;
00568
00569 if (cpl_apertures_get_top(aperts, objnum) + offset > 1024) valover = 0.0 ;
00570 else valover = cpl_image_get_median_window(in, 1,
00571 cpl_apertures_get_bottom(aperts, objnum) + offset, 1,
00572 cpl_apertures_get_top(aperts, objnum) + offset) ;
00573
00574 switch (shadows) {
00575 case TWO_SHADOWS:
00576 if ((valunder < -fabs(valcenter/SPEC_SHADOW_FACT)) &&
00577 (valover < -fabs(valcenter/SPEC_SHADOW_FACT)) &&
00578 (valunder/valover > 0.5) &&
00579 (valunder/valover < 2.0)) return 1 ;
00580 break;
00581
00582 case ONE_SHADOW:
00583 if ((valunder < -fabs(valcenter/SPEC_SHADOW_FACT)) ||
00584 (valover < -fabs(valcenter/SPEC_SHADOW_FACT))) return 1 ;
00585 break;
00586
00587 case NO_SHADOW:
00588 return 1 ;
00589
00590 default:
00591 cpl_msg_error(cpl_func, "unknown spec_detect_mode") ;
00592 break ;
00593 }
00594
00595 cpl_msg_debug(cpl_func, "No spectrum(%d): under=%g, center=%g, over=%g",
00596 shadows, valunder, valcenter, valover);
00597
00598 return 0 ;
00599 }