Coverage Report

Created: 2026-09-14 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/LibRaw/src/postprocessing/postprocessing_aux.cpp
Line
Count
Source
1
/* -*- C++ -*-
2
 * Copyright 2019-2025 LibRaw LLC (info@libraw.org)
3
 *
4
 LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
5
 dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
6
 LibRaw do not use RESTRICTED code from dcraw.c
7
8
 LibRaw is free software; you can redistribute it and/or modify
9
 it under the terms of the one of two licenses as you choose:
10
11
1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
12
   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
13
14
2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
15
   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
16
17
 */
18
19
#include "../../internal/dcraw_defs.h"
20
21
void LibRaw::hat_transform(float *temp, float *base, int st, int size, int sc)
22
0
{
23
0
  int i;
24
0
  for (i = 0; i < sc; i++)
25
0
    temp[i] = 2 * base[st * i] + base[st * (sc - i)] + base[st * (i + sc)];
26
0
  for (; i + sc < size; i++)
27
0
    temp[i] = 2 * base[st * i] + base[st * (i - sc)] + base[st * (i + sc)];
28
0
  for (; i < size; i++)
29
0
    temp[i] = 2 * base[st * i] + base[st * (i - sc)] +
30
0
              base[st * (2 * size - 2 - (i + sc))];
31
0
}
32
33
#if !defined(LIBRAW_USE_OPENMP)
34
void LibRaw::wavelet_denoise()
35
0
{
36
0
  float *fimg = 0, *temp, thold, mul[2], avg, diff;
37
0
  int scale = 1, size, lev, hpass, lpass, row, col, nc, c, i, wlast, blk[2];
38
0
  ushort *window[4];
39
0
  static const float noise[] = {0.8002f, 0.2735f, 0.1202f, 0.0585f,
40
0
                                0.0291f, 0.0152f, 0.0080f, 0.0044f};
41
42
0
  if (iwidth < 65 || iheight < 65) return;
43
0
  if (int64_t(iwidth) * int64_t(iheight) >= 0x15540000LL) return; // ensure pixel count less then 358M so total allocation size is less then 4GB
44
45
0
  while (maximum << scale < 0x10000)
46
0
    scale++;
47
0
  maximum <<= --scale;
48
0
  black <<= scale;
49
0
  FORC4 cblack[c] <<= scale;
50
0
  size = iheight * iwidth;
51
0
  fimg = (float *)malloc((size * 3 + iheight + iwidth + 128) * sizeof *fimg);
52
0
  temp = fimg + size * 3;
53
0
  if ((nc = colors) == 3 && filters)
54
0
    nc++;
55
0
  FORC(nc)
56
0
  { /* denoise R,G1,B,G3 individually */
57
0
    for (i = 0; i < size; i++)
58
0
      fimg[i] = 256.f * sqrtf((float)(image[i][c] << scale));
59
0
    for (hpass = lev = 0; lev < 5; lev++)
60
0
    {
61
0
      lpass = size * ((lev & 1) + 1);
62
0
      for (row = 0; row < iheight; row++)
63
0
      {
64
0
        hat_transform(temp, fimg + hpass + row * iwidth, 1, iwidth, 1 << lev);
65
0
        for (col = 0; col < iwidth; col++)
66
0
          fimg[lpass + row * iwidth + col] = temp[col] * 0.25f;
67
0
      }
68
0
      for (col = 0; col < iwidth; col++)
69
0
      {
70
0
        hat_transform(temp, fimg + lpass + col, iwidth, iheight, 1 << lev);
71
0
        for (row = 0; row < iheight; row++)
72
0
          fimg[lpass + row * iwidth + col] = temp[row] * 0.25f;
73
0
      }
74
0
      thold = threshold * noise[lev];
75
0
      for (i = 0; i < size; i++)
76
0
      {
77
0
        fimg[hpass + i] -= fimg[lpass + i];
78
0
        if (fimg[hpass + i] < -thold)
79
0
          fimg[hpass + i] += thold;
80
0
        else if (fimg[hpass + i] > thold)
81
0
          fimg[hpass + i] -= thold;
82
0
        else
83
0
          fimg[hpass + i] = 0;
84
0
        if (hpass)
85
0
          fimg[i] += fimg[hpass + i];
86
0
      }
87
0
      hpass = lpass;
88
0
    }
89
0
    for (i = 0; i < size; i++)
90
0
      image[i][c] = CLIP(SQR(fimg[i] + fimg[lpass + i]) / 0x10000);
91
0
  }
92
0
  if (filters && colors == 3)
93
0
  { /* pull G1 and G3 closer together */
94
0
    for (row = 0; row < 2; row++)
95
0
    {
96
0
      mul[row] = 0.125f * pre_mul[FC(row + 1, 0) | 1] / pre_mul[FC(row, 0) | 1];
97
0
      blk[row] = cblack[FC(row, 0) | 1];
98
0
    }
99
0
    for (i = 0; i < 4; i++)
100
0
      window[i] = (ushort *)fimg + width * i;
101
0
    for (wlast = -1, row = 1; row < height - 1; row++)
102
0
    {
103
0
      while (wlast < row + 1)
104
0
      {
105
0
        for (wlast++, i = 0; i < 4; i++)
106
0
          window[(i + 3) & 3] = window[i];
107
0
        for (col = FC(wlast, 1) & 1; col < width; col += 2)
108
0
          window[2][col] = BAYER(wlast, col);
109
0
      }
110
0
      thold = threshold / 512;
111
0
      for (col = (FC(row, 0) & 1) + 1; col < width - 1; col += 2)
112
0
      {
113
0
        avg = (window[0][col - 1] + window[0][col + 1] + window[2][col - 1] +
114
0
               window[2][col + 1] - blk[~row & 1] * 4) *
115
0
                  mul[row & 1] +
116
0
              (window[1][col] + blk[row & 1]) * 0.5f;
117
0
        avg = avg < 0 ? 0 : sqrt(avg);
118
0
        diff = sqrtf((float)BAYER(row, col)) - avg;
119
0
        if (diff < -thold)
120
0
          diff += thold;
121
0
        else if (diff > thold)
122
0
          diff -= thold;
123
0
        else
124
0
          diff = 0;
125
0
        BAYER(row, col) = CLIP(SQR(avg + diff) + 0.5);
126
0
      }
127
0
    }
128
0
  }
129
0
  free(fimg);
130
0
}
131
#else /* LIBRAW_USE_OPENMP */
132
void LibRaw::wavelet_denoise()
133
{
134
  float *fimg = 0, *temp, thold, mul[2], avg, diff;
135
  int scale = 1, size, lev, hpass, lpass, row, col, nc, c, i, wlast, blk[2];
136
  ushort *window[4];
137
  static const float noise[] = {0.8002, 0.2735, 0.1202, 0.0585,
138
                                0.0291, 0.0152, 0.0080, 0.0044};
139
140
  if (iwidth < 65 || iheight < 65)
141
    return;
142
  if (int64_t(iwidth) * int64_t(iheight) >= 0x15540000LL)
143
    return; // ensure pixel count less then 358M so total allocation size is less then 4GB
144
145
  while (maximum << scale < 0x10000)
146
    scale++;
147
  maximum <<= --scale;
148
  black <<= scale;
149
  FORC4 cblack[c] <<= scale;
150
  size = iheight * iwidth;
151
  fimg = (float *)malloc((size * 3 + iheight + iwidth) * sizeof *fimg);
152
  temp = fimg + size * 3;
153
  if ((nc = colors) == 3 && filters)
154
    nc++;
155
#pragma omp parallel default(shared) private(                                  \
156
    i, col, row, thold, lev, lpass, hpass, temp, c) firstprivate(scale, size)
157
  {
158
    temp = (float *)malloc((iheight + iwidth) * sizeof *fimg);
159
    FORC(nc)
160
    { /* denoise R,G1,B,G3 individually */
161
#pragma omp for
162
      for (i = 0; i < size; i++)
163
        fimg[i] = 256 * sqrt((double)(image[i][c] << scale));
164
      for (hpass = lev = 0; lev < 5; lev++)
165
      {
166
        lpass = size * ((lev & 1) + 1);
167
#pragma omp for
168
        for (row = 0; row < iheight; row++)
169
        {
170
          hat_transform(temp, fimg + hpass + row * iwidth, 1, iwidth, 1 << lev);
171
          for (col = 0; col < iwidth; col++)
172
            fimg[lpass + row * iwidth + col] = temp[col] * 0.25;
173
        }
174
#pragma omp for
175
        for (col = 0; col < iwidth; col++)
176
        {
177
          hat_transform(temp, fimg + lpass + col, iwidth, iheight, 1 << lev);
178
          for (row = 0; row < iheight; row++)
179
            fimg[lpass + row * iwidth + col] = temp[row] * 0.25;
180
        }
181
        thold = threshold * noise[lev];
182
#pragma omp for
183
        for (i = 0; i < size; i++)
184
        {
185
          fimg[hpass + i] -= fimg[lpass + i];
186
          if (fimg[hpass + i] < -thold)
187
            fimg[hpass + i] += thold;
188
          else if (fimg[hpass + i] > thold)
189
            fimg[hpass + i] -= thold;
190
          else
191
            fimg[hpass + i] = 0;
192
          if (hpass)
193
            fimg[i] += fimg[hpass + i];
194
        }
195
        hpass = lpass;
196
      }
197
#pragma omp for
198
      for (i = 0; i < size; i++)
199
        image[i][c] = CLIP(SQR(fimg[i] + fimg[lpass + i]) / 0x10000);
200
    }
201
    free(temp);
202
  } /* end omp parallel */
203
  /* the following loops are hard to parallelize, no idea yes,
204
   * problem is wlast which is carrying dependency
205
   * second part should be easier, but did not yet get it right.
206
   */
207
  if (filters && colors == 3)
208
  { /* pull G1 and G3 closer together */
209
    for (row = 0; row < 2; row++)
210
    {
211
      mul[row] = 0.125 * pre_mul[FC(row + 1, 0) | 1] / pre_mul[FC(row, 0) | 1];
212
      blk[row] = cblack[FC(row, 0) | 1];
213
    }
214
    for (i = 0; i < 4; i++)
215
      window[i] = (ushort *)fimg + width * i;
216
    for (wlast = -1, row = 1; row < height - 1; row++)
217
    {
218
      while (wlast < row + 1)
219
      {
220
        for (wlast++, i = 0; i < 4; i++)
221
          window[(i + 3) & 3] = window[i];
222
        for (col = FC(wlast, 1) & 1; col < width; col += 2)
223
          window[2][col] = BAYER(wlast, col);
224
      }
225
      thold = threshold / 512;
226
      for (col = (FC(row, 0) & 1) + 1; col < width - 1; col += 2)
227
      {
228
        avg = (window[0][col - 1] + window[0][col + 1] + window[2][col - 1] +
229
               window[2][col + 1] - blk[~row & 1] * 4) *
230
                  mul[row & 1] +
231
              (window[1][col] + blk[row & 1]) * 0.5;
232
        avg = avg < 0 ? 0 : sqrt(avg);
233
        diff = sqrt((double)BAYER(row, col)) - avg;
234
        if (diff < -thold)
235
          diff += thold;
236
        else if (diff > thold)
237
          diff -= thold;
238
        else
239
          diff = 0;
240
        BAYER(row, col) = CLIP(SQR(avg + diff) + 0.5);
241
      }
242
    }
243
  }
244
  free(fimg);
245
}
246
247
#endif
248
void LibRaw::median_filter()
249
0
{
250
0
  ushort(*pix)[4];
251
0
  int pass, c, i, j, k, med[9];
252
0
  static const uchar opt[] = /* Optimal 9-element median search */
253
0
      {1, 2, 4, 5, 7, 8, 0, 1, 3, 4, 6, 7, 1, 2, 4, 5, 7, 8, 0,
254
0
       3, 5, 8, 4, 7, 3, 6, 1, 4, 2, 5, 4, 7, 4, 2, 6, 4, 4, 2};
255
256
0
  for (pass = 1; pass <= med_passes; pass++)
257
0
  {
258
0
    RUN_CALLBACK(LIBRAW_PROGRESS_MEDIAN_FILTER, pass - 1, med_passes);
259
0
    for (c = 0; c < 3; c += 2)
260
0
    {
261
0
      for (pix = image; pix < image + width * height; pix++)
262
0
        pix[0][3] = pix[0][c];
263
0
      for (pix = image + width; pix < image + width * (height - 1); pix++)
264
0
      {
265
0
        if ((pix - image + 1) % width < 2)
266
0
          continue;
267
0
        for (k = 0, i = -width; i <= width; i += width)
268
0
          for (j = i - 1; j <= i + 1; j++)
269
0
            med[k++] = pix[j][3] - pix[j][1];
270
0
        for (i = 0; i < int(sizeof opt); i += 2)
271
0
          if (med[opt[i]] > med[opt[i + 1]])
272
0
            SWAP(med[opt[i]], med[opt[i + 1]]);
273
0
        pix[0][c] = CLIP(med[4] + pix[0][1]);
274
0
      }
275
0
    }
276
0
  }
277
0
}
278
279
void LibRaw::blend_highlights()
280
0
{
281
0
  int clip = INT_MAX, row, col, c, i, j;
282
0
  static const float trans[2][4][4] = {
283
0
      {{1, 1, 1}, {1.7320508f, -1.7320508f, 0}, {-1, -1, 2}},
284
0
      {{1, 1, 1, 1}, {1, -1, 1, -1}, {1, 1, -1, -1}, {1, -1, -1, 1}}};
285
0
  static const float itrans[2][4][4] = {
286
0
      {{1, 0.8660254f, -0.5}, {1, -0.8660254f, -0.5}, {1, 0, 1}},
287
0
      {{1, 1, 1, 1}, {1, -1, 1, -1}, {1, 1, -1, -1}, {1, -1, -1, 1}}};
288
0
  float cam[2][4], lab[2][4], sum[2], chratio;
289
290
0
  if ((unsigned)(colors - 3) > 1)
291
0
    return;
292
0
  RUN_CALLBACK(LIBRAW_PROGRESS_HIGHLIGHTS, 0, 2);
293
0
  FORCC if (clip > (i = int(65535.f * pre_mul[c]))) clip = i;
294
0
  for (row = 0; row < height; row++)
295
0
    for (col = 0; col < width; col++)
296
0
    {
297
0
      FORCC if (image[row * width + col][c] > clip) break;
298
0
      if (c == colors)
299
0
        continue;
300
0
      FORCC
301
0
      {
302
0
        cam[0][c] = image[row * width + col][c];
303
0
        cam[1][c] = MIN(cam[0][c], clip);
304
0
      }
305
0
      for (i = 0; i < 2; i++)
306
0
      {
307
0
        FORCC for (lab[i][c] = 0, j = 0; j < colors; j++) lab[i][c] +=
308
0
            int(trans[colors - 3][c][j] * cam[i][j]);
309
0
        for (sum[i] = 0, c = 1; c < colors; c++)
310
0
          sum[i] += SQR(lab[i][c]);
311
0
      }
312
0
      chratio = sqrt(sum[1] / sum[0]);
313
0
      for (c = 1; c < colors; c++)
314
0
        lab[0][c] *= chratio;
315
0
      FORCC for (cam[0][c] = 0, j = 0; j < colors; j++) cam[0][c] +=
316
0
          itrans[colors - 3][c][j] * lab[0][j];
317
0
      FORCC image[row * width + col][c] = ushort(cam[0][c] / colors);
318
0
    }
319
0
  RUN_CALLBACK(LIBRAW_PROGRESS_HIGHLIGHTS, 1, 2);
320
0
}
321
322
0
#define SCALE (4 >> shrink)
323
void LibRaw::recover_highlights()
324
0
{
325
0
  float *map, sum, wgt, grow;
326
0
  int hsat[4], count, spread, change, val, i;
327
0
  unsigned high, wide, mrow, mcol, row, col, kc, c, d, y, x;
328
0
  ushort *pixel;
329
0
  static const signed char dir[8][2] = {{-1, -1}, {-1, 0}, {-1, 1}, {0, 1},
330
0
                                        {1, 1},   {1, 0},  {1, -1}, {0, -1}};
331
332
0
  grow = powf(2.0f, float(4 - highlight));
333
0
  FORC(unsigned(colors)) hsat[c] = int(32000.f * pre_mul[c]);
334
0
  FORC(unsigned(colors))
335
0
    if(hsat[c]<1)
336
0
      return;
337
0
  for (kc = 0, c = 1; c < (unsigned)colors; c++)
338
0
    if (pre_mul[kc] < pre_mul[c])
339
0
      kc = c;
340
0
  high = height / SCALE;
341
0
  wide = width / SCALE;
342
0
  map = (float *)calloc(high, wide * sizeof *map);
343
0
  FORC(unsigned(colors)) if (c != kc)
344
0
  {
345
0
    RUN_CALLBACK(LIBRAW_PROGRESS_HIGHLIGHTS, c - 1, colors - 1);
346
0
    memset(map, 0, high * wide * sizeof *map);
347
0
    for (mrow = 0; mrow < high; mrow++)
348
0
      for (mcol = 0; mcol < wide; mcol++)
349
0
      {
350
0
        count = 0;
351
0
    sum = wgt = 0;
352
0
        for (row = mrow * SCALE; row < (mrow + 1) * SCALE; row++)
353
0
          for (col = mcol * SCALE; col < (mcol + 1) * SCALE; col++)
354
0
          {
355
0
            pixel = image[row * width + col];
356
0
            if (pixel[c] / hsat[c] == 1 && pixel[kc] > 24000)
357
0
            {
358
0
              sum += pixel[c];
359
0
              wgt += pixel[kc];
360
0
              count++;
361
0
            }
362
0
          }
363
0
        if (count == SCALE * SCALE)
364
0
          map[mrow * wide + mcol] = sum / wgt;
365
0
      }
366
0
    for (spread = int(32.f / grow); spread--;)
367
0
    {
368
0
      for (mrow = 0; mrow < high; mrow++)
369
0
        for (mcol = 0; mcol < wide; mcol++)
370
0
        {
371
0
          if (map[mrow * wide + mcol])
372
0
            continue;
373
0
      sum = 0;
374
0
      count = 0;
375
0
          for (d = 0; d < 8; d++)
376
0
          {
377
0
            y = mrow + dir[d][0];
378
0
            x = mcol + dir[d][1];
379
0
            if (y < high && x < wide && map[y * wide + x] > 0)
380
0
            {
381
0
              sum += (1 + (d & 1)) * map[y * wide + x];
382
0
              count += 1 + (d & 1);
383
0
            }
384
0
          }
385
0
          if (count > 3)
386
0
            map[mrow * wide + mcol] = -(sum + grow) / (count + grow);
387
0
        }
388
0
      for (change = i = 0; i < int(high * wide); i++)
389
0
        if (map[i] < 0)
390
0
        {
391
0
          map[i] = -map[i];
392
0
          change = 1;
393
0
        }
394
0
      if (!change)
395
0
        break;
396
0
    }
397
0
    for (i = 0; i < int(high * wide); i++)
398
0
      if (map[i] == 0)
399
0
        map[i] = 1;
400
0
    for (mrow = 0; mrow < high; mrow++)
401
0
      for (mcol = 0; mcol < wide; mcol++)
402
0
      {
403
0
        for (row = mrow * SCALE; row < (mrow + 1) * SCALE; row++)
404
0
          for (col = mcol * SCALE; col < (mcol + 1) * SCALE; col++)
405
0
          {
406
0
            pixel = image[row * width + col];
407
0
            if (pixel[c] / hsat[c] > 1)
408
0
            {
409
0
              val = int(pixel[kc] * map[mrow * wide + mcol]);
410
0
              if (pixel[c] < val)
411
0
                pixel[c] = CLIP(val);
412
0
            }
413
0
          }
414
0
      }
415
0
  }
416
0
  free(map);
417
0
}
418
#undef SCALE