Files
rawTherapee/rtengine/libraw/src/demosaic/ahd_demosaic.cpp
2024-04-13 23:05:55 -07:00

356 lines
10 KiB
C++

/* -*- C++ -*-
* Copyright 2019-2024 LibRaw LLC (info@libraw.org)
*
LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
LibRaw do not use RESTRICTED code from dcraw.c
LibRaw is free software; you can redistribute it and/or modify
it under the terms of the 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 "../../internal/dcraw_defs.h"
/*
Adaptive Homogeneity-Directed interpolation is based on
the work of Keigo Hirakawa, Thomas Parks, and Paul Lee.
*/
void LibRaw::cielab(ushort rgb[3], short lab[3])
{
int c, i, j, k;
float r, xyz[3];
#ifdef LIBRAW_NOTHREADS
static float cbrt[0x10000], xyz_cam[3][4];
#else
#define cbrt tls->ahd_data.cbrt
#define xyz_cam tls->ahd_data.xyz_cam
#endif
if (!rgb)
{
#ifndef LIBRAW_NOTHREADS
if (cbrt[0] < -1.0f)
#endif
for (i = 0; i < 0x10000; i++)
{
r = i / 65535.0;
cbrt[i] =
r > 0.008856 ? pow(r, 1.f / 3.0f) : 7.787f * r + 16.f / 116.0f;
}
for (i = 0; i < 3; i++)
for (j = 0; j < colors; j++)
for (xyz_cam[i][j] = k = 0; k < 3; k++)
xyz_cam[i][j] += LibRaw_constants::xyz_rgb[i][k] * rgb_cam[k][j] /
LibRaw_constants::d65_white[i];
return;
}
xyz[0] = xyz[1] = xyz[2] = 0.5;
FORCC
{
xyz[0] += xyz_cam[0][c] * rgb[c];
xyz[1] += xyz_cam[1][c] * rgb[c];
xyz[2] += xyz_cam[2][c] * rgb[c];
}
xyz[0] = cbrt[CLIP((int)xyz[0])];
xyz[1] = cbrt[CLIP((int)xyz[1])];
xyz[2] = cbrt[CLIP((int)xyz[2])];
lab[0] = 64 * (116 * xyz[1] - 16);
lab[1] = 64 * 500 * (xyz[0] - xyz[1]);
lab[2] = 64 * 200 * (xyz[1] - xyz[2]);
#ifndef LIBRAW_NOTHREADS
#undef cbrt
#undef xyz_cam
#endif
}
void LibRaw::ahd_interpolate_green_h_and_v(
int top, int left, ushort (*out_rgb)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3])
{
int row, col;
int c, val;
ushort(*pix)[4];
const int rowlimit = MIN(top + LIBRAW_AHD_TILE, height - 2);
const int collimit = MIN(left + LIBRAW_AHD_TILE, width - 2);
for (row = top; row < rowlimit; row++)
{
col = left + (FC(row, left) & 1);
for (c = FC(row, col); col < collimit; col += 2)
{
pix = image + row * width + col;
val =
((pix[-1][1] + pix[0][c] + pix[1][1]) * 2 - pix[-2][c] - pix[2][c]) >>
2;
out_rgb[0][row - top][col - left][1] = ULIM(val, pix[-1][1], pix[1][1]);
val = ((pix[-width][1] + pix[0][c] + pix[width][1]) * 2 -
pix[-2 * width][c] - pix[2 * width][c]) >>
2;
out_rgb[1][row - top][col - left][1] =
ULIM(val, pix[-width][1], pix[width][1]);
}
}
}
void LibRaw::ahd_interpolate_r_and_b_in_rgb_and_convert_to_cielab(
int top, int left, ushort (*inout_rgb)[LIBRAW_AHD_TILE][3],
short (*out_lab)[LIBRAW_AHD_TILE][3])
{
unsigned row, col;
int c, val;
ushort(*pix)[4];
ushort(*rix)[3];
short(*lix)[3];
const unsigned num_pix_per_row = 4 * width;
const unsigned rowlimit = MIN(top + LIBRAW_AHD_TILE - 1, height - 3);
const unsigned collimit = MIN(left + LIBRAW_AHD_TILE - 1, width - 3);
ushort *pix_above;
ushort *pix_below;
int t1, t2;
for (row = top + 1; row < rowlimit; row++)
{
pix = image + row * width + left;
rix = &inout_rgb[row - top][0];
lix = &out_lab[row - top][0];
for (col = left + 1; col < collimit; col++)
{
pix++;
pix_above = &pix[0][0] - num_pix_per_row;
pix_below = &pix[0][0] + num_pix_per_row;
rix++;
lix++;
c = 2 - FC(row, col);
if (c == 1)
{
c = FC(row + 1, col);
t1 = 2 - c;
val = pix[0][1] +
((pix[-1][t1] + pix[1][t1] - rix[-1][1] - rix[1][1]) >> 1);
rix[0][t1] = CLIP(val);
val =
pix[0][1] + ((pix_above[c] + pix_below[c] -
rix[-LIBRAW_AHD_TILE][1] - rix[LIBRAW_AHD_TILE][1]) >>
1);
}
else
{
t1 = -4 + c; /* -4+c: pixel of color c to the left */
t2 = 4 + c; /* 4+c: pixel of color c to the right */
val = rix[0][1] +
((pix_above[t1] + pix_above[t2] + pix_below[t1] + pix_below[t2] -
rix[-LIBRAW_AHD_TILE - 1][1] - rix[-LIBRAW_AHD_TILE + 1][1] -
rix[+LIBRAW_AHD_TILE - 1][1] - rix[+LIBRAW_AHD_TILE + 1][1] +
1) >>
2);
}
rix[0][c] = CLIP(val);
c = FC(row, col);
rix[0][c] = pix[0][c];
cielab(rix[0], lix[0]);
}
}
}
void LibRaw::ahd_interpolate_r_and_b_and_convert_to_cielab(
int top, int left, ushort (*inout_rgb)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3],
short (*out_lab)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3])
{
int direction;
for (direction = 0; direction < 2; direction++)
{
ahd_interpolate_r_and_b_in_rgb_and_convert_to_cielab(
top, left, inout_rgb[direction], out_lab[direction]);
}
}
void LibRaw::ahd_interpolate_build_homogeneity_map(
int top, int left, short (*lab)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3],
char (*out_homogeneity_map)[LIBRAW_AHD_TILE][2])
{
int row, col;
int tr;
int direction;
int i;
short(*lix)[3];
short(*lixs[2])[3];
short *adjacent_lix;
unsigned ldiff[2][4], abdiff[2][4], leps, abeps;
static const int dir[4] = {-1, 1, -LIBRAW_AHD_TILE, LIBRAW_AHD_TILE};
const int rowlimit = MIN(top + LIBRAW_AHD_TILE - 2, height - 4);
const int collimit = MIN(left + LIBRAW_AHD_TILE - 2, width - 4);
int homogeneity;
char(*homogeneity_map_p)[2];
memset(out_homogeneity_map, 0, 2 * LIBRAW_AHD_TILE * LIBRAW_AHD_TILE);
for (row = top + 2; row < rowlimit; row++)
{
tr = row - top;
homogeneity_map_p = &out_homogeneity_map[tr][1];
for (direction = 0; direction < 2; direction++)
{
lixs[direction] = &lab[direction][tr][1];
}
for (col = left + 2; col < collimit; col++)
{
homogeneity_map_p++;
for (direction = 0; direction < 2; direction++)
{
lix = ++lixs[direction];
for (i = 0; i < 4; i++)
{
adjacent_lix = lix[dir[i]];
ldiff[direction][i] = ABS(lix[0][0] - adjacent_lix[0]);
abdiff[direction][i] = SQR(lix[0][1] - adjacent_lix[1]) +
SQR(lix[0][2] - adjacent_lix[2]);
}
}
leps = MIN(MAX(ldiff[0][0], ldiff[0][1]), MAX(ldiff[1][2], ldiff[1][3]));
abeps =
MIN(MAX(abdiff[0][0], abdiff[0][1]), MAX(abdiff[1][2], abdiff[1][3]));
for (direction = 0; direction < 2; direction++)
{
homogeneity = 0;
for (i = 0; i < 4; i++)
{
if (ldiff[direction][i] <= leps && abdiff[direction][i] <= abeps)
{
homogeneity++;
}
}
homogeneity_map_p[0][direction] = homogeneity;
}
}
}
}
void LibRaw::ahd_interpolate_combine_homogeneous_pixels(
int top, int left, ushort (*rgb)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3],
char (*homogeneity_map)[LIBRAW_AHD_TILE][2])
{
int row, col;
int tr, tc;
int i, j;
int direction;
int hm[2];
int c;
const int rowlimit = MIN(top + LIBRAW_AHD_TILE - 3, height - 5);
const int collimit = MIN(left + LIBRAW_AHD_TILE - 3, width - 5);
ushort(*pix)[4];
ushort(*rix[2])[3];
for (row = top + 3; row < rowlimit; row++)
{
tr = row - top;
pix = &image[row * width + left + 2];
for (direction = 0; direction < 2; direction++)
{
rix[direction] = &rgb[direction][tr][2];
}
for (col = left + 3; col < collimit; col++)
{
tc = col - left;
pix++;
for (direction = 0; direction < 2; direction++)
{
rix[direction]++;
}
for (direction = 0; direction < 2; direction++)
{
hm[direction] = 0;
for (i = tr - 1; i <= tr + 1; i++)
{
for (j = tc - 1; j <= tc + 1; j++)
{
hm[direction] += homogeneity_map[i][j][direction];
}
}
}
if (hm[0] != hm[1])
{
memcpy(pix[0], rix[hm[1] > hm[0]][0], 3 * sizeof(ushort));
}
else
{
FORC3 { pix[0][c] = (rix[0][0][c] + rix[1][0][c]) >> 1; }
}
}
}
}
void LibRaw::ahd_interpolate()
{
int terminate_flag = 0;
cielab(0, 0);
border_interpolate(5);
#ifdef LIBRAW_USE_OPENMP
int buffer_count = omp_get_max_threads();
#else
int buffer_count = 1;
#endif
size_t buffer_size = 26 * LIBRAW_AHD_TILE * LIBRAW_AHD_TILE; /* 1664 kB */
char** buffers = malloc_omp_buffers(buffer_count, buffer_size);
#ifdef LIBRAW_USE_OPENMP
#pragma omp parallel for schedule(dynamic) default(none) shared(terminate_flag) firstprivate(buffers)
#endif
for (int top = 2; top < height - 5; top += LIBRAW_AHD_TILE - 6)
{
#ifdef LIBRAW_USE_OPENMP
if (0 == omp_get_thread_num())
#endif
if (callbacks.progress_cb)
{
int rr = (*callbacks.progress_cb)(callbacks.progresscb_data,
LIBRAW_PROGRESS_INTERPOLATE,
top - 2, height - 7);
if (rr)
terminate_flag = 1;
}
#if defined(LIBRAW_USE_OPENMP)
char* buffer = buffers[omp_get_thread_num()];
#else
char* buffer = buffers[0];
#endif
ushort(*rgb)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3];
short(*lab)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3];
char(*homo)[LIBRAW_AHD_TILE][2];
rgb = (ushort(*)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3])buffer;
lab = (short(*)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3])(
buffer + 12 * LIBRAW_AHD_TILE * LIBRAW_AHD_TILE);
homo = (char(*)[LIBRAW_AHD_TILE][2])(buffer + 24 * LIBRAW_AHD_TILE *
LIBRAW_AHD_TILE);
for (int left = 2; !terminate_flag && (left < width - 5);
left += LIBRAW_AHD_TILE - 6)
{
ahd_interpolate_green_h_and_v(top, left, rgb);
ahd_interpolate_r_and_b_and_convert_to_cielab(top, left, rgb, lab);
ahd_interpolate_build_homogeneity_map(top, left, lab, homo);
ahd_interpolate_combine_homogeneous_pixels(top, left, rgb, homo);
}
}
free_omp_buffers(buffers, buffer_count);
if (terminate_flag)
throw LIBRAW_EXCEPTION_CANCELLED_BY_CALLBACK;
}