356 lines
10 KiB
C++
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;
|
|
}
|