Coverage Report

Created: 2026-08-17 07:50

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
  fimg = (float *)malloc((size * 3 + iheight + iwidth) * sizeof *fimg);
151
  temp = fimg + size * 3;
152
  if ((nc = colors) == 3 && filters)
153
    nc++;
154
#pragma omp parallel default(shared) private(                                  \
155
    i, col, row, thold, lev, lpass, hpass, temp, c) firstprivate(scale, size)
156
  {
157
    temp = (float *)malloc((iheight + iwidth) * sizeof *fimg);
158
    FORC(nc)
159
    { /* denoise R,G1,B,G3 individually */
160
#pragma omp for
161
      for (i = 0; i < size; i++)
162
        fimg[i] = 256 * sqrt((double)(image[i][c] << scale));
163
      for (hpass = lev = 0; lev < 5; lev++)
164
      {
165
        lpass = size * ((lev & 1) + 1);
166
#pragma omp for
167
        for (row = 0; row < iheight; row++)
168
        {
169
          hat_transform(temp, fimg + hpass + row * iwidth, 1, iwidth, 1 << lev);
170
          for (col = 0; col < iwidth; col++)
171
            fimg[lpass + row * iwidth + col] = temp[col] * 0.25;
172
        }
173
#pragma omp for
174
        for (col = 0; col < iwidth; col++)
175
        {
176
          hat_transform(temp, fimg + lpass + col, iwidth, iheight, 1 << lev);
177
          for (row = 0; row < iheight; row++)
178
            fimg[lpass + row * iwidth + col] = temp[row] * 0.25;
179
        }
180
        thold = threshold * noise[lev];
181
#pragma omp for
182
        for (i = 0; i < size; i++)
183
        {
184
          fimg[hpass + i] -= fimg[lpass + i];
185
          if (fimg[hpass + i] < -thold)
186
            fimg[hpass + i] += thold;
187
          else if (fimg[hpass + i] > thold)
188
            fimg[hpass + i] -= thold;
189
          else
190
            fimg[hpass + i] = 0;
191
          if (hpass)
192
            fimg[i] += fimg[hpass + i];
193
        }
194
        hpass = lpass;
195
      }
196
#pragma omp for
197
      for (i = 0; i < size; i++)
198
        image[i][c] = CLIP(SQR(fimg[i] + fimg[lpass + i]) / 0x10000);
199
    }
200
    free(temp);
201
  } /* end omp parallel */
202
  /* the following loops are hard to parallelize, no idea yes,
203
   * problem is wlast which is carrying dependency
204
   * second part should be easier, but did not yet get it right.
205
   */
206
  if (filters && colors == 3)
207
  { /* pull G1 and G3 closer together */
208
    for (row = 0; row < 2; row++)
209
    {
210
      mul[row] = 0.125 * pre_mul[FC(row + 1, 0) | 1] / pre_mul[FC(row, 0) | 1];
211
      blk[row] = cblack[FC(row, 0) | 1];
212
    }
213
    for (i = 0; i < 4; i++)
214
      window[i] = (ushort *)fimg + width * i;
215
    for (wlast = -1, row = 1; row < height - 1; row++)
216
    {
217
      while (wlast < row + 1)
218
      {
219
        for (wlast++, i = 0; i < 4; i++)
220
          window[(i + 3) & 3] = window[i];
221
        for (col = FC(wlast, 1) & 1; col < width; col += 2)
222
          window[2][col] = BAYER(wlast, col);
223
      }
224
      thold = threshold / 512;
225
      for (col = (FC(row, 0) & 1) + 1; col < width - 1; col += 2)
226
      {
227
        avg = (window[0][col - 1] + window[0][col + 1] + window[2][col - 1] +
228
               window[2][col + 1] - blk[~row & 1] * 4) *
229
                  mul[row & 1] +
230
              (window[1][col] + blk[row & 1]) * 0.5;
231
        avg = avg < 0 ? 0 : sqrt(avg);
232
        diff = sqrt((double)BAYER(row, col)) - avg;
233
        if (diff < -thold)
234
          diff += thold;
235
        else if (diff > thold)
236
          diff -= thold;
237
        else
238
          diff = 0;
239
        BAYER(row, col) = CLIP(SQR(avg + diff) + 0.5);
240
      }
241
    }
242
  }
243
  free(fimg);
244
}
245
246
#endif
247
void LibRaw::median_filter()
248
0
{
249
0
  ushort(*pix)[4];
250
0
  int pass, c, i, j, k, med[9];
251
0
  static const uchar opt[] = /* Optimal 9-element median search */
252
0
      {1, 2, 4, 5, 7, 8, 0, 1, 3, 4, 6, 7, 1, 2, 4, 5, 7, 8, 0,
253
0
       3, 5, 8, 4, 7, 3, 6, 1, 4, 2, 5, 4, 7, 4, 2, 6, 4, 4, 2};
254
255
0
  for (pass = 1; pass <= med_passes; pass++)
256
0
  {
257
0
    RUN_CALLBACK(LIBRAW_PROGRESS_MEDIAN_FILTER, pass - 1, med_passes);
258
0
    for (c = 0; c < 3; c += 2)
259
0
    {
260
0
      for (pix = image; pix < image + width * height; pix++)
261
0
        pix[0][3] = pix[0][c];
262
0
      for (pix = image + width; pix < image + width * (height - 1); pix++)
263
0
      {
264
0
        if ((pix - image + 1) % width < 2)
265
0
          continue;
266
0
        for (k = 0, i = -width; i <= width; i += width)
267
0
          for (j = i - 1; j <= i + 1; j++)
268
0
            med[k++] = pix[j][3] - pix[j][1];
269
0
        for (i = 0; i < int(sizeof opt); i += 2)
270
0
          if (med[opt[i]] > med[opt[i + 1]])
271
0
            SWAP(med[opt[i]], med[opt[i + 1]]);
272
0
        pix[0][c] = CLIP(med[4] + pix[0][1]);
273
0
      }
274
0
    }
275
0
  }
276
0
}
277
278
void LibRaw::blend_highlights()
279
0
{
280
0
  int clip = INT_MAX, row, col, c, i, j;
281
0
  static const float trans[2][4][4] = {
282
0
      {{1, 1, 1}, {1.7320508f, -1.7320508f, 0}, {-1, -1, 2}},
283
0
      {{1, 1, 1, 1}, {1, -1, 1, -1}, {1, 1, -1, -1}, {1, -1, -1, 1}}};
284
0
  static const float itrans[2][4][4] = {
285
0
      {{1, 0.8660254f, -0.5}, {1, -0.8660254f, -0.5}, {1, 0, 1}},
286
0
      {{1, 1, 1, 1}, {1, -1, 1, -1}, {1, 1, -1, -1}, {1, -1, -1, 1}}};
287
0
  float cam[2][4], lab[2][4], sum[2], chratio;
288
289
0
  if ((unsigned)(colors - 3) > 1)
290
0
    return;
291
0
  RUN_CALLBACK(LIBRAW_PROGRESS_HIGHLIGHTS, 0, 2);
292
0
  FORCC if (clip > (i = int(65535.f * pre_mul[c]))) clip = i;
293
0
  for (row = 0; row < height; row++)
294
0
    for (col = 0; col < width; col++)
295
0
    {
296
0
      FORCC if (image[row * width + col][c] > clip) break;
297
0
      if (c == colors)
298
0
        continue;
299
0
      FORCC
300
0
      {
301
0
        cam[0][c] = image[row * width + col][c];
302
0
        cam[1][c] = MIN(cam[0][c], clip);
303
0
      }
304
0
      for (i = 0; i < 2; i++)
305
0
      {
306
0
        FORCC for (lab[i][c] = 0, j = 0; j < colors; j++) lab[i][c] +=
307
0
            int(trans[colors - 3][c][j] * cam[i][j]);
308
0
        for (sum[i] = 0, c = 1; c < colors; c++)
309
0
          sum[i] += SQR(lab[i][c]);
310
0
      }
311
0
      chratio = sqrt(sum[1] / sum[0]);
312
0
      for (c = 1; c < colors; c++)
313
0
        lab[0][c] *= chratio;
314
0
      FORCC for (cam[0][c] = 0, j = 0; j < colors; j++) cam[0][c] +=
315
0
          itrans[colors - 3][c][j] * lab[0][j];
316
0
      FORCC image[row * width + col][c] = ushort(cam[0][c] / colors);
317
0
    }
318
0
  RUN_CALLBACK(LIBRAW_PROGRESS_HIGHLIGHTS, 1, 2);
319
0
}
320
321
0
#define SCALE (4 >> shrink)
322
void LibRaw::recover_highlights()
323
0
{
324
0
  float *map, sum, wgt, grow;
325
0
  int hsat[4], count, spread, change, val, i;
326
0
  unsigned high, wide, mrow, mcol, row, col, kc, c, d, y, x;
327
0
  ushort *pixel;
328
0
  static const signed char dir[8][2] = {{-1, -1}, {-1, 0}, {-1, 1}, {0, 1},
329
0
                                        {1, 1},   {1, 0},  {1, -1}, {0, -1}};
330
331
0
  grow = powf(2.0f, float(4 - highlight));
332
0
  FORC(unsigned(colors)) hsat[c] = int(32000.f * pre_mul[c]);
333
0
  FORC(unsigned(colors))
334
0
    if(hsat[c]<1)
335
0
      return;
336
0
  for (kc = 0, c = 1; c < (unsigned)colors; c++)
337
0
    if (pre_mul[kc] < pre_mul[c])
338
0
      kc = c;
339
0
  high = height / SCALE;
340
0
  wide = width / SCALE;
341
0
  map = (float *)calloc(high, wide * sizeof *map);
342
0
  FORC(unsigned(colors)) if (c != kc)
343
0
  {
344
0
    RUN_CALLBACK(LIBRAW_PROGRESS_HIGHLIGHTS, c - 1, colors - 1);
345
0
    memset(map, 0, high * wide * sizeof *map);
346
0
    for (mrow = 0; mrow < high; mrow++)
347
0
      for (mcol = 0; mcol < wide; mcol++)
348
0
      {
349
0
        count = 0;
350
0
    sum = wgt = 0;
351
0
        for (row = mrow * SCALE; row < (mrow + 1) * SCALE; row++)
352
0
          for (col = mcol * SCALE; col < (mcol + 1) * SCALE; col++)
353
0
          {
354
0
            pixel = image[row * width + col];
355
0
            if (pixel[c] / hsat[c] == 1 && pixel[kc] > 24000)
356
0
            {
357
0
              sum += pixel[c];
358
0
              wgt += pixel[kc];
359
0
              count++;
360
0
            }
361
0
          }
362
0
        if (count == SCALE * SCALE)
363
0
          map[mrow * wide + mcol] = sum / wgt;
364
0
      }
365
0
    for (spread = int(32.f / grow); spread--;)
366
0
    {
367
0
      for (mrow = 0; mrow < high; mrow++)
368
0
        for (mcol = 0; mcol < wide; mcol++)
369
0
        {
370
0
          if (map[mrow * wide + mcol])
371
0
            continue;
372
0
      sum = 0;
373
0
      count = 0;
374
0
          for (d = 0; d < 8; d++)
375
0
          {
376
0
            y = mrow + dir[d][0];
377
0
            x = mcol + dir[d][1];
378
0
            if (y < high && x < wide && map[y * wide + x] > 0)
379
0
            {
380
0
              sum += (1 + (d & 1)) * map[y * wide + x];
381
0
              count += 1 + (d & 1);
382
0
            }
383
0
          }
384
0
          if (count > 3)
385
0
            map[mrow * wide + mcol] = -(sum + grow) / (count + grow);
386
0
        }
387
0
      for (change = i = 0; i < int(high * wide); i++)
388
0
        if (map[i] < 0)
389
0
        {
390
0
          map[i] = -map[i];
391
0
          change = 1;
392
0
        }
393
0
      if (!change)
394
0
        break;
395
0
    }
396
0
    for (i = 0; i < int(high * wide); i++)
397
0
      if (map[i] == 0)
398
0
        map[i] = 1;
399
0
    for (mrow = 0; mrow < high; mrow++)
400
0
      for (mcol = 0; mcol < wide; mcol++)
401
0
      {
402
0
        for (row = mrow * SCALE; row < (mrow + 1) * SCALE; row++)
403
0
          for (col = mcol * SCALE; col < (mcol + 1) * SCALE; col++)
404
0
          {
405
0
            pixel = image[row * width + col];
406
0
            if (pixel[c] / hsat[c] > 1)
407
0
            {
408
0
              val = int(pixel[kc] * map[mrow * wide + mcol]);
409
0
              if (pixel[c] < val)
410
0
                pixel[c] = CLIP(val);
411
0
            }
412
0
          }
413
0
      }
414
0
  }
415
0
  free(map);
416
0
}
417
#undef SCALE