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