40 #include "irplib_wlxcorr.h"
41 #include "irplib_spectrum.h"
48 #ifndef CPL_SIZE_FORMAT
49 #define CPL_SIZE_FORMAT "d"
54 #define SPECTRUM_HW 16
55 #define MIN_THRESH_FACT 0.9
56 #define MAX_THRESH_FACT 1.1
57 #define SPEC_SHADOW_FACT 30.0
58 #define SPEC_MAXWIDTH 48
64 static int select_valid_spectra(cpl_image *, cpl_apertures *,
int,
65 spec_shadows,
int,
int *,
int **) ;
66 static int valid_spectrum(cpl_image *, cpl_apertures *,
int, spec_shadows,
int,
101 cpl_image * loc_ima ;
102 cpl_image * filt_image ;
103 cpl_image * collapsed ;
107 cpl_vector * line_filt ;
109 double median, stdev, max, mean ;
113 cpl_apertures * aperts ;
120 if (in == NULL)
return -1 ;
121 if (orient!=0 && orient!=1)
return -1 ;
125 loc_ima = cpl_image_duplicate(in) ;
126 cpl_image_flip(loc_ima, 1) ;
128 loc_ima = cpl_image_duplicate(in) ;
132 mask = cpl_mask_new(3, 3) ;
134 filt_image = cpl_image_new(
135 cpl_image_get_size_x(loc_ima),
136 cpl_image_get_size_y(loc_ima),
137 cpl_image_get_type(loc_ima)) ;
138 if (cpl_image_filter_mask(filt_image, loc_ima, mask,
139 CPL_FILTER_MEDIAN, CPL_BORDER_FILTER) != CPL_ERROR_NONE) {
140 cpl_msg_error(__func__,
"Cannot filter the image") ;
141 cpl_mask_delete(mask) ;
142 cpl_image_delete(filt_image) ;
145 cpl_mask_delete(mask) ;
146 cpl_image_delete(loc_ima) ;
149 if ((collapsed = cpl_image_collapse_median_create(filt_image, 1, 0,
151 cpl_msg_error(cpl_func,
"collapsing image: aborting spectrum detection");
152 cpl_image_delete(filt_image) ;
155 cpl_image_delete(filt_image) ;
158 line = cpl_vector_new_from_image_column(collapsed, 1) ;
159 cpl_image_delete(collapsed) ;
160 line_filt = cpl_vector_filter_median_create(line, SPECTRUM_HW) ;
161 cpl_vector_subtract(line, line_filt) ;
162 cpl_vector_delete(line_filt) ;
165 median = cpl_vector_get_median_const(line) ;
166 stdev = cpl_vector_get_stdev(line) ;
167 max = cpl_vector_get_max(line) ;
168 mean = cpl_vector_get_mean(line) ;
171 threshold = median + stdev ;
172 if (threshold > MIN_THRESH_FACT * max) threshold = MIN_THRESH_FACT * max ;
173 if (threshold < MAX_THRESH_FACT * mean) threshold = MAX_THRESH_FACT * mean;
176 collapsed = cpl_image_new(1, cpl_vector_get_size(line), CPL_TYPE_FLOAT) ;
177 pcollapsed = cpl_image_get_data_float(collapsed) ;
178 pline = cpl_vector_get_data(line) ;
179 for (i=0 ; i<cpl_vector_get_size(line) ; i++)
180 pcollapsed[i] = (
float)pline[i] ;
181 cpl_vector_delete(line) ;
184 if ((mask = cpl_mask_threshold_image_create(collapsed, threshold,
186 cpl_msg_error(cpl_func,
"cannot binarise") ;
187 cpl_image_delete(collapsed) ;
190 if (cpl_mask_count(mask) < 1) {
191 cpl_msg_error(cpl_func,
"not enough signal to detect spectra") ;
192 cpl_image_delete(collapsed) ;
193 cpl_mask_delete(mask) ;
197 if ((labels = cpl_image_labelise_mask_create(mask, &nlabels))==NULL) {
198 cpl_msg_error(cpl_func,
"cannot labelise") ;
199 cpl_image_delete(collapsed) ;
200 cpl_mask_delete(mask) ;
203 cpl_mask_delete(mask) ;
206 if ((aperts = cpl_apertures_new_from_image(collapsed, labels)) == NULL) {
207 cpl_msg_error(cpl_func,
"cannot compute apertures") ;
208 cpl_image_delete(collapsed) ;
209 cpl_image_delete(labels) ;
212 cpl_image_delete(labels) ;
215 if (select_valid_spectra(collapsed, aperts, offset, shadows, SPEC_MAXWIDTH,
216 &n_valid_specs, &valid_specs) == -1) {
217 cpl_msg_debug(cpl_func,
218 "Could not select valid spectra from the %"CPL_SIZE_FORMAT
219 " apertures in %"CPL_SIZE_FORMAT
"-col 1D-image, offset=%d"
221 cpl_apertures_get_size(aperts),
222 cpl_image_get_size_y(collapsed), offset, SPEC_MAXWIDTH);
223 if (cpl_msg_get_level() <= CPL_MSG_DEBUG)
224 cpl_apertures_dump(aperts, stderr);
225 cpl_image_delete(collapsed);
226 cpl_apertures_delete(aperts);
229 cpl_image_delete(collapsed) ;
230 if (n_valid_specs < 1) {
231 cpl_msg_error(cpl_func,
"no valid spectrum detected") ;
232 cpl_free(valid_specs) ;
233 cpl_apertures_delete(aperts) ;
238 *pos = cpl_apertures_get_centroid_y(aperts, valid_specs[0]+1) ;
239 brightness = cpl_apertures_get_flux(aperts, valid_specs[0]+1) ;
240 for (i=0 ; i<n_valid_specs ; i++) {
241 if (cpl_apertures_get_flux(aperts, valid_specs[i]+1) > brightness) {
242 *pos = cpl_apertures_get_centroid_y(aperts, valid_specs[i]+1) ;
243 brightness = cpl_apertures_get_flux(aperts, valid_specs[i]+1) ;
246 cpl_apertures_delete(aperts) ;
247 cpl_free(valid_specs) ;
250 if (brightness < min_bright) {
251 cpl_msg_error(cpl_func,
"brightness %f too low <%f", brightness,
274 const cpl_vector * in,
278 cpl_vector ** fwhms_out,
279 cpl_vector ** areas_out)
281 cpl_vector * filtered ;
282 cpl_vector * spec_clean ;
283 cpl_vector * spec_convolved ;
284 double * pspec_convolved ;
286 cpl_vector * conv_kernel ;
287 cpl_vector * extract ;
288 cpl_vector * extract_x ;
289 cpl_vector * big_detected ;
290 cpl_vector * big_fwhms ;
291 cpl_vector * big_area ;
292 double * pbig_detected ;
293 double * pbig_fwhms ;
295 cpl_vector * detected ;
301 double max, med, stdev, cur_val ;
302 double x0, sig, norm, offset ;
303 int nb_det, nb_samples, hwidth, start, stop ;
307 if (in == NULL)
return NULL ;
310 nb_samples = cpl_vector_get_size(in) ;
315 cpl_msg_info(__func__,
"Low Frequency signal removal") ;
316 if ((filtered=cpl_vector_filter_median_create(in, filt_size))==NULL){
317 cpl_msg_error(__func__,
"Cannot filter the spectrum") ;
320 spec_clean = cpl_vector_duplicate(in) ;
321 cpl_vector_subtract(spec_clean, filtered) ;
322 cpl_vector_delete(filtered) ;
327 "set grid;set xlabel 'Position (pixels)';set ylabel 'Intensity (ADU)';",
328 "t 'Filtered extracted spectrum' w lines",
"", spec_clean);
332 spec_convolved = cpl_vector_duplicate(spec_clean) ;
334 cpl_msg_info(__func__,
"Spectrum convolution") ;
336 if ((conv_kernel = irplib_wlxcorr_convolve_create_kernel(fwhm,
338 cpl_msg_error(cpl_func,
"Cannot create convolution kernel") ;
339 cpl_vector_delete(spec_clean) ;
340 cpl_vector_delete(spec_convolved) ;
345 if (irplib_wlxcorr_convolve(spec_convolved, conv_kernel)) {
346 cpl_msg_error(cpl_func,
"Cannot smoothe the signal");
347 cpl_vector_delete(spec_clean) ;
348 cpl_vector_delete(spec_convolved) ;
349 cpl_vector_delete(conv_kernel) ;
352 cpl_vector_delete(conv_kernel) ;
357 "set grid;set xlabel 'Position (pixels)';set ylabel 'Intensity (ADU)';",
358 "t 'Convolved extracted spectrum' w lines",
"", spec_convolved);
363 big_detected = cpl_vector_duplicate(spec_convolved) ;
364 big_fwhms = cpl_vector_duplicate(spec_convolved) ;
365 big_area = cpl_vector_duplicate(spec_convolved) ;
366 pbig_detected = cpl_vector_get_data(big_detected) ;
367 pbig_fwhms = cpl_vector_get_data(big_fwhms) ;
368 pbig_area = cpl_vector_get_data(big_area) ;
370 pspec_convolved = cpl_vector_get_data(spec_convolved) ;
373 pspec_convolved[0] = pspec_convolved[nb_samples-1] = 0.0 ;
376 max = cpl_vector_get_max(spec_convolved) ;
377 stdev = cpl_vector_get_stdev(spec_convolved) ;
378 med = cpl_vector_get_median_const(spec_convolved) ;
382 while (max > med + stdev * sigma) {
385 while (pspec_convolved[i] < max) i++ ;
386 if (i<=0 || i>=nb_samples-1) break ;
389 if (i - hwidth >= 0) start = i - hwidth ;
391 if (i + hwidth <= nb_samples-1) stop = i + hwidth ;
392 else stop = nb_samples-1 ;
393 extract = cpl_vector_extract(spec_clean, start, stop, 1) ;
394 extract_x = cpl_vector_duplicate(extract) ;
395 for (j=0 ; j<cpl_vector_get_size(extract_x) ; j++) {
396 cpl_vector_set(extract_x, j, (
double)j+1) ;
399 if (cpl_vector_fit_gaussian(extract_x, NULL, extract, NULL,
400 CPL_FIT_ALL, &x0, &sig, &norm, &offset, NULL, NULL,
401 NULL) != CPL_ERROR_NONE) {
402 cpl_msg_warning(__func__,
403 "Cannot fit a gaussian at [%d, %d]",
407 pbig_detected[nb_det] = x0+start ;
408 pbig_area[nb_det] = norm ;
409 pbig_fwhms[nb_det] = 2*sig*sqrt(2*log(2)) ;
410 cpl_msg_debug(__func__,
"Line nb %d at position %g",
411 nb_det+1, pbig_detected[nb_det]) ;
414 cpl_vector_delete(extract) ;
415 cpl_vector_delete(extract_x) ;
419 cur_val = pspec_convolved[i] ;
420 while (j>=0 && pspec_convolved[j] < cur_val) {
421 cur_val = pspec_convolved[j] ;
422 pspec_convolved[j] = 0.0 ;
427 cur_val = pspec_convolved[i] ;
428 while (j<=nb_samples-1 && pspec_convolved[j] < cur_val) {
429 cur_val = pspec_convolved[j] ;
430 pspec_convolved[j] = 0.0 ;
434 pspec_convolved[i] = 0.0 ;
437 max = cpl_vector_get_max(spec_convolved) ;
438 stdev = cpl_vector_get_stdev(spec_convolved) ;
439 med = cpl_vector_get_median_const(spec_convolved) ;
441 cpl_vector_delete(spec_convolved) ;
442 cpl_vector_delete(spec_clean) ;
450 detected = cpl_vector_new(nb_det) ;
451 area = cpl_vector_new(nb_det) ;
452 fwhms = cpl_vector_new(nb_det) ;
453 pdetected = cpl_vector_get_data(detected) ;
454 parea = cpl_vector_get_data(area) ;
455 pfwhms = cpl_vector_get_data(fwhms) ;
456 for (i=0 ; i<nb_det ; i++) {
457 pdetected[i] = pbig_detected[i] ;
458 parea[i] = pbig_area[i] ;
459 pfwhms[i] = pbig_fwhms[i] ;
462 cpl_vector_delete(big_detected) ;
463 cpl_vector_delete(big_area) ;
464 cpl_vector_delete(big_fwhms) ;
467 if (fwhms_out == NULL) cpl_vector_delete(fwhms) ;
468 else *fwhms_out = fwhms ;
469 if (areas_out == NULL) cpl_vector_delete(area) ;
470 else *areas_out = area ;
489 static int select_valid_spectra(
491 cpl_apertures * aperts,
493 spec_shadows shadows,
502 *valid_specs = NULL ;
503 nb_aperts = cpl_apertures_get_size(aperts) ;
507 if (nb_aperts < 1)
return -1 ;
511 for (i=0 ; i<nb_aperts ; i++)
512 if (valid_spectrum(in, aperts, offset, shadows, max_spec_width,
513 i+1)) (*n_valid_specs)++ ;
516 if (*n_valid_specs) {
517 *valid_specs = cpl_calloc(*n_valid_specs,
sizeof(
int)) ;
519 for (i=0 ; i<nb_aperts ; i++)
520 if (valid_spectrum(in, aperts, offset, shadows, max_spec_width,
522 (*valid_specs)[j] = i ;
542 static int valid_spectrum(
544 cpl_apertures * aperts,
546 spec_shadows shadows,
551 double valover, valunder, valcenter ;
554 objwidth = cpl_apertures_get_top(aperts, objnum) -
555 cpl_apertures_get_bottom(aperts, objnum) + 1 ;
556 if (objwidth > max_spec_width) {
557 cpl_msg_error(cpl_func,
"object is too wide") ;
562 if (cpl_apertures_get_npix(aperts, objnum) < 2)
return 0 ;
565 if (shadows == NO_SHADOW)
return 1 ;
568 valcenter = cpl_apertures_get_median(aperts, objnum) ;
571 if (cpl_apertures_get_bottom(aperts, objnum) - offset < 1) valunder = 0.0 ;
572 else valunder = cpl_image_get_median_window(in, 1,
573 cpl_apertures_get_bottom(aperts, objnum) - offset, 1,
574 cpl_apertures_get_top(aperts, objnum) - offset) ;
576 if (cpl_apertures_get_top(aperts, objnum) + offset > 1024) valover = 0.0 ;
577 else valover = cpl_image_get_median_window(in, 1,
578 cpl_apertures_get_bottom(aperts, objnum) + offset, 1,
579 cpl_apertures_get_top(aperts, objnum) + offset) ;
583 if ((valunder < -fabs(valcenter/SPEC_SHADOW_FACT)) &&
584 (valover < -fabs(valcenter/SPEC_SHADOW_FACT)) &&
585 (valunder/valover > 0.5) &&
586 (valunder/valover < 2.0))
return 1 ;
590 if ((valunder < -fabs(valcenter/SPEC_SHADOW_FACT)) ||
591 (valover < -fabs(valcenter/SPEC_SHADOW_FACT)))
return 1 ;
598 cpl_msg_error(cpl_func,
"unknown spec_detect_mode") ;
602 cpl_msg_debug(cpl_func,
"No spectrum(%d): under=%g, center=%g, over=%g",
603 shadows, valunder, valcenter, valover);