/* -*- C++ -*- * File: aahd_demosaic.cpp * Copyright 2013 Anton Petrusevich * Created: Wed May 15, 2013 * * This code is licensed under one of three 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). * * 3. LibRaw Software License 27032010 * (See file LICENSE.LibRaw.pdf provided in LibRaw distribution archive for details). * */ 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 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 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 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)); 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; } } } } } const static double xyz_rgb[3][3] = { { 0.412453, 0.357580, 0.180423 }, { 0.212671, 0.715160, 0.072169 }, { 0.019334, 0.119193, 0.950227 } }; const static float d65_white[3] = { 0.950456f, 1.0f, 1.088754f }; 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] = 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] = 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 - sqrt(float(min - eg)); else if (eg > max) eg = max + sqrt(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, bk; 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() { printf("AAHD interpolating\n"); 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(); }