| /* -*- C++ -*- |
| * File: aahd_demosaic.cpp |
| * Copyright 2013 Anton Petrusevich |
| * Created: Wed May 15, 2013 |
| * |
| * This code is licensed under one of two licenses as you choose: |
| * |
| * 1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1 |
| * (See file LICENSE.LGPL provided in LibRaw distribution archive for |
| * details). |
| * |
| * 2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 |
| * (See file LICENSE.CDDL provided in LibRaw distribution archive for |
| * details). |
| * |
| */ |
| |
| #include "third_party/libraw/internal/dmp_include.h" |
| |
| typedef ushort ushort3[3]; |
| typedef int int3[3]; |
| |
| #ifndef Pnw |
| #define Pnw (-1 - nr_width) |
| #define Pn (-nr_width) |
| #define Pne (+1 - nr_width) |
| #define Pe (+1) |
| #define Pse (+1 + nr_width) |
| #define Ps (+nr_width) |
| #define Psw (-1 + nr_width) |
| #define Pw (-1) |
| #endif |
| |
| struct AAHD |
| { |
| int nr_height, nr_width; |
| static const int nr_margin = 4; |
| static const int Thot = 4; |
| static const int Tdead = 4; |
| static const int OverFraction = 8; |
| ushort3 *rgb_ahd[2]; |
| int3 *yuv[2]; |
| char *ndir, *homo[2]; |
| ushort channel_maximum[3], channels_max; |
| ushort channel_minimum[3]; |
| static const float yuv_coeff[3][3]; |
| static float gammaLUT[0x10000]; |
| float yuv_cam[3][3]; |
| LibRaw &libraw; |
| enum |
| { |
| HVSH = 1, |
| HOR = 2, |
| VER = 4, |
| HORSH = HOR | HVSH, |
| VERSH = VER | HVSH, |
| HOT = 8 |
| }; |
| |
| static inline float calc_dist(int c1, int c2) throw() |
| { |
| return c1 > c2 ? (float)c1 / c2 : (float)c2 / c1; |
| } |
| int inline Y(ushort3 &rgb) throw() |
| { |
| return int(yuv_cam[0][0] * rgb[0] + yuv_cam[0][1] * rgb[1] + |
| yuv_cam[0][2] * rgb[2]); |
| } |
| int inline U(ushort3 &rgb) throw() |
| { |
| return int(yuv_cam[1][0] * rgb[0] + yuv_cam[1][1] * rgb[1] + |
| yuv_cam[1][2] * rgb[2]); |
| } |
| int inline V(ushort3 &rgb) throw() |
| { |
| return int(yuv_cam[2][0] * rgb[0] + yuv_cam[2][1] * rgb[1] + |
| yuv_cam[2][2] * rgb[2]); |
| } |
| inline int nr_offset(int row, int col) throw() |
| { |
| return (row * nr_width + col); |
| } |
| ~AAHD(); |
| AAHD(LibRaw &_libraw); |
| void make_ahd_greens(); |
| void make_ahd_gline(int i); |
| void make_ahd_rb(); |
| void make_ahd_rb_hv(int i); |
| void make_ahd_rb_last(int i); |
| void evaluate_ahd(); |
| void combine_image(); |
| void hide_hots(); |
| void refine_hv_dirs(); |
| void refine_hv_dirs(int i, int js); |
| void refine_ihv_dirs(int i); |
| void illustrate_dirs(); |
| void illustrate_dline(int i); |
| }; |
| |
| const float AAHD::yuv_coeff[3][3] = { |
| // YPbPr |
| // { |
| // 0.299f, |
| // 0.587f, |
| // 0.114f }, |
| // { |
| // -0.168736, |
| // -0.331264f, |
| // 0.5f }, |
| // { |
| // 0.5f, |
| // -0.418688f, |
| // -0.081312f } |
| // |
| // Rec. 2020 |
| // Y'= 0,2627R' + 0,6780G' + 0,0593B' |
| // U = (B-Y)/1.8814 = (-0,2627R' - 0,6780G' + 0.9407B) / 1.8814 = |
| //-0.13963R - 0.36037G + 0.5B |
| // V = (R-Y)/1.4647 = (0.7373R - 0,6780G - 0,0593B) / 1.4647 = 0.5R - |
| //0.4629G - 0.04049B |
| {+0.2627f, +0.6780f, +0.0593f}, |
| {-0.13963f, -0.36037f, +0.5f}, |
| {+0.5034f, -0.4629f, -0.0405f} |
| |
| }; |
| |
| float AAHD::gammaLUT[0x10000] = {-1.f}; |
| |
| AAHD::AAHD(LibRaw &_libraw) : libraw(_libraw) |
| { |
| nr_height = libraw.imgdata.sizes.iheight + nr_margin * 2; |
| nr_width = libraw.imgdata.sizes.iwidth + nr_margin * 2; |
| rgb_ahd[0] = (ushort3 *)calloc(nr_height * nr_width, |
| (sizeof(ushort3) * 2 + sizeof(int3) * 2 + 3)); |
| if (!rgb_ahd[0]) |
| throw LIBRAW_EXCEPTION_ALLOC; |
| |
| rgb_ahd[1] = rgb_ahd[0] + nr_height * nr_width; |
| yuv[0] = (int3 *)(rgb_ahd[1] + nr_height * nr_width); |
| yuv[1] = yuv[0] + nr_height * nr_width; |
| ndir = (char *)(yuv[1] + nr_height * nr_width); |
| homo[0] = ndir + nr_height * nr_width; |
| homo[1] = homo[0] + nr_height * nr_width; |
| channel_maximum[0] = channel_maximum[1] = channel_maximum[2] = 0; |
| channel_minimum[0] = libraw.imgdata.image[0][0]; |
| channel_minimum[1] = libraw.imgdata.image[0][1]; |
| channel_minimum[2] = libraw.imgdata.image[0][2]; |
| int iwidth = libraw.imgdata.sizes.iwidth; |
| for (int i = 0; i < 3; ++i) |
| for (int j = 0; j < 3; ++j) |
| { |
| yuv_cam[i][j] = 0; |
| for (int k = 0; k < 3; ++k) |
| yuv_cam[i][j] += yuv_coeff[i][k] * libraw.imgdata.color.rgb_cam[k][j]; |
| } |
| if (gammaLUT[0] < -0.1f) |
| { |
| float r; |
| for (int i = 0; i < 0x10000; i++) |
| { |
| r = (float)i / 0x10000; |
| gammaLUT[i] = |
| 0x10000 * (r < 0.0181 ? 4.5f * r : 1.0993f * pow(r, 0.45f) - .0993f); |
| } |
| } |
| for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
| { |
| int col_cache[48]; |
| for (int j = 0; j < 48; ++j) |
| { |
| int c = libraw.COLOR(i, j); |
| if (c == 3) |
| c = 1; |
| col_cache[j] = c; |
| } |
| int moff = nr_offset(i + nr_margin, nr_margin); |
| for (int j = 0; j < iwidth; ++j, ++moff) |
| { |
| int c = col_cache[j % 48]; |
| unsigned short d = libraw.imgdata.image[i * iwidth + j][c]; |
| if (d != 0) |
| { |
| if (channel_maximum[c] < d) |
| channel_maximum[c] = d; |
| if (channel_minimum[c] > d) |
| channel_minimum[c] = d; |
| rgb_ahd[1][moff][c] = rgb_ahd[0][moff][c] = d; |
| } |
| } |
| } |
| channels_max = |
| MAX(MAX(channel_maximum[0], channel_maximum[1]), channel_maximum[2]); |
| } |
| |
| void AAHD::hide_hots() |
| { |
| int iwidth = libraw.imgdata.sizes.iwidth; |
| for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
| { |
| int js = libraw.COLOR(i, 0) & 1; |
| int kc = libraw.COLOR(i, js); |
| /* |
| * js -- начальная х-координата, которая попадает мимо известного зелёного |
| * kc -- известный цвет в точке интерполирования |
| */ |
| int moff = nr_offset(i + nr_margin, nr_margin + js); |
| for (int j = js; j < iwidth; j += 2, moff += 2) |
| { |
| ushort3 *rgb = &rgb_ahd[0][moff]; |
| int c = rgb[0][kc]; |
| if ((c > rgb[2 * Pe][kc] && c > rgb[2 * Pw][kc] && c > rgb[2 * Pn][kc] && |
| c > rgb[2 * Ps][kc] && c > rgb[Pe][1] && c > rgb[Pw][1] && |
| c > rgb[Pn][1] && c > rgb[Ps][1]) || |
| (c < rgb[2 * Pe][kc] && c < rgb[2 * Pw][kc] && c < rgb[2 * Pn][kc] && |
| c < rgb[2 * Ps][kc] && c < rgb[Pe][1] && c < rgb[Pw][1] && |
| c < rgb[Pn][1] && c < rgb[Ps][1])) |
| { |
| int chot = c >> Thot; |
| int cdead = c << Tdead; |
| int avg = 0; |
| for (int k = -2; k < 3; k += 2) |
| for (int m = -2; m < 3; m += 2) |
| if (m == 0 && k == 0) |
| continue; |
| else |
| avg += rgb[nr_offset(k, m)][kc]; |
| avg /= 8; |
| if (chot > avg || cdead < avg) |
| { |
| ndir[moff] |= HOT; |
| int dh = |
| ABS(rgb[2 * Pw][kc] - rgb[2 * Pe][kc]) + |
| ABS(rgb[Pw][1] - rgb[Pe][1]) + |
| ABS(rgb[Pw][1] - rgb[Pe][1] + rgb[2 * Pe][kc] - rgb[2 * Pw][kc]); |
| int dv = |
| ABS(rgb[2 * Pn][kc] - rgb[2 * Ps][kc]) + |
| ABS(rgb[Pn][1] - rgb[Ps][1]) + |
| ABS(rgb[Pn][1] - rgb[Ps][1] + rgb[2 * Ps][kc] - rgb[2 * Pn][kc]); |
| int d; |
| if (dv > dh) |
| d = Pw; |
| else |
| d = Pn; |
| rgb_ahd[1][moff][kc] = rgb[0][kc] = |
| (rgb[+2 * d][kc] + rgb[-2 * d][kc]) / 2; |
| } |
| } |
| } |
| js ^= 1; |
| moff = nr_offset(i + nr_margin, nr_margin + js); |
| for (int j = js; j < iwidth; j += 2, moff += 2) |
| { |
| ushort3 *rgb = &rgb_ahd[0][moff]; |
| int c = rgb[0][1]; |
| if ((c > rgb[2 * Pe][1] && c > rgb[2 * Pw][1] && c > rgb[2 * Pn][1] && |
| c > rgb[2 * Ps][1] && c > rgb[Pe][kc] && c > rgb[Pw][kc] && |
| c > rgb[Pn][kc ^ 2] && c > rgb[Ps][kc ^ 2]) || |
| (c < rgb[2 * Pe][1] && c < rgb[2 * Pw][1] && c < rgb[2 * Pn][1] && |
| c < rgb[2 * Ps][1] && c < rgb[Pe][kc] && c < rgb[Pw][kc] && |
| c < rgb[Pn][kc ^ 2] && c < rgb[Ps][kc ^ 2])) |
| { |
| int chot = c >> Thot; |
| int cdead = c << Tdead; |
| int avg = 0; |
| for (int k = -2; k < 3; k += 2) |
| for (int m = -2; m < 3; m += 2) |
| if (k == 0 && m == 0) |
| continue; |
| else |
| avg += rgb[nr_offset(k, m)][1]; |
| avg /= 8; |
| if (chot > avg || cdead < avg) |
| { |
| ndir[moff] |= HOT; |
| int dh = |
| ABS(rgb[2 * Pw][1] - rgb[2 * Pe][1]) + |
| ABS(rgb[Pw][kc] - rgb[Pe][kc]) + |
| ABS(rgb[Pw][kc] - rgb[Pe][kc] + rgb[2 * Pe][1] - rgb[2 * Pw][1]); |
| int dv = ABS(rgb[2 * Pn][1] - rgb[2 * Ps][1]) + |
| ABS(rgb[Pn][kc ^ 2] - rgb[Ps][kc ^ 2]) + |
| ABS(rgb[Pn][kc ^ 2] - rgb[Ps][kc ^ 2] + rgb[2 * Ps][1] - |
| rgb[2 * Pn][1]); |
| int d; |
| if (dv > dh) |
| d = Pw; |
| else |
| d = Pn; |
| rgb_ahd[1][moff][1] = rgb[0][1] = |
| (rgb[+2 * d][1] + rgb[-2 * d][1]) / 2; |
| } |
| } |
| } |
| } |
| } |
| |
| void AAHD::evaluate_ahd() |
| { |
| int hvdir[4] = {Pw, Pe, Pn, Ps}; |
| /* |
| * YUV |
| * |
| */ |
| for (int d = 0; d < 2; ++d) |
| { |
| for (int i = 0; i < nr_width * nr_height; ++i) |
| { |
| ushort3 rgb; |
| for (int c = 0; c < 3; ++c) |
| { |
| rgb[c] = ushort(gammaLUT[rgb_ahd[d][i][c]]); |
| } |
| yuv[d][i][0] = Y(rgb); |
| yuv[d][i][1] = U(rgb); |
| yuv[d][i][2] = V(rgb); |
| } |
| } |
| /* */ |
| /* |
| * Lab |
| * |
| float r, cbrt[0x10000], xyz[3], xyz_cam[3][4]; |
| for (int i = 0; i < 0x10000; i++) { |
| r = i / 65535.0; |
| cbrt[i] = r > 0.008856 ? pow((double) r, (double) (1 / 3.0)) : 7.787 * r + 16 |
| / 116.0; |
| } |
| for (int i = 0; i < 3; i++) |
| for (int j = 0; j < 3; j++) { |
| xyz_cam[i][j] = 0; |
| for (int k = 0; k < 3; k++) |
| xyz_cam[i][j] += xyz_rgb[i][k] * libraw.imgdata.color.rgb_cam[k][j] / |
| d65_white[i]; |
| } |
| for (int d = 0; d < 2; ++d) |
| for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) { |
| int moff = nr_offset(i + nr_margin, nr_margin); |
| for (int j = 0; j < libraw.imgdata.sizes.iwidth; j++, ++moff) { |
| xyz[0] = xyz[1] = xyz[2] = 0.5; |
| for (int c = 0; c < 3; c++) { |
| xyz[0] += xyz_cam[0][c] * rgb_ahd[d][moff][c]; |
| xyz[1] += xyz_cam[1][c] * rgb_ahd[d][moff][c]; |
| xyz[2] += xyz_cam[2][c] * rgb_ahd[d][moff][c]; |
| } |
| xyz[0] = cbrt[CLIP((int) xyz[0])]; |
| xyz[1] = cbrt[CLIP((int) xyz[1])]; |
| xyz[2] = cbrt[CLIP((int) xyz[2])]; |
| yuv[d][moff][0] = 64 * (116 * xyz[1] - 16); |
| yuv[d][moff][1] = 64 * 500 * (xyz[0] - xyz[1]); |
| yuv[d][moff][2] = 64 * 200 * (xyz[1] - xyz[2]); |
| } |
| } |
| * Lab */ |
| for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
| { |
| int moff = nr_offset(i + nr_margin, nr_margin); |
| for (int j = 0; j < libraw.imgdata.sizes.iwidth; j++, ++moff) |
| { |
| int3 *ynr; |
| float ydiff[2][4]; |
| int uvdiff[2][4]; |
| for (int d = 0; d < 2; ++d) |
| { |
| ynr = &yuv[d][moff]; |
| for (int k = 0; k < 4; k++) |
| { |
| ydiff[d][k] = float(ABS(ynr[0][0] - ynr[hvdir[k]][0])); |
| uvdiff[d][k] = SQR(ynr[0][1] - ynr[hvdir[k]][1]) + |
| SQR(ynr[0][2] - ynr[hvdir[k]][2]); |
| } |
| } |
| float yeps = |
| MIN(MAX(ydiff[0][0], ydiff[0][1]), MAX(ydiff[1][2], ydiff[1][3])); |
| int uveps = |
| MIN(MAX(uvdiff[0][0], uvdiff[0][1]), MAX(uvdiff[1][2], uvdiff[1][3])); |
| for (int d = 0; d < 2; d++) |
| { |
| ynr = &yuv[d][moff]; |
| for (int k = 0; k < 4; k++) |
| if (ydiff[d][k] <= yeps && uvdiff[d][k] <= uveps) |
| { |
| homo[d][moff + hvdir[k]]++; |
| if (k / 2 == d) |
| { |
| // если в сонаправленном направлении интеполяции следующие точки |
| // так же гомогенны, учтём их тоже |
| for (int m = 2; m < 4; ++m) |
| { |
| int hvd = m * hvdir[k]; |
| if (ABS(ynr[0][0] - ynr[hvd][0]) < yeps && |
| SQR(ynr[0][1] - ynr[hvd][1]) + |
| SQR(ynr[0][2] - ynr[hvd][2]) < |
| uveps) |
| { |
| homo[d][moff + hvd]++; |
| } |
| else |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
| { |
| int moff = nr_offset(i + nr_margin, nr_margin); |
| for (int j = 0; j < libraw.imgdata.sizes.iwidth; j++, ++moff) |
| { |
| char hm[2]; |
| for (int d = 0; d < 2; d++) |
| { |
| hm[d] = 0; |
| char *hh = &homo[d][moff]; |
| for (int hx = -1; hx < 2; hx++) |
| for (int hy = -1; hy < 2; hy++) |
| hm[d] += hh[nr_offset(hy, hx)]; |
| } |
| char d = 0; |
| if (hm[0] != hm[1]) |
| { |
| if (hm[1] > hm[0]) |
| { |
| d = VERSH; |
| } |
| else |
| { |
| d = HORSH; |
| } |
| } |
| else |
| { |
| int3 *ynr = &yuv[1][moff]; |
| int gv = SQR(2 * ynr[0][0] - ynr[Pn][0] - ynr[Ps][0]); |
| gv += SQR(2 * ynr[0][1] - ynr[Pn][1] - ynr[Ps][1]) + |
| SQR(2 * ynr[0][2] - ynr[Pn][2] - ynr[Ps][2]); |
| ynr = &yuv[1][moff + Pn]; |
| gv += (SQR(2 * ynr[0][0] - ynr[Pn][0] - ynr[Ps][0]) + |
| SQR(2 * ynr[0][1] - ynr[Pn][1] - ynr[Ps][1]) + |
| SQR(2 * ynr[0][2] - ynr[Pn][2] - ynr[Ps][2])) / |
| 2; |
| ynr = &yuv[1][moff + Ps]; |
| gv += (SQR(2 * ynr[0][0] - ynr[Pn][0] - ynr[Ps][0]) + |
| SQR(2 * ynr[0][1] - ynr[Pn][1] - ynr[Ps][1]) + |
| SQR(2 * ynr[0][2] - ynr[Pn][2] - ynr[Ps][2])) / |
| 2; |
| ynr = &yuv[0][moff]; |
| int gh = SQR(2 * ynr[0][0] - ynr[Pw][0] - ynr[Pe][0]); |
| gh += SQR(2 * ynr[0][1] - ynr[Pw][1] - ynr[Pe][1]) + |
| SQR(2 * ynr[0][2] - ynr[Pw][2] - ynr[Pe][2]); |
| ynr = &yuv[0][moff + Pw]; |
| gh += (SQR(2 * ynr[0][0] - ynr[Pw][0] - ynr[Pe][0]) + |
| SQR(2 * ynr[0][1] - ynr[Pw][1] - ynr[Pe][1]) + |
| SQR(2 * ynr[0][2] - ynr[Pw][2] - ynr[Pe][2])) / |
| 2; |
| ynr = &yuv[0][moff + Pe]; |
| gh += (SQR(2 * ynr[0][0] - ynr[Pw][0] - ynr[Pe][0]) + |
| SQR(2 * ynr[0][1] - ynr[Pw][1] - ynr[Pe][1]) + |
| SQR(2 * ynr[0][2] - ynr[Pw][2] - ynr[Pe][2])) / |
| 2; |
| if (gv > gh) |
| d = HOR; |
| else |
| d = VER; |
| } |
| ndir[moff] |= d; |
| } |
| } |
| } |
| |
| void AAHD::combine_image() |
| { |
| for (int i = 0, i_out = 0; i < libraw.imgdata.sizes.iheight; ++i) |
| { |
| int moff = nr_offset(i + nr_margin, nr_margin); |
| for (int j = 0; j < libraw.imgdata.sizes.iwidth; j++, ++moff, ++i_out) |
| { |
| if (ndir[moff] & HOT) |
| { |
| int c = libraw.COLOR(i, j); |
| rgb_ahd[1][moff][c] = rgb_ahd[0][moff][c] = |
| libraw.imgdata.image[i_out][c]; |
| } |
| if (ndir[moff] & VER) |
| { |
| libraw.imgdata.image[i_out][0] = rgb_ahd[1][moff][0]; |
| libraw.imgdata.image[i_out][3] = libraw.imgdata.image[i_out][1] = |
| rgb_ahd[1][moff][1]; |
| libraw.imgdata.image[i_out][2] = rgb_ahd[1][moff][2]; |
| } |
| else |
| { |
| libraw.imgdata.image[i_out][0] = rgb_ahd[0][moff][0]; |
| libraw.imgdata.image[i_out][3] = libraw.imgdata.image[i_out][1] = |
| rgb_ahd[0][moff][1]; |
| libraw.imgdata.image[i_out][2] = rgb_ahd[0][moff][2]; |
| } |
| } |
| } |
| } |
| |
| void AAHD::refine_hv_dirs() |
| { |
| for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
| { |
| refine_hv_dirs(i, i & 1); |
| } |
| for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
| { |
| refine_hv_dirs(i, (i & 1) ^ 1); |
| } |
| for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
| { |
| refine_ihv_dirs(i); |
| } |
| } |
| |
| void AAHD::refine_ihv_dirs(int i) |
| { |
| int iwidth = libraw.imgdata.sizes.iwidth; |
| int moff = nr_offset(i + nr_margin, nr_margin); |
| for (int j = 0; j < iwidth; j++, ++moff) |
| { |
| if (ndir[moff] & HVSH) |
| continue; |
| int nv = (ndir[moff + Pn] & VER) + (ndir[moff + Ps] & VER) + |
| (ndir[moff + Pw] & VER) + (ndir[moff + Pe] & VER); |
| int nh = (ndir[moff + Pn] & HOR) + (ndir[moff + Ps] & HOR) + |
| (ndir[moff + Pw] & HOR) + (ndir[moff + Pe] & HOR); |
| nv /= VER; |
| nh /= HOR; |
| if ((ndir[moff] & VER) && nh > 3) |
| { |
| ndir[moff] &= ~VER; |
| ndir[moff] |= HOR; |
| } |
| if ((ndir[moff] & HOR) && nv > 3) |
| { |
| ndir[moff] &= ~HOR; |
| ndir[moff] |= VER; |
| } |
| } |
| } |
| |
| void AAHD::refine_hv_dirs(int i, int js) |
| { |
| int iwidth = libraw.imgdata.sizes.iwidth; |
| int moff = nr_offset(i + nr_margin, nr_margin + js); |
| for (int j = js; j < iwidth; j += 2, moff += 2) |
| { |
| int nv = (ndir[moff + Pn] & VER) + (ndir[moff + Ps] & VER) + |
| (ndir[moff + Pw] & VER) + (ndir[moff + Pe] & VER); |
| int nh = (ndir[moff + Pn] & HOR) + (ndir[moff + Ps] & HOR) + |
| (ndir[moff + Pw] & HOR) + (ndir[moff + Pe] & HOR); |
| bool codir = (ndir[moff] & VER) |
| ? ((ndir[moff + Pn] & VER) || (ndir[moff + Ps] & VER)) |
| : ((ndir[moff + Pw] & HOR) || (ndir[moff + Pe] & HOR)); |
| nv /= VER; |
| nh /= HOR; |
| if ((ndir[moff] & VER) && (nh > 2 && !codir)) |
| { |
| ndir[moff] &= ~VER; |
| ndir[moff] |= HOR; |
| } |
| if ((ndir[moff] & HOR) && (nv > 2 && !codir)) |
| { |
| ndir[moff] &= ~HOR; |
| ndir[moff] |= VER; |
| } |
| } |
| } |
| |
| /* |
| * вычисление недостающих зелёных точек. |
| */ |
| void AAHD::make_ahd_greens() |
| { |
| for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
| { |
| make_ahd_gline(i); |
| } |
| } |
| |
| void AAHD::make_ahd_gline(int i) |
| { |
| int iwidth = libraw.imgdata.sizes.iwidth; |
| int js = libraw.COLOR(i, 0) & 1; |
| int kc = libraw.COLOR(i, js); |
| /* |
| * js -- начальная х-координата, которая попадает мимо известного зелёного |
| * kc -- известный цвет в точке интерполирования |
| */ |
| int hvdir[2] = {Pe, Ps}; |
| for (int d = 0; d < 2; ++d) |
| { |
| int moff = nr_offset(i + nr_margin, nr_margin + js); |
| for (int j = js; j < iwidth; j += 2, moff += 2) |
| { |
| ushort3 *cnr; |
| cnr = &rgb_ahd[d][moff]; |
| int h1 = 2 * cnr[-hvdir[d]][1] - int(cnr[-2 * hvdir[d]][kc] + cnr[0][kc]); |
| int h2 = 2 * cnr[+hvdir[d]][1] - int(cnr[+2 * hvdir[d]][kc] + cnr[0][kc]); |
| int h0 = (h1 + h2) / 4; |
| int eg = cnr[0][kc] + h0; |
| int min = MIN(cnr[-hvdir[d]][1], cnr[+hvdir[d]][1]); |
| int max = MAX(cnr[-hvdir[d]][1], cnr[+hvdir[d]][1]); |
| min -= min / OverFraction; |
| max += max / OverFraction; |
| if (eg < min) |
| eg = min - int(sqrtf(float(min - eg))); |
| else if (eg > max) |
| eg = max + int(sqrtf(float(eg - max))); |
| if (eg > channel_maximum[1]) |
| eg = channel_maximum[1]; |
| else if (eg < channel_minimum[1]) |
| eg = channel_minimum[1]; |
| cnr[0][1] = eg; |
| } |
| } |
| } |
| |
| /* |
| * отладочная функция |
| */ |
| |
| void AAHD::illustrate_dirs() |
| { |
| for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
| { |
| illustrate_dline(i); |
| } |
| } |
| |
| void AAHD::illustrate_dline(int i) |
| { |
| int iwidth = libraw.imgdata.sizes.iwidth; |
| for (int j = 0; j < iwidth; j++) |
| { |
| int x = j + nr_margin; |
| int y = i + nr_margin; |
| rgb_ahd[1][nr_offset(y, x)][0] = rgb_ahd[1][nr_offset(y, x)][1] = |
| rgb_ahd[1][nr_offset(y, x)][2] = rgb_ahd[0][nr_offset(y, x)][0] = |
| rgb_ahd[0][nr_offset(y, x)][1] = rgb_ahd[0][nr_offset(y, x)][2] = 0; |
| int l = ndir[nr_offset(y, x)] & HVSH; |
| l /= HVSH; |
| if (ndir[nr_offset(y, x)] & VER) |
| rgb_ahd[1][nr_offset(y, x)][0] = |
| l * channel_maximum[0] / 4 + channel_maximum[0] / 4; |
| else |
| rgb_ahd[0][nr_offset(y, x)][2] = |
| l * channel_maximum[2] / 4 + channel_maximum[2] / 4; |
| } |
| } |
| |
| void AAHD::make_ahd_rb_hv(int i) |
| { |
| int iwidth = libraw.imgdata.sizes.iwidth; |
| int js = libraw.COLOR(i, 0) & 1; |
| int kc = libraw.COLOR(i, js); |
| js ^= 1; // начальная координата зелёного |
| int hvdir[2] = {Pe, Ps}; |
| // интерполяция вертикальных вертикально и горизонтальных горизонтально |
| for (int j = js; j < iwidth; j += 2) |
| { |
| int x = j + nr_margin; |
| int y = i + nr_margin; |
| int moff = nr_offset(y, x); |
| for (int d = 0; d < 2; ++d) |
| { |
| ushort3 *cnr; |
| cnr = &rgb_ahd[d][moff]; |
| int c = kc ^ (d << 1); // цвет соответсвенного направления, для |
| // горизонтального c = kc, для вертикального c=kc^2 |
| int h1 = cnr[-hvdir[d]][c] - cnr[-hvdir[d]][1]; |
| int h2 = cnr[+hvdir[d]][c] - cnr[+hvdir[d]][1]; |
| int h0 = (h1 + h2) / 2; |
| int eg = cnr[0][1] + h0; |
| // int min = MIN(cnr[-hvdir[d]][c], cnr[+hvdir[d]][c]); |
| // int max = MAX(cnr[-hvdir[d]][c], cnr[+hvdir[d]][c]); |
| // min -= min / OverFraction; |
| // max += max / OverFraction; |
| // if (eg < min) |
| // eg = min - sqrt(min - eg); |
| // else if (eg > max) |
| // eg = max + sqrt(eg - max); |
| if (eg > channel_maximum[c]) |
| eg = channel_maximum[c]; |
| else if (eg < channel_minimum[c]) |
| eg = channel_minimum[c]; |
| cnr[0][c] = eg; |
| } |
| } |
| } |
| |
| void AAHD::make_ahd_rb() |
| { |
| for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
| { |
| make_ahd_rb_hv(i); |
| } |
| for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) |
| { |
| make_ahd_rb_last(i); |
| } |
| } |
| |
| void AAHD::make_ahd_rb_last(int i) |
| { |
| int iwidth = libraw.imgdata.sizes.iwidth; |
| int js = libraw.COLOR(i, 0) & 1; |
| int kc = libraw.COLOR(i, js); |
| /* |
| * js -- начальная х-координата, которая попадает мимо известного зелёного |
| * kc -- известный цвет в точке интерполирования |
| */ |
| int dirs[2][3] = {{Pnw, Pn, Pne}, {Pnw, Pw, Psw}}; |
| int moff = nr_offset(i + nr_margin, nr_margin); |
| for (int j = 0; j < iwidth; j++) |
| { |
| for (int d = 0; d < 2; ++d) |
| { |
| ushort3 *cnr; |
| cnr = &rgb_ahd[d][moff + j]; |
| int c = kc ^ 2; |
| if ((j & 1) != js) |
| { |
| // точка зелёного, для вертикального направления нужен альтернативный |
| // строчному цвет |
| c ^= d << 1; |
| } |
| int bh = 0, bk = 0; |
| int bgd = 0; |
| for (int k = 0; k < 3; ++k) |
| for (int h = 0; h < 3; ++h) |
| { |
| // градиент зелёного плюс градиент {r,b} |
| int gd = |
| ABS(2 * cnr[0][1] - (cnr[+dirs[d][k]][1] + cnr[-dirs[d][h]][1])) + |
| ABS(cnr[+dirs[d][k]][c] - cnr[-dirs[d][h]][c]) / 4 + |
| ABS(cnr[+dirs[d][k]][c] - cnr[+dirs[d][k]][1] + |
| cnr[-dirs[d][h]][1] - cnr[-dirs[d][h]][c]) / |
| 4; |
| if (bgd == 0 || gd < bgd) |
| { |
| bgd = gd; |
| bh = h; |
| bk = k; |
| } |
| } |
| int h1 = cnr[+dirs[d][bk]][c] - cnr[+dirs[d][bk]][1]; |
| int h2 = cnr[-dirs[d][bh]][c] - cnr[-dirs[d][bh]][1]; |
| int eg = cnr[0][1] + (h1 + h2) / 2; |
| // int min = MIN(cnr[+dirs[d][bk]][c], cnr[-dirs[d][bh]][c]); |
| // int max = MAX(cnr[+dirs[d][bk]][c], cnr[-dirs[d][bh]][c]); |
| // min -= min / OverFraction; |
| // max += max / OverFraction; |
| // if (eg < min) |
| // eg = min - sqrt(min - eg); |
| // else if (eg > max) |
| // eg = max + sqrt(eg - max); |
| if (eg > channel_maximum[c]) |
| eg = channel_maximum[c]; |
| else if (eg < channel_minimum[c]) |
| eg = channel_minimum[c]; |
| cnr[0][c] = eg; |
| } |
| } |
| } |
| |
| AAHD::~AAHD() { free(rgb_ahd[0]); } |
| |
| void LibRaw::aahd_interpolate() |
| { |
| AAHD aahd(*this); |
| aahd.hide_hots(); |
| aahd.make_ahd_greens(); |
| aahd.make_ahd_rb(); |
| aahd.evaluate_ahd(); |
| aahd.refine_hv_dirs(); |
| // aahd.illustrate_dirs(); |
| aahd.combine_image(); |
| } |