IIINSTRUMENT Pipeline Reference Manual 4.6.1
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
54static 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/*----------------------------------------------------------------------------*/
90cpl_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/*----------------------------------------------------------------------------*/
278cpl_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/*----------------------------------------------------------------------------*/
514static 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.