blob: e8257fa519812aec476db1e58299853a6ac5d11c [file]
/* -*- 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();
}