/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 | 936M | { |
28 | 936M | int c, i, j, k; |
29 | 936M | float r, xyz[3]; |
30 | | #ifdef LIBRAW_NOTHREADS |
31 | | static float cbrt[0x10000], xyz_cam[3][4]; |
32 | | #else |
33 | 2.95G | #define cbrt tls->ahd_data.cbrt |
34 | 8.40G | #define xyz_cam tls->ahd_data.xyz_cam |
35 | 936M | #endif |
36 | | |
37 | 936M | if (!rgb) |
38 | 2.22k | { |
39 | 2.22k | #ifndef LIBRAW_NOTHREADS |
40 | 2.22k | if (cbrt[0] < -1.0f) |
41 | 2.22k | #endif |
42 | 146M | for (i = 0; i < 0x10000; i++) |
43 | 146M | { |
44 | 146M | r = i / 65535.0f; |
45 | 146M | cbrt[i] = |
46 | 146M | r > 0.008856f ? pow(r, 1.f / 3.0f) : 7.787f * r + 16.f / 116.0f; |
47 | 146M | } |
48 | 8.91k | for (i = 0; i < 3; i++) |
49 | 26.4k | for (j = 0; j < colors; j++) |
50 | 78.9k | for (xyz_cam[i][j] = float( k = 0); k < 3; k++) |
51 | 59.1k | xyz_cam[i][j] += float(LibRaw_constants::xyz_rgb[i][k] * rgb_cam[k][j] / |
52 | 59.1k | LibRaw_constants::d65_white[i]); |
53 | 2.22k | return; |
54 | 2.22k | } |
55 | 936M | xyz[0] = xyz[1] = xyz[2] = 0.5; |
56 | 936M | FORCC |
57 | 2.80G | { |
58 | 2.80G | xyz[0] += xyz_cam[0][c] * rgb[c]; |
59 | 2.80G | xyz[1] += xyz_cam[1][c] * rgb[c]; |
60 | 2.80G | xyz[2] += xyz_cam[2][c] * rgb[c]; |
61 | 2.80G | } |
62 | 936M | xyz[0] = cbrt[CLIP((int)xyz[0])]; |
63 | 936M | xyz[1] = cbrt[CLIP((int)xyz[1])]; |
64 | 936M | xyz[2] = cbrt[CLIP((int)xyz[2])]; |
65 | 936M | lab[0] = short(64 * (116 * xyz[1] - 16)); |
66 | 936M | lab[1] = short(64 * 500 * (xyz[0] - xyz[1])); |
67 | 936M | lab[2] = short(64 * 200 * (xyz[1] - xyz[2])); |
68 | 936M | #ifndef LIBRAW_NOTHREADS |
69 | 936M | #undef cbrt |
70 | 936M | #undef xyz_cam |
71 | 936M | #endif |
72 | 936M | } |
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.42k | { |
77 | 3.42k | int row, col; |
78 | 3.42k | int c, val; |
79 | 3.42k | ushort(*pix)[4]; |
80 | 3.42k | const int rowlimit = MIN(top + LIBRAW_AHD_TILE, height - 2); |
81 | 3.42k | const int collimit = MIN(left + LIBRAW_AHD_TILE, width - 2); |
82 | | |
83 | 810k | for (row = top; row < rowlimit; row++) |
84 | 806k | { |
85 | 806k | col = left + (FC(row, left) & 1); |
86 | 115M | for (c = FC(row, col); col < collimit; col += 2) |
87 | 114M | { |
88 | 114M | pix = image + row * width + col; |
89 | 114M | val = |
90 | 114M | ((pix[-1][1] + pix[0][c] + pix[1][1]) * 2 - pix[-2][c] - pix[2][c]) >> |
91 | 114M | 2; |
92 | 114M | out_rgb[0][row - top][col - left][1] = ULIM(val, pix[-1][1], pix[1][1]); |
93 | 114M | val = ((pix[-width][1] + pix[0][c] + pix[width][1]) * 2 - |
94 | 114M | pix[-2 * width][c] - pix[2 * width][c]) >> |
95 | 114M | 2; |
96 | 114M | out_rgb[1][row - top][col - left][1] = |
97 | 114M | ULIM(val, pix[-width][1], pix[width][1]); |
98 | 114M | } |
99 | 806k | } |
100 | 3.42k | } |
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 | 6.85k | { |
105 | 6.85k | unsigned row, col; |
106 | 6.85k | int c, val; |
107 | 6.85k | ushort(*pix)[4]; |
108 | 6.85k | ushort(*rix)[3]; |
109 | 6.85k | short(*lix)[3]; |
110 | 6.85k | const unsigned num_pix_per_row = 4 * width; |
111 | 6.85k | const unsigned rowlimit = MIN(top + LIBRAW_AHD_TILE - 1, height - 3); |
112 | 6.85k | const unsigned collimit = MIN(left + LIBRAW_AHD_TILE - 1, width - 3); |
113 | 6.85k | ushort *pix_above; |
114 | 6.85k | ushort *pix_below; |
115 | 6.85k | int t1, t2; |
116 | | |
117 | 1.60M | for (row = top + 1; row < rowlimit; row++) |
118 | 1.59M | { |
119 | 1.59M | pix = image + row * width + left; |
120 | 1.59M | rix = &inout_rgb[row - top][0]; |
121 | 1.59M | lix = &out_lab[row - top][0]; |
122 | | |
123 | 454M | for (col = left + 1; col < collimit; col++) |
124 | 452M | { |
125 | 452M | pix++; |
126 | 452M | pix_above = &pix[0][0] - num_pix_per_row; |
127 | 452M | pix_below = &pix[0][0] + num_pix_per_row; |
128 | 452M | rix++; |
129 | 452M | lix++; |
130 | | |
131 | 452M | c = 2 - FC(row, col); |
132 | | |
133 | 452M | if (c == 1) |
134 | 226M | { |
135 | 226M | c = FC(row + 1, col); |
136 | 226M | t1 = 2 - c; |
137 | 226M | val = pix[0][1] + |
138 | 226M | ((pix[-1][t1] + pix[1][t1] - rix[-1][1] - rix[1][1]) >> 1); |
139 | 226M | rix[0][t1] = CLIP(val); |
140 | 226M | val = |
141 | 226M | pix[0][1] + ((pix_above[c] + pix_below[c] - |
142 | 226M | rix[-LIBRAW_AHD_TILE][1] - rix[LIBRAW_AHD_TILE][1]) >> |
143 | 226M | 1); |
144 | 226M | } |
145 | 226M | else |
146 | 226M | { |
147 | 226M | t1 = -4 + c; /* -4+c: pixel of color c to the left */ |
148 | 226M | t2 = 4 + c; /* 4+c: pixel of color c to the right */ |
149 | 226M | val = rix[0][1] + |
150 | 226M | ((pix_above[t1] + pix_above[t2] + pix_below[t1] + pix_below[t2] - |
151 | 226M | rix[-LIBRAW_AHD_TILE - 1][1] - rix[-LIBRAW_AHD_TILE + 1][1] - |
152 | 226M | rix[+LIBRAW_AHD_TILE - 1][1] - rix[+LIBRAW_AHD_TILE + 1][1] + |
153 | 226M | 1) >> |
154 | 226M | 2); |
155 | 226M | } |
156 | 452M | rix[0][c] = CLIP(val); |
157 | 452M | c = FC(row, col); |
158 | 452M | rix[0][c] = pix[0][c]; |
159 | 452M | cielab(rix[0], lix[0]); |
160 | 452M | } |
161 | 1.59M | } |
162 | 6.85k | } |
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.42k | { |
167 | 3.42k | int direction; |
168 | 10.2k | for (direction = 0; direction < 2; direction++) |
169 | 6.85k | { |
170 | 6.85k | ahd_interpolate_r_and_b_in_rgb_and_convert_to_cielab( |
171 | 6.85k | top, left, inout_rgb[direction], out_lab[direction]); |
172 | 6.85k | } |
173 | 3.42k | } |
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.42k | { |
179 | 3.42k | int row, col; |
180 | 3.42k | int tr; |
181 | 3.42k | int direction; |
182 | 3.42k | int i; |
183 | 3.42k | short(*lix)[3]; |
184 | 3.42k | short(*lixs[2])[3]; |
185 | 3.42k | short *adjacent_lix; |
186 | 3.42k | unsigned ldiff[2][4], abdiff[2][4], leps, abeps; |
187 | 3.42k | static const int dir[4] = {-1, 1, -LIBRAW_AHD_TILE, LIBRAW_AHD_TILE}; |
188 | 3.42k | const int rowlimit = MIN(top + LIBRAW_AHD_TILE - 2, height - 4); |
189 | 3.42k | const int collimit = MIN(left + LIBRAW_AHD_TILE - 2, width - 4); |
190 | 3.42k | int homogeneity; |
191 | 3.42k | char(*homogeneity_map_p)[2]; |
192 | | |
193 | 3.42k | memset(out_homogeneity_map, 0, 2 * LIBRAW_AHD_TILE * LIBRAW_AHD_TILE); |
194 | | |
195 | 796k | for (row = top + 2; row < rowlimit; row++) |
196 | 792k | { |
197 | 792k | tr = row - top; |
198 | 792k | homogeneity_map_p = &out_homogeneity_map[tr][1]; |
199 | 2.37M | for (direction = 0; direction < 2; direction++) |
200 | 1.58M | { |
201 | 1.58M | lixs[direction] = &lab[direction][tr][1]; |
202 | 1.58M | } |
203 | | |
204 | 223M | for (col = left + 2; col < collimit; col++) |
205 | 223M | { |
206 | 223M | homogeneity_map_p++; |
207 | | |
208 | 669M | for (direction = 0; direction < 2; direction++) |
209 | 446M | { |
210 | 446M | lix = ++lixs[direction]; |
211 | 2.23G | for (i = 0; i < 4; i++) |
212 | 1.78G | { |
213 | 1.78G | adjacent_lix = lix[dir[i]]; |
214 | 1.78G | ldiff[direction][i] = ABS(lix[0][0] - adjacent_lix[0]); |
215 | 1.78G | abdiff[direction][i] = SQR(lix[0][1] - adjacent_lix[1]) + |
216 | 1.78G | SQR(lix[0][2] - adjacent_lix[2]); |
217 | 1.78G | } |
218 | 446M | } |
219 | 223M | leps = MIN(MAX(ldiff[0][0], ldiff[0][1]), MAX(ldiff[1][2], ldiff[1][3])); |
220 | 223M | abeps = |
221 | 223M | MIN(MAX(abdiff[0][0], abdiff[0][1]), MAX(abdiff[1][2], abdiff[1][3])); |
222 | 669M | for (direction = 0; direction < 2; direction++) |
223 | 446M | { |
224 | 446M | homogeneity = 0; |
225 | 2.23G | for (i = 0; i < 4; i++) |
226 | 1.78G | { |
227 | 1.78G | if (ldiff[direction][i] <= leps && abdiff[direction][i] <= abeps) |
228 | 1.53G | { |
229 | 1.53G | homogeneity++; |
230 | 1.53G | } |
231 | 1.78G | } |
232 | 446M | homogeneity_map_p[0][direction] = homogeneity; |
233 | 446M | } |
234 | 223M | } |
235 | 792k | } |
236 | 3.42k | } |
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.42k | { |
241 | 3.42k | int row, col; |
242 | 3.42k | int tr, tc; |
243 | 3.42k | int i, j; |
244 | 3.42k | int direction; |
245 | 3.42k | int hm[2]; |
246 | 3.42k | int c; |
247 | 3.42k | const int rowlimit = MIN(top + LIBRAW_AHD_TILE - 3, height - 5); |
248 | 3.42k | const int collimit = MIN(left + LIBRAW_AHD_TILE - 3, width - 5); |
249 | | |
250 | 3.42k | ushort(*pix)[4]; |
251 | 3.42k | ushort(*rix[2])[3]; |
252 | | |
253 | 789k | for (row = top + 3; row < rowlimit; row++) |
254 | 786k | { |
255 | 786k | tr = row - top; |
256 | 786k | pix = &image[row * width + left + 2]; |
257 | 2.35M | for (direction = 0; direction < 2; direction++) |
258 | 1.57M | { |
259 | 1.57M | rix[direction] = &rgb[direction][tr][2]; |
260 | 1.57M | } |
261 | | |
262 | 220M | for (col = left + 3; col < collimit; col++) |
263 | 220M | { |
264 | 220M | tc = col - left; |
265 | 220M | pix++; |
266 | 660M | for (direction = 0; direction < 2; direction++) |
267 | 440M | { |
268 | 440M | rix[direction]++; |
269 | 440M | } |
270 | | |
271 | 660M | for (direction = 0; direction < 2; direction++) |
272 | 440M | { |
273 | 440M | hm[direction] = 0; |
274 | 1.76G | for (i = tr - 1; i <= tr + 1; i++) |
275 | 1.32G | { |
276 | 5.28G | for (j = tc - 1; j <= tc + 1; j++) |
277 | 3.96G | { |
278 | 3.96G | hm[direction] += homogeneity_map[i][j][direction]; |
279 | 3.96G | } |
280 | 1.32G | } |
281 | 440M | } |
282 | 220M | if (hm[0] != hm[1]) |
283 | 22.6M | { |
284 | 22.6M | memcpy(pix[0], rix[hm[1] > hm[0]][0], 3 * sizeof(ushort)); |
285 | 22.6M | } |
286 | 197M | else |
287 | 197M | { |
288 | 592M | FORC3 { pix[0][c] = (rix[0][0][c] + rix[1][0][c]) >> 1; } |
289 | 197M | } |
290 | 220M | } |
291 | 786k | } |
292 | 3.42k | } |
293 | | void LibRaw::ahd_interpolate() |
294 | 2.08k | { |
295 | 2.08k | int terminate_flag = 0; |
296 | 2.08k | cielab(0, 0); |
297 | 2.08k | border_interpolate(5); |
298 | | |
299 | | #ifdef LIBRAW_USE_OPENMP |
300 | | int buffer_count = omp_get_max_threads(); |
301 | | #else |
302 | 2.08k | int buffer_count = 1; |
303 | 2.08k | #endif |
304 | | |
305 | 2.08k | size_t buffer_size = 26 * LIBRAW_AHD_TILE * LIBRAW_AHD_TILE; /* 1664 kB */ |
306 | 2.08k | 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 | 4.91k | for (int top = 2; top < height - 5; top += LIBRAW_AHD_TILE - 6) |
312 | 2.83k | { |
313 | | #ifdef LIBRAW_USE_OPENMP |
314 | | if (0 == omp_get_thread_num()) |
315 | | #endif |
316 | 2.83k | 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 | 2.83k | char* buffer = buffers[0]; |
329 | 2.83k | #endif |
330 | | |
331 | 2.83k | ushort(*rgb)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3]; |
332 | 2.83k | short(*lab)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3]; |
333 | 2.83k | char(*homo)[LIBRAW_AHD_TILE][2]; |
334 | | |
335 | 2.83k | rgb = (ushort(*)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3])buffer; |
336 | 2.83k | lab = (short(*)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3])( |
337 | 2.83k | buffer + 12 * LIBRAW_AHD_TILE * LIBRAW_AHD_TILE); |
338 | 2.83k | homo = (char(*)[LIBRAW_AHD_TILE][2])(buffer + 24 * LIBRAW_AHD_TILE * |
339 | 2.83k | LIBRAW_AHD_TILE); |
340 | | |
341 | 6.26k | for (int left = 2; !terminate_flag && (left < width - 5); |
342 | 3.42k | left += LIBRAW_AHD_TILE - 6) |
343 | 3.42k | { |
344 | 3.42k | ahd_interpolate_green_h_and_v(top, left, rgb); |
345 | 3.42k | ahd_interpolate_r_and_b_and_convert_to_cielab(top, left, rgb, lab); |
346 | 3.42k | ahd_interpolate_build_homogeneity_map(top, left, lab, homo); |
347 | 3.42k | ahd_interpolate_combine_homogeneous_pixels(top, left, rgb, homo); |
348 | 3.42k | } |
349 | 2.83k | } |
350 | | |
351 | 2.08k | free_omp_buffers(buffers, buffer_count); |
352 | | |
353 | 2.08k | if (terminate_flag) |
354 | 0 | throw LIBRAW_EXCEPTION_CANCELLED_BY_CALLBACK; |
355 | 2.08k | } |