Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
cacorrect.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2010-2013, 2016, 2018 johannes hanika.
4 Copyright (C) 2010 Kaminsky Andrey.
5 Copyright (C) 2011 Henrik Andersson.
6 Copyright (C) 2011 Karl Mikaelsson.
7 Copyright (C) 2011 Robert Bieber.
8 Copyright (C) 2011-2014, 2016, 2019 Tobias Ellinghaus.
9 Copyright (C) 2012 parafin.
10 Copyright (C) 2012-2013 Pascal de Bruijn.
11 Copyright (C) 2012 Richard Wonka.
12 Copyright (C) 2012 Ulrich Pegelow.
13 Copyright (C) 2013, 2020 Aldric Renaudin.
14 Copyright (C) 2013-2016, 2018 Roman Lebedev.
15 Copyright (C) 2014 Dan Torop.
16 Copyright (C) 2015-2016 Pedro Côrte-Real.
17 Copyright (C) 2016 CarVac.
18 Copyright (C) 2017 Heiko Bauke.
19 Copyright (C) 2017, 2021 luzpaz.
20 Copyright (C) 2018, 2020, 2022-2023, 2025-2026 Aurélien PIERRE.
21 Copyright (C) 2018 Edgardo Hoszowski.
22 Copyright (C) 2018 Kelvie Wong.
23 Copyright (C) 2018 Maurizio Paglia.
24 Copyright (C) 2018-2022 Pascal Obry.
25 Copyright (C) 2018, 2021 rawfiner.
26 Copyright (C) 2019-2022 Hanno Schwalm.
27 Copyright (C) 2020 Diederik Ter Rahe.
28 Copyright (C) 2020-2021 Hubert Kowalski.
29 Copyright (C) 2020 Miroslav Silovic.
30 Copyright (C) 2020 Ralf Brown.
31 Copyright (C) 2022 Martin Bařinka.
32 Copyright (C) 2022 Philipp Lutz.
33 Copyright (C) 2022 Victor Forsiuk.
34
35 darktable is free software: you can redistribute it and/or modify
36 it under the terms of the GNU General Public License as published by
37 the Free Software Foundation, either version 3 of the License, or
38 (at your option) any later version.
39
40 darktable is distributed in the hope that it will be useful,
41 but WITHOUT ANY WARRANTY; without even the implied warranty of
42 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
43 GNU General Public License for more details.
44
45 You should have received a copy of the GNU General Public License
46 along with darktable. If not, see <http://www.gnu.org/licenses/>.
47*/
48#ifdef HAVE_CONFIG_H
49#include "config.h"
50#endif
51#include "bauhaus/bauhaus.h"
52#include "common/darktable.h"
53#include "common/imagebuf.h"
54#include "common/gaussian.h"
55#include "develop/imageop.h"
56#include "develop/imageop_gui.h"
58#include "develop/develop.h"
59
60#include "gui/gtk.h"
61#include "iop/iop_api.h"
62
63#include <gtk/gtk.h>
64#include <stdlib.h>
65
74// this is the version of the modules parameters,
75// and includes version information about compile-time dt
77
78#pragma GCC diagnostic ignored "-Wshadow"
79
81{
82 CACORRETC_MULTI_1 = 1, // $DESCRIPTION: "once"
83 CACORRETC_MULTI_2 = 2, // $DESCRIPTION: "twice"
84 CACORRETC_MULTI_3 = 3, // $DESCRIPTION: "three times"
85 CACORRETC_MULTI_4 = 4, // $DESCRIPTION: "four times"
86 CACORRETC_MULTI_5 = 5, // $DESCRIPTION: "five times"
88
90{
91 gboolean avoidshift; // $DEFAULT: 0 $DESCRIPTION: "avoid colorshift"
92 dt_iop_cacorrect_multi_t iterations; // $DEFAULT: CACORRETC_MULTI_2 $DESCRIPTION: "iterations"
94
100
106
107// this returns a translatable name
108const char *name()
109{
110 // make sure you put all your translatable strings into _() !
111 return _("raw chromatic aberrations");
112}
113
114const char **description(struct dt_iop_module_t *self)
115{
116 return dt_iop_set_description(self, _("correct chromatic aberrations for Bayer sensors"),
117 _("corrective"),
118 _("linear, raw, scene-referred"),
119 _("linear, raw"),
120 _("linear, raw, scene-referred"));
121}
122
123
125{
126 return IOP_GROUP_REPAIR;
127}
128
133
135{
136 return IOP_CS_RAW;
137}
138
141{
142 default_input_format(self, pipe, piece, dsc);
143 dsc->channels = 1;
145}
146
147int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
148 void *new_params, const int new_version)
149{
150 if(old_version == 1 && new_version == 2)
151 {
153 n->avoidshift = FALSE;
154 n->iterations = 1;
155 return 0;
156 }
157 return 1;
158}
159
160/*==================================================================================
161 * begin raw therapee code, hg checkout of march 09, 2016 branch master.
162 *==================================================================================*/
163
164#ifdef __GNUC__
165#define INLINE __inline
166#else
167#define INLINE inline
168#endif
169
170
171static INLINE float SQR(float x)
172{
173 // return std::pow(x,2); Slower than:
174 return (x * x);
175}
176static INLINE float LIM(const float a, const float b, const float c)
177{
178 return MAX(b, MIN(a, c));
179}
180static INLINE float intp(const float a, const float b, const float c)
181{
182 // calculate a * b + (1 - a) * c
183 // following is valid:
184 // intp(a, b+x, c+x) = intp(a, b, c) + x
185 // intp(a, b*x, c*x) = intp(a, b, c) * x
186 return a * (b - c) + c;
187}
188
190//
191// Chromatic Aberration correction on raw bayer cfa data
192//
193// copyright (c) 2008-2010 Emil Martinec <ejmartin@uchicago.edu>
194// copyright (c) for improvements (speedups, iterated correction and avoid colour shift) 2018 Ingo Weyrich <heckflosse67@gmx.de>
195//
196// code dated: September 8, 2018
197//
198// CA_correct_RT.cc is free software: you can redistribute it and/or modify
199// it under the terms of the GNU General Public License as published by
200// the Free Software Foundation, either version 3 of the License, or
201// (at your option) any later version.
202//
203// This program is distributed in the hope that it will be useful,
204// but WITHOUT ANY WARRANTY; without even the implied warranty of
205// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
206// GNU General Public License for more details.
207//
208// You should have received a copy of the GNU General Public License
209// along with this program. If not, see <https://www.gnu.org/licenses/>.
210//
212//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
214static gboolean LinEqSolve(int nDim, double *pfMatr, double *pfVect, double *pfSolution)
215{
216 //==============================================================================
217 // return 1 if system not solving, 0 if system solved
218 // nDim - system dimension
219 // pfMatr - matrix with coefficients
220 // pfVect - vector with free members
221 // pfSolution - vector with system solution
222 // pfMatr becomes triangular after function call
223 // pfVect changes after function call
224 //
225 // Developer: Henry Guennadi Levkin
226 //
227 //==============================================================================
228
229 double fMaxElem;
230 double fAcc;
231
232 int i, j, k, m;
233
234 for(k = 0; k < (nDim - 1); k++)
235 { // base row of matrix
236 // search of line with max element
237 fMaxElem = fabs(pfMatr[k * nDim + k]);
238 m = k;
239
240 for(i = k + 1; i < nDim; i++)
241 {
242 if(fMaxElem < fabs(pfMatr[i * nDim + k]))
243 {
244 fMaxElem = pfMatr[i * nDim + k];
245 m = i;
246 }
247 }
248
249 // permutation of base line (index k) and max element line(index m)
250 if(m != k)
251 {
252 for(i = k; i < nDim; i++)
253 {
254 fAcc = pfMatr[k * nDim + i];
255 pfMatr[k * nDim + i] = pfMatr[m * nDim + i];
256 pfMatr[m * nDim + i] = fAcc;
257 }
258
259 fAcc = pfVect[k];
260 pfVect[k] = pfVect[m];
261 pfVect[m] = fAcc;
262 }
263
264 if(pfMatr[k * nDim + k] == 0.)
265 {
266 // linear system has no solution
267 return FALSE; // needs improvement !!!
268 }
269
270 // triangulation of matrix with coefficients
271 for(j = (k + 1); j < nDim; j++)
272 { // current row of matrix
273 fAcc = -pfMatr[j * nDim + k] / pfMatr[k * nDim + k];
274
275 for(i = k; i < nDim; i++)
276 {
277 pfMatr[j * nDim + i] = pfMatr[j * nDim + i] + fAcc * pfMatr[k * nDim + i];
278 }
279
280 pfVect[j] = pfVect[j] + fAcc * pfVect[k]; // free member recalculation
281 }
282 }
283
284 for(k = (nDim - 1); k >= 0; k--)
285 {
286 pfSolution[k] = pfVect[k];
287
288 for(i = (k + 1); i < nDim; i++)
289 {
290 pfSolution[k] -= (pfMatr[k * nDim + i] * pfSolution[i]);
291 }
292
293 pfSolution[k] = pfSolution[k] / pfMatr[k * nDim + k];
294 }
295
296 return TRUE;
297}
298// end of linear equation solver
299//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
300
301static inline void pixSort(float *a, float *b)
302{
303 if(*a > *b)
304 {
305 float temp = *a;
306 *a = *b;
307 *b = temp;
308 }
309}
310
311/*
312 We want to avoid the module being processed in case the provided size of data is too small resulting in
313 really bad artifacts. This is often the case while zooming in with the current dt pipeline.
314 There is no "maths background" so i chose this after a lot of testing.
315*/
316#define CA_SIZE_MINIMUM (1600)
318int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const i, void *const o)
319{
320 const dt_iop_roi_t *const roi_in = &piece->roi_in;
321 const float *const in2 = (float *)i;
322 float *out = (float *) o;
323
324 const int width = roi_in->width;
325 const int height = roi_in->height;
326 const int h_width = (width + 1) / 2;
327 const int h_height = (height + 1) / 2;
328
329 const uint32_t filters = piece->dsc_in.filters;
330
331 const gboolean valid = MAX(width, height) >= CA_SIZE_MINIMUM;
332
334
335 const gboolean avoidshift = d->avoidshift;
336 const int iterations = d->iterations;
337
338 // Because we can't break parallel processing, we need a switch do handle the errors
339 gboolean processpasstwo = TRUE;
340 int err = 0;
341
342 float *redfactor = NULL;
343 float *bluefactor = NULL;
344 float *oldraw = NULL;
345 float *Gtmp = NULL;
346 float *RawDataTmp = NULL;
347 char *buffer1 = NULL;
348 char *thread_buffers = NULL;
349 size_t padded_buffersize = 0;
350 double fitparams[2][2][16] = { 0 };
351 float blockvar[2][2] = { { 0, 0 }, { 0, 0 } };
352
354
355 if(!valid) return 0;
356
357 const float *const in = out;
358
359 const float caautostrength = 4.0f;
360
361 // multithreaded and partly vectorized by Ingo Weyrich
362 const int ts = 128;
363 const int tsh = ts / 2;
364 // shifts to location of vertical and diagonal neighbors
365 const int v1 = ts, v2 = 2 * ts, v3 = 3 * ts, v4 = 4 * ts;
366
367 // Test for RGB cfa
368 for(int i = 0; i < 2; i++)
369 for(int j = 0; j < 2; j++)
370 if(FC(i, j, filters) == 3)
371 {
372 return 0;
373 }
374
375 if(avoidshift)
376 {
377 const size_t buffsize = (size_t)h_width * h_height;
378 redfactor = dt_pixelpipe_cache_alloc_align_float(buffsize, pipe);
379 if(IS_NULL_PTR(redfactor))
380 {
381 err = 1;
382 goto cleanup;
383 }
384 memset(redfactor, 0, sizeof(float) * buffsize);
385 bluefactor = dt_pixelpipe_cache_alloc_align_float(buffsize, pipe);
386 if(IS_NULL_PTR(bluefactor))
387 {
388 err = 1;
389 goto cleanup;
390 }
391 memset(bluefactor, 0, sizeof(float) * buffsize);
392 oldraw = dt_pixelpipe_cache_alloc_align_float(buffsize * 2, pipe);
393 if(IS_NULL_PTR(oldraw))
394 {
395 err = 1;
396 goto cleanup;
397 }
398 memset(oldraw, 0, sizeof(float) * buffsize * 2);
399 // copy raw values before ca correction
401 for(int row = 0; row < height; row++)
402 {
403 for(int col = (FC(row, 0, filters) & 1); col < width; col += 2)
404 {
405 oldraw[row * h_width + col / 2] = in[row * width + col];
406 }
407 }
408 }
409
410 // temporary array to store simple interpolation of G
411 Gtmp = dt_pixelpipe_cache_alloc_align_float((size_t)height * width, pipe);
412 if(IS_NULL_PTR(Gtmp))
413 {
414 err = 1;
415 goto cleanup;
416 }
417 memset(Gtmp, 0, sizeof(float) * height * width);
418
419 // temporary array to avoid race conflicts, only every second pixel needs to be saved here
420 RawDataTmp = dt_pixelpipe_cache_alloc_align_float(height * width / 2 + 4, pipe);
421 if(IS_NULL_PTR(RawDataTmp))
422 {
423 err = 1;
424 goto cleanup;
425 }
426
427 const int border = 8;
428 const int border2 = 16;
429
430 const int vz1 = (height + border2) % (ts - border2) == 0 ? 1 : 0;
431 const int hz1 = (width + border2) % (ts - border2) == 0 ? 1 : 0;
432 const int vblsz = ceil((float)(height + border2) / (ts - border2) + 2 + vz1);
433 const int hblsz = ceil((float)(width + border2) / (ts - border2) + 2 + hz1);
434
435 buffer1 = (char *)calloc((size_t)vblsz * hblsz * (2 * 2 + 1), sizeof(float));
436 if(IS_NULL_PTR(buffer1))
437 {
438 err = 1;
439 goto cleanup;
440 }
441
442 const size_t buffersize = sizeof(float) * 3 * ts * ts + 6 * sizeof(float) * ts * tsh + 8 * 64 + 63;
443 thread_buffers = (char *)dt_pixelpipe_cache_alloc_perthread(buffersize + 63, sizeof(char), &padded_buffersize);
444 if(IS_NULL_PTR(thread_buffers))
445 {
446 err = 1;
447 goto cleanup;
448 }
449
450 // block CA shift values and weight assigned to block
451 float *blockwt = (float *)buffer1;
452 float(*blockshifts)[2][2] = (float(*)[2][2])(buffer1 + (sizeof(float) * vblsz * hblsz));
453
454 float blockave[2][2] = { { 0, 0 }, { 0, 0 } };
455 float blocksqave[2][2] = { { 0, 0 }, { 0, 0 } };
456 float blockdenom[2][2] = { { 0, 0 }, { 0, 0 } };
457 // order of 2d polynomial fit (polyord), and numpar=polyord^2
458 int polyord = 4, numpar = 16;
459
460 const float eps = 1e-5f, eps2 = 1e-10f; // tolerance to avoid dividing by zero
461
462 for (size_t it = 0; it < iterations && processpasstwo; it++)
463 {
464
465#ifdef _OPENMP
466#pragma omp parallel
467#endif
468 {
469 // direction of the CA shift in a tile
470 int GRBdir[2][3];
471
472 int shifthfloor[3], shiftvfloor[3], shifthceil[3], shiftvceil[3];
473
474 // local quadratic fit to shift data within a tile
475 float coeff[2][3][2];
476 // measured CA shift parameters for a tile
477 float CAshift[2][2];
478 // polynomial fit coefficients
479 // residual CA shift amount within a plaquette
480 float shifthfrac[3], shiftvfrac[3];
481 // per thread data for evaluation of block CA shift variance
482 float blockavethr[2][2] = { { 0, 0 }, { 0, 0 } }, blocksqavethr[2][2] = { { 0, 0 }, { 0, 0 } },
483 blockdenomthr[2][2] = { { 0, 0 }, { 0, 0 } };
484
485 // assign working space
486 char *buffer = dt_get_perthread(thread_buffers, padded_buffersize);
487 char *data = (char *)(((uintptr_t)buffer + (uintptr_t)63) / 64 * 64);
488
489 // shift the beginning of all arrays but the first by 64 bytes to avoid cache miss conflicts on CPUs which
490 // have <=4-way associative L1-Cache
491
492 // rgb data in a tile
493 float *rgb[3];
494 rgb[0] = (float(*))data;
495 rgb[1] = (float(*))(data + 1 * sizeof(float) * ts * ts + 1 * 64);
496 rgb[2] = (float(*))(data + 2 * sizeof(float) * ts * ts + 2 * 64);
497
498 // high pass filter for R/B in vertical direction
499 float *rbhpfh = (float(*))(data + 3 * sizeof(float) * ts * ts + 3 * 64);
500 // high pass filter for R/B in horizontal direction
501 float *rbhpfv = (float(*))(data + 3 * sizeof(float) * ts * ts + sizeof(float) * ts * tsh + 4 * 64);
502 // low pass filter for R/B in horizontal direction
503 float *rblpfh = (float(*))(data + 4 * sizeof(float) * ts * ts + 5 * 64);
504 // low pass filter for R/B in vertical direction
505 float *rblpfv = (float(*))(data + 4 * sizeof(float) * ts * ts + sizeof(float) * ts * tsh + 6 * 64);
506 // low pass filter for colour differences in horizontal direction
507 float *grblpfh = (float(*))(data + 5 * sizeof(float) * ts * ts + 7 * 64);
508 // low pass filter for colour differences in vertical direction
509 float *grblpfv = (float(*))(data + 5 * sizeof(float) * ts * ts + sizeof(float) * ts * tsh + 8 * 64);
510 // colour differences
511 float *grbdiff = rbhpfh; // there is no overlap in buffer usage => share
512 // green interpolated to optical sample points for R/B
513 float *gshift = rbhpfv; // there is no overlap in buffer usage => share
514
515 {
516// Main algorithm: Tile loop calculating correction parameters per tile
517 __OMP_FOR__(collapse(2) nowait)
518 for(int top = -border; top < height; top += ts - border2)
519 for(int left = -border; left < width; left += ts - border2)
520 {
521 memset_zero(buffer, buffersize);
522 const int vblock = ((top + border) / (ts - border2)) + 1;
523 const int hblock = ((left + border) / (ts - border2)) + 1;
524 const int bottom = MIN(top + ts, height + border);
525 const int right = MIN(left + ts, width + border);
526 const int rr1 = bottom - top;
527 const int cc1 = right - left;
528 const int rrmin = top < 0 ? border : 0;
529 const int rrmax = bottom > height ? height - top : rr1;
530 const int ccmin = left < 0 ? border : 0;
531 const int ccmax = right > width ? width - left : cc1;
532
533 // rgb from input CFA data
534 // rgb values should be floating point numbers between 0 and 1
535 // after white balance multipliers are applied
536
537 for(int rr = rrmin; rr < rrmax; rr++)
538 for(int row = rr + top, cc = ccmin; cc < ccmax; cc++)
539 {
540 int col = cc + left;
541 int c = FC(rr, cc, filters);
542 int indx = row * width + col;
543 int indx1 = rr * ts + cc;
544 rgb[c][indx1] = (in[indx]);
545 }
546
547 // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
548 // fill borders
549 if(rrmin > 0)
550 {
551 for(int rr = 0; rr < border; rr++)
552 for(int cc = ccmin; cc < ccmax; cc++)
553 {
554 int c = FC(rr, cc, filters);
555 rgb[c][rr * ts + cc] = rgb[c][(border2 - rr) * ts + cc];
556 }
557 }
558
559 if(rrmax < rr1)
560 {
561 for(int rr = 0; rr < MIN(border, rr1 - rrmax); rr++)
562 for(int cc = ccmin; cc < ccmax; cc++)
563 {
564 int c = FC(rr, cc, filters);
565 rgb[c][(rrmax + rr) * ts + cc] = (in[(height - rr - 2) * width + left + cc]);
566 }
567 }
568
569 if(ccmin > 0)
570 {
571 for(int rr = rrmin; rr < rrmax; rr++)
572 for(int cc = 0; cc < border; cc++)
573 {
574 int c = FC(rr, cc, filters);
575 rgb[c][rr * ts + cc] = rgb[c][rr * ts + border2 - cc];
576 }
577 }
578
579 if(ccmax < cc1)
580 {
581 for(int rr = rrmin; rr < rrmax; rr++)
582 for(int cc = 0; cc < MIN(border, cc1 - ccmax); cc++)
583 {
584 int c = FC(rr, cc, filters);
585 rgb[c][rr * ts + ccmax + cc] = (in[(top + rr) * width + (width - cc - 2)]);
586 }
587 }
588
589 // also, fill the image corners
590 if(rrmin > 0 && ccmin > 0)
591 {
592 for(int rr = 0; rr < border; rr++)
593 for(int cc = 0; cc < border; cc++)
594 {
595 int c = FC(rr, cc, filters);
596 rgb[c][(rr)*ts + cc] = (in[(border2 - rr) * width + border2 - cc]);
597 }
598 }
599
600 if(rrmax < rr1 && ccmax < cc1)
601 {
602 for(int rr = 0; rr < MIN(border, rr1 - rrmax); rr++)
603 for(int cc = 0; cc < MIN(border, cc1 - ccmax); cc++)
604 {
605 int c = FC(rr, cc, filters);
606 rgb[c][(rrmax + rr) * ts + ccmax + cc] = (in[(height - rr - 2) * width + (width - cc - 2)]);
607 }
608 }
609
610 if(rrmin > 0 && ccmax < cc1)
611 {
612 for(int rr = 0; rr < border; rr++)
613 for(int cc = 0; cc < MIN(border, cc1 - ccmax); cc++)
614 {
615 int c = FC(rr, cc, filters);
616 rgb[c][(rr)*ts + ccmax + cc] = (in[(border2 - rr) * width + (width - cc - 2)]);
617 }
618 }
619
620 if(rrmax < rr1 && ccmin > 0)
621 {
622 for(int rr = 0; rr < MIN(border, rr1 - rrmax); rr++)
623 for(int cc = 0; cc < border; cc++)
624 {
625 int c = FC(rr, cc, filters);
626 rgb[c][(rrmax + rr) * ts + cc] = (in[(height - rr - 2) * width + (border2 - cc)]);
627 }
628 }
629
630// end of border fill
631// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
632// end of initialization
633
634 for(int rr = 3; rr < rr1 - 3; rr++)
635 {
636 int row = rr + top;
637 for(int cc = 3 + (FC(rr, 3, filters) & 1), indx = rr * ts + cc, c = FC(rr, cc, filters); cc < cc1 - 3; cc += 2, indx += 2)
638 {
639 // compute directional weights using image gradients
640 float wtu = 1.f / SQR(eps + fabsf(rgb[1][indx + v1] - rgb[1][indx - v1])
641 + fabsf(rgb[c][indx] - rgb[c][indx - v2])
642 + fabsf(rgb[1][indx - v1] - rgb[1][indx - v3]));
643 float wtd = 1.f / SQR(eps + fabsf(rgb[1][indx - v1] - rgb[1][indx + v1])
644 + fabsf(rgb[c][indx] - rgb[c][indx + v2])
645 + fabsf(rgb[1][indx + v1] - rgb[1][indx + v3]));
646 float wtl = 1.f / SQR(eps + fabsf(rgb[1][indx + 1] - rgb[1][indx - 1])
647 + fabsf(rgb[c][indx] - rgb[c][indx - 2])
648 + fabsf(rgb[1][indx - 1] - rgb[1][indx - 3]));
649 float wtr = 1.f / SQR(eps + fabsf(rgb[1][indx - 1] - rgb[1][indx + 1])
650 + fabsf(rgb[c][indx] - rgb[c][indx + 2])
651 + fabsf(rgb[1][indx + 1] - rgb[1][indx + 3]));
652
653 // store in rgb array the interpolated G value at R/B grid points using directional weighted
654 // average
655 rgb[1][indx] = (wtu * rgb[1][indx - v1] + wtd * rgb[1][indx + v1] + wtl * rgb[1][indx - 1]
656 + wtr * rgb[1][indx + 1])
657 / (wtu + wtd + wtl + wtr);
658 }
659
660 if(row > -1 && row < height)
661 {
662 for(int col = MAX(left + 3, 0), indx = rr * ts + 3 - (left < 0 ? (left + 3) : 0);
663 col < MIN(cc1 + left - 3, width); col++, indx++)
664 {
665 Gtmp[row * width + col] = rgb[1][indx];
666 }
667 }
668 }
669
670 for(int rr = 4; rr < rr1 - 4; rr++)
671 {
672 for(int cc = 4 + (FC(rr, 2, filters) & 1), indx = rr * ts + cc, c = FC(rr, cc, filters); cc < cc1 - 4; cc += 2, indx += 2)
673 {
674 rbhpfv[indx >> 1] = fabsf(
675 fabsf((rgb[1][indx] - rgb[c][indx]) - (rgb[1][indx + v4] - rgb[c][indx + v4]))
676 + fabsf((rgb[1][indx - v4] - rgb[c][indx - v4]) - (rgb[1][indx] - rgb[c][indx]))
677 - fabsf((rgb[1][indx - v4] - rgb[c][indx - v4]) - (rgb[1][indx + v4] - rgb[c][indx + v4])));
678 rbhpfh[indx >> 1] = fabsf(
679 fabsf((rgb[1][indx] - rgb[c][indx]) - (rgb[1][indx + 4] - rgb[c][indx + 4]))
680 + fabsf((rgb[1][indx - 4] - rgb[c][indx - 4]) - (rgb[1][indx] - rgb[c][indx]))
681 - fabsf((rgb[1][indx - 4] - rgb[c][indx - 4]) - (rgb[1][indx + 4] - rgb[c][indx + 4])));
682
683 // low and high pass 1D filters of G in vertical/horizontal directions
684 float glpfv = 0.25f * (2.f * rgb[1][indx] + rgb[1][indx + v2] + rgb[1][indx - v2]);
685 float glpfh = 0.25f * (2.f * rgb[1][indx] + rgb[1][indx + 2] + rgb[1][indx - 2]);
686 rblpfv[indx >> 1]
687 = eps + fabsf(glpfv - 0.25f * (2.f * rgb[c][indx] + rgb[c][indx + v2] + rgb[c][indx - v2]));
688 rblpfh[indx >> 1]
689 = eps + fabsf(glpfh - 0.25f * (2.f * rgb[c][indx] + rgb[c][indx + 2] + rgb[c][indx - 2]));
690 grblpfv[indx >> 1]
691 = glpfv + 0.25f * (2.f * rgb[c][indx] + rgb[c][indx + v2] + rgb[c][indx - v2]);
692 grblpfh[indx >> 1] = glpfh + 0.25f * (2.f * rgb[c][indx] + rgb[c][indx + 2] + rgb[c][indx - 2]);
693 }
694 }
695
696 for(int dir = 0; dir < 2; dir++)
697 {
698 for(int k = 0; k < 3; k++)
699 {
700 for(int c = 0; c < 2; c++)
701 {
702 coeff[dir][k][c] = 0;
703 }
704 }
705 }
706
707 // along line segments, find the point along each segment that minimizes the colour variance
708 // averaged over the tile; evaluate for up/down and left/right away from R/B grid point
709 for(int rr = 8; rr < rr1 - 8; rr++)
710 {
711 for(int cc = 8 + (FC(rr, 2, filters) & 1), indx = rr * ts + cc, c = FC(rr, cc, filters); cc < cc1 - 8; cc += 2, indx += 2)
712 {
713
714 // in linear interpolation, colour differences are a quadratic function of interpolation
715 // position;
716 // solve for the interpolation position that minimizes colour difference variance over the tile
717
718 // vertical
719 float gdiff = 0.3125f * (rgb[1][indx + ts] - rgb[1][indx - ts])
720 + 0.09375f * (rgb[1][indx + ts + 1] - rgb[1][indx - ts + 1]
721 + rgb[1][indx + ts - 1] - rgb[1][indx - ts - 1]);
722 float deltgrb = (rgb[c][indx] - rgb[1][indx]);
723
724 float gradwt = fabsf(0.25f * rbhpfv[indx >> 1]
725 + 0.125f * (rbhpfv[(indx >> 1) + 1] + rbhpfv[(indx >> 1) - 1]))
726 * (grblpfv[(indx >> 1) - v1] + grblpfv[(indx >> 1) + v1])
727 / (eps + 0.1f * (grblpfv[(indx >> 1) - v1] + grblpfv[(indx >> 1) + v1])
728 + rblpfv[(indx >> 1) - v1] + rblpfv[(indx >> 1) + v1]);
729
730 coeff[0][0][c >> 1] += gradwt * deltgrb * deltgrb;
731 coeff[0][1][c >> 1] += gradwt * gdiff * deltgrb;
732 coeff[0][2][c >> 1] += gradwt * gdiff * gdiff;
733
734 // horizontal
735 gdiff = 0.3125f * (rgb[1][indx + 1] - rgb[1][indx - 1])
736 + 0.09375f * (rgb[1][indx + 1 + ts] - rgb[1][indx - 1 + ts] + rgb[1][indx + 1 - ts]
737 - rgb[1][indx - 1 - ts]);
738
739 gradwt = fabsf(0.25f * rbhpfh[indx >> 1]
740 + 0.125f * (rbhpfh[(indx >> 1) + v1] + rbhpfh[(indx >> 1) - v1]))
741 * (grblpfh[(indx >> 1) - 1] + grblpfh[(indx >> 1) + 1])
742 / (eps + 0.1f * (grblpfh[(indx >> 1) - 1] + grblpfh[(indx >> 1) + 1])
743 + rblpfh[(indx >> 1) - 1] + rblpfh[(indx >> 1) + 1]);
744
745 coeff[1][0][c >> 1] += gradwt * deltgrb * deltgrb;
746 coeff[1][1][c >> 1] += gradwt * gdiff * deltgrb;
747 coeff[1][2][c >> 1] += gradwt * gdiff * gdiff;
748
749 // In Mathematica,
750 // f[x_]=Expand[Total[Flatten[
751 // ((1-x) RotateLeft[Gint,shift1]+x
752 // RotateLeft[Gint,shift2]-cfapad)^2[[dv;;-1;;2,dh;;-1;;2]]]]];
753 // extremum = -.5Coefficient[f[x],x]/Coefficient[f[x],x^2]
754 }
755 }
756
757 for(int c = 0; c < 2; c++)
758 {
759 for(int dir = 0; dir < 2; dir++)
760 { // vert/hor
761
762 // CAshift[dir][c] are the locations
763 // that minimize colour difference variances;
764 // This is the approximate _optical_ location of the R/B pixels
765 if(coeff[dir][2][c] > eps2)
766 {
767 CAshift[dir][c] = coeff[dir][1][c] / coeff[dir][2][c];
768 blockwt[vblock * hblsz + hblock] = coeff[dir][2][c] / (eps + coeff[dir][0][c]);
769 }
770 else
771 {
772 CAshift[dir][c] = 17.0;
773 blockwt[vblock * hblsz + hblock] = 0;
774 }
775
776 // data structure = CAshift[vert/hor][colour]
777 // dir : 0=vert, 1=hor
778
779 // offset gives NW corner of square containing the min; dir : 0=vert, 1=hor
780 if(fabsf(CAshift[dir][c]) < 2.0f)
781 {
782 blockavethr[dir][c] += CAshift[dir][c];
783 blocksqavethr[dir][c] += SQR(CAshift[dir][c]);
784 blockdenomthr[dir][c] += 1;
785 }
786 // evaluate the shifts to the location that minimizes CA within the tile
787 blockshifts[vblock * hblsz + hblock][c][dir] = CAshift[dir][c]; // vert/hor CA shift for R/B
788
789 } // vert/hor
790 } // colour
791
792 }
793
794// end of diagnostic pass
795#ifdef _OPENMP
796#pragma omp critical(cadetectpass2)
797#endif
798 {
799 for(int dir = 0; dir < 2; dir++)
800 for(int c = 0; c < 2; c++)
801 {
802 blockdenom[dir][c] += blockdenomthr[dir][c];
803 blocksqave[dir][c] += blocksqavethr[dir][c];
804 blockave[dir][c] += blockavethr[dir][c];
805 }
806 }
807#ifdef _OPENMP
808#pragma omp barrier
809#endif
810
811#ifdef _OPENMP
812#pragma omp single
813#endif
814 {
815 for(int dir = 0; dir < 2; dir++)
816 for(int c = 0; c < 2; c++)
817 {
818 if(blockdenom[dir][c])
819 {
820 blockvar[dir][c]
821 = blocksqave[dir][c] / blockdenom[dir][c] - SQR(blockave[dir][c] / blockdenom[dir][c]);
822 }
823 else
824 {
825 processpasstwo = FALSE;
826 break;
827 }
828 }
829
830 // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
831
832 // now prepare for CA correction pass
833 // first, fill border blocks of blockshift array
834 if(processpasstwo)
835 {
836 for(int vblock = 1; vblock < vblsz - 1; vblock++)
837 { // left and right sides
838 for(int c = 0; c < 2; c++)
839 {
840 for(int i = 0; i < 2; i++)
841 {
842 blockshifts[vblock * hblsz][c][i] = blockshifts[(vblock)*hblsz + 2][c][i];
843 blockshifts[vblock * hblsz + hblsz - 1][c][i] = blockshifts[(vblock)*hblsz + hblsz - 3][c][i];
844 }
845 }
846 }
847
848 for(int hblock = 0; hblock < hblsz; hblock++)
849 { // top and bottom sides
850 for(int c = 0; c < 2; c++)
851 {
852 for(int i = 0; i < 2; i++)
853 {
854 blockshifts[hblock][c][i] = blockshifts[2 * hblsz + hblock][c][i];
855 blockshifts[(vblsz - 1) * hblsz + hblock][c][i]
856 = blockshifts[(vblsz - 3) * hblsz + hblock][c][i];
857 }
858 }
859 }
860
861 // end of filling border pixels of blockshift array
862
863 // initialize fit arrays
864 double polymat[2][2][256], shiftmat[2][2][16];
865
866 for(int i = 0; i < 256; i++)
867 {
868 polymat[0][0][i] = polymat[0][1][i] = polymat[1][0][i] = polymat[1][1][i] = 0;
869 }
870
871 for(int i = 0; i < 16; i++)
872 {
873 shiftmat[0][0][i] = shiftmat[0][1][i] = shiftmat[1][0][i] = shiftmat[1][1][i] = 0;
874 }
875
876 int numblox[2] = { 0, 0 };
877
878 for(int vblock = 1; vblock < vblsz - 1; vblock++)
879 for(int hblock = 1; hblock < hblsz - 1; hblock++)
880 {
881 // block 3x3 median of blockshifts for robustness
882 for(int c = 0; c < 2; c++)
883 {
884 float bstemp[2];
885 for(int dir = 0; dir < 2; dir++)
886 {
887 // temporary storage for median filter
888 float p[9];
889 p[0] = blockshifts[(vblock - 1) * hblsz + hblock - 1][c][dir];
890 p[1] = blockshifts[(vblock - 1) * hblsz + hblock][c][dir];
891 p[2] = blockshifts[(vblock - 1) * hblsz + hblock + 1][c][dir];
892 p[3] = blockshifts[(vblock)*hblsz + hblock - 1][c][dir];
893 p[4] = blockshifts[(vblock)*hblsz + hblock][c][dir];
894 p[5] = blockshifts[(vblock)*hblsz + hblock + 1][c][dir];
895 p[6] = blockshifts[(vblock + 1) * hblsz + hblock - 1][c][dir];
896 p[7] = blockshifts[(vblock + 1) * hblsz + hblock][c][dir];
897 p[8] = blockshifts[(vblock + 1) * hblsz + hblock + 1][c][dir];
898 pixSort(&p[1], &p[2]);
899 pixSort(&p[4], &p[5]);
900 pixSort(&p[7], &p[8]);
901 pixSort(&p[0], &p[1]);
902 pixSort(&p[3], &p[4]);
903 pixSort(&p[6], &p[7]);
904 pixSort(&p[1], &p[2]);
905 pixSort(&p[4], &p[5]);
906 pixSort(&p[7], &p[8]);
907 pixSort(&p[0], &p[3]);
908 pixSort(&p[5], &p[8]);
909 pixSort(&p[4], &p[7]);
910 pixSort(&p[3], &p[6]);
911 pixSort(&p[1], &p[4]);
912 pixSort(&p[2], &p[5]);
913 pixSort(&p[4], &p[7]);
914 pixSort(&p[4], &p[2]);
915 pixSort(&p[6], &p[4]);
916 pixSort(&p[4], &p[2]);
917 bstemp[dir] = p[4];
918 }
919
920 // now prepare coefficient matrix; use only data points within caautostrength/2 std devs of
921 // zero
922 if(SQR(bstemp[0]) > caautostrength * blockvar[0][c]
923 || SQR(bstemp[1]) > caautostrength * blockvar[1][c])
924 {
925 continue;
926 }
927
928 numblox[c]++;
929
930 for(int dir = 0; dir < 2; dir++)
931 {
932 double powVblockInit = 1.0;
933 for(int i = 0; i < polyord; i++)
934 {
935 double powHblockInit = 1.0;
936 for(int j = 0; j < polyord; j++)
937 {
938 double powVblock = powVblockInit;
939 for(int m = 0; m < polyord; m++)
940 {
941 double powHblock = powHblockInit;
942 for(int n = 0; n < polyord; n++)
943 {
944 polymat[c][dir][numpar * (polyord * i + j) + (polyord * m + n)]
945 += powVblock * powHblock * blockwt[vblock * hblsz + hblock];
946 powHblock *= hblock;
947 }
948 powVblock *= vblock;
949 }
950 shiftmat[c][dir][(polyord * i + j)]
951 += powVblockInit * powHblockInit * bstemp[dir] * blockwt[vblock * hblsz + hblock];
952 powHblockInit *= hblock;
953 }
954 powVblockInit *= vblock;
955 } // monomials
956 } // dir
957 } // c
958 } // blocks
959
960 numblox[1] = MIN(numblox[0], numblox[1]);
961
962 // if too few data points, restrict the order of the fit to linear
963 if(numblox[1] < 32)
964 {
965 polyord = 2;
966 numpar = 4;
967
968 if(numblox[1] < 10)
969 {
970 fprintf(stderr, ", numblox = %d \n", numblox[1]);
971 processpasstwo = FALSE;
972 }
973 }
974
975 if(processpasstwo)
976
977 // fit parameters to blockshifts
978 for(int c = 0; c < 2; c++)
979 for(int dir = 0; dir < 2; dir++)
980 {
981 if(!LinEqSolve(numpar, polymat[c][dir], shiftmat[c][dir], fitparams[c][dir]))
982 {
983 fprintf(stderr, ", correction pass failed -- can't solve linear equations for colour %d direction %d", c, dir);
984 processpasstwo = FALSE;
985 }
986 }
987 }
988
989 // fitparams[polyord*i+j] gives the coefficients of (vblock^i hblock^j) in a polynomial fit for i,j<=4
990 }
991 // end of initialization for CA correction pass
992 // only executed if cared and cablue are zero
993 }
994
995 // Main algorithm: Tile loop
996 if(processpasstwo)
997 {
998
999 __OMP_FOR__(collapse(2) nowait)
1000
1001 for(int top = -border; top < height; top += ts - border2)
1002 for(int left = -border; left < width; left += ts - border2)
1003 {
1004 memset(buffer, 0, buffersize);
1005 float lblockshifts[2][2];
1006 const int vblock = ((top + border) / (ts - border2)) + 1;
1007 const int hblock = ((left + border) / (ts - border2)) + 1;
1008 const int bottom = MIN(top + ts, height + border);
1009 const int right = MIN(left + ts, width + border);
1010 const int rr1 = bottom - top;
1011 const int cc1 = right - left;
1012
1013 const int rrmin = top < 0 ? border : 0;
1014 const int rrmax = bottom > height ? height - top : rr1;
1015 const int ccmin = left < 0 ? border : 0;
1016 const int ccmax = right > width ? width - left : cc1;
1017
1018 // rgb from input CFA data
1019 // rgb values should be floating point number between 0 and 1
1020 // after white balance multipliers are applied
1021
1022 for(int rr = rrmin; rr < rrmax; rr++)
1023 for(int row = rr + top, cc = ccmin; cc < ccmax; cc++)
1024 {
1025 int col = cc + left;
1026 int c = FC(rr, cc, filters);
1027 int indx = row * width + col;
1028 int indx1 = rr * ts + cc;
1029 rgb[c][indx1] = (in[indx]);
1030
1031 if((c & 1) == 0)
1032 {
1033 rgb[1][indx1] = Gtmp[indx];
1034 }
1035 }
1036
1037 // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1038 // fill borders
1039 if(rrmin > 0)
1040 {
1041 for(int rr = 0; rr < border; rr++)
1042 for(int cc = ccmin; cc < ccmax; cc++)
1043 {
1044 int c = FC(rr, cc, filters);
1045 rgb[c][rr * ts + cc] = rgb[c][(border2 - rr) * ts + cc];
1046 rgb[1][rr * ts + cc] = rgb[1][(border2 - rr) * ts + cc];
1047 }
1048 }
1049
1050 if(rrmax < rr1)
1051 {
1052 for(int rr = 0; rr < MIN(border, rr1 - rrmax); rr++)
1053 for(int cc = ccmin; cc < ccmax; cc++)
1054 {
1055 int c = FC(rr, cc, filters);
1056 rgb[c][(rrmax + rr) * ts + cc] = (in[(height - rr - 2) * width + left + cc]);
1057 rgb[1][(rrmax + rr) * ts + cc] = Gtmp[(height - rr - 2) * width + left + cc];
1058 }
1059 }
1060
1061 if(ccmin > 0)
1062 {
1063 for(int rr = rrmin; rr < rrmax; rr++)
1064 for(int cc = 0; cc < border; cc++)
1065 {
1066 int c = FC(rr, cc, filters);
1067 rgb[c][rr * ts + cc] = rgb[c][rr * ts + border2 - cc];
1068 rgb[1][rr * ts + cc] = rgb[1][rr * ts + border2 - cc];
1069 }
1070 }
1071
1072 if(ccmax < cc1)
1073 {
1074 for(int rr = rrmin; rr < rrmax; rr++)
1075 for(int cc = 0; cc < MIN(border, cc1 - ccmax); cc++)
1076 {
1077 int c = FC(rr, cc, filters);
1078 rgb[c][rr * ts + ccmax + cc] = (in[(top + rr) * width + (width - cc - 2)]);
1079 rgb[1][rr * ts + ccmax + cc] = Gtmp[(top + rr) * width + (width - cc - 2)];
1080 }
1081 }
1082
1083 // also, fill the image corners
1084 if(rrmin > 0 && ccmin > 0)
1085 {
1086 for(int rr = 0; rr < border; rr++)
1087 for(int cc = 0; cc < border; cc++)
1088 {
1089 int c = FC(rr, cc, filters);
1090 rgb[c][(rr)*ts + cc] = (in[(border2 - rr) * width + border2 - cc]);
1091 rgb[1][(rr)*ts + cc] = Gtmp[(border2 - rr) * width + border2 - cc];
1092 }
1093 }
1094
1095 if(rrmax < rr1 && ccmax < cc1)
1096 {
1097 for(int rr = 0; rr < MIN(border, rr1 - rrmax); rr++)
1098 for(int cc = 0; cc < MIN(border, cc1 - ccmax); cc++)
1099 {
1100 int c = FC(rr, cc, filters);
1101 rgb[c][(rrmax + rr) * ts + ccmax + cc] = (in[(height - rr - 2) * width + (width - cc - 2)]);
1102 rgb[1][(rrmax + rr) * ts + ccmax + cc] = Gtmp[(height - rr - 2) * width + (width - cc - 2)];
1103 }
1104 }
1105
1106 if(rrmin > 0 && ccmax < cc1)
1107 {
1108 for(int rr = 0; rr < border; rr++)
1109 for(int cc = 0; cc < MIN(border, cc1 - ccmax); cc++)
1110 {
1111 int c = FC(rr, cc, filters);
1112 rgb[c][(rr)*ts + ccmax + cc] = (in[(border2 - rr) * width + (width - cc - 2)]);
1113 rgb[1][(rr)*ts + ccmax + cc] = Gtmp[(border2 - rr) * width + (width - cc - 2)];
1114 }
1115 }
1116
1117 if(rrmax < rr1 && ccmin > 0)
1118 {
1119 for(int rr = 0; rr < MIN(border, rr1 - rrmax); rr++)
1120 for(int cc = 0; cc < border; cc++)
1121 {
1122 int c = FC(rr, cc, filters);
1123 rgb[c][(rrmax + rr) * ts + cc] = (in[(height - rr - 2) * width + (border2 - cc)]);
1124 rgb[1][(rrmax + rr) * ts + cc] = Gtmp[(height - rr - 2) * width + (border2 - cc)];
1125 }
1126 }
1127
1128 // end of border fill
1129 {
1130 // CA auto correction; use CA diagnostic pass to set shift parameters
1131 lblockshifts[0][0] = lblockshifts[0][1] = 0;
1132 lblockshifts[1][0] = lblockshifts[1][1] = 0;
1133 double powVblock = 1.0;
1134 for(int i = 0; i < polyord; i++)
1135 {
1136 double powHblock = powVblock;
1137 for(int j = 0; j < polyord; j++)
1138 {
1139 // printf("i= %d j= %d polycoeff= %f \n",i,j,fitparams[0][0][polyord*i+j]);
1140 lblockshifts[0][0] += powHblock * fitparams[0][0][polyord * i + j];
1141 lblockshifts[0][1] += powHblock * fitparams[0][1][polyord * i + j];
1142 lblockshifts[1][0] += powHblock * fitparams[1][0][polyord * i + j];
1143 lblockshifts[1][1] += powHblock * fitparams[1][1][polyord * i + j];
1144 powHblock *= hblock;
1145 }
1146 powVblock *= vblock;
1147 }
1148 const float bslim = 3.99; // max allowed CA shift
1149 lblockshifts[0][0] = LIM(lblockshifts[0][0], -bslim, bslim);
1150 lblockshifts[0][1] = LIM(lblockshifts[0][1], -bslim, bslim);
1151 lblockshifts[1][0] = LIM(lblockshifts[1][0], -bslim, bslim);
1152 lblockshifts[1][1] = LIM(lblockshifts[1][1], -bslim, bslim);
1153 } // end of setting CA shift parameters
1154
1155
1156 for(int c = 0; c < 3; c += 2)
1157 {
1158
1159 // some parameters for the bilinear interpolation
1160 shiftvfloor[c] = floor((float)lblockshifts[c >> 1][0]);
1161 shiftvceil[c] = ceil((float)lblockshifts[c >> 1][0]);
1162 if (lblockshifts[c>>1][0] < 0.f) {
1163 float tmp = shiftvfloor[c];
1164 shiftvfloor[c] = shiftvceil[c];
1165 shiftvceil[c] = tmp;
1166 }
1167 shiftvfrac[c] = fabsf(lblockshifts[c>>1][0] - shiftvfloor[c]);
1168
1169 shifthfloor[c] = floor((float)lblockshifts[c >> 1][1]);
1170 shifthceil[c] = ceil((float)lblockshifts[c >> 1][1]);
1171 if (lblockshifts[c>>1][1] < 0.f) {
1172 float tmp = shifthfloor[c];
1173 shifthfloor[c] = shifthceil[c];
1174 shifthceil[c] = tmp;
1175 }
1176 shifthfrac[c] = fabsf(lblockshifts[c>>1][1] - shifthfloor[c]);
1177
1178
1179 GRBdir[0][c] = lblockshifts[c >> 1][0] > 0 ? 2 : -2;
1180 GRBdir[1][c] = lblockshifts[c >> 1][1] > 0 ? 2 : -2;
1181 }
1182
1183
1184 for(int rr = 4; rr < rr1 - 4; rr++)
1185 {
1186 for(int cc = 4 + (FC(rr, 2, filters) & 1), c = FC(rr, cc, filters); cc < cc1 - 4; cc += 2)
1187 {
1188 // perform CA correction using colour ratios or colour differences
1189 float Ginthfloor = intp(shifthfrac[c], rgb[1][(rr + shiftvfloor[c]) * ts + cc + shifthceil[c]],
1190 rgb[1][(rr + shiftvfloor[c]) * ts + cc + shifthfloor[c]]);
1191 float Ginthceil = intp(shifthfrac[c], rgb[1][(rr + shiftvceil[c]) * ts + cc + shifthceil[c]],
1192 rgb[1][(rr + shiftvceil[c]) * ts + cc + shifthfloor[c]]);
1193 // Gint is bilinear interpolation of G at CA shift point
1194 float Gint = intp(shiftvfrac[c], Ginthceil, Ginthfloor);
1195
1196 // determine R/B at grid points using colour differences at shift point plus interpolated G
1197 // value at grid point
1198 // but first we need to interpolate G-R/G-B to grid points...
1199 grbdiff[((rr)*ts + cc) >> 1] = Gint - rgb[c][(rr)*ts + cc];
1200 gshift[((rr)*ts + cc) >> 1] = Gint;
1201 }
1202 }
1203
1204 shifthfrac[0] /= 2.f;
1205 shifthfrac[2] /= 2.f;
1206 shiftvfrac[0] /= 2.f;
1207 shiftvfrac[2] /= 2.f;
1208
1209 // this loop does not deserve vectorization in mainly because the most expensive part with the
1210 // divisions does not happen often (less than 1/10 in my tests)
1211 for(int rr = 8; rr < rr1 - 8; rr++)
1212 for(int cc = 8 + (FC(rr, 2, filters) & 1), c = FC(rr, cc, filters), indx = rr * ts + cc;
1213 cc < cc1 - 8; cc += 2, indx += 2)
1214 {
1215
1216 float grbdiffold = rgb[1][indx] - rgb[c][indx];
1217
1218 // interpolate colour difference from optical R/B locations to grid locations
1219 float grbdiffinthfloor
1220 = intp(shifthfrac[c], grbdiff[(indx - GRBdir[1][c]) >> 1], grbdiff[indx >> 1]);
1221 float grbdiffinthceil
1222 = intp(shifthfrac[c], grbdiff[((rr - GRBdir[0][c]) * ts + cc - GRBdir[1][c]) >> 1],
1223 grbdiff[((rr - GRBdir[0][c]) * ts + cc) >> 1]);
1224 // grbdiffint is bilinear interpolation of G-R/G-B at grid point
1225 float grbdiffint = intp(shiftvfrac[c], grbdiffinthceil, grbdiffinthfloor);
1226
1227 // now determine R/B at grid points using interpolated colour differences and interpolated G
1228 // value at grid point
1229 float RBint = rgb[1][indx] - grbdiffint;
1230
1231 if(fabsf(RBint - rgb[c][indx]) < 0.25f * (RBint + rgb[c][indx]))
1232 {
1233 if(fabsf(grbdiffold) > fabsf(grbdiffint))
1234 {
1235 rgb[c][indx] = RBint;
1236 }
1237 }
1238 else
1239 {
1240
1241 // gradient weights using difference from G at CA shift points and G at grid points
1242 float p0 = 1.0f / (eps + fabsf(rgb[1][indx] - gshift[indx >> 1]));
1243 float p1 = 1.0f / (eps + fabsf(rgb[1][indx] - gshift[(indx - GRBdir[1][c]) >> 1]));
1244 float p2 = 1.0f / (eps + fabsf(rgb[1][indx] - gshift[((rr - GRBdir[0][c]) * ts + cc) >> 1]));
1245 float p3
1246 = 1.0f / (eps + fabsf(rgb[1][indx]
1247 - gshift[((rr - GRBdir[0][c]) * ts + cc - GRBdir[1][c]) >> 1]));
1248
1249 grbdiffint = (p0 * grbdiff[indx >> 1] + p1 * grbdiff[(indx - GRBdir[1][c]) >> 1]
1250 + p2 * grbdiff[((rr - GRBdir[0][c]) * ts + cc) >> 1]
1251 + p3 * grbdiff[((rr - GRBdir[0][c]) * ts + cc - GRBdir[1][c]) >> 1])
1252 / (p0 + p1 + p2 + p3);
1253
1254 // now determine R/B at grid points using interpolated colour differences and interpolated G
1255 // value at grid point
1256 if(fabsf(grbdiffold) > fabsf(grbdiffint))
1257 {
1258 rgb[c][indx] = rgb[1][indx] - grbdiffint;
1259 }
1260 }
1261
1262 // if colour difference interpolation overshot the correction, just desaturate
1263 if(grbdiffold * grbdiffint < 0)
1264 {
1265 rgb[c][indx] = rgb[1][indx] - 0.5f * (grbdiffold + grbdiffint);
1266 }
1267 }
1268
1269 // copy CA corrected results to temporary image matrix
1270 for(int rr = border; rr < rr1 - border; rr++)
1271 {
1272 int c = FC(rr + top, left + border + (FC(rr + top, 2, filters) & 1), filters);
1273
1274 for(int row = rr + top, cc = border + (FC(rr, 2, filters) & 1),
1275 indx = (row * width + cc + left) >> 1;
1276 cc < cc1 - border; cc += 2, indx++)
1277 {
1278 // int col = cc + left;
1279 RawDataTmp[indx] = rgb[c][(rr)*ts + cc];
1280 }
1281 }
1282 }
1283
1284#ifdef _OPENMP
1285#pragma omp barrier
1286#endif
1287// copy temporary image matrix back to image matrix
1288
1289 __OMP_FOR__()
1290
1291 for(int row = 0; row < height; row++)
1292 for(int col = 0 + (FC(row, 0, filters) & 1), indx = (row * width + col) >> 1; col < width;
1293 col += 2, indx++)
1294 {
1295 out[row * width + col] = RawDataTmp[indx];
1296 }
1297 }
1298
1299 // clean up
1300 }
1301 }
1302
1303 if(avoidshift && processpasstwo)
1304 {
1305 // to avoid or at least reduce the colour shift caused by raw ca correction we compute the per pixel difference factors
1306 // of red and blue channel and apply a gaussian blur to them.
1307 // Then we apply the resulting factors per pixel on the result of raw ca correction
1309 for(int row = 0; row < height; row++)
1310 {
1311 const int firstCol = FC(row, 0, filters) & 1;
1312 const int color = FC(row, firstCol, filters);
1313 float *nongreen = (color == 0) ? redfactor : bluefactor;
1314 for(int col = firstCol; col < width; col += 2)
1315 {
1316 nongreen[(row / 2) * h_width + col / 2] = (in[row * width + col] <= 1.0f || oldraw[row * h_width + col / 2] <= 1.0f)
1317 ? 1.0f : LIM(oldraw[row * h_width + col / 2] / in[row * width + col], 0.5f, 2.0f);
1318 }
1319 }
1320
1321 if(height % 2)
1322 {
1323 // odd height => factors are not set in last row => use values of preceding row
1324 for(int col = 0; col < h_width; col++)
1325 {
1326 redfactor[(h_height-1) * h_width + col] = redfactor[(h_height-2) * h_width + col];
1327 bluefactor[(h_height-1) * h_width + col] = bluefactor[(h_height-2) * h_width + col];
1328 }
1329 }
1330
1331 if(width % 2)
1332 {
1333 // odd width => factors for one channel are not set in last column => use value of preceding column
1334 const int ngRow = 1 - (FC(0, 0, filters) & 1);
1335 const int ngCol = FC(ngRow, 0, filters) & 1;
1336 const int color = FC(ngRow, ngCol, filters);
1337 float *nongreen = (color == 0) ? redfactor : bluefactor;
1338 for(int row = 0; row < h_height; row++)
1339 {
1340 nongreen[row * h_width + h_width - 1] = nongreen[row * h_width + h_width - 2];
1341 }
1342 }
1343
1344 // blur correction factors
1345 float valmax[] = { 10.0f };
1346 float valmin[] = { 0.1f };
1347 dt_gaussian_t *red = dt_gaussian_init(h_width, h_height, 1, valmax, valmin, 30.0f, 0);
1348 dt_gaussian_t *blue = dt_gaussian_init(h_width, h_height, 1, valmax, valmin, 30.0f, 0);
1349 if(IS_NULL_PTR(red) || IS_NULL_PTR(blue))
1350 {
1351 err = 1;
1352 if(red) dt_gaussian_free(red);
1353 if(blue) dt_gaussian_free(blue);
1354 goto cleanup;
1355 }
1356 if(red && blue)
1357 {
1358 dt_gaussian_blur(red, redfactor, redfactor);
1359 dt_gaussian_blur(blue, bluefactor, bluefactor);
1360
1361#ifdef _OPENMP
1362 #pragma omp for
1363#endif
1364 for(int row = 2; row < height - 2; row++)
1365 {
1366 const int firstCol = FC(row, 0, filters) & 1;
1367 const int color = FC(row, firstCol, filters);
1368 float *nongreen = (color == 0) ? redfactor : bluefactor;
1369 for(int col = firstCol; col < width - 2; col += 2)
1370 {
1371 const float correction = nongreen[row / 2 * h_width + col / 2];
1372 out[row * width + col] *= correction;
1373 }
1374 }
1375 }
1376 if(red) dt_gaussian_free(red);
1377 if(blue) dt_gaussian_free(blue);
1378 }
1379
1380cleanup:
1381 dt_pixelpipe_cache_free_align(thread_buffers);
1382 dt_free(buffer1);
1388 return err;
1389}
1390
1391/*==================================================================================
1392 * end raw therapee code
1393 *==================================================================================*/
1394
1395/* Single source of truth for "does this image support CA correction".
1396 * Bayer-only CFA operation: requires a mosaiced buffer (needs_demosaic) that is not X-Trans
1397 * (filters != 9u) and not monochrome. The old DT_IMAGE_RAW-only test let an already-demosaiced
1398 * raw (sRAW / linear DNG) through whenever its stale filters value happened to be non-9u.
1399 * Shared by reload_defaults() (fresh-history defaults + GUI) and force_enable() (history
1400 * sanitization), so the rule cannot drift between the two. */
1401static gboolean _cacorrect_supported(const dt_image_t *img)
1402{
1403 return dt_image_needs_demosaic(img) && (img->dsc.filters != 9u) && !dt_image_is_monochrome(img);
1404}
1405
1407{
1408 dt_image_t *img = &module->dev->image_storage;
1409 const gboolean active = _cacorrect_supported(img);
1410 // can't be switched on for non-Bayer-mosaic images:
1411 module->hide_enable_button = !active;
1412 dt_iop_fmt_log(module, "reload_defaults: class=%s needs_demosaic=%d filters=%u mono=%d -> hide_enable=%d",
1415}
1416
1417gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
1418{
1419 // History sanitization: a CA-correction entry copied/pasted onto an unsupported image
1420 // (non-mosaic, X-Trans or monochrome) must be forced off here, at history-read time, instead
1421 // of being patched later in commit_params() on the pipeline node.
1422 const gboolean active = _cacorrect_supported(&self->dev->image_storage);
1423 const gboolean state = current_state && active;
1424 dt_iop_fmt_log(self, "force_enable: class=%s supported=%d current=%d -> %d",
1426 active, current_state, state);
1427 return state;
1428}
1429
1433{
1436
1437 // Image-type gating is handled at history level by force_enable()/reload_defaults(); nothing
1438 // type-related is decided here anymore.
1439 d->iterations = p->iterations;
1440 d->avoidshift = p->avoidshift;
1441 dt_iop_fmt_log(self, "commit: class=%s enabled=%d",
1443}
1444
1446{
1448 piece->data_size = sizeof(dt_iop_cacorrect_data_t);
1449}
1450
1452{
1453 dt_free_align(piece->data);
1454 piece->data = NULL;
1455}
1456
1458{
1461
1462 dt_image_t *img = &self->dev->image_storage;
1463
1464 const gboolean active = _cacorrect_supported(img);
1465 self->hide_enable_button = !active;
1466
1467 gtk_stack_set_visible_child_name(GTK_STACK(self->widget), active ? "raw" : "non_raw");
1468
1469 gtk_widget_set_visible(g->avoidshift, active);
1470 gtk_widget_set_visible(g->iterations, active);
1471 dt_bauhaus_combobox_set_from_value(g->iterations, p->iterations);
1472 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->avoidshift), p->avoidshift);
1473}
1474
1475void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
1476{
1479
1480 dt_image_t *img = &self->dev->image_storage;
1481
1482 const gboolean active = _cacorrect_supported(img);
1483
1484 gtk_stack_set_visible_child_name(GTK_STACK(self->widget), active ? "raw" : "non_raw");
1485
1486 gtk_widget_set_visible(g->avoidshift, active);
1487 dt_bauhaus_combobox_set_from_value(g->iterations, p->iterations);
1488 gtk_widget_set_visible(g->iterations, active);
1489}
1490
1492{
1494}
1495
1497{
1499
1500 GtkWidget *box_raw = self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1501
1502 g->iterations = dt_bauhaus_combobox_from_params(self, "iterations");
1503 gtk_widget_set_tooltip_text(g->iterations, _("iteration runs, default is twice"));
1504
1505 g->avoidshift = dt_bauhaus_toggle_from_params(self, "avoidshift");
1506 gtk_widget_set_tooltip_text(g->avoidshift, _("activate colorshift correction for blue & red channels"));
1507
1508 // start building top level widget
1509 self->widget = gtk_stack_new();
1510 gtk_stack_set_homogeneous(GTK_STACK(self->widget), FALSE);
1511 gtk_stack_add_named(GTK_STACK(self->widget), box_raw, "raw");
1512
1513 GtkWidget *label_non_raw = dt_ui_label_new(_("automatic chromatic aberration correction\nonly for Bayer raw files"));
1514 gtk_stack_add_named(GTK_STACK(self->widget), label_non_raw, "non_raw");
1515}
1516
1517#undef CA_SIZE_MINIMUM
1518// clang-format off
1519// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1520// vim: shiftwidth=2 expandtab tabstop=2 cindent
1521// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1522// clang-format on
#define SQR(a)
Definition ashift.c:128
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void cleanup(dt_imageio_module_format_t *self)
Definition avif.c:164
#define m
Definition basecurve.c:278
gboolean dt_bauhaus_combobox_set_from_value(GtkWidget *widget, int value)
Definition bauhaus.c:2330
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
static __DT_CLONE_TARGETS__ gboolean LinEqSolve(int nDim, double *pfMatr, double *pfVect, double *pfSolution)
Definition cacorrect.c:214
const char ** description(struct dt_iop_module_t *self)
Definition cacorrect.c:114
int default_group()
Definition cacorrect.c:124
static void pixSort(float *a, float *b)
Definition cacorrect.c:301
void reload_defaults(dt_iop_module_t *module)
Definition cacorrect.c:1406
#define INLINE
Definition cacorrect.c:167
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *params, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition cacorrect.c:1431
void gui_update(dt_iop_module_t *self)
Definition cacorrect.c:1457
#define CA_SIZE_MINIMUM
Definition cacorrect.c:316
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition cacorrect.c:1445
static float LIM(const float a, const float b, const float c)
Definition cacorrect.c:176
const char * name()
Definition cacorrect.c:108
void gui_init(dt_iop_module_t *self)
Definition cacorrect.c:1496
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
Definition cacorrect.c:1475
void gui_cleanup(dt_iop_module_t *self)
Definition cacorrect.c:1491
static gboolean _cacorrect_supported(const dt_image_t *img)
Definition cacorrect.c:1401
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition cacorrect.c:134
void input_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
Definition cacorrect.c:139
int flags()
Definition cacorrect.c:129
dt_iop_cacorrect_multi_t
WARNING: mem allocs are not protected against out-of-memory (NULL buffers) because the code is a mess...
Definition cacorrect.c:81
@ CACORRETC_MULTI_1
Definition cacorrect.c:82
@ CACORRETC_MULTI_4
Definition cacorrect.c:85
@ CACORRETC_MULTI_3
Definition cacorrect.c:84
@ CACORRETC_MULTI_5
Definition cacorrect.c:86
@ CACORRETC_MULTI_2
Definition cacorrect.c:83
static float intp(const float a, const float b, const float c)
Definition cacorrect.c:180
__DT_CLONE_TARGETS__ int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const i, void *const o)
Definition cacorrect.c:318
gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
Definition cacorrect.c:1417
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition cacorrect.c:1451
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
Definition cacorrect.c:147
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
@ IOP_CS_RAW
static dt_aligned_pixel_t rgb
const dt_colormatrix_t dt_aligned_pixel_t out
const float top
static const int row
dt_image_pipe_class_t dt_image_pipe_class(const dt_image_t *img)
const char * dt_image_pipe_class_name(const dt_image_pipe_class_t klass)
gboolean dt_image_is_monochrome(const dt_image_t *img)
gboolean dt_image_needs_demosaic(const dt_image_t *img)
static void memset_zero(void *const buffer, size_t size)
Set the memory buffer to zero as a pack of unsigned char.
Definition darktable.h:881
#define dt_pixelpipe_cache_alloc_perthread(n, objsize, padded_size)
Definition darktable.h:1006
#define dt_free_align(ptr)
Definition darktable.h:481
static void * dt_calloc_align(size_t size)
Definition darktable.h:488
#define __OMP_FOR__(...)
Definition darktable.h:261
#define dt_free(ptr)
Definition darktable.h:456
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define dt_pixelpipe_cache_free_align(mem)
Definition darktable.h:453
#define dt_pixelpipe_cache_alloc_align_float(pixels, pipe)
Definition darktable.h:442
#define __DT_CLONE_TARGETS__
Definition darktable.h:367
#define dt_get_perthread(buf, padsize)
Definition darktable.h:1035
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
static int FC(const int row, const int col, const unsigned int filters)
void dt_iop_params_t
Definition dev_history.h:41
void default_input_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
Definition format.c:57
void dt_iop_buffer_dsc_update_bpp(dt_iop_buffer_dsc_t *dsc)
Definition format.c:28
void dt_gaussian_free(dt_gaussian_t *g)
Definition gaussian.c:330
__DT_CLONE_TARGETS__ void dt_gaussian_blur(dt_gaussian_t *g, const float *const in, float *const out)
Definition gaussian.c:171
dt_gaussian_t * dt_gaussian_init(const int width, const int height, const int channels, const float *max, const float *min, const float sigma, const int order)
Definition gaussian.c:122
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
static GtkWidget * dt_ui_label_new(const gchar *str)
Definition gtk.h:461
static void dt_iop_image_copy_by_size(float *const __restrict__ out, const float *const __restrict__ in, const size_t width, const size_t height, const size_t ch)
Definition imagebuf.h:87
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
Definition imageop.c:3141
#define IOP_GUI_FREE
Definition imageop.h:602
#define dt_iop_fmt_log(module, fmt,...)
Debug helper to trace a module's input-format-driven decisions on the -d pipe channel (DT_DEBUG_PIPE)...
Definition imageop.h:453
@ IOP_FLAGS_DEPRECATED
Definition imageop.h:168
@ IOP_FLAGS_ONE_INSTANCE
Definition imageop.h:172
@ IOP_GROUP_REPAIR
Definition imageop.h:140
#define IOP_GUI_ALLOC(module)
Definition imageop.h:599
GtkWidget * dt_bauhaus_toggle_from_params(dt_iop_module_t *self, const char *param)
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
static const float x
const float *const const float coeff[3]
float *const restrict const size_t k
#define eps
Definition rcd.c:81
struct _GtkWidget GtkWidget
Definition splash.h:29
const float uint32_t state[4]
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
struct dt_develop_t * dev
dt_image_t image_storage
Definition develop.h:259
dt_iop_buffer_dsc_t dsc
Definition image.h:337
uint32_t filters
Definition format.h:60
unsigned int channels
Definition format.h:54
dt_iop_cacorrect_multi_t iterations
Definition cacorrect.c:92
int32_t hide_enable_button
Definition imageop.h:262
GtkWidget * widget
Definition imageop.h:337
struct dt_develop_t * dev
Definition imageop.h:296
dt_iop_gui_data_t * gui_data
Definition imageop.h:311
dt_iop_params_t * params
Definition imageop.h:307
Region of interest passed through the pixelpipe.
Definition imageop.h:72
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29