Coverage Report

Created: 2026-04-29 07:00

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