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