28 #include "irplib_wlxcorr.h"
29 #include "irplib_spectrum.h"
39 #define SPECTRUM_HW 16
40 #define MIN_THRESH_FACT 0.9
41 #define MAX_THRESH_FACT 1.1
42 #define SPEC_SHADOW_FACT 30.0
43 #define SPEC_MAXWIDTH 48
49 static int select_valid_spectra(cpl_image *, cpl_apertures *,
int,
50 spec_shadows,
int,
int *,
int **) ;
51 static int valid_spectrum(cpl_image *, cpl_apertures *,
int, spec_shadows,
int,
87 cpl_image * filt_image ;
88 cpl_image * collapsed ;
92 cpl_vector * line_filt ;
94 double median, stdev, max, mean ;
98 cpl_apertures * aperts ;
105 if (in == NULL)
return -1 ;
106 if (orient!=0 && orient!=1)
return -1 ;
110 loc_ima = cpl_image_duplicate(in) ;
111 cpl_image_flip(loc_ima, 1) ;
113 loc_ima = cpl_image_duplicate(in) ;
117 mask = cpl_mask_new(3, 3) ;
119 filt_image = cpl_image_new(
120 cpl_image_get_size_x(loc_ima),
121 cpl_image_get_size_y(loc_ima),
122 cpl_image_get_type(loc_ima)) ;
123 if (cpl_image_filter_mask(filt_image, loc_ima, mask,
124 CPL_FILTER_MEDIAN, CPL_BORDER_FILTER) != CPL_ERROR_NONE) {
125 cpl_msg_error(__func__,
"Cannot filter the image") ;
126 cpl_mask_delete(mask) ;
127 cpl_image_delete(filt_image) ;
130 cpl_mask_delete(mask) ;
131 cpl_image_delete(loc_ima) ;
134 if ((collapsed = cpl_image_collapse_median_create(filt_image, 1, 0,
136 cpl_msg_error(cpl_func,
"collapsing image: aborting spectrum detection");
137 cpl_image_delete(filt_image) ;
140 cpl_image_delete(filt_image) ;
143 line = cpl_vector_new_from_image_column(collapsed, 1) ;
144 cpl_image_delete(collapsed) ;
145 line_filt = cpl_vector_filter_median_create(line, SPECTRUM_HW) ;
146 cpl_vector_subtract(line, line_filt) ;
147 cpl_vector_delete(line_filt) ;
150 median = cpl_vector_get_median_const(line) ;
151 stdev = cpl_vector_get_stdev(line) ;
152 max = cpl_vector_get_max(line) ;
153 mean = cpl_vector_get_mean(line) ;
156 threshold = median + stdev ;
157 if (threshold > MIN_THRESH_FACT * max) threshold = MIN_THRESH_FACT * max ;
158 if (threshold < MAX_THRESH_FACT * mean) threshold = MAX_THRESH_FACT * mean;
161 collapsed = cpl_image_new(1, cpl_vector_get_size(line), CPL_TYPE_FLOAT) ;
162 pcollapsed = cpl_image_get_data_float(collapsed) ;
163 pline = cpl_vector_get_data(line) ;
164 for (i=0 ; i<cpl_vector_get_size(line) ; i++)
165 pcollapsed[i] = (
float)pline[i] ;
166 cpl_vector_delete(line) ;
169 if ((mask = cpl_mask_threshold_image_create(collapsed, threshold,
171 cpl_msg_error(cpl_func,
"cannot binarise") ;
172 cpl_image_delete(collapsed) ;
175 if (cpl_mask_count(mask) < 1) {
176 cpl_msg_error(cpl_func,
"not enough signal to detect spectra") ;
177 cpl_image_delete(collapsed) ;
178 cpl_mask_delete(mask) ;
182 if ((labels = cpl_image_labelise_mask_create(mask, &nlabels))==NULL) {
183 cpl_msg_error(cpl_func,
"cannot labelise") ;
184 cpl_image_delete(collapsed) ;
185 cpl_mask_delete(mask) ;
188 cpl_mask_delete(mask) ;
191 if ((aperts = cpl_apertures_new_from_image(collapsed, labels)) == NULL) {
192 cpl_msg_error(cpl_func,
"cannot compute apertures") ;
193 cpl_image_delete(collapsed) ;
194 cpl_image_delete(labels) ;
197 cpl_image_delete(labels) ;
200 if (select_valid_spectra(collapsed, aperts, offset, shadows, SPEC_MAXWIDTH,
201 &n_valid_specs, &valid_specs) == -1) {
202 cpl_msg_debug(cpl_func,
203 "Could not select valid spectra from the %"CPL_SIZE_FORMAT
204 " apertures in %"CPL_SIZE_FORMAT
"-col 1D-image, offset=%d"
206 cpl_apertures_get_size(aperts),
207 cpl_image_get_size_y(collapsed), offset, SPEC_MAXWIDTH);
208 if (cpl_msg_get_level() <= CPL_MSG_DEBUG)
209 cpl_apertures_dump(aperts, stderr);
210 cpl_image_delete(collapsed);
211 cpl_apertures_delete(aperts);
214 cpl_image_delete(collapsed) ;
215 if (n_valid_specs < 1) {
216 cpl_msg_error(cpl_func,
"no valid spectrum detected") ;
217 cpl_free(valid_specs) ;
218 cpl_apertures_delete(aperts) ;
223 *pos = cpl_apertures_get_centroid_y(aperts, valid_specs[0]+1) ;
224 brightness = cpl_apertures_get_flux(aperts, valid_specs[0]+1) ;
225 for (i=0 ; i<n_valid_specs ; i++) {
226 if (cpl_apertures_get_flux(aperts, valid_specs[i]+1) > brightness) {
227 *pos = cpl_apertures_get_centroid_y(aperts, valid_specs[i]+1) ;
228 brightness = cpl_apertures_get_flux(aperts, valid_specs[i]+1) ;
231 cpl_apertures_delete(aperts) ;
232 cpl_free(valid_specs) ;
235 if (brightness < min_bright) {
236 cpl_msg_error(cpl_func,
"brightness %f too low <%f", brightness,
259 const cpl_vector * in,
263 cpl_vector ** fwhms_out,
264 cpl_vector ** areas_out)
266 cpl_vector * filtered ;
267 cpl_vector * spec_clean ;
268 cpl_vector * spec_convolved ;
269 double * pspec_convolved ;
271 cpl_vector * big_detected ;
272 cpl_vector * big_fwhms ;
273 cpl_vector * big_area ;
274 double * pbig_detected ;
275 double * pbig_fwhms ;
277 cpl_vector * detected ;
280 double max, med, stdev ;
281 double x0, sig, norm, offset ;
282 int nb_det, nb_samples, hwidth, start, stop ;
286 if (in == NULL)
return NULL ;
289 nb_samples = cpl_vector_get_size(in) ;
294 cpl_msg_info(__func__,
"Low Frequency signal removal") ;
295 if ((filtered=cpl_vector_filter_median_create(in, filt_size))==NULL){
296 cpl_msg_error(__func__,
"Cannot filter the spectrum") ;
299 spec_clean = cpl_vector_duplicate(in) ;
300 cpl_vector_subtract(spec_clean, filtered) ;
301 cpl_vector_delete(filtered) ;
306 "set grid;set xlabel 'Position (pixels)';set ylabel 'Intensity (ADU)';",
307 "t 'Filtered extracted spectrum' w lines",
"", spec_clean);
311 spec_convolved = cpl_vector_duplicate(spec_clean) ;
313 cpl_vector * conv_kernel ;
314 cpl_msg_info(__func__,
"Spectrum convolution") ;
316 if ((conv_kernel = irplib_wlxcorr_convolve_create_kernel(fwhm,
318 cpl_msg_error(cpl_func,
"Cannot create convolution kernel") ;
319 cpl_vector_delete(spec_clean) ;
320 cpl_vector_delete(spec_convolved) ;
325 if (irplib_wlxcorr_convolve(spec_convolved, conv_kernel)) {
326 cpl_msg_error(cpl_func,
"Cannot smoothe the signal");
327 cpl_vector_delete(spec_clean) ;
328 cpl_vector_delete(spec_convolved) ;
329 cpl_vector_delete(conv_kernel) ;
332 cpl_vector_delete(conv_kernel) ;
337 "set grid;set xlabel 'Position (pixels)';set ylabel 'Intensity (ADU)';",
338 "t 'Convolved extracted spectrum' w lines",
"", spec_convolved);
343 big_detected = cpl_vector_duplicate(spec_convolved) ;
344 big_fwhms = cpl_vector_duplicate(spec_convolved) ;
345 big_area = cpl_vector_duplicate(spec_convolved) ;
346 pbig_detected = cpl_vector_get_data(big_detected) ;
347 pbig_fwhms = cpl_vector_get_data(big_fwhms) ;
348 pbig_area = cpl_vector_get_data(big_area) ;
350 pspec_convolved = cpl_vector_get_data(spec_convolved) ;
353 pspec_convolved[0] = pspec_convolved[nb_samples-1] = 0.0 ;
356 max = cpl_vector_get_max(spec_convolved) ;
357 stdev = cpl_vector_get_stdev(spec_convolved) ;
358 med = cpl_vector_get_median_const(spec_convolved) ;
362 while (max > med + stdev * sigma) {
363 cpl_vector * extract ;
364 cpl_vector * extract_x ;
369 while (pspec_convolved[i] < max) i++ ;
370 if (i<=0 || i>=nb_samples-1) break ;
373 if (i - hwidth >= 0) start = i - hwidth ;
375 if (i + hwidth <= nb_samples-1) stop = i + hwidth ;
376 else stop = nb_samples-1 ;
377 extract = cpl_vector_extract(spec_clean, start, stop, 1) ;
378 extract_x = cpl_vector_duplicate(extract) ;
379 for (j=0 ; j<cpl_vector_get_size(extract_x) ; j++) {
380 cpl_vector_set(extract_x, j, (
double)j+1) ;
383 if (cpl_vector_fit_gaussian(extract_x, NULL, extract, NULL,
384 CPL_FIT_ALL, &x0, &sig, &norm, &offset, NULL, NULL,
385 NULL) != CPL_ERROR_NONE) {
386 cpl_msg_warning(__func__,
387 "Cannot fit a gaussian at [%d, %d]",
391 pbig_detected[nb_det] = x0+start ;
392 pbig_area[nb_det] = norm ;
393 pbig_fwhms[nb_det] = 2*sig*sqrt(2*log(2)) ;
394 cpl_msg_debug(__func__,
"Line nb %d at position %g",
395 nb_det+1, pbig_detected[nb_det]) ;
398 cpl_vector_delete(extract) ;
399 cpl_vector_delete(extract_x) ;
403 cur_val = pspec_convolved[i] ;
404 while (j>=0 && pspec_convolved[j] < cur_val) {
405 cur_val = pspec_convolved[j] ;
406 pspec_convolved[j] = 0.0 ;
411 cur_val = pspec_convolved[i] ;
412 while (j<=nb_samples-1 && pspec_convolved[j] < cur_val) {
413 cur_val = pspec_convolved[j] ;
414 pspec_convolved[j] = 0.0 ;
418 pspec_convolved[i] = 0.0 ;
421 max = cpl_vector_get_max(spec_convolved) ;
422 stdev = cpl_vector_get_stdev(spec_convolved) ;
423 med = cpl_vector_get_median_const(spec_convolved) ;
425 cpl_vector_delete(spec_convolved) ;
426 cpl_vector_delete(spec_clean) ;
437 detected = cpl_vector_new(nb_det) ;
438 area = cpl_vector_new(nb_det) ;
439 fwhms = cpl_vector_new(nb_det) ;
440 pdetected = cpl_vector_get_data(detected) ;
441 parea = cpl_vector_get_data(area) ;
442 pfwhms = cpl_vector_get_data(fwhms) ;
443 for (i=0 ; i<nb_det ; i++) {
444 pdetected[i] = pbig_detected[i] ;
445 parea[i] = pbig_area[i] ;
446 pfwhms[i] = pbig_fwhms[i] ;
449 cpl_vector_delete(big_detected) ;
450 cpl_vector_delete(big_area) ;
451 cpl_vector_delete(big_fwhms) ;
454 if (fwhms_out == NULL) cpl_vector_delete(fwhms) ;
455 else *fwhms_out = fwhms ;
456 if (areas_out == NULL) cpl_vector_delete(area) ;
457 else *areas_out = area ;
476 static int select_valid_spectra(
478 cpl_apertures * aperts,
480 spec_shadows shadows,
489 *valid_specs = NULL ;
490 nb_aperts = cpl_apertures_get_size(aperts) ;
494 if (nb_aperts < 1)
return -1 ;
498 for (i=0 ; i<nb_aperts ; i++)
499 if (valid_spectrum(in, aperts, offset, shadows, max_spec_width,
500 i+1)) (*n_valid_specs)++ ;
503 if (*n_valid_specs) {
504 *valid_specs = cpl_calloc(*n_valid_specs,
sizeof(
int)) ;
506 for (i=0 ; i<nb_aperts ; i++)
507 if (valid_spectrum(in, aperts, offset, shadows, max_spec_width,
509 (*valid_specs)[j] = i ;
529 static int valid_spectrum(
531 cpl_apertures * aperts,
533 spec_shadows shadows,
538 double valover, valunder, valcenter ;
541 objwidth = cpl_apertures_get_top(aperts, objnum) -
542 cpl_apertures_get_bottom(aperts, objnum) + 1 ;
543 if (objwidth > max_spec_width) {
544 cpl_msg_error(cpl_func,
"object is too wide") ;
549 if (cpl_apertures_get_npix(aperts, objnum) < 2)
return 0 ;
552 if (shadows == NO_SHADOW)
return 1 ;
555 valcenter = cpl_apertures_get_median(aperts, objnum) ;
558 if (cpl_apertures_get_bottom(aperts, objnum) - offset < 1) valunder = 0.0 ;
559 else valunder = cpl_image_get_median_window(in, 1,
560 cpl_apertures_get_bottom(aperts, objnum) - offset, 1,
561 cpl_apertures_get_top(aperts, objnum) - offset) ;
563 if (cpl_apertures_get_top(aperts, objnum) + offset > 1024) valover = 0.0 ;
564 else valover = cpl_image_get_median_window(in, 1,
565 cpl_apertures_get_bottom(aperts, objnum) + offset, 1,
566 cpl_apertures_get_top(aperts, objnum) + offset) ;
570 if ((valunder < -fabs(valcenter/SPEC_SHADOW_FACT)) &&
571 (valover < -fabs(valcenter/SPEC_SHADOW_FACT)) &&
572 (valunder/valover > 0.5) &&
573 (valunder/valover < 2.0))
return 1 ;
577 if ((valunder < -fabs(valcenter/SPEC_SHADOW_FACT)) ||
578 (valover < -fabs(valcenter/SPEC_SHADOW_FACT)))
return 1 ;
585 cpl_msg_error(cpl_func,
"unknown spec_detect_mode") ;
589 cpl_msg_debug(cpl_func,
"No spectrum(%d): under=%g, center=%g, over=%g",
590 shadows, valunder, valcenter, valover);
int irplib_spectrum_find_brightest(const cpl_image *in, int offset, spec_shadows shadows, double min_bright, int orient, double *pos)
Finds the brightest spectrum in an image.
cpl_vector * irplib_spectrum_detect_peaks(const cpl_vector *in, int fwhm, double sigma, int display, cpl_vector **fwhms_out, cpl_vector **areas_out)
Detect the brightest features in a spectrum.