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