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