/* -*- 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
*/
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 hru = nraw[nr_offset(y - 1, x + 1)][1] / nraw[nr_offset(y - 1, x + 1)][kc];
float hld = 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 * .4;
float o = ec - base;
return base + sqrt(s * (o + s)) - s;
}
static inline float scale_under(float ec, float base) {
float s = base * .6;
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 WIN32
#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);
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 > 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.2;
max *= 1.2;
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 + channel_maximum[0] / 4;
else
nraw[nr_offset(y, x)][2] = l * channel_maximum[2] / 4 + channel_maximum[2] / 4;
}
}
/*
* интерполяция красных и синих.
*
* сначала интерполируются недостающие цвета, по диагональным направлениям от которых находятся известные,
* затем ситуация сводится к тому как интерполировались зелёные.
*/
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.2;
max *= 1.2;
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;
float h1, h2;
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.2;
max_r *= 1.2;
min_b /= 1.2;
max_b *= 1.2;
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 WIN32
#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() {
#ifdef DCRAW_VERBOSE
printf("DHT interpolating\n");
#endif
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();
}