blob: 2570a93bbfa501d9ad4d03510a2d81e63e49917f [file]
/* -*- C++ -*-
* File: dht_demosaic.cpp
* Copyright 2013 Anton Petrusevich
* Created: Tue Apr 9, 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).
*
*/
/*
* функция вычисляет яркостную дистанцию.
* если две яркости отличаются в два раза, например 10 и 20, то они имеют такой
* же вес при принятии решения об интерполировании, как и 100 и 200 --
* фотографическая яркость между ними 1 стоп. дистанция всегда >=1
*/
#include "third_party/libraw/internal/dmp_include.h"
static inline float calc_dist(float c1, float c2)
{
return c1 > c2 ? c1 / c2 : c2 / c1;
}
struct DHT
{
int nr_height, nr_width;
static const int nr_topmargin = 4, nr_leftmargin = 4;
float (*nraw)[3];
ushort channel_maximum[3];
float channel_minimum[3];
LibRaw &libraw;
enum
{
HVSH = 1,
HOR = 2,
VER = 4,
HORSH = HOR | HVSH,
VERSH = VER | HVSH,
DIASH = 8,
LURD = 16,
RULD = 32,
LURDSH = LURD | DIASH,
RULDSH = RULD | DIASH,
HOT = 64
};
static inline float Thot(void) throw() { return 64.0f; }
static inline float Tg(void) throw() { return 256.0f; }
static inline float T(void) throw() { return 1.4f; }
char *ndir;
inline int nr_offset(int row, int col) throw()
{
return (row * nr_width + col);
}
int get_hv_grb(int x, int y, int kc)
{
float hv1 = 2 * nraw[nr_offset(y - 1, x)][1] /
(nraw[nr_offset(y - 2, x)][kc] + nraw[nr_offset(y, x)][kc]);
float hv2 = 2 * nraw[nr_offset(y + 1, x)][1] /
(nraw[nr_offset(y + 2, x)][kc] + nraw[nr_offset(y, x)][kc]);
float kv = calc_dist(hv1, hv2) *
calc_dist(nraw[nr_offset(y, x)][kc] * nraw[nr_offset(y, x)][kc],
(nraw[nr_offset(y - 2, x)][kc] *
nraw[nr_offset(y + 2, x)][kc]));
kv *= kv;
kv *= kv;
kv *= kv;
float dv =
kv *
calc_dist(nraw[nr_offset(y - 3, x)][1] * nraw[nr_offset(y + 3, x)][1],
nraw[nr_offset(y - 1, x)][1] * nraw[nr_offset(y + 1, x)][1]);
float hh1 = 2 * nraw[nr_offset(y, x - 1)][1] /
(nraw[nr_offset(y, x - 2)][kc] + nraw[nr_offset(y, x)][kc]);
float hh2 = 2 * nraw[nr_offset(y, x + 1)][1] /
(nraw[nr_offset(y, x + 2)][kc] + nraw[nr_offset(y, x)][kc]);
float kh = calc_dist(hh1, hh2) *
calc_dist(nraw[nr_offset(y, x)][kc] * nraw[nr_offset(y, x)][kc],
(nraw[nr_offset(y, x - 2)][kc] *
nraw[nr_offset(y, x + 2)][kc]));
kh *= kh;
kh *= kh;
kh *= kh;
float dh =
kh *
calc_dist(nraw[nr_offset(y, x - 3)][1] * nraw[nr_offset(y, x + 3)][1],
nraw[nr_offset(y, x - 1)][1] * nraw[nr_offset(y, x + 1)][1]);
float e = calc_dist(dh, dv);
char d = dh < dv ? (e > Tg() ? HORSH : HOR) : (e > Tg() ? VERSH : VER);
return d;
}
int get_hv_rbg(int x, int y, int hc)
{
float hv1 = 2 * nraw[nr_offset(y - 1, x)][hc ^ 2] /
(nraw[nr_offset(y - 2, x)][1] + nraw[nr_offset(y, x)][1]);
float hv2 = 2 * nraw[nr_offset(y + 1, x)][hc ^ 2] /
(nraw[nr_offset(y + 2, x)][1] + nraw[nr_offset(y, x)][1]);
float kv = calc_dist(hv1, hv2) *
calc_dist(nraw[nr_offset(y, x)][1] * nraw[nr_offset(y, x)][1],
(nraw[nr_offset(y - 2, x)][1] *
nraw[nr_offset(y + 2, x)][1]));
kv *= kv;
kv *= kv;
kv *= kv;
float dv = kv * calc_dist(nraw[nr_offset(y - 3, x)][hc ^ 2] *
nraw[nr_offset(y + 3, x)][hc ^ 2],
nraw[nr_offset(y - 1, x)][hc ^ 2] *
nraw[nr_offset(y + 1, x)][hc ^ 2]);
float hh1 = 2 * nraw[nr_offset(y, x - 1)][hc] /
(nraw[nr_offset(y, x - 2)][1] + nraw[nr_offset(y, x)][1]);
float hh2 = 2 * nraw[nr_offset(y, x + 1)][hc] /
(nraw[nr_offset(y, x + 2)][1] + nraw[nr_offset(y, x)][1]);
float kh = calc_dist(hh1, hh2) *
calc_dist(nraw[nr_offset(y, x)][1] * nraw[nr_offset(y, x)][1],
(nraw[nr_offset(y, x - 2)][1] *
nraw[nr_offset(y, x + 2)][1]));
kh *= kh;
kh *= kh;
kh *= kh;
float dh =
kh * calc_dist(
nraw[nr_offset(y, x - 3)][hc] * nraw[nr_offset(y, x + 3)][hc],
nraw[nr_offset(y, x - 1)][hc] * nraw[nr_offset(y, x + 1)][hc]);
float e = calc_dist(dh, dv);
char d = dh < dv ? (e > Tg() ? HORSH : HOR) : (e > Tg() ? VERSH : VER);
return d;
}
int get_diag_grb(int x, int y, int kc)
{
float hlu =
nraw[nr_offset(y - 1, x - 1)][1] / nraw[nr_offset(y - 1, x - 1)][kc];
float hrd =
nraw[nr_offset(y + 1, x + 1)][1] / nraw[nr_offset(y + 1, x + 1)][kc];
float dlurd =
calc_dist(hlu, hrd) *
calc_dist(nraw[nr_offset(y - 1, x - 1)][1] *
nraw[nr_offset(y + 1, x + 1)][1],
nraw[nr_offset(y, x)][1] * nraw[nr_offset(y, x)][1]);
float druld =
calc_dist(hlu, hrd) *
calc_dist(nraw[nr_offset(y - 1, x + 1)][1] *
nraw[nr_offset(y + 1, x - 1)][1],
nraw[nr_offset(y, x)][1] * nraw[nr_offset(y, x)][1]);
float e = calc_dist(dlurd, druld);
char d =
druld < dlurd ? (e > T() ? RULDSH : RULD) : (e > T() ? LURDSH : LURD);
return d;
}
int get_diag_rbg(int x, int y, int /* hc */)
{
float dlurd = calc_dist(
nraw[nr_offset(y - 1, x - 1)][1] * nraw[nr_offset(y + 1, x + 1)][1],
nraw[nr_offset(y, x)][1] * nraw[nr_offset(y, x)][1]);
float druld = calc_dist(
nraw[nr_offset(y - 1, x + 1)][1] * nraw[nr_offset(y + 1, x - 1)][1],
nraw[nr_offset(y, x)][1] * nraw[nr_offset(y, x)][1]);
float e = calc_dist(dlurd, druld);
char d =
druld < dlurd ? (e > T() ? RULDSH : RULD) : (e > T() ? LURDSH : LURD);
return d;
}
static inline float scale_over(float ec, float base)
{
float s = base * .4f;
float o = ec - base;
return base + sqrt(s * (o + s)) - s;
}
static inline float scale_under(float ec, float base)
{
float s = base * .6f;
float o = base - ec;
return base - sqrt(s * (o + s)) + s;
}
~DHT();
DHT(LibRaw &_libraw);
void copy_to_image();
void make_greens();
void make_diag_dirs();
void make_hv_dirs();
void refine_hv_dirs(int i, int js);
void refine_diag_dirs(int i, int js);
void refine_ihv_dirs(int i);
void refine_idiag_dirs(int i);
void illustrate_dirs();
void illustrate_dline(int i);
void make_hv_dline(int i);
void make_diag_dline(int i);
void make_gline(int i);
void make_rbdiag(int i);
void make_rbhv(int i);
void make_rb();
void hide_hots();
void restore_hots();
};
typedef float float3[3];
/*
* информация о цветах копируется во float в общем то с одной целью -- чтобы
* вместо 0 можно было писать 0.5, что больше подходит для вычисления яркостной
* разницы. причина: в целых числах разница в 1 стоп составляет ряд 8,4,2,1,0 --
* последнее число должно быть 0.5, которое непредствамио в целых числах. так же
* это изменение позволяет не думать о специальных случаях деления на ноль.
*
* альтернативное решение: умножить все данные на 2 и в младший бит внести 1.
* правда, всё равно придётся следить, чтобы при интерпретации зелёного цвета не
* получился 0 при округлении, иначе проблема при интерпретации синих и красных.
*
*/
DHT::DHT(LibRaw &_libraw) : libraw(_libraw)
{
nr_height = libraw.imgdata.sizes.iheight + nr_topmargin * 2;
nr_width = libraw.imgdata.sizes.iwidth + nr_leftmargin * 2;
nraw = (float3 *)malloc(nr_height * nr_width * sizeof(float3));
int iwidth = libraw.imgdata.sizes.iwidth;
ndir = (char *)calloc(nr_height * nr_width, 1);
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];
for (int i = 0; i < nr_height * nr_width; ++i)
nraw[i][0] = nraw[i][1] = nraw[i][2] = 0.5;
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
{
int col_cache[48];
for (int j = 0; j < 48; ++j)
{
int l = libraw.COLOR(i, j);
if (l == 3)
l = 1;
col_cache[j] = l;
}
for (int j = 0; j < iwidth; ++j)
{
int l = col_cache[j % 48];
unsigned short c = libraw.imgdata.image[i * iwidth + j][l];
if (c != 0)
{
if (channel_maximum[l] < c)
channel_maximum[l] = c;
if (channel_minimum[l] > c)
channel_minimum[l] = c;
nraw[nr_offset(i + nr_topmargin, j + nr_leftmargin)][l] = (float)c;
}
}
}
channel_minimum[0] += .5;
channel_minimum[1] += .5;
channel_minimum[2] += .5;
}
void DHT::hide_hots()
{
int iwidth = libraw.imgdata.sizes.iwidth;
#if defined(LIBRAW_USE_OPENMP)
#pragma omp parallel for schedule(guided) firstprivate(iwidth)
#endif
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 -- известный цвет в точке интерполирования
*/
for (int j = js; j < iwidth; j += 2)
{
int x = j + nr_leftmargin;
int y = i + nr_topmargin;
float c = nraw[nr_offset(y, x)][kc];
if ((c > nraw[nr_offset(y, x + 2)][kc] &&
c > nraw[nr_offset(y, x - 2)][kc] &&
c > nraw[nr_offset(y - 2, x)][kc] &&
c > nraw[nr_offset(y + 2, x)][kc] &&
c > nraw[nr_offset(y, x + 1)][1] &&
c > nraw[nr_offset(y, x - 1)][1] &&
c > nraw[nr_offset(y - 1, x)][1] &&
c > nraw[nr_offset(y + 1, x)][1]) ||
(c < nraw[nr_offset(y, x + 2)][kc] &&
c < nraw[nr_offset(y, x - 2)][kc] &&
c < nraw[nr_offset(y - 2, x)][kc] &&
c < nraw[nr_offset(y + 2, x)][kc] &&
c < nraw[nr_offset(y, x + 1)][1] &&
c < nraw[nr_offset(y, x - 1)][1] &&
c < nraw[nr_offset(y - 1, x)][1] &&
c < nraw[nr_offset(y + 1, x)][1]))
{
float 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 += nraw[nr_offset(y + k, x + m)][kc];
avg /= 8;
// float dev = 0;
// for (int k = -2; k < 3; k += 2)
// for (int l = -2; l < 3; l += 2)
// if (k == 0 && l == 0)
// continue;
// else {
// float t = nraw[nr_offset(y + k, x + l)][kc] -
//avg; dev += t * t;
// }
// dev /= 8;
// dev = sqrt(dev);
if (calc_dist(c, avg) > Thot())
{
ndir[nr_offset(y, x)] |= HOT;
float dv = calc_dist(
nraw[nr_offset(y - 2, x)][kc] * nraw[nr_offset(y - 1, x)][1],
nraw[nr_offset(y + 2, x)][kc] * nraw[nr_offset(y + 1, x)][1]);
float dh = calc_dist(
nraw[nr_offset(y, x - 2)][kc] * nraw[nr_offset(y, x - 1)][1],
nraw[nr_offset(y, x + 2)][kc] * nraw[nr_offset(y, x + 1)][1]);
if (dv > dh)
nraw[nr_offset(y, x)][kc] = (nraw[nr_offset(y, x + 2)][kc] +
nraw[nr_offset(y, x - 2)][kc]) /
2;
else
nraw[nr_offset(y, x)][kc] = (nraw[nr_offset(y - 2, x)][kc] +
nraw[nr_offset(y + 2, x)][kc]) /
2;
}
}
}
for (int j = js ^ 1; j < iwidth; j += 2)
{
int x = j + nr_leftmargin;
int y = i + nr_topmargin;
float c = nraw[nr_offset(y, x)][1];
if ((c > nraw[nr_offset(y, x + 2)][1] &&
c > nraw[nr_offset(y, x - 2)][1] &&
c > nraw[nr_offset(y - 2, x)][1] &&
c > nraw[nr_offset(y + 2, x)][1] &&
c > nraw[nr_offset(y, x + 1)][kc] &&
c > nraw[nr_offset(y, x - 1)][kc] &&
c > nraw[nr_offset(y - 1, x)][kc ^ 2] &&
c > nraw[nr_offset(y + 1, x)][kc ^ 2]) ||
(c < nraw[nr_offset(y, x + 2)][1] &&
c < nraw[nr_offset(y, x - 2)][1] &&
c < nraw[nr_offset(y - 2, x)][1] &&
c < nraw[nr_offset(y + 2, x)][1] &&
c < nraw[nr_offset(y, x + 1)][kc] &&
c < nraw[nr_offset(y, x - 1)][kc] &&
c < nraw[nr_offset(y - 1, x)][kc ^ 2] &&
c < nraw[nr_offset(y + 1, x)][kc ^ 2]))
{
float 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 += nraw[nr_offset(y + k, x + m)][1];
avg /= 8;
// float dev = 0;
// for (int k = -2; k < 3; k += 2)
// for (int l = -2; l < 3; l += 2)
// if (k == 0 && l == 0)
// continue;
// else {
// float t = nraw[nr_offset(y + k, x + l)][1] -
//avg; dev += t * t;
// }
// dev /= 8;
// dev = sqrt(dev);
if (calc_dist(c, avg) > Thot())
{
ndir[nr_offset(y, x)] |= HOT;
float dv = calc_dist(
nraw[nr_offset(y - 2, x)][1] * nraw[nr_offset(y - 1, x)][kc ^ 2],
nraw[nr_offset(y + 2, x)][1] * nraw[nr_offset(y + 1, x)][kc ^ 2]);
float dh = calc_dist(
nraw[nr_offset(y, x - 2)][1] * nraw[nr_offset(y, x - 1)][kc],
nraw[nr_offset(y, x + 2)][1] * nraw[nr_offset(y, x + 1)][kc]);
if (dv > dh)
nraw[nr_offset(y, x)][1] =
(nraw[nr_offset(y, x + 2)][1] + nraw[nr_offset(y, x - 2)][1]) /
2;
else
nraw[nr_offset(y, x)][1] =
(nraw[nr_offset(y - 2, x)][1] + nraw[nr_offset(y + 2, x)][1]) /
2;
}
}
}
}
}
void DHT::restore_hots()
{
int iwidth = libraw.imgdata.sizes.iwidth;
#if defined(LIBRAW_USE_OPENMP)
#ifdef _MSC_VER
#pragma omp parallel for firstprivate(iwidth)
#else
#pragma omp parallel for schedule(guided) firstprivate(iwidth) collapse(2)
#endif
#endif
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
{
for (int j = 0; j < iwidth; ++j)
{
int x = j + nr_leftmargin;
int y = i + nr_topmargin;
if (ndir[nr_offset(y, x)] & HOT)
{
int l = libraw.COLOR(i, j);
nraw[nr_offset(i + nr_topmargin, j + nr_leftmargin)][l] =
libraw.imgdata.image[i * iwidth + j][l];
}
}
}
}
void DHT::make_diag_dirs()
{
#if defined(LIBRAW_USE_OPENMP)
#pragma omp parallel for schedule(guided)
#endif
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
{
make_diag_dline(i);
}
//#if defined(LIBRAW_USE_OPENMP)
//#pragma omp parallel for schedule(guided)
//#endif
// for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) {
// refine_diag_dirs(i, i & 1);
// }
//#if defined(LIBRAW_USE_OPENMP)
//#pragma omp parallel for schedule(guided)
//#endif
// for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) {
// refine_diag_dirs(i, (i & 1) ^ 1);
// }
#if defined(LIBRAW_USE_OPENMP)
#pragma omp parallel for schedule(guided)
#endif
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
{
refine_idiag_dirs(i);
}
}
void DHT::make_hv_dirs()
{
#if defined(LIBRAW_USE_OPENMP)
#pragma omp parallel for schedule(guided)
#endif
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
{
make_hv_dline(i);
}
#if defined(LIBRAW_USE_OPENMP)
#pragma omp parallel for schedule(guided)
#endif
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
{
refine_hv_dirs(i, i & 1);
}
#if defined(LIBRAW_USE_OPENMP)
#pragma omp parallel for schedule(guided)
#endif
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
{
refine_hv_dirs(i, (i & 1) ^ 1);
}
#if defined(LIBRAW_USE_OPENMP)
#pragma omp parallel for schedule(guided)
#endif
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
{
refine_ihv_dirs(i);
}
}
void DHT::refine_hv_dirs(int i, int js)
{
int iwidth = libraw.imgdata.sizes.iwidth;
for (int j = js; j < iwidth; j += 2)
{
int x = j + nr_leftmargin;
int y = i + nr_topmargin;
if (ndir[nr_offset(y, x)] & HVSH)
continue;
int nv =
(ndir[nr_offset(y - 1, x)] & VER) + (ndir[nr_offset(y + 1, x)] & VER) +
(ndir[nr_offset(y, x - 1)] & VER) + (ndir[nr_offset(y, x + 1)] & VER);
int nh =
(ndir[nr_offset(y - 1, x)] & HOR) + (ndir[nr_offset(y + 1, x)] & HOR) +
(ndir[nr_offset(y, x - 1)] & HOR) + (ndir[nr_offset(y, x + 1)] & HOR);
bool codir = (ndir[nr_offset(y, x)] & VER)
? ((ndir[nr_offset(y - 1, x)] & VER) ||
(ndir[nr_offset(y + 1, x)] & VER))
: ((ndir[nr_offset(y, x - 1)] & HOR) ||
(ndir[nr_offset(y, x + 1)] & HOR));
nv /= VER;
nh /= HOR;
if ((ndir[nr_offset(y, x)] & VER) && (nh > 2 && !codir))
{
ndir[nr_offset(y, x)] &= ~VER;
ndir[nr_offset(y, x)] |= HOR;
}
if ((ndir[nr_offset(y, x)] & HOR) && (nv > 2 && !codir))
{
ndir[nr_offset(y, x)] &= ~HOR;
ndir[nr_offset(y, x)] |= VER;
}
}
}
void DHT::refine_ihv_dirs(int i)
{
int iwidth = libraw.imgdata.sizes.iwidth;
for (int j = 0; j < iwidth; j++)
{
int x = j + nr_leftmargin;
int y = i + nr_topmargin;
if (ndir[nr_offset(y, x)] & HVSH)
continue;
int nv =
(ndir[nr_offset(y - 1, x)] & VER) + (ndir[nr_offset(y + 1, x)] & VER) +
(ndir[nr_offset(y, x - 1)] & VER) + (ndir[nr_offset(y, x + 1)] & VER);
int nh =
(ndir[nr_offset(y - 1, x)] & HOR) + (ndir[nr_offset(y + 1, x)] & HOR) +
(ndir[nr_offset(y, x - 1)] & HOR) + (ndir[nr_offset(y, x + 1)] & HOR);
nv /= VER;
nh /= HOR;
if ((ndir[nr_offset(y, x)] & VER) && nh > 3)
{
ndir[nr_offset(y, x)] &= ~VER;
ndir[nr_offset(y, x)] |= HOR;
}
if ((ndir[nr_offset(y, x)] & HOR) && nv > 3)
{
ndir[nr_offset(y, x)] &= ~HOR;
ndir[nr_offset(y, x)] |= VER;
}
}
}
void DHT::make_hv_dline(int i)
{
int iwidth = libraw.imgdata.sizes.iwidth;
int js = libraw.COLOR(i, 0) & 1;
int kc = libraw.COLOR(i, js);
/*
* js -- начальная х-координата, которая попадает мимо известного зелёного
* kc -- известный цвет в точке интерполирования
*/
for (int j = 0; j < iwidth; j++)
{
int x = j + nr_leftmargin;
int y = i + nr_topmargin;
char d = 0;
if ((j & 1) == js)
{
d = get_hv_grb(x, y, kc);
}
else
{
d = get_hv_rbg(x, y, kc);
}
ndir[nr_offset(y, x)] |= d;
}
}
void DHT::make_diag_dline(int i)
{
int iwidth = libraw.imgdata.sizes.iwidth;
int js = libraw.COLOR(i, 0) & 1;
int kc = libraw.COLOR(i, js);
/*
* js -- начальная х-координата, которая попадает мимо известного зелёного
* kc -- известный цвет в точке интерполирования
*/
for (int j = 0; j < iwidth; j++)
{
int x = j + nr_leftmargin;
int y = i + nr_topmargin;
char d = 0;
if ((j & 1) == js)
{
d = get_diag_grb(x, y, kc);
}
else
{
d = get_diag_rbg(x, y, kc);
}
ndir[nr_offset(y, x)] |= d;
}
}
void DHT::refine_diag_dirs(int i, int js)
{
int iwidth = libraw.imgdata.sizes.iwidth;
for (int j = js; j < iwidth; j += 2)
{
int x = j + nr_leftmargin;
int y = i + nr_topmargin;
if (ndir[nr_offset(y, x)] & DIASH)
continue;
int nv = (ndir[nr_offset(y - 1, x)] & LURD) +
(ndir[nr_offset(y + 1, x)] & LURD) +
(ndir[nr_offset(y, x - 1)] & LURD) +
(ndir[nr_offset(y, x + 1)] & LURD) +
(ndir[nr_offset(y - 1, x - 1)] & LURD) +
(ndir[nr_offset(y - 1, x + 1)] & LURD) +
(ndir[nr_offset(y + 1, x - 1)] & LURD) +
(ndir[nr_offset(y + 1, x + 1)] & LURD);
int nh = (ndir[nr_offset(y - 1, x)] & RULD) +
(ndir[nr_offset(y + 1, x)] & RULD) +
(ndir[nr_offset(y, x - 1)] & RULD) +
(ndir[nr_offset(y, x + 1)] & RULD) +
(ndir[nr_offset(y - 1, x - 1)] & RULD) +
(ndir[nr_offset(y - 1, x + 1)] & RULD) +
(ndir[nr_offset(y + 1, x - 1)] & RULD) +
(ndir[nr_offset(y + 1, x + 1)] & RULD);
bool codir = (ndir[nr_offset(y, x)] & LURD)
? ((ndir[nr_offset(y - 1, x - 1)] & LURD) ||
(ndir[nr_offset(y + 1, x + 1)] & LURD))
: ((ndir[nr_offset(y - 1, x + 1)] & RULD) ||
(ndir[nr_offset(y + 1, x - 1)] & RULD));
nv /= LURD;
nh /= RULD;
if ((ndir[nr_offset(y, x)] & LURD) && (nh > 4 && !codir))
{
ndir[nr_offset(y, x)] &= ~LURD;
ndir[nr_offset(y, x)] |= RULD;
}
if ((ndir[nr_offset(y, x)] & RULD) && (nv > 4 && !codir))
{
ndir[nr_offset(y, x)] &= ~RULD;
ndir[nr_offset(y, x)] |= LURD;
}
}
}
void DHT::refine_idiag_dirs(int i)
{
int iwidth = libraw.imgdata.sizes.iwidth;
for (int j = 0; j < iwidth; j++)
{
int x = j + nr_leftmargin;
int y = i + nr_topmargin;
if (ndir[nr_offset(y, x)] & DIASH)
continue;
int nv = (ndir[nr_offset(y - 1, x)] & LURD) +
(ndir[nr_offset(y + 1, x)] & LURD) +
(ndir[nr_offset(y, x - 1)] & LURD) +
(ndir[nr_offset(y, x + 1)] & LURD) +
(ndir[nr_offset(y - 1, x - 1)] & LURD) +
(ndir[nr_offset(y - 1, x + 1)] & LURD) +
(ndir[nr_offset(y + 1, x - 1)] & LURD) +
(ndir[nr_offset(y + 1, x + 1)] & LURD);
int nh = (ndir[nr_offset(y - 1, x)] & RULD) +
(ndir[nr_offset(y + 1, x)] & RULD) +
(ndir[nr_offset(y, x - 1)] & RULD) +
(ndir[nr_offset(y, x + 1)] & RULD) +
(ndir[nr_offset(y - 1, x - 1)] & RULD) +
(ndir[nr_offset(y - 1, x + 1)] & RULD) +
(ndir[nr_offset(y + 1, x - 1)] & RULD) +
(ndir[nr_offset(y + 1, x + 1)] & RULD);
nv /= LURD;
nh /= RULD;
if ((ndir[nr_offset(y, x)] & LURD) && nh > 7)
{
ndir[nr_offset(y, x)] &= ~LURD;
ndir[nr_offset(y, x)] |= RULD;
}
if ((ndir[nr_offset(y, x)] & RULD) && nv > 7)
{
ndir[nr_offset(y, x)] &= ~RULD;
ndir[nr_offset(y, x)] |= LURD;
}
}
}
/*
* вычисление недостающих зелёных точек.
*/
void DHT::make_greens()
{
#if defined(LIBRAW_USE_OPENMP)
#pragma omp parallel for schedule(guided)
#endif
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
{
make_gline(i);
}
}
void DHT::make_gline(int i)
{
int iwidth = libraw.imgdata.sizes.iwidth;
int js = libraw.COLOR(i, 0) & 1;
int kc = libraw.COLOR(i, js);
/*
* js -- начальная х-координата, которая попадает мимо известного зелёного
* kc -- известный цвет в точке интерполирования
*/
for (int j = js; j < iwidth; j += 2)
{
int x = j + nr_leftmargin;
int y = i + nr_topmargin;
int dx, dy, dx2, dy2;
float h1, h2;
if (ndir[nr_offset(y, x)] & VER)
{
dx = dx2 = 0;
dy = -1;
dy2 = 1;
h1 = 2 * nraw[nr_offset(y - 1, x)][1] /
(nraw[nr_offset(y - 2, x)][kc] + nraw[nr_offset(y, x)][kc]);
h2 = 2 * nraw[nr_offset(y + 1, x)][1] /
(nraw[nr_offset(y + 2, x)][kc] + nraw[nr_offset(y, x)][kc]);
}
else
{
dy = dy2 = 0;
dx = 1;
dx2 = -1;
h1 = 2 * nraw[nr_offset(y, x + 1)][1] /
(nraw[nr_offset(y, x + 2)][kc] + nraw[nr_offset(y, x)][kc]);
h2 = 2 * nraw[nr_offset(y, x - 1)][1] /
(nraw[nr_offset(y, x - 2)][kc] + nraw[nr_offset(y, x)][kc]);
}
float b1 = 1 / calc_dist(nraw[nr_offset(y, x)][kc],
nraw[nr_offset(y + dy * 2, x + dx * 2)][kc]);
float b2 = 1 / calc_dist(nraw[nr_offset(y, x)][kc],
nraw[nr_offset(y + dy2 * 2, x + dx2 * 2)][kc]);
b1 *= b1;
b2 *= b2;
float eg = nraw[nr_offset(y, x)][kc] * (b1 * h1 + b2 * h2) / (b1 + b2);
float min, max;
min = MIN(nraw[nr_offset(y + dy, x + dx)][1],
nraw[nr_offset(y + dy2, x + dx2)][1]);
max = MAX(nraw[nr_offset(y + dy, x + dx)][1],
nraw[nr_offset(y + dy2, x + dx2)][1]);
min /= 1.2f;
max *= 1.2f;
if (eg < min)
eg = scale_under(eg, min);
else if (eg > max)
eg = scale_over(eg, max);
if (eg > channel_maximum[1])
eg = channel_maximum[1];
else if (eg < channel_minimum[1])
eg = channel_minimum[1];
nraw[nr_offset(y, x)][1] = eg;
}
}
/*
* отладочная функция
*/
void DHT::illustrate_dirs()
{
#if defined(LIBRAW_USE_OPENMP)
#pragma omp parallel for schedule(guided)
#endif
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
{
illustrate_dline(i);
}
}
void DHT::illustrate_dline(int i)
{
int iwidth = libraw.imgdata.sizes.iwidth;
for (int j = 0; j < iwidth; j++)
{
int x = j + nr_leftmargin;
int y = i + nr_topmargin;
nraw[nr_offset(y, x)][0] = nraw[nr_offset(y, x)][1] =
nraw[nr_offset(y, x)][2] = 0.5;
int l = ndir[nr_offset(y, x)] & 8;
// l >>= 3; // WTF?
l = 1;
if (ndir[nr_offset(y, x)] & HOT)
nraw[nr_offset(y, x)][0] =
l * channel_maximum[0] / 4.f + channel_maximum[0] / 4.f;
else
nraw[nr_offset(y, x)][2] =
l * channel_maximum[2] / 4.f + channel_maximum[2] / 4.f;
}
}
/*
* интерполяция красных и синих.
*
* сначала интерполируются недостающие цвета, по диагональным направлениям от
* которых находятся известные, затем ситуация сводится к тому как
* интерполировались зелёные.
*/
void DHT::make_rbdiag(int i)
{
int iwidth = libraw.imgdata.sizes.iwidth;
int js = libraw.COLOR(i, 0) & 1;
int uc = libraw.COLOR(i, js);
int cl = uc ^ 2;
/*
* js -- начальная х-координата, которая попадает на уже интерполированный
* зелёный al -- известный цвет (кроме зелёного) в точке интерполирования cl
* -- неизвестный цвет
*/
for (int j = js; j < iwidth; j += 2)
{
int x = j + nr_leftmargin;
int y = i + nr_topmargin;
int dx, dy, dx2, dy2;
if (ndir[nr_offset(y, x)] & LURD)
{
dx = -1;
dx2 = 1;
dy = -1;
dy2 = 1;
}
else
{
dx = -1;
dx2 = 1;
dy = 1;
dy2 = -1;
}
float g1 = 1 / calc_dist(nraw[nr_offset(y, x)][1],
nraw[nr_offset(y + dy, x + dx)][1]);
float g2 = 1 / calc_dist(nraw[nr_offset(y, x)][1],
nraw[nr_offset(y + dy2, x + dx2)][1]);
g1 *= g1 * g1;
g2 *= g2 * g2;
float eg;
eg = nraw[nr_offset(y, x)][1] *
(g1 * nraw[nr_offset(y + dy, x + dx)][cl] /
nraw[nr_offset(y + dy, x + dx)][1] +
g2 * nraw[nr_offset(y + dy2, x + dx2)][cl] /
nraw[nr_offset(y + dy2, x + dx2)][1]) /
(g1 + g2);
float min, max;
min = MIN(nraw[nr_offset(y + dy, x + dx)][cl],
nraw[nr_offset(y + dy2, x + dx2)][cl]);
max = MAX(nraw[nr_offset(y + dy, x + dx)][cl],
nraw[nr_offset(y + dy2, x + dx2)][cl]);
min /= 1.2f;
max *= 1.2f;
if (eg < min)
eg = scale_under(eg, min);
else if (eg > max)
eg = scale_over(eg, max);
if (eg > channel_maximum[cl])
eg = channel_maximum[cl];
else if (eg < channel_minimum[cl])
eg = channel_minimum[cl];
nraw[nr_offset(y, x)][cl] = eg;
}
}
/*
* интерполяция красных и синих в точках где был известен только зелёный,
* направления горизонтальные или вертикальные
*/
void DHT::make_rbhv(int i)
{
int iwidth = libraw.imgdata.sizes.iwidth;
int js = (libraw.COLOR(i, 0) & 1) ^ 1;
for (int j = js; j < iwidth; j += 2)
{
int x = j + nr_leftmargin;
int y = i + nr_topmargin;
/*
* поскольку сверху-снизу и справа-слева уже есть все необходимые красные и
* синие, то можно выбрать наилучшее направление исходя из информации по
* обоим цветам.
*/
int dx, dy, dx2, dy2;
if (ndir[nr_offset(y, x)] & VER)
{
dx = dx2 = 0;
dy = -1;
dy2 = 1;
}
else
{
dy = dy2 = 0;
dx = 1;
dx2 = -1;
}
float g1 = 1 / calc_dist(nraw[nr_offset(y, x)][1],
nraw[nr_offset(y + dy, x + dx)][1]);
float g2 = 1 / calc_dist(nraw[nr_offset(y, x)][1],
nraw[nr_offset(y + dy2, x + dx2)][1]);
g1 *= g1;
g2 *= g2;
float eg_r, eg_b;
eg_r = nraw[nr_offset(y, x)][1] *
(g1 * nraw[nr_offset(y + dy, x + dx)][0] /
nraw[nr_offset(y + dy, x + dx)][1] +
g2 * nraw[nr_offset(y + dy2, x + dx2)][0] /
nraw[nr_offset(y + dy2, x + dx2)][1]) /
(g1 + g2);
eg_b = nraw[nr_offset(y, x)][1] *
(g1 * nraw[nr_offset(y + dy, x + dx)][2] /
nraw[nr_offset(y + dy, x + dx)][1] +
g2 * nraw[nr_offset(y + dy2, x + dx2)][2] /
nraw[nr_offset(y + dy2, x + dx2)][1]) /
(g1 + g2);
float min_r, max_r;
min_r = MIN(nraw[nr_offset(y + dy, x + dx)][0],
nraw[nr_offset(y + dy2, x + dx2)][0]);
max_r = MAX(nraw[nr_offset(y + dy, x + dx)][0],
nraw[nr_offset(y + dy2, x + dx2)][0]);
float min_b, max_b;
min_b = MIN(nraw[nr_offset(y + dy, x + dx)][2],
nraw[nr_offset(y + dy2, x + dx2)][2]);
max_b = MAX(nraw[nr_offset(y + dy, x + dx)][2],
nraw[nr_offset(y + dy2, x + dx2)][2]);
min_r /= 1.2f;
max_r *= 1.2f;
min_b /= 1.2f;
max_b *= 1.2f;
if (eg_r < min_r)
eg_r = scale_under(eg_r, min_r);
else if (eg_r > max_r)
eg_r = scale_over(eg_r, max_r);
if (eg_b < min_b)
eg_b = scale_under(eg_b, min_b);
else if (eg_b > max_b)
eg_b = scale_over(eg_b, max_b);
if (eg_r > channel_maximum[0])
eg_r = channel_maximum[0];
else if (eg_r < channel_minimum[0])
eg_r = channel_minimum[0];
if (eg_b > channel_maximum[2])
eg_b = channel_maximum[2];
else if (eg_b < channel_minimum[2])
eg_b = channel_minimum[2];
nraw[nr_offset(y, x)][0] = eg_r;
nraw[nr_offset(y, x)][2] = eg_b;
}
}
void DHT::make_rb()
{
#if defined(LIBRAW_USE_OPENMP)
#pragma omp barrier
#pragma omp parallel for schedule(guided)
#endif
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
{
make_rbdiag(i);
}
#if defined(LIBRAW_USE_OPENMP)
#pragma omp barrier
#pragma omp parallel for schedule(guided)
#endif
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
{
make_rbhv(i);
}
}
/*
* перенос изображения в выходной массив
*/
void DHT::copy_to_image()
{
int iwidth = libraw.imgdata.sizes.iwidth;
#if defined(LIBRAW_USE_OPENMP)
#ifdef _MSC_VER
#pragma omp parallel for
#else
#pragma omp parallel for schedule(guided) collapse(2)
#endif
#endif
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
{
for (int j = 0; j < iwidth; ++j)
{
libraw.imgdata.image[i * iwidth + j][0] =
(unsigned short)(nraw[nr_offset(i + nr_topmargin, j + nr_leftmargin)]
[0]);
libraw.imgdata.image[i * iwidth + j][2] =
(unsigned short)(nraw[nr_offset(i + nr_topmargin, j + nr_leftmargin)]
[2]);
libraw.imgdata.image[i * iwidth + j][1] =
libraw.imgdata.image[i * iwidth + j][3] =
(unsigned short)(nraw[nr_offset(i + nr_topmargin,
j + nr_leftmargin)][1]);
}
}
}
DHT::~DHT()
{
free(nraw);
free(ndir);
}
void LibRaw::dht_interpolate()
{
if (imgdata.idata.filters != 0x16161616
&& imgdata.idata.filters != 0x61616161
&& imgdata.idata.filters != 0x49494949
&& imgdata.idata.filters != 0x94949494
)
{
ahd_interpolate();
return;
}
DHT dht(*this);
dht.hide_hots();
dht.make_hv_dirs();
// dht.illustrate_dirs();
dht.make_greens();
dht.make_diag_dirs();
// dht.illustrate_dirs();
dht.make_rb();
dht.restore_hots();
dht.copy_to_image();
}