/src/libraw/src/demosaic/ahd_demosaic.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 | | /* |
22 | | Adaptive Homogeneity-Directed interpolation is based on |
23 | | the work of Keigo Hirakawa, Thomas Parks, and Paul Lee. |
24 | | */ |
25 | | |
26 | | void LibRaw::cielab(ushort rgb[3], short lab[3]) |
27 | 1.12G | { |
28 | 1.12G | int c, i, j, k; |
29 | 1.12G | float r, xyz[3]; |
30 | | #ifdef LIBRAW_NOTHREADS |
31 | | static float cbrt[0x10000], xyz_cam[3][4]; |
32 | | #else |
33 | 3.52G | #define cbrt tls->ahd_data.cbrt |
34 | 10.0G | #define xyz_cam tls->ahd_data.xyz_cam |
35 | 1.12G | #endif |
36 | | |
37 | 1.12G | if (!rgb) |
38 | 2.48k | { |
39 | 2.48k | #ifndef LIBRAW_NOTHREADS |
40 | 2.48k | if (cbrt[0] < -1.0f) |
41 | 2.48k | #endif |
42 | 162M | for (i = 0; i < 0x10000; i++) |
43 | 162M | { |
44 | 162M | r = i / 65535.0f; |
45 | 162M | cbrt[i] = |
46 | 162M | r > 0.008856f ? pow(r, 1.f / 3.0f) : 7.787f * r + 16.f / 116.0f; |
47 | 162M | } |
48 | 9.93k | for (i = 0; i < 3; i++) |
49 | 29.5k | for (j = 0; j < colors; j++) |
50 | 88.5k | for (xyz_cam[i][j] = float( k = 0); k < 3; k++) |
51 | 66.4k | xyz_cam[i][j] += float(LibRaw_constants::xyz_rgb[i][k] * rgb_cam[k][j] / |
52 | 66.4k | LibRaw_constants::d65_white[i]); |
53 | 2.48k | return; |
54 | 2.48k | } |
55 | 1.12G | xyz[0] = xyz[1] = xyz[2] = 0.5; |
56 | 1.12G | FORCC |
57 | 3.35G | { |
58 | 3.35G | xyz[0] += xyz_cam[0][c] * rgb[c]; |
59 | 3.35G | xyz[1] += xyz_cam[1][c] * rgb[c]; |
60 | 3.35G | xyz[2] += xyz_cam[2][c] * rgb[c]; |
61 | 3.35G | } |
62 | 1.12G | xyz[0] = cbrt[CLIP((int)xyz[0])]; |
63 | 1.12G | xyz[1] = cbrt[CLIP((int)xyz[1])]; |
64 | 1.12G | xyz[2] = cbrt[CLIP((int)xyz[2])]; |
65 | 1.12G | lab[0] = short(64 * (116 * xyz[1] - 16)); |
66 | 1.12G | lab[1] = short(64 * 500 * (xyz[0] - xyz[1])); |
67 | 1.12G | lab[2] = short(64 * 200 * (xyz[1] - xyz[2])); |
68 | 1.12G | #ifndef LIBRAW_NOTHREADS |
69 | 1.12G | #undef cbrt |
70 | 1.12G | #undef xyz_cam |
71 | 1.12G | #endif |
72 | 1.12G | } |
73 | | |
74 | | void LibRaw::ahd_interpolate_green_h_and_v( |
75 | | int top, int left, ushort (*out_rgb)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3]) |
76 | 3.90k | { |
77 | 3.90k | int row, col; |
78 | 3.90k | int c, val; |
79 | 3.90k | ushort(*pix)[4]; |
80 | 3.90k | const int rowlimit = MIN(top + LIBRAW_AHD_TILE, height - 2); |
81 | 3.90k | const int collimit = MIN(left + LIBRAW_AHD_TILE, width - 2); |
82 | | |
83 | 954k | for (row = top; row < rowlimit; row++) |
84 | 950k | { |
85 | 950k | col = left + (FC(row, left) & 1); |
86 | 136M | for (c = FC(row, col); col < collimit; col += 2) |
87 | 135M | { |
88 | 135M | pix = image + row * width + col; |
89 | 135M | val = |
90 | 135M | ((pix[-1][1] + pix[0][c] + pix[1][1]) * 2 - pix[-2][c] - pix[2][c]) >> |
91 | 135M | 2; |
92 | 135M | out_rgb[0][row - top][col - left][1] = ULIM(val, pix[-1][1], pix[1][1]); |
93 | 135M | val = ((pix[-width][1] + pix[0][c] + pix[width][1]) * 2 - |
94 | 135M | pix[-2 * width][c] - pix[2 * width][c]) >> |
95 | 135M | 2; |
96 | 135M | out_rgb[1][row - top][col - left][1] = |
97 | 135M | ULIM(val, pix[-width][1], pix[width][1]); |
98 | 135M | } |
99 | 950k | } |
100 | 3.90k | } |
101 | | void LibRaw::ahd_interpolate_r_and_b_in_rgb_and_convert_to_cielab( |
102 | | int top, int left, ushort (*inout_rgb)[LIBRAW_AHD_TILE][3], |
103 | | short (*out_lab)[LIBRAW_AHD_TILE][3]) |
104 | 7.81k | { |
105 | 7.81k | unsigned row, col; |
106 | 7.81k | int c, val; |
107 | 7.81k | ushort(*pix)[4]; |
108 | 7.81k | ushort(*rix)[3]; |
109 | 7.81k | short(*lix)[3]; |
110 | 7.81k | const unsigned num_pix_per_row = 4 * width; |
111 | 7.81k | const unsigned rowlimit = MIN(top + LIBRAW_AHD_TILE - 1, height - 3); |
112 | 7.81k | const unsigned collimit = MIN(left + LIBRAW_AHD_TILE - 1, width - 3); |
113 | 7.81k | ushort *pix_above; |
114 | 7.81k | ushort *pix_below; |
115 | 7.81k | int t1, t2; |
116 | | |
117 | 1.89M | for (row = top + 1; row < rowlimit; row++) |
118 | 1.88M | { |
119 | 1.88M | pix = image + row * width + left; |
120 | 1.88M | rix = &inout_rgb[row - top][0]; |
121 | 1.88M | lix = &out_lab[row - top][0]; |
122 | | |
123 | 536M | for (col = left + 1; col < collimit; col++) |
124 | 534M | { |
125 | 534M | pix++; |
126 | 534M | pix_above = &pix[0][0] - num_pix_per_row; |
127 | 534M | pix_below = &pix[0][0] + num_pix_per_row; |
128 | 534M | rix++; |
129 | 534M | lix++; |
130 | | |
131 | 534M | c = 2 - FC(row, col); |
132 | | |
133 | 534M | if (c == 1) |
134 | 267M | { |
135 | 267M | c = FC(row + 1, col); |
136 | 267M | t1 = 2 - c; |
137 | 267M | val = pix[0][1] + |
138 | 267M | ((pix[-1][t1] + pix[1][t1] - rix[-1][1] - rix[1][1]) >> 1); |
139 | 267M | rix[0][t1] = CLIP(val); |
140 | 267M | val = |
141 | 267M | pix[0][1] + ((pix_above[c] + pix_below[c] - |
142 | 267M | rix[-LIBRAW_AHD_TILE][1] - rix[LIBRAW_AHD_TILE][1]) >> |
143 | 267M | 1); |
144 | 267M | } |
145 | 267M | else |
146 | 267M | { |
147 | 267M | t1 = -4 + c; /* -4+c: pixel of color c to the left */ |
148 | 267M | t2 = 4 + c; /* 4+c: pixel of color c to the right */ |
149 | 267M | val = rix[0][1] + |
150 | 267M | ((pix_above[t1] + pix_above[t2] + pix_below[t1] + pix_below[t2] - |
151 | 267M | rix[-LIBRAW_AHD_TILE - 1][1] - rix[-LIBRAW_AHD_TILE + 1][1] - |
152 | 267M | rix[+LIBRAW_AHD_TILE - 1][1] - rix[+LIBRAW_AHD_TILE + 1][1] + |
153 | 267M | 1) >> |
154 | 267M | 2); |
155 | 267M | } |
156 | 534M | rix[0][c] = CLIP(val); |
157 | 534M | c = FC(row, col); |
158 | 534M | rix[0][c] = pix[0][c]; |
159 | 534M | cielab(rix[0], lix[0]); |
160 | 534M | } |
161 | 1.88M | } |
162 | 7.81k | } |
163 | | void LibRaw::ahd_interpolate_r_and_b_and_convert_to_cielab( |
164 | | int top, int left, ushort (*inout_rgb)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3], |
165 | | short (*out_lab)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3]) |
166 | 3.90k | { |
167 | 3.90k | int direction; |
168 | 11.7k | for (direction = 0; direction < 2; direction++) |
169 | 7.81k | { |
170 | 7.81k | ahd_interpolate_r_and_b_in_rgb_and_convert_to_cielab( |
171 | 7.81k | top, left, inout_rgb[direction], out_lab[direction]); |
172 | 7.81k | } |
173 | 3.90k | } |
174 | | |
175 | | void LibRaw::ahd_interpolate_build_homogeneity_map( |
176 | | int top, int left, short (*lab)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3], |
177 | | char (*out_homogeneity_map)[LIBRAW_AHD_TILE][2]) |
178 | 3.90k | { |
179 | 3.90k | int row, col; |
180 | 3.90k | int tr; |
181 | 3.90k | int direction; |
182 | 3.90k | int i; |
183 | 3.90k | short(*lix)[3]; |
184 | 3.90k | short(*lixs[2])[3]; |
185 | 3.90k | short *adjacent_lix; |
186 | 3.90k | unsigned ldiff[2][4], abdiff[2][4], leps, abeps; |
187 | 3.90k | static const int dir[4] = {-1, 1, -LIBRAW_AHD_TILE, LIBRAW_AHD_TILE}; |
188 | 3.90k | const int rowlimit = MIN(top + LIBRAW_AHD_TILE - 2, height - 4); |
189 | 3.90k | const int collimit = MIN(left + LIBRAW_AHD_TILE - 2, width - 4); |
190 | 3.90k | int homogeneity; |
191 | 3.90k | char(*homogeneity_map_p)[2]; |
192 | | |
193 | 3.90k | memset(out_homogeneity_map, 0, 2 * LIBRAW_AHD_TILE * LIBRAW_AHD_TILE); |
194 | | |
195 | 938k | for (row = top + 2; row < rowlimit; row++) |
196 | 934k | { |
197 | 934k | tr = row - top; |
198 | 934k | homogeneity_map_p = &out_homogeneity_map[tr][1]; |
199 | 2.80M | for (direction = 0; direction < 2; direction++) |
200 | 1.86M | { |
201 | 1.86M | lixs[direction] = &lab[direction][tr][1]; |
202 | 1.86M | } |
203 | | |
204 | 264M | for (col = left + 2; col < collimit; col++) |
205 | 263M | { |
206 | 263M | homogeneity_map_p++; |
207 | | |
208 | 791M | for (direction = 0; direction < 2; direction++) |
209 | 527M | { |
210 | 527M | lix = ++lixs[direction]; |
211 | 2.63G | for (i = 0; i < 4; i++) |
212 | 2.11G | { |
213 | 2.11G | adjacent_lix = lix[dir[i]]; |
214 | 2.11G | ldiff[direction][i] = ABS(lix[0][0] - adjacent_lix[0]); |
215 | 2.11G | abdiff[direction][i] = SQR(lix[0][1] - adjacent_lix[1]) + |
216 | 2.11G | SQR(lix[0][2] - adjacent_lix[2]); |
217 | 2.11G | } |
218 | 527M | } |
219 | 263M | leps = MIN(MAX(ldiff[0][0], ldiff[0][1]), MAX(ldiff[1][2], ldiff[1][3])); |
220 | 263M | abeps = |
221 | 263M | MIN(MAX(abdiff[0][0], abdiff[0][1]), MAX(abdiff[1][2], abdiff[1][3])); |
222 | 791M | for (direction = 0; direction < 2; direction++) |
223 | 527M | { |
224 | 527M | homogeneity = 0; |
225 | 2.63G | for (i = 0; i < 4; i++) |
226 | 2.11G | { |
227 | 2.11G | if (ldiff[direction][i] <= leps && abdiff[direction][i] <= abeps) |
228 | 1.86G | { |
229 | 1.86G | homogeneity++; |
230 | 1.86G | } |
231 | 2.11G | } |
232 | 527M | homogeneity_map_p[0][direction] = homogeneity; |
233 | 527M | } |
234 | 263M | } |
235 | 934k | } |
236 | 3.90k | } |
237 | | void LibRaw::ahd_interpolate_combine_homogeneous_pixels( |
238 | | int top, int left, ushort (*rgb)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3], |
239 | | char (*homogeneity_map)[LIBRAW_AHD_TILE][2]) |
240 | 3.90k | { |
241 | 3.90k | int row, col; |
242 | 3.90k | int tr, tc; |
243 | 3.90k | int i, j; |
244 | 3.90k | int direction; |
245 | 3.90k | int hm[2]; |
246 | 3.90k | int c; |
247 | 3.90k | const int rowlimit = MIN(top + LIBRAW_AHD_TILE - 3, height - 5); |
248 | 3.90k | const int collimit = MIN(left + LIBRAW_AHD_TILE - 3, width - 5); |
249 | | |
250 | 3.90k | ushort(*pix)[4]; |
251 | 3.90k | ushort(*rix[2])[3]; |
252 | | |
253 | 930k | for (row = top + 3; row < rowlimit; row++) |
254 | 926k | { |
255 | 926k | tr = row - top; |
256 | 926k | pix = &image[row * width + left + 2]; |
257 | 2.78M | for (direction = 0; direction < 2; direction++) |
258 | 1.85M | { |
259 | 1.85M | rix[direction] = &rgb[direction][tr][2]; |
260 | 1.85M | } |
261 | | |
262 | 261M | for (col = left + 3; col < collimit; col++) |
263 | 260M | { |
264 | 260M | tc = col - left; |
265 | 260M | pix++; |
266 | 780M | for (direction = 0; direction < 2; direction++) |
267 | 520M | { |
268 | 520M | rix[direction]++; |
269 | 520M | } |
270 | | |
271 | 780M | for (direction = 0; direction < 2; direction++) |
272 | 520M | { |
273 | 520M | hm[direction] = 0; |
274 | 2.08G | for (i = tr - 1; i <= tr + 1; i++) |
275 | 1.56G | { |
276 | 6.24G | for (j = tc - 1; j <= tc + 1; j++) |
277 | 4.68G | { |
278 | 4.68G | hm[direction] += homogeneity_map[i][j][direction]; |
279 | 4.68G | } |
280 | 1.56G | } |
281 | 520M | } |
282 | 260M | if (hm[0] != hm[1]) |
283 | 19.4M | { |
284 | 19.4M | memcpy(pix[0], rix[hm[1] > hm[0]][0], 3 * sizeof(ushort)); |
285 | 19.4M | } |
286 | 240M | else |
287 | 240M | { |
288 | 722M | FORC3 { pix[0][c] = (rix[0][0][c] + rix[1][0][c]) >> 1; } |
289 | 240M | } |
290 | 260M | } |
291 | 926k | } |
292 | 3.90k | } |
293 | | void LibRaw::ahd_interpolate() |
294 | 2.30k | { |
295 | 2.30k | int terminate_flag = 0; |
296 | 2.30k | cielab(0, 0); |
297 | 2.30k | border_interpolate(5); |
298 | | |
299 | | #ifdef LIBRAW_USE_OPENMP |
300 | | int buffer_count = omp_get_max_threads(); |
301 | | #else |
302 | 2.30k | int buffer_count = 1; |
303 | 2.30k | #endif |
304 | | |
305 | 2.30k | size_t buffer_size = 26 * LIBRAW_AHD_TILE * LIBRAW_AHD_TILE; /* 1664 kB */ |
306 | 2.30k | char** buffers = malloc_omp_buffers(buffer_count, buffer_size); |
307 | | |
308 | | #ifdef LIBRAW_USE_OPENMP |
309 | | #pragma omp parallel for schedule(dynamic) default(none) shared(terminate_flag) firstprivate(buffers) |
310 | | #endif |
311 | 5.45k | for (int top = 2; top < height - 5; top += LIBRAW_AHD_TILE - 6) |
312 | 3.14k | { |
313 | | #ifdef LIBRAW_USE_OPENMP |
314 | | if (0 == omp_get_thread_num()) |
315 | | #endif |
316 | 3.14k | if (callbacks.progress_cb) |
317 | 0 | { |
318 | 0 | int rr = (*callbacks.progress_cb)(callbacks.progresscb_data, |
319 | 0 | LIBRAW_PROGRESS_INTERPOLATE, |
320 | 0 | top - 2, height - 7); |
321 | 0 | if (rr) |
322 | 0 | terminate_flag = 1; |
323 | 0 | } |
324 | | |
325 | | #if defined(LIBRAW_USE_OPENMP) |
326 | | char* buffer = buffers[omp_get_thread_num()]; |
327 | | #else |
328 | 3.14k | char* buffer = buffers[0]; |
329 | 3.14k | #endif |
330 | | |
331 | 3.14k | ushort(*rgb)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3]; |
332 | 3.14k | short(*lab)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3]; |
333 | 3.14k | char(*homo)[LIBRAW_AHD_TILE][2]; |
334 | | |
335 | 3.14k | rgb = (ushort(*)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3])buffer; |
336 | 3.14k | lab = (short(*)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3])( |
337 | 3.14k | buffer + 12 * LIBRAW_AHD_TILE * LIBRAW_AHD_TILE); |
338 | 3.14k | homo = (char(*)[LIBRAW_AHD_TILE][2])(buffer + 24 * LIBRAW_AHD_TILE * |
339 | 3.14k | LIBRAW_AHD_TILE); |
340 | | |
341 | 7.05k | for (int left = 2; !terminate_flag && (left < width - 5); |
342 | 3.90k | left += LIBRAW_AHD_TILE - 6) |
343 | 3.90k | { |
344 | 3.90k | ahd_interpolate_green_h_and_v(top, left, rgb); |
345 | 3.90k | ahd_interpolate_r_and_b_and_convert_to_cielab(top, left, rgb, lab); |
346 | 3.90k | ahd_interpolate_build_homogeneity_map(top, left, lab, homo); |
347 | 3.90k | ahd_interpolate_combine_homogeneous_pixels(top, left, rgb, homo); |
348 | 3.90k | } |
349 | 3.14k | } |
350 | | |
351 | 2.30k | free_omp_buffers(buffers, buffer_count); |
352 | | |
353 | 2.30k | if (terminate_flag) |
354 | 0 | throw LIBRAW_EXCEPTION_CANCELLED_BY_CALLBACK; |
355 | 2.30k | } |