MUSE Pipeline Reference Manual  1.0.2
muse_image_fwhm.c
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set sw=2 sts=2 et cin: */
3 /*
4  * This file is part of the MUSE Instrument Pipeline
5  * Copyright (C) 2007-2014 European Southern Observatory
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21 
22 #include <muse.h>
23 #include <string.h>
24 #include <strings.h>
25 
26 /*----------------------------------------------------------------------------*/
60 /*----------------------------------------------------------------------------*/
61 
64 /* a version of cpl_apertures_extract() without filtering */
65 static cpl_apertures *
66 image_fwhm_cplapertures_extract(cpl_image *aImage, cpl_vector *aSigmas,
67  cpl_size *aIndex)
68 {
69  cpl_ensure(aImage && aSigmas, CPL_ERROR_NULL_INPUT, NULL);
70  cpl_apertures *aperts = NULL;
71  cpl_errorstate prestate = cpl_errorstate_get();
72  cpl_size i, n = cpl_vector_get_size(aSigmas);
73  for (i = 0; i < n; i++) {
74  const double sigma = cpl_vector_get(aSigmas, i);
75  if (sigma <= 0.0) {
76  break;
77  }
78 
79  /* Compute the threshold */
80  double mdev, median = cpl_image_get_median_dev(aImage, &mdev),
81  threshold = median + sigma * mdev;
82  /* Binarise the image and divide into separate objects */
83  cpl_mask *selection = cpl_mask_threshold_image_create(aImage, threshold,
84  DBL_MAX);
85  cpl_size nlabels;
86  cpl_image *labels = cpl_image_labelise_mask_create(selection, &nlabels);
87  cpl_mask_delete(selection);
88  /* Create the detected apertures list */
89  aperts = cpl_apertures_new_from_image(aImage, labels);
90  cpl_image_delete(labels);
91 
92  if (aperts) {
93  break;
94  }
95  }
96  cpl_ensure(aperts, CPL_ERROR_DATA_NOT_FOUND, NULL);
97  /* Recover from any errors set in the extraction */
98  cpl_errorstate_set(prestate);
99  if (aIndex) {
100  *aIndex = i;
101  }
102  return aperts;
103 } /* image_fwhm_cplapertures_extract() */
104 
105 #define PRINT_USAGE(rc) \
106  fprintf(stderr, "Usage: %s [ -s sigma ] [ -x extindex | -n extname] " \
107  "IMAGE\n", argv[0]); \
108  cpl_end(); return (rc);
109 
110 int main(int argc, char **argv)
111 {
112  cpl_init(CPL_INIT_DEFAULT);
113 
114  if (argc <= 1) {
115  /* filename is needed at least */
116  PRINT_USAGE(1);
117  }
118 
119  char *iname = NULL, /* image name */
120  *extname = NULL; /* half-optional extension name */
121  double sigma = -1; /* detection sigma level */
122  int iext = -1; /* half-optional extension number */
123 
124  /* argument processing */
125  int i;
126  for (i = 1; i < argc; i++) {
127  if (strncmp(argv[i], "-s", 3) == 0) {
128  /* skip to next arg to get sigma value */
129  i++;
130  if (i < argc) {
131  sigma = atof(argv[i]);
132  if (sigma <= 0.) {
133  PRINT_USAGE(3);
134  }
135  } else {
136  PRINT_USAGE(2);
137  }
138  } else if (strncmp(argv[i], "-x", 3) == 0) {
139  /* skip to next arg to get sigma value */
140  i++;
141  if (i < argc) {
142  iext = atoi(argv[i]);
143  if (iext < 0) {
144  PRINT_USAGE(5);
145  }
146  } else {
147  PRINT_USAGE(4);
148  }
149  } else if (strncmp(argv[i], "-n", 3) == 0) {
150  /* skip to next arg to get sigma value */
151  i++;
152  if (i < argc) {
153  extname = argv[i];
154  } else {
155  PRINT_USAGE(6);
156  }
157  } else if (strncmp(argv[i], "-", 1) == 0) { /* unallowed options */
158  PRINT_USAGE(9);
159  } else {
160  if (iname) {
161  break; /* we have the possible names, skip the rest */
162  }
163  iname = argv[i] /* set the name for the image */;
164  }
165  } /* for i (all arguments) */
166  if (extname && iext >= 0) {
167  PRINT_USAGE(7);
168  }
169 
170  int next = cpl_fits_count_extensions(iname);
171  if (next < 0) {
172  PRINT_USAGE(10);
173  }
174  if (extname) {
175  iext = cpl_fits_find_extension(iname, extname);
176  }
177  cpl_image *image = NULL;
178  if (iext >= 0) { /* load specified extension */
179  image = cpl_image_load(iname, CPL_TYPE_UNSPECIFIED, 0, iext);
180  } else { /* iterate through extensions, try to load the first image */
181  cpl_errorstate ps = cpl_errorstate_get();
182  do {
183  image = cpl_image_load(iname, CPL_TYPE_UNSPECIFIED, 0, ++iext);
184  } while (!image && iext <= next);
185  cpl_errorstate_set(ps); /* recover from multiple trials to load the image */
186  }
187  if (!image) {
188  PRINT_USAGE(11);
189  }
190  cpl_propertylist *header = cpl_propertylist_load(iname, iext);
191  int nx = cpl_image_get_size_x(image),
192  ny = cpl_image_get_size_y(image);
193  char *extdisp = NULL; /* some extension property (name or index) to display */
194  if (cpl_propertylist_has(header, "EXTNAME")) {
195  extdisp = cpl_strdup(cpl_propertylist_get_string(header, "EXTNAME"));
196  } else {
197  extdisp = cpl_sprintf("%d", iext);
198  }
199  /* mark bad pixels (NAN) as bad in the CPL image, to ignore them for statistics */
200  cpl_image_reject_value(image, CPL_VALUE_NAN);
201  int nrej = cpl_image_count_rejected(image);
202  printf("# Input image \"%s[%s]\" (size %dx%d, rejected %d)\n", iname, extdisp,
203  nx, ny, nrej);
204  cpl_free(extdisp);
205 
206  cpl_vector *vsigmas = NULL;
207  if (sigma > 0) {
208  vsigmas = cpl_vector_new(1);
209  cpl_vector_set(vsigmas, 0, sigma);
210  } else {
211  vsigmas = cpl_vector_new(6);
212  cpl_vector_set(vsigmas, 0, 50.);
213  cpl_vector_set(vsigmas, 1, 30.);
214  cpl_vector_set(vsigmas, 2, 20.);
215  cpl_vector_set(vsigmas, 3, 10.);
216  cpl_vector_set(vsigmas, 4, 8.);
217  cpl_vector_set(vsigmas, 5, 5.);
218  }
219  cpl_size isigma = -1;
220  cpl_errorstate prestate = cpl_errorstate_get();
221  cpl_apertures *apertures = image_fwhm_cplapertures_extract(image, vsigmas, &isigma);
222  if (!apertures || !cpl_errorstate_is_equal(prestate)) {
223  cpl_image_delete(image);
224  fprintf(stderr, "%s: no sources found for FWHM measurement down to %.1f "
225  "sigma limit!\n", argv[0],
226  cpl_vector_get(vsigmas, cpl_vector_get_size(vsigmas) - 1));
227  cpl_vector_delete(vsigmas);
228  return 20;
229  }
230  int ndet = cpl_apertures_get_size(apertures);
231  printf("# %s: computing FWHM QC parameters for %d source%s found down to the "
232  "%.1f sigma threshold\n", argv[0], ndet, ndet == 1 ? "" : "s",
233  cpl_vector_get(vsigmas, isigma));
234  cpl_vector_delete(vsigmas);
235 
236  /* get some kind of WCS for conversion of FWHM from pixels to arcsec, *
237  * by default assume WFM and x=RA and y=DEC */
238  double cd11 = kMuseSpaxelSizeX_WFM, cd12 = 0., cd21 = 0.,
239  cd22 = kMuseSpaxelSizeY_WFM;
240  cpl_wcs *wcs = cpl_wcs_new_from_propertylist(header);
241  if (!wcs ||
242  !strncasecmp(cpl_propertylist_get_string(header, "CTYPE1"), "PIXEL", 5)) {
243  printf("# %s: FWHM parameter estimation (%d sources): simple conversion "
244  "to arcsec (CTYPE=%s/%s)!\n", argv[0], ndet,
245  cpl_propertylist_get_string(header, "CTYPE1"),
246  cpl_propertylist_get_string(header, "CTYPE2"));
247  if (muse_pfits_get_mode(header) > MUSE_MODE_WFM_AO_N) { /* NFM scaling */
248  cd11 = kMuseSpaxelSizeX_WFM;
249  cd22 = kMuseSpaxelSizeY_WFM;
250  }
251  } else {
252  const cpl_matrix *cd = cpl_wcs_get_cd(wcs);
253  /* take the absolute and scale by 3600 to get positive arcseconds */
254  cd11 = fabs(cpl_matrix_get(cd, 0, 0)) * 3600.,
255  cd12 = fabs(cpl_matrix_get(cd, 0, 1)) * 3600.,
256  cd21 = fabs(cpl_matrix_get(cd, 1, 0)) * 3600.,
257  cd22 = fabs(cpl_matrix_get(cd, 1, 1)) * 3600.;
258  printf("# %s: FWHM parameter estimation (%d sources): full "
259  "conversion to arcsec (CD=%.2f,%.2f,%.2f,%.2f)\n", argv[0], ndet,
260  cd11, cd12, cd21, cd22);
261  }
262  cpl_wcs_delete(wcs); /* rely on internal NULL check */
263 
264  /* Compute FWHM of all apertures */
265  cpl_vector *vfwhm = cpl_vector_new(ndet),
266  *vgfwhm = cpl_vector_new(ndet);
267  printf("#index xPOS yPOS xFWHM yFWHM xgFWHM ygFWHM\n");
268  int n, idx = 0;
269  for (n = 1; n <= ndet; n++) {
270  double xcen = cpl_apertures_get_centroid_x(apertures, n),
271  ycen = cpl_apertures_get_centroid_y(apertures, n);
272  if (xcen < 2 || nx - xcen < 2 || ycen < 2 || ny - ycen < 2) {
273  fprintf(stderr, "#bad %4d %7.3f %7.3f too close to the edge\n",
274  n, xcen, ycen);
275  fflush(NULL);
276  continue; /* skip, too close to image edge */
277  }
278  /* direct determination */
279  double xfwhm, yfwhm;
280  cpl_image_get_fwhm(image, lround(xcen), lround(ycen), &xfwhm, &yfwhm);
281 
282  /* Gaussian FWHM */
283  cpl_array *params = cpl_array_new(7, CPL_TYPE_DOUBLE);
284  cpl_array_set_double(params, 0, cpl_apertures_get_min(apertures, n));
285  cpl_array_set_double(params, 1, cpl_apertures_get_flux(apertures, n));
286  cpl_array_set_double(params, 2, 0);
287  cpl_array_set_double(params, 3, xcen);
288  cpl_array_set_double(params, 4, ycen);
289  cpl_array_set_double(params, 5, xfwhm > 0 ? xfwhm / CPL_MATH_FWHM_SIG : 1.);
290  cpl_array_set_double(params, 6, yfwhm > 0 ? yfwhm / CPL_MATH_FWHM_SIG : 1.);
291  cpl_size xsize = cpl_apertures_get_right(apertures, n)
292  - cpl_apertures_get_left(apertures, n) + 1,
293  ysize = cpl_apertures_get_top(apertures, n)
294  - cpl_apertures_get_bottom(apertures, n) + 1;
295  xsize = xsize < 4 ? 4 : xsize;
296  ysize = ysize < 4 ? 4 : ysize;
297  cpl_error_code rc = cpl_fit_image_gaussian(image, NULL,
298  cpl_apertures_get_pos_x(apertures, n),
299  cpl_apertures_get_pos_y(apertures, n),
300  xsize, ysize, params, NULL, NULL,
301  NULL, NULL, NULL, NULL, NULL, NULL,
302  NULL);
303  double xgfwhm = cpl_array_get(params, 5, NULL) * CPL_MATH_FWHM_SIG,
304  ygfwhm = cpl_array_get(params, 6, NULL) * CPL_MATH_FWHM_SIG;
305  cpl_array_delete(params);
306  /* check that the fit suceeded, and that it gave reasonable values */
307 #define MAX_AXIS_RATIO 5.
308 #define MAX_FWHM_RATIO 4.
309  if (rc != CPL_ERROR_NONE || xgfwhm > nx || ygfwhm > ny ||
310  fabs(xgfwhm/ygfwhm) > MAX_AXIS_RATIO || fabs(ygfwhm/xgfwhm) > MAX_AXIS_RATIO ||
311  fabs(xgfwhm/xfwhm) > MAX_FWHM_RATIO || fabs(ygfwhm/yfwhm) > MAX_FWHM_RATIO ||
312  fabs(xfwhm/xgfwhm) > MAX_FWHM_RATIO || fabs(yfwhm/ygfwhm) > MAX_FWHM_RATIO) {
313  fprintf(stderr, "#bad %4d %7.3f %7.3f %5.2f %5.2f %5.2f %5.2f "
314  "rc = %d: %s\n", n, xcen, ycen, xfwhm, yfwhm, xgfwhm, ygfwhm,
315  rc, cpl_error_get_message());
316  fflush(NULL);
317  continue; /* skip this faulty one */
318  }
319 
320  /* linear WCS scaling */
321  xfwhm = cd11 * xfwhm + cd12 * yfwhm;
322  yfwhm = cd22 * yfwhm + cd21 * xfwhm;
323  xgfwhm = cd11 * xgfwhm + cd12 * ygfwhm;
324  ygfwhm = cd22 * ygfwhm + cd21 * xgfwhm;
325  printf("%6d %7.3f %7.3f %5.2f %5.2f %5.2f %5.2f\n",
326  n, xcen, ycen, xfwhm, yfwhm, xgfwhm, ygfwhm);
327  fflush(stdout);
328 
329  cpl_vector_set(vfwhm, idx, (xfwhm + yfwhm) / 2.);
330  cpl_vector_set(vgfwhm, idx++, (xgfwhm + ygfwhm) / 2.);
331  } /* for n (aperture number) */
332  cpl_apertures_delete(apertures);
333  cpl_vector_set_size(vfwhm, idx);
334  cpl_vector_set_size(vgfwhm, idx);
335  printf("#Summary:\n#\tdirect FWHM %.3f +/- %.3f (%.3f) arcsec\n"
336  "#\tGaussian FWHM %.3f +/- %.3f (%.3f) arcsec\n", cpl_vector_get_mean(vfwhm),
337  cpl_vector_get_stdev(vfwhm), cpl_vector_get_median(vfwhm),
338  cpl_vector_get_mean(vgfwhm), cpl_vector_get_stdev(vgfwhm),
339  cpl_vector_get_median(vgfwhm));
340  cpl_vector_delete(vfwhm);
341  cpl_vector_delete(vgfwhm);
342 
343  cpl_image_delete(image);
344  cpl_end();
345  return 0;
346 }
347 
muse_ins_mode muse_pfits_get_mode(const cpl_propertylist *aHeaders)
find out the observation mode
Definition: muse_pfits.c:1097