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 * conv_kernel ;
272 cpl_vector * extract ;
273 cpl_vector * extract_x ;
274 cpl_vector * big_detected ;
275 cpl_vector * big_fwhms ;
276 cpl_vector * big_area ;
277 double * pbig_detected ;
278 double * pbig_fwhms ;
280 cpl_vector * detected ;
286 double max, med, stdev, cur_val ;
287 double x0, sig, norm, offset ;
288 int nb_det, nb_samples, hwidth, start, stop ;
292 if (in == NULL)
return NULL ;
295 nb_samples = cpl_vector_get_size(in) ;
300 cpl_msg_info(__func__,
"Low Frequency signal removal") ;
301 if ((filtered=cpl_vector_filter_median_create(in, filt_size))==NULL){
302 cpl_msg_error(__func__,
"Cannot filter the spectrum") ;
305 spec_clean = cpl_vector_duplicate(in) ;
306 cpl_vector_subtract(spec_clean, filtered) ;
307 cpl_vector_delete(filtered) ;
312 "set grid;set xlabel 'Position (pixels)';set ylabel 'Intensity (ADU)';",
313 "t 'Filtered extracted spectrum' w lines",
"", spec_clean);
317 spec_convolved = cpl_vector_duplicate(spec_clean) ;
319 cpl_msg_info(__func__,
"Spectrum convolution") ;
321 if ((conv_kernel = irplib_wlxcorr_convolve_create_kernel(fwhm,
323 cpl_msg_error(cpl_func,
"Cannot create convolution kernel") ;
324 cpl_vector_delete(spec_clean) ;
325 cpl_vector_delete(spec_convolved) ;
330 if (irplib_wlxcorr_convolve(spec_convolved, conv_kernel)) {
331 cpl_msg_error(cpl_func,
"Cannot smoothe the signal");
332 cpl_vector_delete(spec_clean) ;
333 cpl_vector_delete(spec_convolved) ;
334 cpl_vector_delete(conv_kernel) ;
337 cpl_vector_delete(conv_kernel) ;
342 "set grid;set xlabel 'Position (pixels)';set ylabel 'Intensity (ADU)';",
343 "t 'Convolved extracted spectrum' w lines",
"", spec_convolved);
348 big_detected = cpl_vector_duplicate(spec_convolved) ;
349 big_fwhms = cpl_vector_duplicate(spec_convolved) ;
350 big_area = cpl_vector_duplicate(spec_convolved) ;
351 pbig_detected = cpl_vector_get_data(big_detected) ;
352 pbig_fwhms = cpl_vector_get_data(big_fwhms) ;
353 pbig_area = cpl_vector_get_data(big_area) ;
355 pspec_convolved = cpl_vector_get_data(spec_convolved) ;
358 pspec_convolved[0] = pspec_convolved[nb_samples-1] = 0.0 ;
361 max = cpl_vector_get_max(spec_convolved) ;
362 stdev = cpl_vector_get_stdev(spec_convolved) ;
363 med = cpl_vector_get_median_const(spec_convolved) ;
367 while (max > med + stdev * sigma) {
370 while (pspec_convolved[i] < max) i++ ;
371 if (i<=0 || i>=nb_samples-1) break ;
374 if (i - hwidth >= 0) start = i - hwidth ;
376 if (i + hwidth <= nb_samples-1) stop = i + hwidth ;
377 else stop = nb_samples-1 ;
378 extract = cpl_vector_extract(spec_clean, start, stop, 1) ;
379 extract_x = cpl_vector_duplicate(extract) ;
380 for (j=0 ; j<cpl_vector_get_size(extract_x) ; j++) {
381 cpl_vector_set(extract_x, j, (
double)j+1) ;
384 if (cpl_vector_fit_gaussian(extract_x, NULL, extract, NULL,
385 CPL_FIT_ALL, &x0, &sig, &norm, &offset, NULL, NULL,
386 NULL) != CPL_ERROR_NONE) {
387 cpl_msg_warning(__func__,
388 "Cannot fit a gaussian at [%d, %d]",
392 pbig_detected[nb_det] = x0+start ;
393 pbig_area[nb_det] = norm ;
394 pbig_fwhms[nb_det] = 2*sig*sqrt(2*log(2)) ;
395 cpl_msg_debug(__func__,
"Line nb %d at position %g",
396 nb_det+1, pbig_detected[nb_det]) ;
399 cpl_vector_delete(extract) ;
400 cpl_vector_delete(extract_x) ;
404 cur_val = pspec_convolved[i] ;
405 while (j>=0 && pspec_convolved[j] < cur_val) {
406 cur_val = pspec_convolved[j] ;
407 pspec_convolved[j] = 0.0 ;
412 cur_val = pspec_convolved[i] ;
413 while (j<=nb_samples-1 && pspec_convolved[j] < cur_val) {
414 cur_val = pspec_convolved[j] ;
415 pspec_convolved[j] = 0.0 ;
419 pspec_convolved[i] = 0.0 ;
422 max = cpl_vector_get_max(spec_convolved) ;
423 stdev = cpl_vector_get_stdev(spec_convolved) ;
424 med = cpl_vector_get_median_const(spec_convolved) ;
426 cpl_vector_delete(spec_convolved) ;
427 cpl_vector_delete(spec_clean) ;
435 detected = cpl_vector_new(nb_det) ;
436 area = cpl_vector_new(nb_det) ;
437 fwhms = cpl_vector_new(nb_det) ;
438 pdetected = cpl_vector_get_data(detected) ;
439 parea = cpl_vector_get_data(area) ;
440 pfwhms = cpl_vector_get_data(fwhms) ;
441 for (i=0 ; i<nb_det ; i++) {
442 pdetected[i] = pbig_detected[i] ;
443 parea[i] = pbig_area[i] ;
444 pfwhms[i] = pbig_fwhms[i] ;
447 cpl_vector_delete(big_detected) ;
448 cpl_vector_delete(big_area) ;
449 cpl_vector_delete(big_fwhms) ;
452 if (fwhms_out == NULL) cpl_vector_delete(fwhms) ;
453 else *fwhms_out = fwhms ;
454 if (areas_out == NULL) cpl_vector_delete(area) ;
455 else *areas_out = area ;
474 static int select_valid_spectra(
476 cpl_apertures * aperts,
478 spec_shadows shadows,
487 *valid_specs = NULL ;
488 nb_aperts = cpl_apertures_get_size(aperts) ;
492 if (nb_aperts < 1)
return -1 ;
496 for (i=0 ; i<nb_aperts ; i++)
497 if (valid_spectrum(in, aperts, offset, shadows, max_spec_width,
498 i+1)) (*n_valid_specs)++ ;
501 if (*n_valid_specs) {
502 *valid_specs = cpl_calloc(*n_valid_specs,
sizeof(
int)) ;
504 for (i=0 ; i<nb_aperts ; i++)
505 if (valid_spectrum(in, aperts, offset, shadows, max_spec_width,
507 (*valid_specs)[j] = i ;
527 static int valid_spectrum(
529 cpl_apertures * aperts,
531 spec_shadows shadows,
536 double valover, valunder, valcenter ;
539 objwidth = cpl_apertures_get_top(aperts, objnum) -
540 cpl_apertures_get_bottom(aperts, objnum) + 1 ;
541 if (objwidth > max_spec_width) {
542 cpl_msg_error(cpl_func,
"object is too wide") ;
547 if (cpl_apertures_get_npix(aperts, objnum) < 2)
return 0 ;
550 if (shadows == NO_SHADOW)
return 1 ;
553 valcenter = cpl_apertures_get_median(aperts, objnum) ;
556 if (cpl_apertures_get_bottom(aperts, objnum) - offset < 1) valunder = 0.0 ;
557 else valunder = cpl_image_get_median_window(in, 1,
558 cpl_apertures_get_bottom(aperts, objnum) - offset, 1,
559 cpl_apertures_get_top(aperts, objnum) - offset) ;
561 if (cpl_apertures_get_top(aperts, objnum) + offset > 1024) valover = 0.0 ;
562 else valover = cpl_image_get_median_window(in, 1,
563 cpl_apertures_get_bottom(aperts, objnum) + offset, 1,
564 cpl_apertures_get_top(aperts, objnum) + offset) ;
568 if ((valunder < -fabs(valcenter/SPEC_SHADOW_FACT)) &&
569 (valover < -fabs(valcenter/SPEC_SHADOW_FACT)) &&
570 (valunder/valover > 0.5) &&
571 (valunder/valover < 2.0))
return 1 ;
575 if ((valunder < -fabs(valcenter/SPEC_SHADOW_FACT)) ||
576 (valover < -fabs(valcenter/SPEC_SHADOW_FACT)))
return 1 ;
583 cpl_msg_error(cpl_func,
"unknown spec_detect_mode") ;
587 cpl_msg_debug(cpl_func,
"No spectrum(%d): under=%g, center=%g, over=%g",
588 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.