CR2RE Pipeline Reference Manual 1.7.0
hdrl_correlation.c
1/*
2 * This file is part of the HDRL
3 * Copyright (C) 2017 European Southern Observatory
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24/*-----------------------------------------------------------------------------
25 Includes
26 -----------------------------------------------------------------------------*/
27#include "hdrl_correlation.h"
28
29#include <math.h>
30
31
32/*-----------------------------------------------------------------------------
33 Data structures used internally
34 -----------------------------------------------------------------------------*/
35typedef struct{
36 double mean;
37 double stdev;
38}mean_and_stdev;
39
40/*-----------------------------------------------------------------------------
41 Private Functions
42 -----------------------------------------------------------------------------*/
43
44static inline
45double calculate_xcorr_sample(const cpl_size shift, const cpl_array * arr1,
46 const cpl_array * arr2, const double mean1, const double mean2,
47 const double stdev1, const double stdev2);
48
49static inline
50mean_and_stdev calculate_mean_and_stdev(const cpl_array * arr1);
51
52static inline cpl_error_code
53hdrl_compute_xcorrelation_refine(hdrl_xcorrelation_result* xcorr_res,
54 const double bin, const double wrange);
55
60/*-----------------------------------------------------------------------------
61 Functions
62 -----------------------------------------------------------------------------*/
63
64/* ---------------------------------------------------------------------------*/
76/* ---------------------------------------------------------------------------*/
77
78hdrl_xcorrelation_result *
79hdrl_xcorrelation_result_wrap(cpl_array * x_corr, const cpl_size max_idx,
80 const cpl_size half_window){
81
82 cpl_ensure(x_corr != NULL, CPL_ERROR_NULL_INPUT, NULL);
83 cpl_ensure(max_idx >= 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
84 cpl_ensure(max_idx < cpl_array_get_size(x_corr),
85 CPL_ERROR_ILLEGAL_INPUT, NULL);
86
87 hdrl_xcorrelation_result * to_ret = cpl_calloc(1, sizeof(*to_ret));
88
89 to_ret->xcorr = x_corr;
90 to_ret->pix_peakpos = max_idx;
91 to_ret->half_window = half_window;
92 return to_ret;
93}
94
95/* ---------------------------------------------------------------------------*/
100/* ---------------------------------------------------------------------------*/
101void hdrl_xcorrelation_result_delete(hdrl_xcorrelation_result * self){
102 if(self == NULL) return;
103
104 cpl_array_delete(self->xcorr);
105 cpl_free(self);
106}
107
108/* ---------------------------------------------------------------------------*/
116/* ---------------------------------------------------------------------------*/
117cpl_size
118hdrl_xcorrelation_result_get_peak_pixel(const hdrl_xcorrelation_result * self){
119
120 cpl_ensure(self != NULL, CPL_ERROR_NULL_INPUT, 0);
121
122 return self->pix_peakpos;
123}
124
125/* ---------------------------------------------------------------------------*/
135/* ---------------------------------------------------------------------------*/
137 (const hdrl_xcorrelation_result * self){
138
139 cpl_ensure(self != NULL, CPL_ERROR_NULL_INPUT, 0);
140
141 return self->peakpos;
142}
143
144/* ---------------------------------------------------------------------------*/
152/* ---------------------------------------------------------------------------*/
154 (const hdrl_xcorrelation_result * self){
155
156 cpl_ensure(self != NULL, CPL_ERROR_NULL_INPUT, 0);
157
158 return self->half_window;
159}
160
161/* ---------------------------------------------------------------------------*/
169/* ---------------------------------------------------------------------------*/
170double
171hdrl_xcorrelation_result_get_sigma(const hdrl_xcorrelation_result * self){
172
173 cpl_ensure(self != NULL, CPL_ERROR_NULL_INPUT, 0);
174
175 return self->sigma;
176}
177
178/* ---------------------------------------------------------------------------*/
186/* ---------------------------------------------------------------------------*/
187const cpl_array *
188hdrl_xcorrelation_result_get_correlation(const hdrl_xcorrelation_result * self){
189
190 cpl_ensure(self != NULL, CPL_ERROR_NULL_INPUT, NULL);
191
192 return self->xcorr;
193}
194
195/* ---------------------------------------------------------------------------*/
208/* ---------------------------------------------------------------------------*/
209hdrl_xcorrelation_result * hdrl_compute_xcorrelation(
210 const cpl_array * arr1, const cpl_array * arr2,
211 const cpl_size half_window, const cpl_boolean normalize){
212
213 cpl_ensure(half_window > 1, CPL_ERROR_INCOMPATIBLE_INPUT, NULL);
214
215 const mean_and_stdev not_normalized = {0.0, 1.0};
216
217 cpl_ensure(arr1 != NULL && arr2 != NULL, CPL_ERROR_NULL_INPUT, NULL);
218
219 cpl_size xcorr_length = 2 * half_window + 1;
220 cpl_array * corr = cpl_array_new(xcorr_length, HDRL_TYPE_DATA);
221
222 const mean_and_stdev d1 = normalize ?
223 calculate_mean_and_stdev(arr1) : not_normalized;
224 const mean_and_stdev d2 = normalize ?
225 calculate_mean_and_stdev(arr2) : not_normalized;
226
227 cpl_size max_idx = -1;
228 double max_corr = 0.0;
229
230 for(cpl_size i = -half_window; i <= half_window; ++i){
231 const double cr = calculate_xcorr_sample(i, arr1, arr2, d1.mean, d2.mean,
232 d1.stdev, d2.stdev);
233
234 const cpl_size idx = i + half_window;
235 cpl_array_set(corr, idx, cr);
236
237 if(isnan(cr)) continue;
238
239 if(cr >= max_corr || max_idx < 0){
240 max_corr = cr;
241 max_idx = idx;
242 }
243 }
244
245 return hdrl_xcorrelation_result_wrap(corr, max_idx, half_window);
246}
247
248static inline
249cpl_error_code check_if_bad(const hdrl_xcorrelation_result * gfit,
250 const cpl_boolean check_refine){
251
252 cpl_ensure_code(gfit != NULL, CPL_ERROR_ILLEGAL_OUTPUT);
253 cpl_ensure_code(hdrl_xcorrelation_result_get_peak_pixel(gfit) >=0,
254 CPL_ERROR_ILLEGAL_OUTPUT);
255
256 if(check_refine){
257
258 const double px = hdrl_xcorrelation_result_get_peak_subpixel(gfit);
259 cpl_ensure_code(px >= 0.0 && !isnan(px), CPL_ERROR_ILLEGAL_OUTPUT);
260
261 const double sigma = hdrl_xcorrelation_result_get_sigma(gfit);
262 cpl_ensure_code( sigma > 0.0 && !isnan(sigma),CPL_ERROR_ILLEGAL_OUTPUT);
263 }
264
265 return CPL_ERROR_NONE;
266}
267
268static inline
269cpl_boolean check_and_delete_if_bad(hdrl_xcorrelation_result ** gfit_arg,
270 const cpl_boolean check_refine){
271
272 cpl_error_code fail = check_if_bad(*gfit_arg, check_refine);
273
274 if(fail){
276 *gfit_arg = NULL;
277 }
278
279 return fail ? CPL_FALSE : CPL_TRUE;
280}
281
282/* ---------------------------------------------------------------------------*/
296/* ---------------------------------------------------------------------------*/
297hdrl_xcorrelation_result * hdrl_compute_offset_gaussian(
298 const cpl_array * arr1,
299 const cpl_array * arr2,
300 const cpl_size half_win, const cpl_boolean normalize,
301 const double bin, const double wrange){
302
303 cpl_ensure(half_win > 1, CPL_ERROR_ILLEGAL_INPUT, NULL);
304 cpl_ensure(arr1 != NULL, CPL_ERROR_NULL_INPUT, NULL);
305 cpl_ensure(arr2 != NULL, CPL_ERROR_NULL_INPUT, NULL);
306
307 hdrl_xcorrelation_result * gfit =
308 hdrl_compute_offset_gaussian_internal(arr1, arr2, half_win,
309 normalize, bin, wrange);
310
311 cpl_ensure(gfit != NULL, CPL_ERROR_ILLEGAL_OUTPUT, NULL);
312
313 const cpl_size half_win2 =
314 (cpl_size)(3. * CPL_MATH_FWHM_SIG * gfit->sigma / bin);
315
317
318 gfit = hdrl_compute_offset_gaussian_internal(arr1, arr2, half_win2,
319 normalize, bin, wrange);
320
321 return gfit;
322}
323
324/* ---------------------------------------------------------------------------*/
337/* ---------------------------------------------------------------------------*/
338hdrl_xcorrelation_result * hdrl_compute_offset_gaussian_internal(
339 const cpl_array * arr1, const cpl_array * arr2,
340 const cpl_size half_win, const cpl_boolean normalize,
341 const double bin, const double wrange){
342
343 hdrl_xcorrelation_result * res =
344 hdrl_compute_xcorrelation(arr1, arr2, half_win, normalize);
345
346 if(!check_and_delete_if_bad(&res, CPL_FALSE)) return NULL;
347
348 cpl_error_code fail =
349 hdrl_compute_xcorrelation_refine(res, bin, wrange);
350
351 if(fail){
353 return NULL;
354 }
355
356 if(!check_and_delete_if_bad(&res, CPL_TRUE)) return NULL;
357
358 return res;
359}
360
361/* ---------------------------------------------------------------------------*/
371/* ---------------------------------------------------------------------------*/
372static inline cpl_error_code
373hdrl_compute_xcorrelation_refine(hdrl_xcorrelation_result* xcorr_res,
374 const double bin, const double wrange){
375
376 const cpl_array * xcorr = hdrl_xcorrelation_result_get_correlation(xcorr_res);
377 const cpl_size maxpos = hdrl_xcorrelation_result_get_peak_pixel(xcorr_res);
378
379 const cpl_size xcorr_size = cpl_array_get_size(xcorr);
380 const cpl_size pre_idx = CPL_MAX(0, maxpos - 1);
381 const cpl_size post_idx = CPL_MIN(maxpos + 1, xcorr_size - 1);
382
383 const double a = cpl_array_get(xcorr, pre_idx, NULL);
384 const double b = cpl_array_get(xcorr, post_idx, NULL);
385 const double c = cpl_array_get(xcorr, maxpos, NULL);
386 /* Find sub-pixel peak initial estimation. Use parabolic assumption */
387 const double fraction = (b - a) / ( 4. * c - 2. * a - 2. * b );
388 const double subpix_offset = maxpos - fraction;
389
390 xcorr_res->peakpos = subpix_offset * bin;
391 xcorr_res->sigma = bin * 10;
392 xcorr_res->area = 1.0;
393
394 cpl_size num_elems = 0;
395
396 cpl_vector * wavs_windowed = cpl_vector_new(xcorr_size);
397 cpl_vector * corr_windowed = cpl_vector_new(xcorr_size);
398
399 for(cpl_size i = 0; i < xcorr_size; ++i){
400 const double w = i * bin;
401 int rej = 0;
402 const double xcorr_data = cpl_array_get(xcorr, i, &rej);
403
404 if(rej || isnan(xcorr_data)) continue;
405 if(w < xcorr_res->peakpos - wrange
406 || w > xcorr_res->peakpos + wrange) continue;
407
408 cpl_vector_set(corr_windowed, num_elems, xcorr_data);
409 cpl_vector_set(wavs_windowed, num_elems, w);
410
411 num_elems++;
412 }
413
414 if(num_elems <= 0){
415 cpl_vector_delete(wavs_windowed);
416 cpl_vector_delete(corr_windowed);
417 cpl_ensure_code(CPL_FALSE, CPL_ERROR_ILLEGAL_OUTPUT);
418 }
419
420 cpl_vector_set_size(corr_windowed, num_elems);
421 cpl_vector_set_size(wavs_windowed, num_elems);
422
423 cpl_error_code code = cpl_vector_fit_gaussian(wavs_windowed, NULL,
424 corr_windowed, NULL, CPL_FIT_ALL, &xcorr_res->peakpos, &xcorr_res->sigma,
425 &xcorr_res->area, &xcorr_res->offset, &xcorr_res->mse, NULL, NULL);
426
427 /* if the fitting does not converge, CPL_ERROR_CONTINUE is set, the
428 * output parameters are calculated using a best-effort approach.
429 * See the documentation of cpl_vector_fit_gaussian .*/
430 if(code == CPL_ERROR_CONTINUE){
431 cpl_error_reset();
432 }
433
434 cpl_vector_delete(wavs_windowed);
435 cpl_vector_delete(corr_windowed);
436
437 return cpl_error_get_code();
438}
439
440static inline
441double calculate_xcorr_sample(const cpl_size shift,
442 const cpl_array * arr1, const cpl_array * arr2,
443 const double mean1, const double mean2,
444 const double stdev1, const double stdev2){
445
446 double d = 0.0;
447 cpl_size num_el = 0;
448
449 const double norm = 1.00 / sqrt(stdev1 * stdev2);
450
451 const cpl_size l1 = cpl_array_get_size(arr1);
452 const cpl_size l2 = cpl_array_get_size(arr2);
453
454 for(cpl_size i = 0; i < l2; i++){
455 const cpl_size j = i + shift;
456 int rej1 = 0, rej2 = 0;
457 if(j < 0) continue;
458 if(j >= l1) continue;
459
460 const double v1 = cpl_array_get(arr1, j, &rej1);
461 const double v2 = cpl_array_get(arr2, i, &rej2);
462
463 if(rej1 || rej2) continue;
464
465 const double val = norm * (v1 - mean1) * (v2 - mean2);
466
467 d += val;
468 num_el++;
469 }
470 return d / (double)num_el;
471}
472
473static inline
474mean_and_stdev calculate_mean_and_stdev(const cpl_array * arr1){
475
476 const double mean = cpl_array_get_mean(arr1);
477 const double stdev = cpl_array_get_stdev(arr1);
478
479 return (mean_and_stdev){mean, stdev};
480}
void hdrl_xcorrelation_result_delete(hdrl_xcorrelation_result *self)
Destructor for hdrl_xcorrelation_result.
hdrl_xcorrelation_result * hdrl_compute_xcorrelation(const cpl_array *arr1, const cpl_array *arr2, const cpl_size half_window, const cpl_boolean normalize)
Calculate cross-correlation.
const cpl_array * hdrl_xcorrelation_result_get_correlation(const hdrl_xcorrelation_result *self)
Getter for the cross correlation.
cpl_size hdrl_xcorrelation_result_get_peak_pixel(const hdrl_xcorrelation_result *self)
Get the index where the cross correlation reaches its maximum.
cpl_size hdrl_xcorrelation_result_get_half_window(const hdrl_xcorrelation_result *self)
Get the half_window used to calculate the cross-correlation.
hdrl_xcorrelation_result * hdrl_compute_offset_gaussian_internal(const cpl_array *arr1, const cpl_array *arr2, const cpl_size half_win, const cpl_boolean normalize, const double bin, const double wrange)
Calculate gaussian fit on cross-correlation.
hdrl_xcorrelation_result * hdrl_compute_offset_gaussian(const cpl_array *arr1, const cpl_array *arr2, const cpl_size half_win, const cpl_boolean normalize, const double bin, const double wrange)
Calculate gaussian fit on cross-correlation, does a second fitting for refinement.
double hdrl_xcorrelation_result_get_peak_subpixel(const hdrl_xcorrelation_result *self)
Get the index where the cross correlation reaches its maximum, with sub-pixel precision.
double hdrl_xcorrelation_result_get_sigma(const hdrl_xcorrelation_result *self)
Get the estimated standard deviation of the correlation.
hdrl_xcorrelation_result * hdrl_xcorrelation_result_wrap(cpl_array *x_corr, const cpl_size max_idx, const cpl_size half_window)
Constructor for hdrl_xcorrelation_result.
double fraction(double x, double y, double r_out)
Fraction of pixel bounded.