VISIR Pipeline Reference Manual  4.1.0
visir_spc_distortion.c
1 /* $Id: visir_spc_distortion.c,v 1.50 2013-03-18 15:12:11 jtaylor Exp $
2  *
3  * This file is part of the VISIR Pipeline
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: jtaylor $
23  * $Date: 2013-03-18 15:12:11 $
24  * $Revision: 1.50 $
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 <string.h>
37 #include <math.h>
38 
39 #include <cpl.h>
40 
41 #include "visir_spc_distortion.h"
42 #include "visir_utils.h"
43 
44 /*----------------------------------------------------------------------------*/
48 /*----------------------------------------------------------------------------*/
49 
50 /*-----------------------------------------------------------------------------
51  Private Functions prototypes
52  -----------------------------------------------------------------------------*/
53 
54 static cpl_error_code visir_spc_det_warp_xy(int, double, double, double, double,
55  double, double, double, double,
56  double, double, double *, double*);
57 
58 /*-----------------------------------------------------------------------------
59  Functions code
60  -----------------------------------------------------------------------------*/
63 /*----------------------------------------------------------------------------*/
89 /*----------------------------------------------------------------------------*/
90 cpl_error_code visir_spc_det_fix(cpl_image ** images, int nimages,
91  cpl_boolean is_interm,
92  double wlen, visir_spc_resol resol,
93  double phi, double ksi, double eps,
94  double delta, int doplot)
95 {
96 
97  cpl_image * spectrum = NULL;
98 
99  int nx = 0; /* Avoid (false) uninit warning */
100  int ny = 0; /* Avoid (false) uninit warning */
101  int k;
102 
103 
104  skip_if (0);
105 
106  /* FIXME: LRP mode has an as yet unparameterized distortion */
107  if (resol == VISIR_SPC_R_LRP) return CPL_ERROR_NONE;
108 
109  skip_if (images == NULL);
110  skip_if (nimages <= 0);
111 
112  skip_if (!is_interm && nimages != 1);
113 
114  for (k=0; k < nimages; k++) {
115  if (images[k] == NULL) break;
116 
117  if (cpl_image_get_type(images[k]) != CPL_TYPE_FLOAT) {
118  /* Done only with --fixcombi (used for backwards compatability */
119  cpl_image * to_float = cpl_image_cast(images[k], CPL_TYPE_FLOAT);
120 
121  cpl_image_delete(images[k]);
122 
123  images[k] = to_float;
124 
125  }
126 
127  if (k == 0) {
128  nx = cpl_image_get_size_x(images[0]);
129  ny = cpl_image_get_size_y(images[0]);
130 
131  skip_if (nx < 1);
132  } else {
133  skip_if (nx != cpl_image_get_size_x(images[k]));
134  skip_if (ny != cpl_image_get_size_y(images[k]));
135  }
136 
137  }
138 
139  /* In HR only phi is used */
140  /* HR Grism mode has its own recipe and needs no hard-coded changes here */
141  if (resol == VISIR_SPC_R_HR) {
142 
143  visir_optmod ins_settings;
144 
145  /* for a or b detection aqu or drs does not matter */
146  if (visir_spc_optmod_init(VISIR_SPC_R_HR, wlen, &ins_settings, 0)) {
147  visir_error_set(CPL_ERROR_ILLEGAL_INPUT);
148  skip_if(1);
149  }
150 
151  if (visir_spc_optmod_side_is_A(&ins_settings)) {
152  /* Line curvature is zero in HR-A */
153  eps = 0;
154  /* Spectrum curvature is 1.5 times bigger in HR-A than in LMR */
155  delta *= 1.5;
156  } else {
157  /* Line curvature is 2.21 times bigger in HR-B than in LR */
158  eps *= 0.115/0.052;
159  /* Spectrum curvature is 2 times smaller in HR-B than in LMR */
160  delta *= 0.5;
161  }
162  ksi = 0; /* ksi is zero */
163  } else if (resol == VISIR_SPC_R_MR) {
164  eps *= 0.25; /* Line curvature is 4 times smaller in MR than in LR */
165  }
166 
167  cpl_msg_info(cpl_func, "Skew and Curvature: %g %g %g %g", phi, ksi, eps,
168  delta);
169 
170  /* The angles are coded in degrees and then converted to radians */
171  ksi *= CPL_MATH_RAD_DEG;
172  phi *= CPL_MATH_RAD_DEG;
173 
174  if (!is_interm) {
175 
176  float * px;
177  const double fdead = 0.25;
178  int ndead = 0; /* Number of rows of dead pixels */
179  const int mdeadmax = 4; /* Check 3 outermost rows */
180  int i,j;
181 
182  skip_if (visir_image_reject_hot(images[0], NULL));
183 
184  /* Check that the top row of pixels are not too small */
185 
186  spectrum = cpl_image_collapse_create(images[0], 1);
187  skip_if (0);
188 
189  px = cpl_image_get_data(spectrum);
190 
191  if (doplot > 1) visir_image_col_plot("", "t 'The collapsed 1/2-cycle "
192  "spectrum'", "", spectrum, 1,1,1);
193 
194  for (j = 1; j < mdeadmax; j++) {
195  const float g0 = px[ny -j] - px[ny-1-j];
196  const float g1 = px[ny-1-j] - px[ny-2-j];
197 
198  cpl_msg_debug(cpl_func, "GRAD(%g): %g <> %g", ny-j-0.5, g0, g1);
199 
200  if ((g1 < 0 && g0 * fdead < g1) || (g1 >=0 && g0 < g1 * fdead))
201  ndead = j;
202  }
203 
204  if (ndead) {
205  cpl_msg_info(cpl_func, "Rejecting dead pixels %d -> %d. "
206  "GRAD: %g << %g", ny-ndead, ny-1,
207  px[ny -ndead] - px[ny-1-ndead],
208  px[ny-1-ndead] - px[ny-2-ndead]);
209 
210  for (i = 0; i < nx; i++) {
211  for (j = ny-ndead; j < ny; j++) {
212  skip_if (cpl_image_reject(images[0], i+1, j+1));
213  }
214  }
215  ndead = 0;
216  }
217 
218  px = cpl_image_get_data(spectrum);
219 
220  for (j = 1; j < mdeadmax; j++) {
221  const float g0 = px[j-1] - px[j];
222  const float g1 = px[j] - px[j+1];
223 
224  cpl_msg_debug(cpl_func, "GRAD(%g:%d): %g <> %g", j-0.5, ndead, g0, g1);
225 
226  if ((g1 < 0 && g0 * fdead < g1) || (g1 >=0 && g0 < g1 * fdead))
227  ndead = j;
228  }
229 
230  if (ndead) {
231  cpl_msg_info(cpl_func, "Rejecting dead pixels 1 -> %d. GRAD: "
232  "%g << %g", ndead, px[ndead-1] - px[ndead],
233  px[ndead] - px[ndead+1]);
234 
235  for (i = 0; i < nx; i++) {
236  for (j = 0; j < ndead; j++) {
237  skip_if (cpl_image_reject(images[0], i+1, j+1));
238  }
239  }
240  }
241  cpl_image_delete(spectrum);
242  spectrum = NULL;
243 
244  }
245 
246  /* Apply the distortion correction */
247  skip_if (visir_spc_det_warp(images, nimages, 0., 0., phi, ksi, eps, delta));
248 
249  if (doplot > 1) visir_image_plot("", "t 'The first corrected image'",
250  "", images[0]);
251 
252  end_skip;
253 
254  cpl_image_delete(spectrum);
255 
256  return cpl_error_get_code();
257 
258 }
259 
260 
261 /*----------------------------------------------------------------------------*/
277 /*----------------------------------------------------------------------------*/
278 cpl_error_code visir_spc_det_warp(cpl_image ** images, int nimages,
279  double xshift, double yshift,
280  double phi, double ksi,
281  double eps, double delta)
282 {
283 
284 
285  const double radius = CPL_KERNEL_DEF_WIDTH;
286  const int tabsperpix = CPL_KERNEL_TABSPERPIX;
287  const int nx = cpl_image_get_size_x(images[0]);
288  const int ny = cpl_image_get_size_y(images[0]);
289  const cpl_boolean do_warp = phi != 0 || ksi != 0 ||
290  eps != 0 || delta != 0 || xshift != 0 || yshift != 0;
291  int iu, iv;
292  int iimage;
293  int ipix;
294 
295  cpl_image * copy = NULL;
296  cpl_vector * xyprofile = NULL;
297  cpl_image * coord_x = NULL;
298  cpl_image * coord_y = NULL;
299  double * piv = NULL; /* Avoid (false) uninit warning */
300  double * piu = NULL;
301  cpl_error_code didfail = CPL_ERROR_NONE;
302  cpl_errorstate cleanstate = cpl_errorstate_get();
303 
304 
305  skip_if (0);
306 
307  if (do_warp) {
308  double x = 0.0; /* Avoid (false) uninit warning */
309  double y = 0.0; /* Avoid (false) uninit warning */
310 
311  /* Avoid unnecessary interpolation */
312 
313  xyprofile = cpl_vector_new(1 + radius * tabsperpix);
314  cpl_vector_fill_kernel_profile(xyprofile, CPL_KERNEL_DEFAULT, radius);
315  coord_x = cpl_image_new(nx, ny, CPL_TYPE_DOUBLE);
316  coord_y = cpl_image_new(nx, ny, CPL_TYPE_DOUBLE);
317 
318  piu = cpl_image_get_data(coord_x);
319  piv = cpl_image_get_data(coord_y);
320 
321  skip_if (0);
322 
323  for (ipix = 0, iv = 1; iv <= ny; iv++) {
324  for (iu = 1; iu <= nx; iu++, ipix++) {
325 
326  skip_if (visir_spc_det_warp_xy(1, nx, ny, iu, iv,
327  xshift, yshift, phi, ksi, eps,
328  delta, &x, &y));
329  piu[ipix] = x;
330  piv[ipix] = y;
331  }
332  }
333 
334  }
335 
336 #ifdef _OPENMP
337 #pragma omp parallel for private(iimage, ipix, iv, iu, copy) if (nimages > 1)
338 #endif
339  for (iimage = 0 ; iimage < nimages; iimage++) {
340  cpl_image * image = images[iimage];
341  cpl_mask * rejected = NULL;
342  float * px = cpl_image_get_data(image);
343  double fluxprev, fluxpost;
344  int nbad = 0; /* Avoid (false) uninit warning */
345  cpl_binary * preject = NULL;
346 
347 
348  if (!do_warp || cpl_msg_get_level() <= CPL_MSG_DEBUG) {
349  nbad = cpl_image_count_rejected(image);
350 
351  cpl_msg_debug(cpl_func, "Raw image has %d bad pixels", nbad);
352  }
353 
354  if (cpl_error_get_code()) {
355  didfail = cpl_error_set(cpl_func, cpl_error_get_code());
356  irplib_error_recover(cleanstate, "Failure for image %d", iimage+1);
357  continue;
358  }
359 
360  if (!do_warp && nbad == 0) continue;
361 
362  fluxprev = cpl_image_get_flux(image);
363 
364  if (cpl_error_get_code()) {
365  didfail = cpl_error_set(cpl_func, cpl_error_get_code());
366  irplib_error_recover(cleanstate, "Failure for image %d", iimage+1);
367  continue;
368  }
369 
370  if (do_warp) {
371  copy = cpl_image_duplicate(image);
372 
373  if (cpl_error_get_code()) {
374  didfail = cpl_error_set(cpl_func, cpl_error_get_code());
375  irplib_error_recover(cleanstate, "Failure for image %d",
376  iimage+1);
377  continue;
378  }
379 
380  nbad = 0;
381  for (ipix = 0, iv = 1; iv <= ny; iv++) {
382  for (iu = 1; iu <= nx; iu++, ipix++) {
383  double value;
384  double confidence;
385 
386  value = cpl_image_get_interpolated(copy, piu[ipix], piv[ipix],
387  xyprofile, radius,
388  xyprofile, radius,
389  &confidence);
390  if (confidence < 0) {
391  cpl_mask_delete(rejected);
392  didfail = cpl_error_set(cpl_func,
393  CPL_ERROR_ILLEGAL_INPUT);
394  irplib_error_recover(cleanstate, "Failure for image %d",
395  iimage+1);
396  continue;
397  }
398 
399  if (confidence < 0.5) {
400  nbad++;
401  if (rejected == NULL) {
402  rejected = cpl_mask_new(nx, ny);
403  preject = cpl_mask_get_data(rejected);
404  }
405  preject[ipix] = CPL_BINARY_1;
406  px[ipix] = NAN;
407  } else {
408  /* The equivalent of at least 2 of the
409  nearest 4 pixels are good */
410  px[ipix] = value;
411  }
412  }
413  }
414 
415  cpl_image_delete(copy);
416  copy = NULL;
417  }
418 
419  if (nbad > 0) {
420  if (do_warp) {
421  const cpl_error_code error
422  = cpl_image_reject_from_mask(image, rejected);
423 
424  cpl_mask_delete(rejected);
425  rejected = NULL;
426  preject = NULL;
427 
428  if (error) {
429  didfail = cpl_error_set(cpl_func, cpl_error_get_code());
430  irplib_error_recover(cleanstate, "Failure for image %d",
431  iimage+1);
432  continue;
433  }
434  }
435  if (iimage == 0)
436  cpl_msg_info(cpl_func, "Cleaning %d bad pixels in "
437  "each of the %d images", nbad, nimages);
438  if (cpl_detector_interpolate_rejected(image)) {
439  didfail = cpl_error_set(cpl_func, cpl_error_get_code());
440  irplib_error_recover(cleanstate, "Failure for image %d",
441  iimage+1);
442  continue;
443  }
444 
445  }
446 
447  fluxpost = cpl_image_get_flux(image);
448 
449  if (cpl_error_get_code()) {
450  didfail = cpl_error_set(cpl_func, cpl_error_get_code());
451  irplib_error_recover(cleanstate, "Failure for image %d", iimage+1);
452  continue;
453  }
454 
455  cpl_msg_info(cpl_func, "Corrected distortion in image %d, flux: %g => %g",
456  1+iimage, fluxprev, fluxpost);
457 
458  }
459  skip_if(didfail);
460 
461  end_skip;
462 
463  if (cpl_error_get_code())
464  cpl_msg_error(cpl_func, "Detector unwarp failed (at %s): %s",
465  cpl_error_get_where(), cpl_error_get_message());
466 
467  cpl_vector_delete(xyprofile);
468  cpl_image_delete(copy);
469  cpl_image_delete(coord_x);
470  cpl_image_delete(coord_y);
471 
472  return cpl_error_get_code();
473 }
474 
477 /*----------------------------------------------------------------------------*/
513 /*----------------------------------------------------------------------------*/
514 static cpl_error_code visir_spc_det_warp_xy(int inv, double nx, double ny,
515  double x, double y,
516  double xshift, double yshift,
517  double phi, double ksi,
518  double eps, double delta,
519  double *pu, double *pv)
520 {
521 
522 
523 #ifdef VISIR_DET_WARP_ROTATE
524  double z;
525  const double skew = phi + ksi;
526 #endif
527 
528  cpl_ensure_code(pu, CPL_ERROR_NULL_INPUT);
529  cpl_ensure_code(pv, CPL_ERROR_NULL_INPUT);
530 
531  cpl_ensure_code(nx > 0, CPL_ERROR_ILLEGAL_INPUT);
532  cpl_ensure_code(ny > 0, CPL_ERROR_ILLEGAL_INPUT);
533 
534 
535  /* Transform Origo to center of detector */
536  x -= (nx+1)/2;
537  y -= (ny+1)/2;
538 
539  if (inv) {
540 
541  if (delta != 0) {
542  /* Correct vertical curvature */
543  const double R = ((ny/2)*(ny/2) + delta*delta) / fabs(2 * delta);
544 
545  cpl_ensure_code(R >= y, CPL_ERROR_ILLEGAL_INPUT);
546 
547  x -= (R - sqrt(R*R - y * y)) * (delta > 0 ? 1 : -1);
548  }
549 
550  if (eps != 0) {
551  /* Correct horizontal curvature */
552  const double R = ((nx/2)*(nx/2) + eps*eps) / fabs(2 * eps);
553 
554  cpl_ensure_code(R >= x, CPL_ERROR_ILLEGAL_INPUT);
555 
556  y += (R - sqrt(R*R - x * x)) * (eps > 0 ? 1 : -1);
557  }
558 
559  /* Skew into horizontal lines */
560  y -= x * tan(phi);
561 
562  /* Skew into vertical lines */
563  x -= y * tan(ksi);
564 
565  x -= xshift;
566  y -= yshift;
567 
568  } else {
569 
570  x += xshift;
571  y += yshift;
572 
573  /* Skew into vertical lines */
574  x += y * tan(ksi);
575 
576  /* Skew into horizontal lines */
577  y += x * tan(phi);
578 
579  if (eps != 0) {
580  /* Correct horizontal curvature */
581  const double R = ((ny/2)*(ny/2) + eps*eps) / fabs(2 * eps);
582 
583  cpl_ensure_code(R >= x, CPL_ERROR_ILLEGAL_INPUT);
584 
585  y -= (R - sqrt(R*R - x * x)) * (eps > 0 ? 1 : -1);
586  }
587  if (delta != 0) {
588  /* Correct vertical curvature */
589  const double R = ((ny/2)*(ny/2) + delta*delta) / fabs(2 * delta);
590 
591  cpl_ensure_code(R >= y, CPL_ERROR_ILLEGAL_INPUT);
592 
593  x += (R - sqrt(R*R - y * y)) * (delta > 0 ? 1 : -1);
594  }
595  }
596 
597  /* Transform Origo back from center of detector */
598  x += (nx+1)/2;
599  y += (ny+1)/2;
600 
601  *pu = x;
602  *pv = y;
603 
604  return CPL_ERROR_NONE;
605 
606 }
cpl_error_code visir_image_reject_hot(cpl_image *self, const char *bpmfile)
Reject the hot pixels in an image.