X-shooter Pipeline Reference Manual 3.8.15
xsh_spectrum.c
Go to the documentation of this file.
1/* $Id: xsh_spectrum.c,v 1.9 2012-12-16 14:08:47 amodigli Exp $
2 *
3 * This file is part of the xsh package
4 * Copyright (C) 2002,2003 European Southern Observatory
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA
19 */
20
21/*
22 * $Author: amodigli $
23 * $Date: 2012-12-16 14:08:47 $
24 * $Revision: 1.9 $
25 * $Name: not supported by cvs2svn $
26 */
27
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31
32/*-----------------------------------------------------------------------------
33 Includes
34 -----------------------------------------------------------------------------*/
35
36#include <math.h>
37#include <float.h>
38#include <cpl.h>
39
40#include "xsh_spectrum.h"
41#include "xsh_utils_wrappers.h"
42
43/*-----------------------------------------------------------------------------
44 Define
45 -----------------------------------------------------------------------------*/
46
47#define SPECTRUM_HW 16
48#define MIN_THRESH_FACT 0.9
49#define MAX_THRESH_FACT 1.1
50#define SPEC_SHADOW_FACT 30.0 /* Negative spectrum intensity*/
51#define SPEC_MAXWIDTH 48
52
53/*-----------------------------------------------------------------------------
54 Functions prototypes
55 -----------------------------------------------------------------------------*/
56
57static int select_valid_spectra(cpl_image *, cpl_apertures *, int,
58 spec_shadows, int, int *, int **) ;
59static int valid_spectrum(cpl_image *, cpl_apertures *, int, spec_shadows, int,
60 int) ;
61
62/*----------------------------------------------------------------------------*/
66/*----------------------------------------------------------------------------*/
67
70/*----------------------------------------------------------------------------*/
85/*----------------------------------------------------------------------------*/
87 const cpl_image * in,
88 int offset,
89 spec_shadows shadows,
90 double min_bright,
91 int orient,
92 double * pos)
93{
94 cpl_image * loc_ima ;
95 cpl_image * filt_image ;
96 cpl_matrix * kernel ;
97 cpl_image * collapsed ;
98 float * pcollapsed ;
99 cpl_vector * line ;
100 double * pline ;
101 cpl_vector * line_filt ;
102 double threshold ;
103 double median, stdev, max, mean ;
104 cpl_mask * mask ;
105 cpl_image * labels ;
106 cpl_size nlabels ;
107 cpl_apertures * aperts ;
108 int n_valid_specs ;
109 int * valid_specs ;
110 double brightness;
111 //double brightest ;
112 int i ;
113
114 /* Test entries */
115 if (in == NULL) return -1 ;
116 if (orient!=0 && orient!=1) return -1 ;
117
118 /* Flip the image if necessary */
119 if (orient == 1) {
120 loc_ima = cpl_image_duplicate(in) ;
121 cpl_image_flip(loc_ima, 1) ;
122 } else {
123 loc_ima = cpl_image_duplicate(in) ;
124 }
125
126 /* Get rid of very high frequencies */
127 kernel = cpl_matrix_new(3, 3) ;
128 cpl_matrix_fill(kernel, 1.0) ;
129 if ((filt_image = xsh_image_filter_median(loc_ima, kernel)) == NULL) {
130 cpl_matrix_delete(kernel) ;
131 cpl_image_delete(loc_ima) ;
132 cpl_msg_error(cpl_func, "cannot filter the image") ;
133 return -1 ;
134 }
135 cpl_image_delete(loc_ima) ;
136 cpl_matrix_delete(kernel) ;
137
138 /* Collapse the image */
139 if ((collapsed = cpl_image_collapse_median_create(filt_image, 1, 0,
140 0)) == NULL) {
141 cpl_msg_error(cpl_func, "collapsing image: aborting spectrum detection");
142 cpl_image_delete(filt_image) ;
143 return -1 ;
144 }
145 cpl_image_delete(filt_image) ;
146
147 /* Subtract low frequency signal */
148 line = cpl_vector_new_from_image_column(collapsed, 1) ;
149 cpl_image_delete(collapsed) ;
150 line_filt = cpl_vector_filter_median_create(line, SPECTRUM_HW) ;
151 cpl_vector_subtract(line, line_filt) ;
152 cpl_vector_delete(line_filt) ;
153
154 /* Get relevant stats for thresholding */
155 median = cpl_vector_get_median_const(line) ;
156 stdev = cpl_vector_get_stdev(line) ;
157 max = cpl_vector_get_max(line) ;
158 mean = cpl_vector_get_mean(line) ;
159
160 /* Set the threshold */
161 threshold = median + stdev ;
163 if (threshold < MAX_THRESH_FACT * mean) threshold = MAX_THRESH_FACT * mean;
164
165 /* Recreate the image */
166 collapsed = cpl_image_new(1, cpl_vector_get_size(line), CPL_TYPE_FLOAT) ;
167 pcollapsed = cpl_image_get_data_float(collapsed) ;
168 pline = cpl_vector_get_data(line) ;
169 for (i=0 ; i<cpl_vector_get_size(line) ; i++)
170 pcollapsed[i] = (float)pline[i] ;
171 cpl_vector_delete(line) ;
172
173 /* Binarise the image */
174 if ((mask = cpl_mask_threshold_image_create(collapsed, threshold,
175 DBL_MAX)) == NULL) {
176 cpl_msg_error(cpl_func, "cannot binarise") ;
177 cpl_image_delete(collapsed) ;
178 return -1 ;
179 }
180 if (cpl_mask_count(mask) < 1) {
181 cpl_msg_error(cpl_func, "not enough signal to detect spectra") ;
182 cpl_image_delete(collapsed) ;
183 cpl_mask_delete(mask) ;
184 return -1 ;
185 }
186 /* Labelise the different detected apertures */
187 if ((labels = cpl_image_labelise_mask_create(mask, &nlabels))==NULL) {
188 cpl_msg_error(cpl_func, "cannot labelise") ;
189 cpl_image_delete(collapsed) ;
190 cpl_mask_delete(mask) ;
191 return -1 ;
192 }
193 cpl_mask_delete(mask) ;
194
195 /* Create the detected apertures list */
196 if ((aperts = cpl_apertures_new_from_image(collapsed, labels)) == NULL) {
197 cpl_msg_error(cpl_func, "cannot compute apertures") ;
198 cpl_image_delete(collapsed) ;
199 cpl_image_delete(labels) ;
200 return -1 ;
201 }
202 cpl_image_delete(labels) ;
203
204 /* Select only relevant specs, create corresponding LUT's */
205 if (select_valid_spectra(collapsed, aperts, offset, shadows, SPEC_MAXWIDTH,
206 &n_valid_specs, &valid_specs) == -1) {
207 cpl_msg_debug(cpl_func, "cannot select valid spectra") ;
208 cpl_image_delete(collapsed) ;
209 cpl_apertures_delete(aperts) ;
210 return -1 ;
211 }
212 cpl_image_delete(collapsed) ;
213 if (n_valid_specs < 1) {
214 cpl_msg_error(cpl_func, "no valid spectrum detected") ;
215 cpl_free(valid_specs) ;
216 cpl_apertures_delete(aperts) ;
217 return -1 ;
218 }
219
220 /* Look for the brightest, among the detected spectra */
221 *pos = cpl_apertures_get_centroid_y(aperts, valid_specs[0]+1) ;
222 //brightest = valid_specs[0] ;
223 brightness = cpl_apertures_get_flux(aperts, valid_specs[0]+1) ;
224 for (i=0 ; i<n_valid_specs ; i++) {
225 if (cpl_apertures_get_flux(aperts, valid_specs[i]+1) > brightness) {
226 *pos = cpl_apertures_get_centroid_y(aperts, valid_specs[i]+1) ;
227 //brightest = valid_specs[i] ;
228 brightness = cpl_apertures_get_flux(aperts, valid_specs[i]+1) ;
229 }
230 }
231 cpl_apertures_delete(aperts) ;
232 cpl_free(valid_specs) ;
233
234 /* Minimum brightness required */
235 if (brightness < min_bright) {
236 cpl_msg_error(cpl_func, "brightness %f too low <%f", brightness,
237 min_bright) ;
238 return -1 ;
239 }
240
241 /* Return */
242 return 0 ;
243}
244
245/*----------------------------------------------------------------------------*/
254/*----------------------------------------------------------------------------*/
256 const cpl_vector * in,
257 int fwhm,
258 double kappa,
259 int display)
260{
261 cpl_vector * filtered ;
262 cpl_vector * spec_clean ;
263 double * pspec_clean ;
264 int filt_size ;
265 cpl_vector * conv_kernel ;
266 cpl_vector * big_detected ;
267 double * pbig_detected ;
268 cpl_vector * detected ;
269 double * pdetected ;
270 double max, med, stdev, cur_val ;
271 int nb_det, nb_samples ;
272 int i, j ;
273
274 /* Test entries */
275 if (in == NULL) return NULL ;
276
277 /* Initialise */
278 nb_samples = cpl_vector_get_size(in) ;
279 filt_size = 5 ;
280
281 /* Subrtract the low frequency part */
282 cpl_msg_info(__func__, "Low Frequency signal removal") ;
283 if ((filtered=cpl_vector_filter_median_create(in, filt_size))==NULL){
284 cpl_msg_error(__func__, "Cannot filter the spectrum") ;
285 return NULL ;
286 }
287 spec_clean = cpl_vector_duplicate(in) ;
288 cpl_vector_subtract(spec_clean, filtered) ;
289 cpl_vector_delete(filtered) ;
290
291 /* Display if requested */
292 if (display) {
293 cpl_plot_vector(
294 "set grid;set xlabel 'Position (pixels)';set ylabel 'Intensity (ADU)';",
295 "t 'Filtered extracted spectrum' w lines", "", spec_clean);
296 }
297
298 /* Convolve */
299 cpl_msg_info(__func__, "Spectrum convolution") ;
300 /* Create convolution kernel */
301 if ((conv_kernel = cpl_wlcalib_xc_convolve_create_kernel(fwhm,
302 fwhm)) == NULL) {
303 cpl_msg_error(cpl_func, "Cannot create convolution kernel") ;
304 cpl_vector_delete(spec_clean) ;
305 return NULL ;
306 }
307
308 /* Smooth the instrument resolution */
309 if (cpl_wlcalib_xc_convolve(spec_clean, conv_kernel)) {
310 cpl_msg_error(cpl_func, "Cannot smoothe the signal");
311 cpl_vector_delete(spec_clean) ;
312 cpl_vector_delete(conv_kernel) ;
313 return NULL ;
314 }
315 cpl_vector_delete(conv_kernel) ;
316
317 /* Display if requested */
318 if (display) {
319 cpl_plot_vector(
320 "set grid;set xlabel 'Position (pixels)';set ylabel 'Intensity (ADU)';",
321 "t 'Convolved extracted spectrum' w lines", "", spec_clean);
322 }
323
324 /* Apply the detection */
325 big_detected = cpl_vector_duplicate(spec_clean) ;
326 pbig_detected = cpl_vector_get_data(big_detected) ;
327 pspec_clean = cpl_vector_get_data(spec_clean) ;
328
329 /* To avoid detection on the side */
330 pspec_clean[0] = pspec_clean[nb_samples-1] = 0.0 ;
331
332 /* Compute stats */
333 max = cpl_vector_get_max(spec_clean) ;
334 stdev = cpl_vector_get_stdev(spec_clean) ;
335 med = cpl_vector_get_median_const(spec_clean) ;
336
337 /* Loop on the detected lines */
338 nb_det = 0 ;
339 while (max > med + stdev * kappa) {
340 /* Compute the position */
341 i=0 ;
342 while (pspec_clean[i] < max) i++ ;
343 if (i<=0 || i>=nb_samples-1) break ;
344
345 /* Store the detected line */
346 pbig_detected[nb_det] = (pspec_clean[i]*i +
347 pspec_clean[i-1]*(i-1) + pspec_clean[i+1]*(i+1)) /
348 (pspec_clean[i]+pspec_clean[i-1]+pspec_clean[i+1]);
349 /* Position = index + 1 */
350 pbig_detected[nb_det] ++ ;
351 //cpl_msg_info(__func__, "Line nb %d at position %g",
352 // nb_det+1, pbig_detected[nb_det]) ;
353 nb_det ++ ;
354
355 /* Cancel out the line on the left */
356 j = i-1 ;
357 cur_val = pspec_clean[i] ;
358 while (j>=0 && pspec_clean[j] < cur_val) {
359 cur_val = pspec_clean[j] ;
360 pspec_clean[j] = 0.0 ;
361 j-- ;
362 }
363 /* Cancel out the line on the right */
364 j = i+1 ;
365 cur_val = pspec_clean[i] ;
366 while (j<=nb_samples-1 && pspec_clean[j] < cur_val) {
367 cur_val = pspec_clean[j] ;
368 pspec_clean[j] = 0.0 ;
369 j++ ;
370 }
371 /* Cancel out the line on center */
372 pspec_clean[i] = 0.0 ;
373
374 /* Recompute the stats */
375 max = cpl_vector_get_max(spec_clean) ;
376 stdev = cpl_vector_get_stdev(spec_clean) ;
377 med = cpl_vector_get_median_const(spec_clean) ;
378 }
379 cpl_vector_delete(spec_clean) ;
380 cpl_msg_info(__func__, "%d lines detected", nb_det) ;
381
382 /* Create the output vector */
383 if (nb_det == 0) {
384 detected = NULL ;
385 } else {
386 detected = cpl_vector_new(nb_det) ;
387 pdetected = cpl_vector_get_data(detected) ;
388 pbig_detected = cpl_vector_get_data(big_detected) ;
389 for (i=0 ; i<nb_det ; i++) pdetected[i] = pbig_detected[i] ;
390 }
391 cpl_vector_delete(big_detected) ;
392
393 /* Return */
394 return detected ;
395}
396
399/*----------------------------------------------------------------------------*/
411/*----------------------------------------------------------------------------*/
413 cpl_image * in,
414 cpl_apertures * aperts,
415 int offset,
416 spec_shadows shadows,
417 int max_spec_width,
418 int * n_valid_specs,
419 int ** valid_specs)
420{
421 int nb_aperts ;
422 int i, j ;
423
424 /* Initialise */
425 *valid_specs = NULL ;
426 nb_aperts = cpl_apertures_get_size(aperts) ;
427 *n_valid_specs = 0 ;
428
429 /* Test entries */
430 if (nb_aperts < 1) return -1 ;
431
432 /* Count nb of valid specs */
433 j = 0 ;
434 for (i=0 ; i<nb_aperts ; i++)
435 if (valid_spectrum(in, aperts, offset, shadows, max_spec_width,
436 i+1)) (*n_valid_specs)++ ;
437
438 /* Associate to each spectrum, its object number */
439 if (*n_valid_specs) {
440 *valid_specs = cpl_calloc(*n_valid_specs, sizeof(int)) ;
441 j = 0 ;
442 for (i=0 ; i<nb_aperts ; i++)
443 if (valid_spectrum(in, aperts, offset, shadows, max_spec_width,
444 i+1)) {
445 (*valid_specs)[j] = i ;
446 j++ ;
447 }
448 } else return -1 ;
449
450 return 0 ;
451}
452
453/*---------------------------------------------------------------------------*/
464/*----------------------------------------------------------------------------*/
465static int valid_spectrum(
466 cpl_image * in,
467 cpl_apertures * aperts,
468 int offset,
469 spec_shadows shadows,
470 int max_spec_width,
471 int objnum)
472{
473 int objwidth ;
474 double valover, valunder, valcenter ;
475
476 /* Find objwidth */
477 objwidth = cpl_apertures_get_top(aperts, objnum) -
478 cpl_apertures_get_bottom(aperts, objnum) + 1 ;
479 if (objwidth > max_spec_width) {
480 cpl_msg_error(cpl_func, "object is too wide") ;
481 return 0 ;
482 }
483
484 /* Object is too small */
485 if (cpl_apertures_get_npix(aperts, objnum) < 2) return 0 ;
486
487 /* no shadow required */
488 if (shadows == NO_SHADOW) return 1 ;
489
490 /* Get the median of the object (valcenter) */
491 valcenter = cpl_apertures_get_median(aperts, objnum) ;
492
493 /* Get the black shadows medians (valunder and valover) */
494 if (cpl_apertures_get_bottom(aperts, objnum) - offset < 1) valunder = 0.0 ;
495 else valunder = cpl_image_get_median_window(in, 1,
496 cpl_apertures_get_bottom(aperts, objnum) - offset, 1,
497 cpl_apertures_get_top(aperts, objnum) - offset) ;
498
499 if (cpl_apertures_get_top(aperts, objnum) + offset > 1024) valover = 0.0 ;
500 else valover = cpl_image_get_median_window(in, 1,
501 cpl_apertures_get_bottom(aperts, objnum) + offset, 1,
502 cpl_apertures_get_top(aperts, objnum) + offset) ;
503
504 switch (shadows) {
505 case TWO_SHADOWS:
506 if ((valunder < -fabs(valcenter/SPEC_SHADOW_FACT)) &&
507 (valover < -fabs(valcenter/SPEC_SHADOW_FACT)) &&
508 (valunder/valover > 0.5) &&
509 (valunder/valover < 2.0)) return 1 ;
510 else return 0 ;
511
512 case ONE_SHADOW:
513 if ((valunder < -fabs(valcenter/SPEC_SHADOW_FACT)) ||
514 (valover < -fabs(valcenter/SPEC_SHADOW_FACT))) return 1 ;
515 else return 0 ;
516
517 case NO_SHADOW:
518 return 1 ;
519
520 default:
521 cpl_msg_error(cpl_func, "unknown spec_detect_mode") ;
522 break ;
523 }
524
525 return 0 ;
526}
int xsh_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.
Definition: xsh_spectrum.c:86
cpl_vector * xsh_spectrum_detect_peaks(const cpl_vector *in, int fwhm, double kappa, int display)
Detect the brightest features in a spectrum.
Definition: xsh_spectrum.c:255
double kappa
Definition: xsh_detmon_lg.c:81
int threshold
Definition: xsh_detmon_lg.c:90
#define max(a, b)
static int select_valid_spectra(cpl_image *, cpl_apertures *, int, spec_shadows, int, int *, int **)
Selects the valid spectra in a spectral image.
Definition: xsh_spectrum.c:412
#define MAX_THRESH_FACT
Definition: xsh_spectrum.c:49
#define MIN_THRESH_FACT
Definition: xsh_spectrum.c:48
#define SPEC_MAXWIDTH
Definition: xsh_spectrum.c:51
#define SPEC_SHADOW_FACT
Definition: xsh_spectrum.c:50
#define SPECTRUM_HW
Definition: xsh_spectrum.c:47
static int valid_spectrum(cpl_image *, cpl_apertures *, int, spec_shadows, int, int)
Helper function to select_valid_spectra.
Definition: xsh_spectrum.c:465
@ NO_SHADOW
Definition: xsh_spectrum.h:47
@ TWO_SHADOWS
Definition: xsh_spectrum.h:43
@ ONE_SHADOW
Definition: xsh_spectrum.h:45
enum SPEC_SHADOWS spec_shadows
cpl_image * xsh_image_filter_median(const cpl_image *img, const cpl_matrix *mx)