/src/LibRaw/src/demosaic/aahd_demosaic.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- C++ -*- |
2 | | * File: aahd_demosaic.cpp |
3 | | * Copyright 2013 Anton Petrusevich |
4 | | * Created: Wed May 15, 2013 |
5 | | * |
6 | | * This code is licensed under one of two licenses as you choose: |
7 | | * |
8 | | * 1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1 |
9 | | * (See file LICENSE.LGPL provided in LibRaw distribution archive for |
10 | | * details). |
11 | | * |
12 | | * 2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 |
13 | | * (See file LICENSE.CDDL provided in LibRaw distribution archive for |
14 | | * details). |
15 | | * |
16 | | */ |
17 | | |
18 | | #include "../../internal/dmp_include.h" |
19 | | |
20 | | typedef ushort ushort3[3]; |
21 | | typedef int int3[3]; |
22 | | |
23 | | #ifndef Pnw |
24 | 0 | #define Pnw (-1 - nr_width) |
25 | 0 | #define Pn (-nr_width) |
26 | 0 | #define Pne (+1 - nr_width) |
27 | 0 | #define Pe (+1) |
28 | | #define Pse (+1 + nr_width) |
29 | 0 | #define Ps (+nr_width) |
30 | 0 | #define Psw (-1 + nr_width) |
31 | 0 | #define Pw (-1) |
32 | | #endif |
33 | | |
34 | | struct AAHD |
35 | | { |
36 | | int nr_height, nr_width; |
37 | | static const int nr_margin = 4; |
38 | | static const int Thot = 4; |
39 | | static const int Tdead = 4; |
40 | | static const int OverFraction = 8; |
41 | | ushort3 *rgb_ahd[2]; |
42 | | int3 *yuv[2]; |
43 | | char *ndir, *homo[2]; |
44 | | ushort channel_maximum[3], channels_max; |
45 | | ushort channel_minimum[3]; |
46 | | static const float yuv_coeff[3][3]; |
47 | | static float gammaLUT[0x10000]; |
48 | | float yuv_cam[3][3]; |
49 | | LibRaw &libraw; |
50 | | enum |
51 | | { |
52 | | HVSH = 1, |
53 | | HOR = 2, |
54 | | VER = 4, |
55 | | HORSH = HOR | HVSH, |
56 | | VERSH = VER | HVSH, |
57 | | HOT = 8 |
58 | | }; |
59 | | |
60 | | static inline float calc_dist(int c1, int c2) throw() |
61 | 0 | { |
62 | 0 | return c1 > c2 ? (float)c1 / c2 : (float)c2 / c1; |
63 | 0 | } |
64 | | int inline Y(ushort3 &rgb) throw() |
65 | 0 | { |
66 | 0 | return int(yuv_cam[0][0] * rgb[0] + yuv_cam[0][1] * rgb[1] + |
67 | 0 | yuv_cam[0][2] * rgb[2]); |
68 | 0 | } |
69 | | int inline U(ushort3 &rgb) throw() |
70 | 0 | { |
71 | 0 | return int(yuv_cam[1][0] * rgb[0] + yuv_cam[1][1] * rgb[1] + |
72 | 0 | yuv_cam[1][2] * rgb[2]); |
73 | 0 | } |
74 | | int inline V(ushort3 &rgb) throw() |
75 | 0 | { |
76 | 0 | return int(yuv_cam[2][0] * rgb[0] + yuv_cam[2][1] * rgb[1] + |
77 | 0 | yuv_cam[2][2] * rgb[2]); |
78 | 0 | } |
79 | | inline int nr_offset(int row, int col) throw() |
80 | 0 | { |
81 | 0 | return (row * nr_width + col); |
82 | 0 | } |
83 | | ~AAHD(); |
84 | | AAHD(LibRaw &_libraw); |
85 | | void make_ahd_greens(); |
86 | | void make_ahd_gline(int i); |
87 | | void make_ahd_rb(); |
88 | | void make_ahd_rb_hv(int i); |
89 | | void make_ahd_rb_last(int i); |
90 | | void evaluate_ahd(); |
91 | | void combine_image(); |
92 | | void hide_hots(); |
93 | | void refine_hv_dirs(); |
94 | | void refine_hv_dirs(int i, int js); |
95 | | void refine_ihv_dirs(int i); |
96 | | void illustrate_dirs(); |
97 | | void illustrate_dline(int i); |
98 | | }; |
99 | | |
100 | | const float AAHD::yuv_coeff[3][3] = { |
101 | | // YPbPr |
102 | | // { |
103 | | // 0.299f, |
104 | | // 0.587f, |
105 | | // 0.114f }, |
106 | | // { |
107 | | // -0.168736, |
108 | | // -0.331264f, |
109 | | // 0.5f }, |
110 | | // { |
111 | | // 0.5f, |
112 | | // -0.418688f, |
113 | | // -0.081312f } |
114 | | // |
115 | | // Rec. 2020 |
116 | | // Y'= 0,2627R' + 0,6780G' + 0,0593B' |
117 | | // U = (B-Y)/1.8814 = (-0,2627R' - 0,6780G' + 0.9407B) / 1.8814 = |
118 | | //-0.13963R - 0.36037G + 0.5B |
119 | | // V = (R-Y)/1.4647 = (0.7373R - 0,6780G - 0,0593B) / 1.4647 = 0.5R - |
120 | | //0.4629G - 0.04049B |
121 | | {+0.2627f, +0.6780f, +0.0593f}, |
122 | | {-0.13963f, -0.36037f, +0.5f}, |
123 | | {+0.5034f, -0.4629f, -0.0405f} |
124 | | |
125 | | }; |
126 | | |
127 | | float AAHD::gammaLUT[0x10000] = {-1.f}; |
128 | | |
129 | 0 | AAHD::AAHD(LibRaw &_libraw) : libraw(_libraw) |
130 | 0 | { |
131 | 0 | nr_height = libraw.imgdata.sizes.iheight + nr_margin * 2; |
132 | 0 | nr_width = libraw.imgdata.sizes.iwidth + nr_margin * 2; |
133 | 0 | rgb_ahd[0] = (ushort3 *)calloc(nr_height * nr_width, |
134 | 0 | (sizeof(ushort3) * 2 + sizeof(int3) * 2 + 3)); |
135 | 0 | if (!rgb_ahd[0]) |
136 | 0 | throw LIBRAW_EXCEPTION_ALLOC; |
137 | | |
138 | 0 | rgb_ahd[1] = rgb_ahd[0] + nr_height * nr_width; |
139 | 0 | yuv[0] = (int3 *)(rgb_ahd[1] + nr_height * nr_width); |
140 | 0 | yuv[1] = yuv[0] + nr_height * nr_width; |
141 | 0 | ndir = (char *)(yuv[1] + nr_height * nr_width); |
142 | 0 | homo[0] = ndir + nr_height * nr_width; |
143 | 0 | homo[1] = homo[0] + nr_height * nr_width; |
144 | 0 | channel_maximum[0] = channel_maximum[1] = channel_maximum[2] = 0; |
145 | 0 | channel_minimum[0] = libraw.imgdata.image[0][0]; |
146 | 0 | channel_minimum[1] = libraw.imgdata.image[0][1]; |
147 | 0 | channel_minimum[2] = libraw.imgdata.image[0][2]; |
148 | 0 | int iwidth = libraw.imgdata.sizes.iwidth; |
149 | 0 | for (int i = 0; i < 3; ++i) |
150 | 0 | for (int j = 0; j < 3; ++j) |
151 | 0 | { |
152 | 0 | yuv_cam[i][j] = 0; |
153 | 0 | for (int k = 0; k < 3; ++k) |
154 | 0 | yuv_cam[i][j] += yuv_coeff[i][k] * libraw.imgdata.color.rgb_cam[k][j]; |
155 | 0 | } |
156 | 0 | if (gammaLUT[0] < -0.1f) |
157 | 0 | { |
158 | 0 | float r; |
159 | 0 | for (int i = 0; i < 0x10000; i++) |
160 | 0 | { |
161 | 0 | r = (float)i / 0x10000; |
162 | 0 | gammaLUT[i] = |
163 | 0 | 0x10000 * (r < 0.0181 ? 4.5f * r : 1.0993f * pow(r, 0.45f) - .0993f); |
164 | 0 | } |
165 | 0 | } |
166 | 0 | for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
167 | 0 | { |
168 | 0 | int col_cache[48]; |
169 | 0 | for (int j = 0; j < 48; ++j) |
170 | 0 | { |
171 | 0 | int c = libraw.COLOR(i, j); |
172 | 0 | if (c == 3) |
173 | 0 | c = 1; |
174 | 0 | col_cache[j] = c; |
175 | 0 | } |
176 | 0 | int moff = nr_offset(i + nr_margin, nr_margin); |
177 | 0 | for (int j = 0; j < iwidth; ++j, ++moff) |
178 | 0 | { |
179 | 0 | int c = col_cache[j % 48]; |
180 | 0 | unsigned short d = libraw.imgdata.image[i * iwidth + j][c]; |
181 | 0 | if (d != 0) |
182 | 0 | { |
183 | 0 | if (channel_maximum[c] < d) |
184 | 0 | channel_maximum[c] = d; |
185 | 0 | if (channel_minimum[c] > d) |
186 | 0 | channel_minimum[c] = d; |
187 | 0 | rgb_ahd[1][moff][c] = rgb_ahd[0][moff][c] = d; |
188 | 0 | } |
189 | 0 | } |
190 | 0 | } |
191 | 0 | channels_max = |
192 | 0 | MAX(MAX(channel_maximum[0], channel_maximum[1]), channel_maximum[2]); |
193 | 0 | } |
194 | | |
195 | | void AAHD::hide_hots() |
196 | 0 | { |
197 | 0 | int iwidth = libraw.imgdata.sizes.iwidth; |
198 | 0 | for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
199 | 0 | { |
200 | 0 | int js = libraw.COLOR(i, 0) & 1; |
201 | 0 | int kc = libraw.COLOR(i, js); |
202 | | /* |
203 | | * js -- начальная х-координата, которая попадает мимо известного зелёного |
204 | | * kc -- известный цвет в точке интерполирования |
205 | | */ |
206 | 0 | int moff = nr_offset(i + nr_margin, nr_margin + js); |
207 | 0 | for (int j = js; j < iwidth; j += 2, moff += 2) |
208 | 0 | { |
209 | 0 | ushort3 *rgb = &rgb_ahd[0][moff]; |
210 | 0 | int c = rgb[0][kc]; |
211 | 0 | if ((c > rgb[2 * Pe][kc] && c > rgb[2 * Pw][kc] && c > rgb[2 * Pn][kc] && |
212 | 0 | c > rgb[2 * Ps][kc] && c > rgb[Pe][1] && c > rgb[Pw][1] && |
213 | 0 | c > rgb[Pn][1] && c > rgb[Ps][1]) || |
214 | 0 | (c < rgb[2 * Pe][kc] && c < rgb[2 * Pw][kc] && c < rgb[2 * Pn][kc] && |
215 | 0 | c < rgb[2 * Ps][kc] && c < rgb[Pe][1] && c < rgb[Pw][1] && |
216 | 0 | c < rgb[Pn][1] && c < rgb[Ps][1])) |
217 | 0 | { |
218 | 0 | int chot = c >> Thot; |
219 | 0 | int cdead = c << Tdead; |
220 | 0 | int avg = 0; |
221 | 0 | for (int k = -2; k < 3; k += 2) |
222 | 0 | for (int m = -2; m < 3; m += 2) |
223 | 0 | if (m == 0 && k == 0) |
224 | 0 | continue; |
225 | 0 | else |
226 | 0 | avg += rgb[nr_offset(k, m)][kc]; |
227 | 0 | avg /= 8; |
228 | 0 | if (chot > avg || cdead < avg) |
229 | 0 | { |
230 | 0 | ndir[moff] |= HOT; |
231 | 0 | int dh = |
232 | 0 | ABS(rgb[2 * Pw][kc] - rgb[2 * Pe][kc]) + |
233 | 0 | ABS(rgb[Pw][1] - rgb[Pe][1]) + |
234 | 0 | ABS(rgb[Pw][1] - rgb[Pe][1] + rgb[2 * Pe][kc] - rgb[2 * Pw][kc]); |
235 | 0 | int dv = |
236 | 0 | ABS(rgb[2 * Pn][kc] - rgb[2 * Ps][kc]) + |
237 | 0 | ABS(rgb[Pn][1] - rgb[Ps][1]) + |
238 | 0 | ABS(rgb[Pn][1] - rgb[Ps][1] + rgb[2 * Ps][kc] - rgb[2 * Pn][kc]); |
239 | 0 | int d; |
240 | 0 | if (dv > dh) |
241 | 0 | d = Pw; |
242 | 0 | else |
243 | 0 | d = Pn; |
244 | 0 | rgb_ahd[1][moff][kc] = rgb[0][kc] = |
245 | 0 | (rgb[+2 * d][kc] + rgb[-2 * d][kc]) / 2; |
246 | 0 | } |
247 | 0 | } |
248 | 0 | } |
249 | 0 | js ^= 1; |
250 | 0 | moff = nr_offset(i + nr_margin, nr_margin + js); |
251 | 0 | for (int j = js; j < iwidth; j += 2, moff += 2) |
252 | 0 | { |
253 | 0 | ushort3 *rgb = &rgb_ahd[0][moff]; |
254 | 0 | int c = rgb[0][1]; |
255 | 0 | if ((c > rgb[2 * Pe][1] && c > rgb[2 * Pw][1] && c > rgb[2 * Pn][1] && |
256 | 0 | c > rgb[2 * Ps][1] && c > rgb[Pe][kc] && c > rgb[Pw][kc] && |
257 | 0 | c > rgb[Pn][kc ^ 2] && c > rgb[Ps][kc ^ 2]) || |
258 | 0 | (c < rgb[2 * Pe][1] && c < rgb[2 * Pw][1] && c < rgb[2 * Pn][1] && |
259 | 0 | c < rgb[2 * Ps][1] && c < rgb[Pe][kc] && c < rgb[Pw][kc] && |
260 | 0 | c < rgb[Pn][kc ^ 2] && c < rgb[Ps][kc ^ 2])) |
261 | 0 | { |
262 | 0 | int chot = c >> Thot; |
263 | 0 | int cdead = c << Tdead; |
264 | 0 | int avg = 0; |
265 | 0 | for (int k = -2; k < 3; k += 2) |
266 | 0 | for (int m = -2; m < 3; m += 2) |
267 | 0 | if (k == 0 && m == 0) |
268 | 0 | continue; |
269 | 0 | else |
270 | 0 | avg += rgb[nr_offset(k, m)][1]; |
271 | 0 | avg /= 8; |
272 | 0 | if (chot > avg || cdead < avg) |
273 | 0 | { |
274 | 0 | ndir[moff] |= HOT; |
275 | 0 | int dh = |
276 | 0 | ABS(rgb[2 * Pw][1] - rgb[2 * Pe][1]) + |
277 | 0 | ABS(rgb[Pw][kc] - rgb[Pe][kc]) + |
278 | 0 | ABS(rgb[Pw][kc] - rgb[Pe][kc] + rgb[2 * Pe][1] - rgb[2 * Pw][1]); |
279 | 0 | int dv = ABS(rgb[2 * Pn][1] - rgb[2 * Ps][1]) + |
280 | 0 | ABS(rgb[Pn][kc ^ 2] - rgb[Ps][kc ^ 2]) + |
281 | 0 | ABS(rgb[Pn][kc ^ 2] - rgb[Ps][kc ^ 2] + rgb[2 * Ps][1] - |
282 | 0 | rgb[2 * Pn][1]); |
283 | 0 | int d; |
284 | 0 | if (dv > dh) |
285 | 0 | d = Pw; |
286 | 0 | else |
287 | 0 | d = Pn; |
288 | 0 | rgb_ahd[1][moff][1] = rgb[0][1] = |
289 | 0 | (rgb[+2 * d][1] + rgb[-2 * d][1]) / 2; |
290 | 0 | } |
291 | 0 | } |
292 | 0 | } |
293 | 0 | } |
294 | 0 | } |
295 | | |
296 | | void AAHD::evaluate_ahd() |
297 | 0 | { |
298 | 0 | int hvdir[4] = {Pw, Pe, Pn, Ps}; |
299 | | /* |
300 | | * YUV |
301 | | * |
302 | | */ |
303 | 0 | for (int d = 0; d < 2; ++d) |
304 | 0 | { |
305 | 0 | for (int i = 0; i < nr_width * nr_height; ++i) |
306 | 0 | { |
307 | 0 | ushort3 rgb; |
308 | 0 | for (int c = 0; c < 3; ++c) |
309 | 0 | { |
310 | 0 | rgb[c] = ushort(gammaLUT[rgb_ahd[d][i][c]]); |
311 | 0 | } |
312 | 0 | yuv[d][i][0] = Y(rgb); |
313 | 0 | yuv[d][i][1] = U(rgb); |
314 | 0 | yuv[d][i][2] = V(rgb); |
315 | 0 | } |
316 | 0 | } |
317 | | /* */ |
318 | | /* |
319 | | * Lab |
320 | | * |
321 | | float r, cbrt[0x10000], xyz[3], xyz_cam[3][4]; |
322 | | for (int i = 0; i < 0x10000; i++) { |
323 | | r = i / 65535.0; |
324 | | cbrt[i] = r > 0.008856 ? pow((double) r, (double) (1 / 3.0)) : 7.787 * r + 16 |
325 | | / 116.0; |
326 | | } |
327 | | for (int i = 0; i < 3; i++) |
328 | | for (int j = 0; j < 3; j++) { |
329 | | xyz_cam[i][j] = 0; |
330 | | for (int k = 0; k < 3; k++) |
331 | | xyz_cam[i][j] += xyz_rgb[i][k] * libraw.imgdata.color.rgb_cam[k][j] / |
332 | | d65_white[i]; |
333 | | } |
334 | | for (int d = 0; d < 2; ++d) |
335 | | for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) { |
336 | | int moff = nr_offset(i + nr_margin, nr_margin); |
337 | | for (int j = 0; j < libraw.imgdata.sizes.iwidth; j++, ++moff) { |
338 | | xyz[0] = xyz[1] = xyz[2] = 0.5; |
339 | | for (int c = 0; c < 3; c++) { |
340 | | xyz[0] += xyz_cam[0][c] * rgb_ahd[d][moff][c]; |
341 | | xyz[1] += xyz_cam[1][c] * rgb_ahd[d][moff][c]; |
342 | | xyz[2] += xyz_cam[2][c] * rgb_ahd[d][moff][c]; |
343 | | } |
344 | | xyz[0] = cbrt[CLIP((int) xyz[0])]; |
345 | | xyz[1] = cbrt[CLIP((int) xyz[1])]; |
346 | | xyz[2] = cbrt[CLIP((int) xyz[2])]; |
347 | | yuv[d][moff][0] = 64 * (116 * xyz[1] - 16); |
348 | | yuv[d][moff][1] = 64 * 500 * (xyz[0] - xyz[1]); |
349 | | yuv[d][moff][2] = 64 * 200 * (xyz[1] - xyz[2]); |
350 | | } |
351 | | } |
352 | | * Lab */ |
353 | 0 | for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
354 | 0 | { |
355 | 0 | int moff = nr_offset(i + nr_margin, nr_margin); |
356 | 0 | for (int j = 0; j < libraw.imgdata.sizes.iwidth; j++, ++moff) |
357 | 0 | { |
358 | 0 | int3 *ynr; |
359 | 0 | float ydiff[2][4]; |
360 | 0 | int uvdiff[2][4]; |
361 | 0 | for (int d = 0; d < 2; ++d) |
362 | 0 | { |
363 | 0 | ynr = &yuv[d][moff]; |
364 | 0 | for (int k = 0; k < 4; k++) |
365 | 0 | { |
366 | 0 | ydiff[d][k] = float(ABS(ynr[0][0] - ynr[hvdir[k]][0])); |
367 | 0 | uvdiff[d][k] = SQR(ynr[0][1] - ynr[hvdir[k]][1]) + |
368 | 0 | SQR(ynr[0][2] - ynr[hvdir[k]][2]); |
369 | 0 | } |
370 | 0 | } |
371 | 0 | float yeps = |
372 | 0 | MIN(MAX(ydiff[0][0], ydiff[0][1]), MAX(ydiff[1][2], ydiff[1][3])); |
373 | 0 | int uveps = |
374 | 0 | MIN(MAX(uvdiff[0][0], uvdiff[0][1]), MAX(uvdiff[1][2], uvdiff[1][3])); |
375 | 0 | for (int d = 0; d < 2; d++) |
376 | 0 | { |
377 | 0 | ynr = &yuv[d][moff]; |
378 | 0 | for (int k = 0; k < 4; k++) |
379 | 0 | if (ydiff[d][k] <= yeps && uvdiff[d][k] <= uveps) |
380 | 0 | { |
381 | 0 | homo[d][moff + hvdir[k]]++; |
382 | 0 | if (k / 2 == d) |
383 | 0 | { |
384 | | // если в сонаправленном направлении интеполяции следующие точки |
385 | | // так же гомогенны, учтём их тоже |
386 | 0 | for (int m = 2; m < 4; ++m) |
387 | 0 | { |
388 | 0 | int hvd = m * hvdir[k]; |
389 | 0 | if (ABS(ynr[0][0] - ynr[hvd][0]) < yeps && |
390 | 0 | SQR(ynr[0][1] - ynr[hvd][1]) + |
391 | 0 | SQR(ynr[0][2] - ynr[hvd][2]) < |
392 | 0 | uveps) |
393 | 0 | { |
394 | 0 | homo[d][moff + hvd]++; |
395 | 0 | } |
396 | 0 | else |
397 | 0 | break; |
398 | 0 | } |
399 | 0 | } |
400 | 0 | } |
401 | 0 | } |
402 | 0 | } |
403 | 0 | } |
404 | 0 | for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
405 | 0 | { |
406 | 0 | int moff = nr_offset(i + nr_margin, nr_margin); |
407 | 0 | for (int j = 0; j < libraw.imgdata.sizes.iwidth; j++, ++moff) |
408 | 0 | { |
409 | 0 | char hm[2]; |
410 | 0 | for (int d = 0; d < 2; d++) |
411 | 0 | { |
412 | 0 | hm[d] = 0; |
413 | 0 | char *hh = &homo[d][moff]; |
414 | 0 | for (int hx = -1; hx < 2; hx++) |
415 | 0 | for (int hy = -1; hy < 2; hy++) |
416 | 0 | hm[d] += hh[nr_offset(hy, hx)]; |
417 | 0 | } |
418 | 0 | char d = 0; |
419 | 0 | if (hm[0] != hm[1]) |
420 | 0 | { |
421 | 0 | if (hm[1] > hm[0]) |
422 | 0 | { |
423 | 0 | d = VERSH; |
424 | 0 | } |
425 | 0 | else |
426 | 0 | { |
427 | 0 | d = HORSH; |
428 | 0 | } |
429 | 0 | } |
430 | 0 | else |
431 | 0 | { |
432 | 0 | int3 *ynr = &yuv[1][moff]; |
433 | 0 | int gv = SQR(2 * ynr[0][0] - ynr[Pn][0] - ynr[Ps][0]); |
434 | 0 | gv += SQR(2 * ynr[0][1] - ynr[Pn][1] - ynr[Ps][1]) + |
435 | 0 | SQR(2 * ynr[0][2] - ynr[Pn][2] - ynr[Ps][2]); |
436 | 0 | ynr = &yuv[1][moff + Pn]; |
437 | 0 | gv += (SQR(2 * ynr[0][0] - ynr[Pn][0] - ynr[Ps][0]) + |
438 | 0 | SQR(2 * ynr[0][1] - ynr[Pn][1] - ynr[Ps][1]) + |
439 | 0 | SQR(2 * ynr[0][2] - ynr[Pn][2] - ynr[Ps][2])) / |
440 | 0 | 2; |
441 | 0 | ynr = &yuv[1][moff + Ps]; |
442 | 0 | gv += (SQR(2 * ynr[0][0] - ynr[Pn][0] - ynr[Ps][0]) + |
443 | 0 | SQR(2 * ynr[0][1] - ynr[Pn][1] - ynr[Ps][1]) + |
444 | 0 | SQR(2 * ynr[0][2] - ynr[Pn][2] - ynr[Ps][2])) / |
445 | 0 | 2; |
446 | 0 | ynr = &yuv[0][moff]; |
447 | 0 | int gh = SQR(2 * ynr[0][0] - ynr[Pw][0] - ynr[Pe][0]); |
448 | 0 | gh += SQR(2 * ynr[0][1] - ynr[Pw][1] - ynr[Pe][1]) + |
449 | 0 | SQR(2 * ynr[0][2] - ynr[Pw][2] - ynr[Pe][2]); |
450 | 0 | ynr = &yuv[0][moff + Pw]; |
451 | 0 | gh += (SQR(2 * ynr[0][0] - ynr[Pw][0] - ynr[Pe][0]) + |
452 | 0 | SQR(2 * ynr[0][1] - ynr[Pw][1] - ynr[Pe][1]) + |
453 | 0 | SQR(2 * ynr[0][2] - ynr[Pw][2] - ynr[Pe][2])) / |
454 | 0 | 2; |
455 | 0 | ynr = &yuv[0][moff + Pe]; |
456 | 0 | gh += (SQR(2 * ynr[0][0] - ynr[Pw][0] - ynr[Pe][0]) + |
457 | 0 | SQR(2 * ynr[0][1] - ynr[Pw][1] - ynr[Pe][1]) + |
458 | 0 | SQR(2 * ynr[0][2] - ynr[Pw][2] - ynr[Pe][2])) / |
459 | 0 | 2; |
460 | 0 | if (gv > gh) |
461 | 0 | d = HOR; |
462 | 0 | else |
463 | 0 | d = VER; |
464 | 0 | } |
465 | 0 | ndir[moff] |= d; |
466 | 0 | } |
467 | 0 | } |
468 | 0 | } |
469 | | |
470 | | void AAHD::combine_image() |
471 | 0 | { |
472 | 0 | for (int i = 0, i_out = 0; i < libraw.imgdata.sizes.iheight; ++i) |
473 | 0 | { |
474 | 0 | int moff = nr_offset(i + nr_margin, nr_margin); |
475 | 0 | for (int j = 0; j < libraw.imgdata.sizes.iwidth; j++, ++moff, ++i_out) |
476 | 0 | { |
477 | 0 | if (ndir[moff] & HOT) |
478 | 0 | { |
479 | 0 | int c = libraw.COLOR(i, j); |
480 | 0 | rgb_ahd[1][moff][c] = rgb_ahd[0][moff][c] = |
481 | 0 | libraw.imgdata.image[i_out][c]; |
482 | 0 | } |
483 | 0 | if (ndir[moff] & VER) |
484 | 0 | { |
485 | 0 | libraw.imgdata.image[i_out][0] = rgb_ahd[1][moff][0]; |
486 | 0 | libraw.imgdata.image[i_out][3] = libraw.imgdata.image[i_out][1] = |
487 | 0 | rgb_ahd[1][moff][1]; |
488 | 0 | libraw.imgdata.image[i_out][2] = rgb_ahd[1][moff][2]; |
489 | 0 | } |
490 | 0 | else |
491 | 0 | { |
492 | 0 | libraw.imgdata.image[i_out][0] = rgb_ahd[0][moff][0]; |
493 | 0 | libraw.imgdata.image[i_out][3] = libraw.imgdata.image[i_out][1] = |
494 | 0 | rgb_ahd[0][moff][1]; |
495 | 0 | libraw.imgdata.image[i_out][2] = rgb_ahd[0][moff][2]; |
496 | 0 | } |
497 | 0 | } |
498 | 0 | } |
499 | 0 | } |
500 | | |
501 | | void AAHD::refine_hv_dirs() |
502 | 0 | { |
503 | 0 | for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
504 | 0 | { |
505 | 0 | refine_hv_dirs(i, i & 1); |
506 | 0 | } |
507 | 0 | for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
508 | 0 | { |
509 | 0 | refine_hv_dirs(i, (i & 1) ^ 1); |
510 | 0 | } |
511 | 0 | for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
512 | 0 | { |
513 | 0 | refine_ihv_dirs(i); |
514 | 0 | } |
515 | 0 | } |
516 | | |
517 | | void AAHD::refine_ihv_dirs(int i) |
518 | 0 | { |
519 | 0 | int iwidth = libraw.imgdata.sizes.iwidth; |
520 | 0 | int moff = nr_offset(i + nr_margin, nr_margin); |
521 | 0 | for (int j = 0; j < iwidth; j++, ++moff) |
522 | 0 | { |
523 | 0 | if (ndir[moff] & HVSH) |
524 | 0 | continue; |
525 | 0 | int nv = (ndir[moff + Pn] & VER) + (ndir[moff + Ps] & VER) + |
526 | 0 | (ndir[moff + Pw] & VER) + (ndir[moff + Pe] & VER); |
527 | 0 | int nh = (ndir[moff + Pn] & HOR) + (ndir[moff + Ps] & HOR) + |
528 | 0 | (ndir[moff + Pw] & HOR) + (ndir[moff + Pe] & HOR); |
529 | 0 | nv /= VER; |
530 | 0 | nh /= HOR; |
531 | 0 | if ((ndir[moff] & VER) && nh > 3) |
532 | 0 | { |
533 | 0 | ndir[moff] &= ~VER; |
534 | 0 | ndir[moff] |= HOR; |
535 | 0 | } |
536 | 0 | if ((ndir[moff] & HOR) && nv > 3) |
537 | 0 | { |
538 | 0 | ndir[moff] &= ~HOR; |
539 | 0 | ndir[moff] |= VER; |
540 | 0 | } |
541 | 0 | } |
542 | 0 | } |
543 | | |
544 | | void AAHD::refine_hv_dirs(int i, int js) |
545 | 0 | { |
546 | 0 | int iwidth = libraw.imgdata.sizes.iwidth; |
547 | 0 | int moff = nr_offset(i + nr_margin, nr_margin + js); |
548 | 0 | for (int j = js; j < iwidth; j += 2, moff += 2) |
549 | 0 | { |
550 | 0 | int nv = (ndir[moff + Pn] & VER) + (ndir[moff + Ps] & VER) + |
551 | 0 | (ndir[moff + Pw] & VER) + (ndir[moff + Pe] & VER); |
552 | 0 | int nh = (ndir[moff + Pn] & HOR) + (ndir[moff + Ps] & HOR) + |
553 | 0 | (ndir[moff + Pw] & HOR) + (ndir[moff + Pe] & HOR); |
554 | 0 | bool codir = (ndir[moff] & VER) |
555 | 0 | ? ((ndir[moff + Pn] & VER) || (ndir[moff + Ps] & VER)) |
556 | 0 | : ((ndir[moff + Pw] & HOR) || (ndir[moff + Pe] & HOR)); |
557 | 0 | nv /= VER; |
558 | 0 | nh /= HOR; |
559 | 0 | if ((ndir[moff] & VER) && (nh > 2 && !codir)) |
560 | 0 | { |
561 | 0 | ndir[moff] &= ~VER; |
562 | 0 | ndir[moff] |= HOR; |
563 | 0 | } |
564 | 0 | if ((ndir[moff] & HOR) && (nv > 2 && !codir)) |
565 | 0 | { |
566 | 0 | ndir[moff] &= ~HOR; |
567 | 0 | ndir[moff] |= VER; |
568 | 0 | } |
569 | 0 | } |
570 | 0 | } |
571 | | |
572 | | /* |
573 | | * вычисление недостающих зелёных точек. |
574 | | */ |
575 | | void AAHD::make_ahd_greens() |
576 | 0 | { |
577 | 0 | for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
578 | 0 | { |
579 | 0 | make_ahd_gline(i); |
580 | 0 | } |
581 | 0 | } |
582 | | |
583 | | void AAHD::make_ahd_gline(int i) |
584 | 0 | { |
585 | 0 | int iwidth = libraw.imgdata.sizes.iwidth; |
586 | 0 | int js = libraw.COLOR(i, 0) & 1; |
587 | 0 | int kc = libraw.COLOR(i, js); |
588 | | /* |
589 | | * js -- начальная х-координата, которая попадает мимо известного зелёного |
590 | | * kc -- известный цвет в точке интерполирования |
591 | | */ |
592 | 0 | int hvdir[2] = {Pe, Ps}; |
593 | 0 | for (int d = 0; d < 2; ++d) |
594 | 0 | { |
595 | 0 | int moff = nr_offset(i + nr_margin, nr_margin + js); |
596 | 0 | for (int j = js; j < iwidth; j += 2, moff += 2) |
597 | 0 | { |
598 | 0 | ushort3 *cnr; |
599 | 0 | cnr = &rgb_ahd[d][moff]; |
600 | 0 | int h1 = 2 * cnr[-hvdir[d]][1] - int(cnr[-2 * hvdir[d]][kc] + cnr[0][kc]); |
601 | 0 | int h2 = 2 * cnr[+hvdir[d]][1] - int(cnr[+2 * hvdir[d]][kc] + cnr[0][kc]); |
602 | 0 | int h0 = (h1 + h2) / 4; |
603 | 0 | int eg = cnr[0][kc] + h0; |
604 | 0 | int min = MIN(cnr[-hvdir[d]][1], cnr[+hvdir[d]][1]); |
605 | 0 | int max = MAX(cnr[-hvdir[d]][1], cnr[+hvdir[d]][1]); |
606 | 0 | min -= min / OverFraction; |
607 | 0 | max += max / OverFraction; |
608 | 0 | if (eg < min) |
609 | 0 | eg = min - int(sqrtf(float(min - eg))); |
610 | 0 | else if (eg > max) |
611 | 0 | eg = max + int(sqrtf(float(eg - max))); |
612 | 0 | if (eg > channel_maximum[1]) |
613 | 0 | eg = channel_maximum[1]; |
614 | 0 | else if (eg < channel_minimum[1]) |
615 | 0 | eg = channel_minimum[1]; |
616 | 0 | cnr[0][1] = eg; |
617 | 0 | } |
618 | 0 | } |
619 | 0 | } |
620 | | |
621 | | /* |
622 | | * отладочная функция |
623 | | */ |
624 | | |
625 | | void AAHD::illustrate_dirs() |
626 | 0 | { |
627 | 0 | for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
628 | 0 | { |
629 | 0 | illustrate_dline(i); |
630 | 0 | } |
631 | 0 | } |
632 | | |
633 | | void AAHD::illustrate_dline(int i) |
634 | 0 | { |
635 | 0 | int iwidth = libraw.imgdata.sizes.iwidth; |
636 | 0 | for (int j = 0; j < iwidth; j++) |
637 | 0 | { |
638 | 0 | int x = j + nr_margin; |
639 | 0 | int y = i + nr_margin; |
640 | 0 | rgb_ahd[1][nr_offset(y, x)][0] = rgb_ahd[1][nr_offset(y, x)][1] = |
641 | 0 | rgb_ahd[1][nr_offset(y, x)][2] = rgb_ahd[0][nr_offset(y, x)][0] = |
642 | 0 | rgb_ahd[0][nr_offset(y, x)][1] = rgb_ahd[0][nr_offset(y, x)][2] = 0; |
643 | 0 | int l = ndir[nr_offset(y, x)] & HVSH; |
644 | 0 | l /= HVSH; |
645 | 0 | if (ndir[nr_offset(y, x)] & VER) |
646 | 0 | rgb_ahd[1][nr_offset(y, x)][0] = |
647 | 0 | l * channel_maximum[0] / 4 + channel_maximum[0] / 4; |
648 | 0 | else |
649 | 0 | rgb_ahd[0][nr_offset(y, x)][2] = |
650 | 0 | l * channel_maximum[2] / 4 + channel_maximum[2] / 4; |
651 | 0 | } |
652 | 0 | } |
653 | | |
654 | | void AAHD::make_ahd_rb_hv(int i) |
655 | 0 | { |
656 | 0 | int iwidth = libraw.imgdata.sizes.iwidth; |
657 | 0 | int js = libraw.COLOR(i, 0) & 1; |
658 | 0 | int kc = libraw.COLOR(i, js); |
659 | 0 | js ^= 1; // начальная координата зелёного |
660 | 0 | int hvdir[2] = {Pe, Ps}; |
661 | | // интерполяция вертикальных вертикально и горизонтальных горизонтально |
662 | 0 | for (int j = js; j < iwidth; j += 2) |
663 | 0 | { |
664 | 0 | int x = j + nr_margin; |
665 | 0 | int y = i + nr_margin; |
666 | 0 | int moff = nr_offset(y, x); |
667 | 0 | for (int d = 0; d < 2; ++d) |
668 | 0 | { |
669 | 0 | ushort3 *cnr; |
670 | 0 | cnr = &rgb_ahd[d][moff]; |
671 | 0 | int c = kc ^ (d << 1); // цвет соответсвенного направления, для |
672 | | // горизонтального c = kc, для вертикального c=kc^2 |
673 | 0 | int h1 = cnr[-hvdir[d]][c] - cnr[-hvdir[d]][1]; |
674 | 0 | int h2 = cnr[+hvdir[d]][c] - cnr[+hvdir[d]][1]; |
675 | 0 | int h0 = (h1 + h2) / 2; |
676 | 0 | int eg = cnr[0][1] + h0; |
677 | | // int min = MIN(cnr[-hvdir[d]][c], cnr[+hvdir[d]][c]); |
678 | | // int max = MAX(cnr[-hvdir[d]][c], cnr[+hvdir[d]][c]); |
679 | | // min -= min / OverFraction; |
680 | | // max += max / OverFraction; |
681 | | // if (eg < min) |
682 | | // eg = min - sqrt(min - eg); |
683 | | // else if (eg > max) |
684 | | // eg = max + sqrt(eg - max); |
685 | 0 | if (eg > channel_maximum[c]) |
686 | 0 | eg = channel_maximum[c]; |
687 | 0 | else if (eg < channel_minimum[c]) |
688 | 0 | eg = channel_minimum[c]; |
689 | 0 | cnr[0][c] = eg; |
690 | 0 | } |
691 | 0 | } |
692 | 0 | } |
693 | | |
694 | | void AAHD::make_ahd_rb() |
695 | 0 | { |
696 | 0 | for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
697 | 0 | { |
698 | 0 | make_ahd_rb_hv(i); |
699 | 0 | } |
700 | 0 | for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
701 | 0 | { |
702 | 0 | make_ahd_rb_last(i); |
703 | 0 | } |
704 | 0 | } |
705 | | |
706 | | void AAHD::make_ahd_rb_last(int i) |
707 | 0 | { |
708 | 0 | int iwidth = libraw.imgdata.sizes.iwidth; |
709 | 0 | int js = libraw.COLOR(i, 0) & 1; |
710 | 0 | int kc = libraw.COLOR(i, js); |
711 | | /* |
712 | | * js -- начальная х-координата, которая попадает мимо известного зелёного |
713 | | * kc -- известный цвет в точке интерполирования |
714 | | */ |
715 | 0 | int dirs[2][3] = {{Pnw, Pn, Pne}, {Pnw, Pw, Psw}}; |
716 | 0 | int moff = nr_offset(i + nr_margin, nr_margin); |
717 | 0 | for (int j = 0; j < iwidth; j++) |
718 | 0 | { |
719 | 0 | for (int d = 0; d < 2; ++d) |
720 | 0 | { |
721 | 0 | ushort3 *cnr; |
722 | 0 | cnr = &rgb_ahd[d][moff + j]; |
723 | 0 | int c = kc ^ 2; |
724 | 0 | if ((j & 1) != js) |
725 | 0 | { |
726 | | // точка зелёного, для вертикального направления нужен альтернативный |
727 | | // строчному цвет |
728 | 0 | c ^= d << 1; |
729 | 0 | } |
730 | 0 | int bh = 0, bk = 0; |
731 | 0 | int bgd = 0; |
732 | 0 | for (int k = 0; k < 3; ++k) |
733 | 0 | for (int h = 0; h < 3; ++h) |
734 | 0 | { |
735 | | // градиент зелёного плюс градиент {r,b} |
736 | 0 | int gd = |
737 | 0 | ABS(2 * cnr[0][1] - (cnr[+dirs[d][k]][1] + cnr[-dirs[d][h]][1])) + |
738 | 0 | ABS(cnr[+dirs[d][k]][c] - cnr[-dirs[d][h]][c]) / 4 + |
739 | 0 | ABS(cnr[+dirs[d][k]][c] - cnr[+dirs[d][k]][1] + |
740 | 0 | cnr[-dirs[d][h]][1] - cnr[-dirs[d][h]][c]) / |
741 | 0 | 4; |
742 | 0 | if (bgd == 0 || gd < bgd) |
743 | 0 | { |
744 | 0 | bgd = gd; |
745 | 0 | bh = h; |
746 | 0 | bk = k; |
747 | 0 | } |
748 | 0 | } |
749 | 0 | int h1 = cnr[+dirs[d][bk]][c] - cnr[+dirs[d][bk]][1]; |
750 | 0 | int h2 = cnr[-dirs[d][bh]][c] - cnr[-dirs[d][bh]][1]; |
751 | 0 | int eg = cnr[0][1] + (h1 + h2) / 2; |
752 | | // int min = MIN(cnr[+dirs[d][bk]][c], cnr[-dirs[d][bh]][c]); |
753 | | // int max = MAX(cnr[+dirs[d][bk]][c], cnr[-dirs[d][bh]][c]); |
754 | | // min -= min / OverFraction; |
755 | | // max += max / OverFraction; |
756 | | // if (eg < min) |
757 | | // eg = min - sqrt(min - eg); |
758 | | // else if (eg > max) |
759 | | // eg = max + sqrt(eg - max); |
760 | 0 | if (eg > channel_maximum[c]) |
761 | 0 | eg = channel_maximum[c]; |
762 | 0 | else if (eg < channel_minimum[c]) |
763 | 0 | eg = channel_minimum[c]; |
764 | 0 | cnr[0][c] = eg; |
765 | 0 | } |
766 | 0 | } |
767 | 0 | } |
768 | | |
769 | 0 | AAHD::~AAHD() { free(rgb_ahd[0]); } |
770 | | |
771 | | void LibRaw::aahd_interpolate() |
772 | 0 | { |
773 | 0 | AAHD aahd(*this); |
774 | 0 | aahd.hide_hots(); |
775 | 0 | aahd.make_ahd_greens(); |
776 | 0 | aahd.make_ahd_rb(); |
777 | 0 | aahd.evaluate_ahd(); |
778 | 0 | aahd.refine_hv_dirs(); |
779 | | // aahd.illustrate_dirs(); |
780 | 0 | aahd.combine_image(); |
781 | 0 | } |