Additional cleanups for dcp.* (#3343)

This commit is contained in:
Flössie 2016-06-12 12:07:15 +02:00
parent b8749f8484
commit 97dae796c6
3 changed files with 295 additions and 282 deletions

View File

@ -17,6 +17,7 @@
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>. * along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <iostream>
#include <cstring> #include <cstring>
#include "dcp.h" #include "dcp.h"
@ -36,104 +37,92 @@ namespace
// This sRGB gamma is taken from DNG reference code, with the added linear extension past 1.0, as we run clipless here // This sRGB gamma is taken from DNG reference code, with the added linear extension past 1.0, as we run clipless here
void invert3x3(const DCPProfile::Matrix& a, DCPProfile::Matrix& b) DCPProfile::Matrix invert3x3(const DCPProfile::Matrix& a)
{ {
const double& a00 = a[0][0]; const double res00 = a[1][1] * a[2][2] - a[2][1] * a[1][2];
const double& a01 = a[0][1]; const double res10 = a[2][0] * a[1][2] - a[1][0] * a[2][2];
const double& a02 = a[0][2]; const double res20 = a[1][0] * a[2][1] - a[2][0] * a[1][1];
const double& a10 = a[1][0];
const double& a11 = a[1][1];
const double& a12 = a[1][2];
const double& a20 = a[2][0];
const double& a21 = a[2][1];
const double& a22 = a[2][2];
double temp[3][3]; const double det = a[0][0] * res00 + a[0][1] * res10 + a[0][2] * res20;
temp[0][0] = a11 * a22 - a21 * a12; if (std::fabs(det) < 1.0e-10) {
temp[0][1] = a21 * a02 - a01 * a22; std::cerr << "DCP matrix cannot be inverted! Expect weird output." << std::endl;
temp[0][2] = a01 * a12 - a11 * a02; return a;
temp[1][0] = a20 * a12 - a10 * a22;
temp[1][1] = a00 * a22 - a20 * a02;
temp[1][2] = a10 * a02 - a00 * a12;
temp[2][0] = a10 * a21 - a20 * a11;
temp[2][1] = a20 * a01 - a00 * a21;
temp[2][2] = a00 * a11 - a10 * a01;
const double det = a00 * temp[0][0] + a01 * temp[1][0] + a02 * temp[2][0];
if (fabs(det) < 1.0e-10) {
abort(); // Can't be inverted, we shouldn't be dealing with such matrices
} }
for (int j = 0; j < 3; ++j) { DCPProfile::Matrix res;
for (int k = 0; k < 3; ++k) {
b[j][k] = temp[j][k] / det; res[0][0] = res00 / det;
} res[0][1] = (a[2][1] * a[0][2] - a[0][1] * a[2][2]) / det;
} res[0][2] = (a[0][1] * a[1][2] - a[1][1] * a[0][2]) / det;
res[1][0] = res10 / det;
res[1][1] = (a[0][0] * a[2][2] - a[2][0] * a[0][2]) / det;
res[1][2] = (a[1][0] * a[0][2] - a[0][0] * a[1][2]) / det;
res[2][0] = res20 / det;
res[2][1] = (a[2][0] * a[0][1] - a[0][0] * a[2][1]) / det;
res[2][2] = (a[0][0] * a[1][1] - a[1][0] * a[0][1]) / det;
return res;
} }
void multiply3x3(const DCPProfile::Matrix& a, const DCPProfile::Matrix& b, DCPProfile::Matrix& c) DCPProfile::Matrix multiply3x3(const DCPProfile::Matrix& a, const DCPProfile::Matrix& b)
{ {
// Use temp to support having output same as input DCPProfile::Matrix res;
DCPProfile::Matrix m;
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) { for (int j = 0; j < 3; ++j) {
m[i][j] = 0; res[i][j] = 0;
for (int k = 0; k < 3; ++k) { for (int k = 0; k < 3; ++k) {
m[i][j] += a[i][k] * b[k][j]; res[i][j] += a[i][k] * b[k][j];
} }
} }
} }
c = m; return res;
} }
void multiply3x3_v3(const DCPProfile::Matrix& a, const DCPProfile::Triple& b, DCPProfile::Triple& c) DCPProfile::Triple multiply3x3_v3(const DCPProfile::Matrix& a, const DCPProfile::Triple& b)
{ {
// Use temp to support having output same as input DCPProfile::Triple res = {};
DCPProfile::Triple m = {};
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) { for (int j = 0; j < 3; ++j) {
m[i] += a[i][j] * b[j]; res[i] += a[i][j] * b[j];
} }
} }
c = m; return res;
} }
void mix3x3(const DCPProfile::Matrix& a, double mul_a, const DCPProfile::Matrix& b, double mul_b, DCPProfile::Matrix& c) DCPProfile::Matrix mix3x3(const DCPProfile::Matrix& a, double mul_a, const DCPProfile::Matrix& b, double mul_b)
{ {
DCPProfile::Matrix m; DCPProfile::Matrix res;
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) { for (int j = 0; j < 3; ++j) {
m[i][j] = a[i][j] * mul_a + b[i][j] * mul_b; res[i][j] = a[i][j] * mul_a + b[i][j] * mul_b;
} }
} }
c = m; return res;
} }
void mapWhiteMatrix(const DCPProfile::Triple& white1, const DCPProfile::Triple& white2, DCPProfile::Matrix& b) DCPProfile::Matrix mapWhiteMatrix(const DCPProfile::Triple& white1, const DCPProfile::Triple& white2)
{ {
// Code adapted from dng_color_spec::MapWhiteMatrix // Code adapted from dng_color_spec::MapWhiteMatrix
// Use the linearized Bradford adaptation matrix // Use the linearized Bradford adaptation matrix
const DCPProfile::Matrix mb = {{ const DCPProfile::Matrix mb = {
{
{ 0.8951, 0.2664, -0.1614 }, { 0.8951, 0.2664, -0.1614 },
{ -0.7502, 1.7135, 0.0367 }, { -0.7502, 1.7135, 0.0367 },
{ 0.0389, -0.0685, 1.0296 } { 0.0389, -0.0685, 1.0296 }
} }
}; };
DCPProfile::Triple w1; DCPProfile::Triple w1 = multiply3x3_v3(mb, white1);
multiply3x3_v3(mb, white1, w1); DCPProfile::Triple w2 = multiply3x3_v3(mb, white2);
DCPProfile::Triple w2;
multiply3x3_v3(mb, white2, w2);
// Negative white coordinates are kind of meaningless. // Negative white coordinates are kind of meaningless.
w1[0] = std::max(w1[0], 0.0); w1[0] = std::max(w1[0], 0.0);
@ -149,44 +138,46 @@ void mapWhiteMatrix(const DCPProfile::Triple& white1, const DCPProfile::Triple&
a[1][1] = std::max(0.1, std::min(w1[1] > 0.0 ? w2[1] / w1[1] : 10.0, 10.0)); a[1][1] = std::max(0.1, std::min(w1[1] > 0.0 ? w2[1] / w1[1] : 10.0, 10.0));
a[2][2] = std::max(0.1, std::min(w1[2] > 0.0 ? w2[2] / w1[2] : 10.0, 10.0)); a[2][2] = std::max(0.1, std::min(w1[2] > 0.0 ? w2[2] / w1[2] : 10.0, 10.0));
DCPProfile::Matrix temp; return multiply3x3(multiply3x3(invert3x3(mb), a), mb);
invert3x3(mb, temp);
multiply3x3(temp, a, temp);
multiply3x3(temp, mb, b);
} }
void xyzToXy(const DCPProfile::Triple& xyz, double xy[2]) std::array<double, 2> xyzToXy(const DCPProfile::Triple& xyz)
{ {
const double total = xyz[0] + xyz[1] + xyz[2]; const double total = xyz[0] + xyz[1] + xyz[2];
if (total > 0.0) { return
xy[0] = xyz[0] / total; total > 0.0
xy[1] = xyz[1] / total; ? std::array<double, 2>{
} else { xyz[0] / total,
xy[0] = 0.3457; xyz[1] / total
xy[1] = 0.3585; }
} : std::array<double, 2>{
0.3457,
0.3585
};
} }
void xyToXyz(const double xy[2], DCPProfile::Triple& xyz) DCPProfile::Triple xyToXyz(std::array<double, 2> xy)
{ {
double temp[2] = {xy[0], xy[1]};
// Restrict xy coord to someplace inside the range of real xy coordinates. // Restrict xy coord to someplace inside the range of real xy coordinates.
// This prevents math from doing strange things when users specify // This prevents math from doing strange things when users specify
// extreme temperature/tint coordinates. // extreme temperature/tint coordinates.
temp[0] = std::max(0.000001, std::min(temp[0], 0.999999)); xy[0] = std::max(0.000001, std::min(xy[0], 0.999999));
temp[1] = std::max(0.000001, std::min(temp[1], 0.999999)); xy[1] = std::max(0.000001, std::min(xy[1], 0.999999));
if (temp[0] + temp[1] > 0.999999) { const double sum = xy[0] + xy[1];
double scale = 0.999999 / (temp[0] + temp[1]);
temp[0] *= scale; if (sum > 0.999999) {
temp[1] *= scale; const double scale = 0.999999 / sum;
xy[0] *= scale;
xy[1] *= scale;
} }
xyz[0] = temp[0] / temp[1]; return {
xyz[1] = 1.0; xy[0] / xy[1],
xyz[2] = (1.0 - temp[0] - temp[1]) / temp[1]; 1.0,
(1.0 - xy[0] - xy[1]) / xy[1]
};
} }
double calibrationIlluminantToTemperature(int light) double calibrationIlluminantToTemperature(int light)
@ -277,7 +268,7 @@ double calibrationIlluminantToTemperature(int light)
} }
} }
void xyCoordToTemperature(const double white_xy[2], double* temp, double* tint) double xyCoordToTemperature(const std::array<double, 2>& white_xy)
{ {
struct Ruvt { struct Ruvt {
double r; double r;
@ -322,8 +313,7 @@ void xyCoordToTemperature(const double white_xy[2], double* temp, double* tint)
constexpr double tint_scale = -3000.0; constexpr double tint_scale = -3000.0;
double temperature = 0; double res = 0;
double computed_tint = 0;
// Convert to uv space. // Convert to uv space.
double u = 2.0 * white_xy[0] / (1.5 - white_xy[0] + 6.0 * white_xy[1]); double u = 2.0 * white_xy[0] / (1.5 - white_xy[0] + 6.0 * white_xy[1]);
@ -367,7 +357,7 @@ void xyCoordToTemperature(const double white_xy[2], double* temp, double* tint)
} }
// Interpolate the temperature. // Interpolate the temperature.
temperature = 1.0e6 / (temp_table[index - 1].r * f + temp_table[index].r * (1.0 - f)); res = 1.0e6 / (temp_table[index - 1].r * f + temp_table[index].r * (1.0 - f));
// Find delta from black body point to test coordinate. // Find delta from black body point to test coordinate.
uu = u - (temp_table [index - 1].u * f + temp_table [index].u * (1.0 - f)); uu = u - (temp_table [index - 1].u * f + temp_table [index].u * (1.0 - f));
@ -378,9 +368,6 @@ void xyCoordToTemperature(const double white_xy[2], double* temp, double* tint)
len = sqrt (du * du + dv * dv); len = sqrt (du * du + dv * dv);
du /= len; du /= len;
dv /= len; dv /= len;
// Find distance along slope.
computed_tint = (uu * du + vv * dv) * tint_scale;
break; break;
} }
@ -390,13 +377,7 @@ void xyCoordToTemperature(const double white_xy[2], double* temp, double* tint)
last_dv = dv; last_dv = dv;
} }
if (temp != nullptr) { return res;
*temp = temperature;
}
if (tint != nullptr) {
*tint = computed_tint;
}
} }
} }
@ -758,9 +739,8 @@ DCPProfile::DCPProfile(const Glib::ustring& filename) :
tag = tagDir->getTag(toUnderlying(TagKey::COLOR_MATRIX_1)); tag = tagDir->getTag(toUnderlying(TagKey::COLOR_MATRIX_1));
if (!tag) { if (!tag) {
// FIXME: better error handling std::cerr << "DCP '" << filename << "' is missing 'ColorMatrix1'. Skipped." << std::endl;
fprintf(stderr, "Bad DCP, no ColorMatrix1\n"); return;
abort();
} }
has_color_matrix_1 = true; has_color_matrix_1 = true;
@ -963,6 +943,11 @@ DCPProfile::~DCPProfile()
{ {
} }
DCPProfile::operator bool() const
{
return has_color_matrix_1;
}
bool DCPProfile::getHasToneCurve() const bool DCPProfile::getHasToneCurve() const
{ {
return has_tone_curve; return has_tone_curve;
@ -1007,8 +992,7 @@ void DCPProfile::apply(
BENCHFUN BENCHFUN
const TMatrix work_matrix = iccStore->workingSpaceInverseMatrix(working_space); const TMatrix work_matrix = iccStore->workingSpaceInverseMatrix(working_space);
Matrix xyz_cam; // Camera RGB to XYZ D50 matrix const Matrix xyz_cam = makeXyzCam(white_balance, pre_mul, cam_wb_matrix, preferred_illuminant); // Camera RGB to XYZ D50 matrix
makeXyzCam(white_balance, pre_mul, cam_wb_matrix, preferred_illuminant, xyz_cam);
const std::vector<HsbModify> delta_base = makeHueSatMap(white_balance, preferred_illuminant); const std::vector<HsbModify> delta_base = makeHueSatMap(white_balance, preferred_illuminant);
@ -1232,7 +1216,7 @@ void DCPProfile::step2ApplyTile(float* rc, float* gc, float* bc, int width, int
} }
} }
void DCPProfile::findXyztoCamera(const double white_xy[2], int preferred_illuminant, Matrix& xyz_to_camera) const DCPProfile::Matrix DCPProfile::findXyztoCamera(const std::array<double, 2>& white_xy, int preferred_illuminant) const
{ {
bool has_col_1 = has_color_matrix_1; bool has_col_1 = has_color_matrix_1;
bool has_col_2 = has_color_matrix_2; bool has_col_2 = has_color_matrix_2;
@ -1248,17 +1232,14 @@ void DCPProfile::findXyztoCamera(const double white_xy[2], int preferred_illumin
} }
// Mix if we have two matrices // Mix if we have two matrices
double mix;
Matrix col;
if (has_col_1 && has_col_2) { if (has_col_1 && has_col_2) {
double wbtemp;
/* /*
Note: We're using DNG SDK reference code for XY to temperature translation to get the exact same mix as Note: We're using DNG SDK reference code for XY to temperature translation to get the exact same mix as
the reference code does. the reference code does.
*/ */
xyCoordToTemperature(white_xy, &wbtemp, nullptr); const double wbtemp = xyCoordToTemperature(white_xy);
double mix;
if (wbtemp <= temperature_1) { if (wbtemp <= temperature_1) {
mix = 1.0; mix = 1.0;
} else if (wbtemp >= temperature_2) { } else if (wbtemp >= temperature_2) {
@ -1270,45 +1251,36 @@ void DCPProfile::findXyztoCamera(const double white_xy[2], int preferred_illumin
// Interpolate // Interpolate
if (mix >= 1.0) { if (mix >= 1.0) {
col = color_matrix_1; return color_matrix_1;
} else if (mix <= 0.0) { } else if (mix <= 0.0) {
col = color_matrix_2; return color_matrix_2;
} else { } else {
mix3x3(color_matrix_1, mix, color_matrix_2, 1.0 - mix, col); return mix3x3(color_matrix_1, mix, color_matrix_2, 1.0 - mix);
} }
} else if (has_col_1) { } else if (has_col_1) {
col = color_matrix_1; return color_matrix_1;
} else { } else {
col = color_matrix_2; return color_matrix_2;
} }
xyz_to_camera = col;
} }
void DCPProfile::neutralToXy(const Triple& neutral, int preferred_illuminant, double xy[2]) const std::array<double, 2> DCPProfile::neutralToXy(const Triple& neutral, int preferred_illuminant) const
{ {
enum { enum {
MAX_PASSES = 30 MAX_PASSES = 30
}; };
double last_xy[2] = {0.3457, 0.3585}; // D50 std::array<double, 2> last_xy = {0.3457, 0.3585}; // D50
for (unsigned int pass = 0; pass < MAX_PASSES; ++pass) { for (unsigned int pass = 0; pass < MAX_PASSES; ++pass) {
Matrix xyz_to_camera; const Matrix& xyz_to_camera = findXyztoCamera(last_xy, preferred_illuminant);
findXyztoCamera(last_xy, preferred_illuminant, xyz_to_camera); const Matrix& inv_m = invert3x3(xyz_to_camera);
const Triple& next_xyz = multiply3x3_v3(inv_m, neutral);
Matrix inv_m; std::array<double, 2> next_xy = xyzToXy(next_xyz);
Triple next_xyz;
double next_xy[2];
invert3x3(xyz_to_camera, inv_m);
multiply3x3_v3(inv_m, neutral, next_xyz);
xyzToXy(next_xyz, next_xy);
if (fabs(next_xy[0] - last_xy[0]) + if (std::fabs(next_xy[0] - last_xy[0]) + std::fabs(next_xy[1] - last_xy[1]) < 0.0000001) {
fabs(next_xy[1] - last_xy[1]) < 0.0000001) { return next_xy;
xy[0] = next_xy[0];
xy[1] = next_xy[1];
return;
} }
// If we reach the limit without converging, we are most likely // If we reach the limit without converging, we are most likely
@ -1319,15 +1291,13 @@ void DCPProfile::neutralToXy(const Triple& neutral, int preferred_illuminant, do
next_xy[1] = (last_xy[1] + next_xy[1]) * 0.5; next_xy[1] = (last_xy[1] + next_xy[1]) * 0.5;
} }
last_xy[0] = next_xy[0]; last_xy = next_xy;
last_xy[1] = next_xy[1];
} }
xy[0] = last_xy[0]; return last_xy;
xy[1] = last_xy[1];
} }
void DCPProfile::makeXyzCam(const ColorTemp& white_balance, const Triple& pre_mul, const Matrix& cam_wb_matrix, int preferred_illuminant, Matrix& xyz_cam) const DCPProfile::Matrix DCPProfile::makeXyzCam(const ColorTemp& white_balance, const Triple& pre_mul, const Matrix& cam_wb_matrix, int preferred_illuminant) const
{ {
// Code adapted from dng_color_spec::FindXYZtoCamera. // Code adapted from dng_color_spec::FindXYZtoCamera.
// Note that we do not support monochrome or colorplanes > 3 (no reductionMatrix support), // Note that we do not support monochrome or colorplanes > 3 (no reductionMatrix support),
@ -1340,32 +1310,24 @@ void DCPProfile::makeXyzCam(const ColorTemp& white_balance, const Triple& pre_mu
double r, g, b; double r, g, b;
white_balance.getMultipliers(r, g, b); white_balance.getMultipliers(r, g, b);
// camWbMatrix == imatrices.xyz_cam constexpr Matrix xyz_srgb = {
Matrix cam_xyz; {
invert3x3(cam_wb_matrix, cam_xyz);
Matrix cam_rgb;
constexpr Matrix xyz_srgb = {{
{xyz_sRGB[0][0], xyz_sRGB[0][1], xyz_sRGB[0][2]}, {xyz_sRGB[0][0], xyz_sRGB[0][1], xyz_sRGB[0][2]},
{xyz_sRGB[1][0], xyz_sRGB[1][1], xyz_sRGB[1][2]}, {xyz_sRGB[1][0], xyz_sRGB[1][1], xyz_sRGB[1][2]},
{xyz_sRGB[2][0], xyz_sRGB[2][1], xyz_sRGB[2][2]} {xyz_sRGB[2][0], xyz_sRGB[2][1], xyz_sRGB[2][2]}
} }
}; };
multiply3x3(cam_xyz, xyz_srgb, cam_rgb); const Matrix cam_rgb = multiply3x3(invert3x3(cam_wb_matrix), xyz_srgb);
double camwb_red = cam_rgb[0][0] * r + cam_rgb[0][1] * g + cam_rgb[0][2] * b; double camwb_red = cam_rgb[0][0] * r + cam_rgb[0][1] * g + cam_rgb[0][2] * b;
double camwb_green = cam_rgb[1][0] * r + cam_rgb[1][1] * g + cam_rgb[1][2] * b; double camwb_green = cam_rgb[1][0] * r + cam_rgb[1][1] * g + cam_rgb[1][2] * b;
double camwb_blue = cam_rgb[2][0] * r + cam_rgb[2][1] * g + cam_rgb[2][2] * b; double camwb_blue = cam_rgb[2][0] * r + cam_rgb[2][1] * g + cam_rgb[2][2] * b;
neutral[0] = camwb_red / pre_mul[0]; neutral[0] = camwb_red / pre_mul[0];
neutral[1] = camwb_green / pre_mul[1]; neutral[1] = camwb_green / pre_mul[1];
neutral[2] = camwb_blue / pre_mul[2]; neutral[2] = camwb_blue / pre_mul[2];
double maxentry = 0;
for (int i = 0; i < 3; i++) { const double maxentry = std::max({neutral[0], neutral[1], neutral[2]});
if (neutral[i] > maxentry) {
maxentry = neutral[i];
}
}
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; ++i) {
neutral[i] /= maxentry; neutral[i] /= maxentry;
} }
} }
@ -1374,8 +1336,7 @@ void DCPProfile::makeXyzCam(const ColorTemp& white_balance, const Triple& pre_mu
DCP ColorMatrix or ColorMatrices if dual-illuminant. This is the DNG reference code way to DCP ColorMatrix or ColorMatrices if dual-illuminant. This is the DNG reference code way to
do it, which is a bit different from RT's own white balance model at the time of writing. do it, which is a bit different from RT's own white balance model at the time of writing.
When RT's white balance can make use of the DCP color matrices we could use that instead. */ When RT's white balance can make use of the DCP color matrices we could use that instead. */
double white_xy[2]; const std::array<double, 2> white_xy = neutralToXy(neutral, preferred_illuminant);
neutralToXy(neutral, preferred_illuminant, white_xy);
bool has_fwd_1 = has_forward_matrix_1; bool has_fwd_1 = has_forward_matrix_1;
bool has_fwd_2 = has_forward_matrix_2; bool has_fwd_2 = has_forward_matrix_2;
@ -1404,18 +1365,17 @@ void DCPProfile::makeXyzCam(const ColorTemp& white_balance, const Triple& pre_mu
double mix = 1.0; double mix = 1.0;
if ((has_col_1 && has_col_2) || (has_fwd_1 && has_fwd_2)) { if ((has_col_1 && has_col_2) || (has_fwd_1 && has_fwd_2)) {
double wbtemp;
/* DNG ref way to convert XY to temperature, which affect matrix mixing. A different model here /* DNG ref way to convert XY to temperature, which affect matrix mixing. A different model here
typically does not affect the result too much, ie it's probably not strictly necessary to typically does not affect the result too much, ie it's probably not strictly necessary to
use the DNG reference code here, but we do it for now. */ use the DNG reference code here, but we do it for now. */
xyCoordToTemperature(white_xy, &wbtemp, nullptr); const double wbtemp = xyCoordToTemperature(white_xy);
if (wbtemp <= temperature_1) { if (wbtemp <= temperature_1) {
mix = 1.0; mix = 1.0;
} else if (wbtemp >= temperature_2) { } else if (wbtemp >= temperature_2) {
mix = 0.0; mix = 0.0;
} else { } else {
double invT = 1.0 / wbtemp; const double& invT = 1.0 / wbtemp;
mix = (invT - (1.0 / temperature_2)) / ((1.0 / temperature_1) - (1.0 / temperature_2)); mix = (invT - (1.0 / temperature_2)) / ((1.0 / temperature_1) - (1.0 / temperature_2));
} }
} }
@ -1430,7 +1390,7 @@ void DCPProfile::makeXyzCam(const ColorTemp& white_balance, const Triple& pre_mu
} else if (mix <= 0.0) { } else if (mix <= 0.0) {
color_matrix = color_matrix_2; color_matrix = color_matrix_2;
} else { } else {
mix3x3(color_matrix_1, mix, color_matrix_2, 1.0 - mix, color_matrix); color_matrix = mix3x3(color_matrix_1, mix, color_matrix_2, 1.0 - mix);
} }
} else if (has_col_1) { } else if (has_col_1) {
color_matrix = color_matrix_1; color_matrix = color_matrix_1;
@ -1446,8 +1406,7 @@ void DCPProfile::makeXyzCam(const ColorTemp& white_balance, const Triple& pre_mu
will show incorrect color. will show incorrect color.
*/ */
Triple white_xyz; const Triple white_xyz = xyToXyz(white_xy);
xyToXyz(white_xy, white_xyz);
Matrix cam_xyz; Matrix cam_xyz;
@ -1462,7 +1421,7 @@ void DCPProfile::makeXyzCam(const ColorTemp& white_balance, const Triple& pre_mu
} else if (mix <= 0.0) { } else if (mix <= 0.0) {
fwd = forward_matrix_2; fwd = forward_matrix_2;
} else { } else {
mix3x3(forward_matrix_1, mix, forward_matrix_2, 1.0 - mix, fwd); fwd = mix3x3(forward_matrix_1, mix, forward_matrix_2, 1.0 - mix);
} }
} else if (has_fwd_1) { } else if (has_fwd_1) {
fwd = forward_matrix_1; fwd = forward_matrix_1;
@ -1471,76 +1430,66 @@ void DCPProfile::makeXyzCam(const ColorTemp& white_balance, const Triple& pre_mu
} }
// adapted from dng_color_spec::SetWhiteXY // adapted from dng_color_spec::SetWhiteXY
Triple camera_white; const Triple camera_white = multiply3x3_v3(color_matrix, white_xyz);
multiply3x3_v3(color_matrix, white_xyz, camera_white); const Matrix white_diag = {
{
const Matrix white_diag = {{
{camera_white[0], 0, 0}, {camera_white[0], 0, 0},
{0, camera_white[1], 0}, {0, camera_white[1], 0},
{0, 0, camera_white[2]} {0, 0, camera_white[2]}
} }
}; };
Matrix white_diag_inv;
invert3x3(white_diag, white_diag_inv);
Matrix xyz_cam; cam_xyz = invert3x3(multiply3x3(fwd, invert3x3(white_diag)));
multiply3x3(fwd, white_diag_inv, xyz_cam);
invert3x3(xyz_cam, cam_xyz);
} else { } else {
Matrix white_matrix; constexpr Triple white_d50 = {0.3457, 0.3585, 0.2958}; // D50
const Triple white_d50 = {0.3457, 0.3585, 0.2958}; // D50
mapWhiteMatrix(white_d50, white_xyz, white_matrix); cam_xyz = multiply3x3(color_matrix, mapWhiteMatrix(white_d50, white_xyz));
multiply3x3(color_matrix, white_matrix, cam_xyz);
} }
// Convert cam_xyz (XYZ D50 to CameraRGB, "PCS to Camera" in DNG terminology) to mXYZCAM // Convert cam_xyz (XYZ D50 to CameraRGB, "PCS to Camera" in DNG terminology) to mXYZCAM
{ // This block can probably be simplified, seems unnecessary to pass through the sRGB matrix
// This block can probably be simplified, seems unnecessary to pass through the sRGB matrix // (probably dcraw legacy), it does no harm though as we don't clip anything.
// (probably dcraw legacy), it does no harm though as we don't clip anything. int i, j, k;
int i, j, k;
// Multiply out XYZ colorspace // Multiply out XYZ colorspace
double cam_rgb[3][3] = {}; double cam_rgb[3][3] = {};
for (i = 0; i < 3; ++i) { for (i = 0; i < 3; ++i) {
for (j = 0; j < 3; ++j) { for (j = 0; j < 3; ++j) {
for (k = 0; k < 3; ++k) { for (k = 0; k < 3; ++k) {
cam_rgb[i][j] += cam_xyz[i][k] * xyz_sRGB[k][j]; cam_rgb[i][j] += cam_xyz[i][k] * xyz_sRGB[k][j];
}
}
}
// Normalize cam_rgb so that cam_rgb * (1,1,1) is (1,1,1,1)
double num;
for (i = 0; i < 3; ++i) {
for (num = j = 0; j < 3; ++j) {
num += cam_rgb[i][j];
}
for (j = 0; j < 3; ++j) {
cam_rgb[i][j] /= num;
}
}
double rgb_cam[3][3] = {};
RawImageSource::inverse33(cam_rgb, rgb_cam);
for (i = 0; i < 3; ++i) {
for (j = 0; j < 3; ++j) {
xyz_cam[i][j] = 0;
}
}
for (i = 0; i < 3; ++i) {
for (j = 0; j < 3; ++j) {
for (k = 0; k < 3; ++k) {
xyz_cam[i][j] += xyz_sRGB[i][k] * rgb_cam[k][j];
}
} }
} }
} }
// Normalize cam_rgb so that cam_rgb * (1,1,1) is (1,1,1,1)
double num;
for (i = 0; i < 3; ++i) {
for (num = j = 0; j < 3; ++j) {
num += cam_rgb[i][j];
}
for (j = 0; j < 3; ++j) {
cam_rgb[i][j] /= num;
}
}
double rgb_cam[3][3] = {};
RawImageSource::inverse33(cam_rgb, rgb_cam);
Matrix res = {};
for (i = 0; i < 3; ++i) {
for (j = 0; j < 3; ++j) {
for (k = 0; k < 3; ++k) {
res[i][j] += xyz_sRGB[i][k] * rgb_cam[k][j];
}
}
}
return res;
} }
std::vector<DCPProfile::HsbModify> DCPProfile::makeHueSatMap(const ColorTemp& white_balance, int preferred_illuminant) const std::vector<DCPProfile::HsbModify> DCPProfile::makeHueSatMap(const ColorTemp& white_balance, int preferred_illuminant) const
@ -1670,7 +1619,7 @@ void DCPProfile::hsdApply(const HsdTableInfo& table_info, const std::vector<HsbM
const float v_scaled = v_encoded * table_info.pc.v_scale; const float v_scaled = v_encoded * table_info.pc.v_scale;
int h_index0 = (int) h_scaled; int h_index0 = h_scaled;
const int s_index0 = std::max(std::min<int>(s_scaled, table_info.pc.max_sat_index0), 0); const int s_index0 = std::max(std::min<int>(s_scaled, table_info.pc.max_sat_index0), 0);
const int v_index0 = std::max(std::min<int>(v_scaled, table_info.pc.max_val_index0), 0); const int v_index0 = std::max(std::min<int>(v_scaled, table_info.pc.max_val_index0), 0);
@ -1823,10 +1772,14 @@ DCPProfile* DCPStore::getProfile(const Glib::ustring& filename) const
DCPProfile* const res = new DCPProfile(filename); DCPProfile* const res = new DCPProfile(filename);
// Add profile if (*res) {
profile_cache[filename] = res; // Add profile
profile_cache[filename] = res;
return res;
}
return res; delete res;
return nullptr;
} }
DCPProfile* DCPStore::getStdProfile(const Glib::ustring& cam_short_name) const DCPProfile* DCPStore::getStdProfile(const Glib::ustring& cam_short_name) const

View File

@ -66,6 +66,8 @@ public:
DCPProfile(const Glib::ustring& filename); DCPProfile(const Glib::ustring& filename);
~DCPProfile(); ~DCPProfile();
explicit operator bool() const;
bool getHasToneCurve() const; bool getHasToneCurve() const;
bool getHasLookTable() const; bool getHasLookTable() const;
bool getHasHueSatMap() const; bool getHasHueSatMap() const;
@ -112,9 +114,9 @@ private:
} pc; } pc;
}; };
void findXyztoCamera(const double white_xy[2], int preferred_illuminant, Matrix& xyz_to_camera) const; Matrix findXyztoCamera(const std::array<double, 2>& white_xy, int preferred_illuminant) const;
void neutralToXy(const Triple& neutral, int preferred_illuminant, double xy[2]) const; std::array<double, 2> neutralToXy(const Triple& neutral, int preferred_illuminant) const;
void makeXyzCam(const ColorTemp& white_balance, const Triple& pre_mul, const Matrix& cam_wb_matrix, int preferred_illuminant, Matrix& xyz_cam) const; Matrix makeXyzCam(const ColorTemp& white_balance, const Triple& pre_mul, const Matrix& cam_wb_matrix, int preferred_illuminant) const;
std::vector<HsbModify> makeHueSatMap(const ColorTemp& white_balance, int preferred_illuminant) const; std::vector<HsbModify> makeHueSatMap(const ColorTemp& white_balance, int preferred_illuminant) const;
void hsdApply(const HsdTableInfo& table_info, const std::vector<HsbModify>& table_base, float& h, float& s, float& v) const; void hsdApply(const HsdTableInfo& table_info, const std::vector<HsbModify>& table_base, float& h, float& s, float& v) const;

View File

@ -20,184 +20,242 @@
#define _ICCMATRICES_ #define _ICCMATRICES_
// Bradford transform between illuminants // Bradford transform between illuminants
const double d65_d50[3][3] = {{0.9555766, -0.0230393, 0.0631636}, constexpr double d65_d50[3][3] = {
{0.9555766, -0.0230393, 0.0631636},
{ -0.0282895, 1.0099416, 0.0210077}, { -0.0282895, 1.0099416, 0.0210077},
{0.0122982, -0.0204830, 1.3299098} {0.0122982, -0.0204830, 1.3299098}
}; };
const double d50_d65[3][3] = {{ 1.0478112, 0.0228866, -0.0501270}, constexpr double d50_d65[3][3] = {
{ 1.0478112, 0.0228866, -0.0501270},
{0.0295424, 0.9904844, -0.0170491}, {0.0295424, 0.9904844, -0.0170491},
{ -0.0092345, 0.0150436, 0.7521316} { -0.0092345, 0.0150436, 0.7521316}
}; };
// Color space conversion to/from XYZ; color spaces adapted to D65 // Color space conversion to/from XYZ; color spaces adapted to D65
const double xyz_sRGBd65[3][3] = {{0.4124564, 0.3575761, 0.1804375}, constexpr double xyz_sRGBd65[3][3] = {
{0.4124564, 0.3575761, 0.1804375},
{0.2126729, 0.7151522, 0.0721750}, // WARNING: the summ of this line is > 1.0 {0.2126729, 0.7151522, 0.0721750}, // WARNING: the summ of this line is > 1.0
{0.0193339, 0.1191920, 0.9503041} {0.0193339, 0.1191920, 0.9503041}
}; };
const double sRGBd65_xyz[3][3] = {{ 3.2404542, -1.5371385, -0.4985314}, constexpr double sRGBd65_xyz[3][3] = {
{ 3.2404542, -1.5371385, -0.4985314},
{ -0.9692660, 1.8760108, 0.0415560}, { -0.9692660, 1.8760108, 0.0415560},
{0.0556434, -0.2040259, 1.0572252} {0.0556434, -0.2040259, 1.0572252}
}; };
//%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%
// TEST using Gabor's matrices // TEST using Gabor's matrices
/*const double xyz_sRGB[3][3] = {{0.435859, 0.385336, 0.143023}, /*constexpr double xyz_sRGB[3][3] = {
{0.435859, 0.385336, 0.143023},
{0.222385, 0.717021, 0.0605936 }, {0.222385, 0.717021, 0.0605936 },
{0.0139162, 0.0971389, 0.713817}}; {0.0139162, 0.0971389, 0.713817}};
const double sRGB_xyz[3][3] = {{3.13593293538656, -1.61878246026431, -0.490913888760734}, constexpr double sRGB_xyz[3][3] = {
{3.13593293538656, -1.61878246026431, -0.490913888760734},
{-0.978702373022194, 1.91609508555177, 0.0334453372795315}, {-0.978702373022194, 1.91609508555177, 0.0334453372795315},
{0.0720490013929888, -0.22919049060526, 1.40593851447263}};*/ {0.0720490013929888, -0.22919049060526, 1.40593851447263}};*/
//%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%
// Color space conversion to/from XYZ; color spaces adapted to D50 using Bradford transform // Color space conversion to/from XYZ; color spaces adapted to D50 using Bradford transform
const double xyz_sRGB[3][3] = {{0.4360747, 0.3850649, 0.1430804}, constexpr double xyz_sRGB[3][3] = {
{0.4360747, 0.3850649, 0.1430804},
{0.2225045, 0.7168786, 0.0606169}, {0.2225045, 0.7168786, 0.0606169},
{0.0139322, 0.0971045, 0.7141733} {0.0139322, 0.0971045, 0.7141733}
}; };
const double sRGB_xyz[3][3] = {{3.1338561, -1.6168667, -0.4906146}, constexpr double sRGB_xyz[3][3] = {
{3.1338561, -1.6168667, -0.4906146},
{ -0.9787684, 1.9161415, 0.0334540}, { -0.9787684, 1.9161415, 0.0334540},
{0.0719453, -0.2289914, 1.4052427} {0.0719453, -0.2289914, 1.4052427}
}; };
const double xyz_adobe[3][3] = {{0.6097559, 0.2052401, 0.1492240}, constexpr double xyz_adobe[3][3] = {
{0.6097559, 0.2052401, 0.1492240},
{0.3111242, 0.6256560, 0.0632197}, {0.3111242, 0.6256560, 0.0632197},
{0.0194811, 0.0608902, 0.7448387} {0.0194811, 0.0608902, 0.7448387}
}; };
const double adobe_xyz[3][3] = {{1.9624274, -0.6105343, -0.3413404}, constexpr double adobe_xyz[3][3] = {
{1.9624274, -0.6105343, -0.3413404},
{ -0.9787684, 1.9161415, 0.0334540}, { -0.9787684, 1.9161415, 0.0334540},
{0.0286869, -0.1406752, 1.3487655} {0.0286869, -0.1406752, 1.3487655}
}; };
const double xyz_prophoto[3][3] = {{0.7976749, 0.1351917, 0.0313534}, constexpr double xyz_prophoto[3][3] = {
{0.7976749, 0.1351917, 0.0313534},
{0.2880402, 0.7118741, 0.0000857}, {0.2880402, 0.7118741, 0.0000857},
{0.0000000, 0.0000000, 0.8252100} {0.0000000, 0.0000000, 0.8252100}
}; };
const double prophoto_xyz[3][3] = {{1.3459433, -0.2556075, -0.0511118}, constexpr double prophoto_xyz[3][3] = {
{1.3459433, -0.2556075, -0.0511118},
{ -0.5445989, 1.5081673, 0.0205351}, { -0.5445989, 1.5081673, 0.0205351},
{0.0000000, 0.0000000, 1.2118128} {0.0000000, 0.0000000, 1.2118128}
}; };
/* /*
const double xyz_rec2020[3][3] = {{0.636958, 0.144617, 0.168881}, constexpr double xyz_rec2020[3][3] = {
{0.636958, 0.144617, 0.168881},
{0.262700, 0.677998, 0.059302}, {0.262700, 0.677998, 0.059302},
{0.0000000, 0.028073, 1.060985} {0.0000000, 0.028073, 1.060985}
}; };
const double rec2020_xyz[3][3] = {{1.716651, -0.355671, -0.253366}, constexpr double rec2020_xyz[3][3] = {
{1.716651, -0.355671, -0.253366},
{ -0.666684, 1.616481, 0.015769}, { -0.666684, 1.616481, 0.015769},
{0.017640, -0.042771, 0.942103} {0.017640, -0.042771, 0.942103}
}; };
*/ */
const double xyz_rec2020[3][3] = {{0.6734241, 0.1656411, 0.1251286}, constexpr double xyz_rec2020[3][3] = {
{0.6734241, 0.1656411, 0.1251286},
{0.2790177, 0.6753402, 0.0456377}, {0.2790177, 0.6753402, 0.0456377},
{ -0.0019300, 0.0299784, 0.7973330} { -0.0019300, 0.0299784, 0.7973330}
}; };
const double rec2020_xyz[3][3] = {{1.6473376, -0.3935675, -0.2359961}, constexpr double rec2020_xyz[3][3] = {
{1.6473376, -0.3935675, -0.2359961},
{ -0.6826036, 1.6475887, 0.0128190}, { -0.6826036, 1.6475887, 0.0128190},
{0.0296524, -0.0628993, 1.2531279} {0.0296524, -0.0628993, 1.2531279}
}; };
const double xyz_widegamut[3][3] = {{0.7161046, 0.1009296, 0.1471858}, constexpr double xyz_widegamut[3][3] = {
{0.7161046, 0.1009296, 0.1471858},
{0.2581874, 0.7249378, 0.0168748}, {0.2581874, 0.7249378, 0.0168748},
{0.0000000, 0.0517813, 0.7734287} {0.0000000, 0.0517813, 0.7734287}
}; };
const double widegamut_xyz[3][3] = {{ 1.4628067, -0.1840623, -0.2743606}, constexpr double widegamut_xyz[3][3] = {
{ 1.4628067, -0.1840623, -0.2743606},
{ -0.5217933, 1.4472381, 0.0677227}, { -0.5217933, 1.4472381, 0.0677227},
{0.0349342, -0.0968930, 1.2884099} {0.0349342, -0.0968930, 1.2884099}
}; };
const double xyz_bruce[3][3] = {{0.4941816, 0.3204834, 0.1495550}, constexpr double xyz_bruce[3][3] = {
{0.4941816, 0.3204834, 0.1495550},
{0.2521531, 0.6844869, 0.0633600}, {0.2521531, 0.6844869, 0.0633600},
{0.0157886, 0.0629304, 0.7464909} {0.0157886, 0.0629304, 0.7464909}
}; };
const double bruce_xyz[3][3] = {{2.6502856, -1.2014485, -0.4289936}, constexpr double bruce_xyz[3][3] = {
{2.6502856, -1.2014485, -0.4289936},
{ -0.9787684, 1.9161415, 0.0334540}, { -0.9787684, 1.9161415, 0.0334540},
{0.0264570, -0.1361227, 1.3458542} {0.0264570, -0.1361227, 1.3458542}
}; };
const double xyz_beta[3][3] = {{0.6712537, 0.1745834, 0.1183829}, constexpr double xyz_beta[3][3] = {
{0.6712537, 0.1745834, 0.1183829},
{0.3032726, 0.6637861, 0.0329413}, {0.3032726, 0.6637861, 0.0329413},
{0.0000000, 0.0407010, 0.7845090} {0.0000000, 0.0407010, 0.7845090}
}; };
const double beta_xyz[3][3] = {{1.6832270, -0.4282363, -0.2360185}, constexpr double beta_xyz[3][3] = {
{1.6832270, -0.4282363, -0.2360185},
{ -0.7710229, 1.7065571, 0.0446900}, { -0.7710229, 1.7065571, 0.0446900},
{0.0400013, -0.0885376, 1.2723640} {0.0400013, -0.0885376, 1.2723640}
}; };
const double xyz_best[3][3] = {{0.6326696, 0.2045558, 0.1269946}, constexpr double xyz_best[3][3] = {
{0.6326696, 0.2045558, 0.1269946},
{0.2284569, 0.7373523, 0.0341908}, {0.2284569, 0.7373523, 0.0341908},
{0.0000000, 0.0095142, 0.8156958} {0.0000000, 0.0095142, 0.8156958}
}; };
const double best_xyz[3][3] = {{1.7552599, -0.4836786, -0.2530000}, constexpr double best_xyz[3][3] = {
{1.7552599, -0.4836786, -0.2530000},
{ -0.5441336, 1.5068789, 0.0215528}, { -0.5441336, 1.5068789, 0.0215528},
{0.0063467, -0.0175761, 1.2256959} {0.0063467, -0.0175761, 1.2256959}
}; };
/*const double sRGB_d50[3][3] = {{0.4360520246092, 0.2224915978656, 0.0139291219896}, /*
{0.38508159282, 0.716886060114, 0.09709700166}, constexpr double sRGB_d50[3][3] = {
{0.1430874138552, 0.0606214863936, 0.714185469944}}; {0.4360520246092, 0.2224915978656, 0.0139291219896},
{0.38508159282, 0.716886060114, 0.09709700166},
{0.1430874138552, 0.0606214863936, 0.714185469944}
};
const double d50_sRGB[3][3] = {{3.13405134405167,-0.978762729953942, 0.0719425766617001}, constexpr double d50_sRGB[3][3] = {
{-1.61702771153574,1.91614222810656, -0.228971178679309}, {3.13405134405167,-0.978762729953942, 0.0719425766617001},
{-0.49065220876631,0.0334496273068589, 1.40521830559074}};*/ {-1.61702771153574,1.91614222810656, -0.228971178679309},
{-0.49065220876631,0.0334496273068589, 1.40521830559074}
};
*/
/* /*
// Gabor's matrices // Gabor's matrices
const double sRGB_d50[3][3] = {{0.435859, 0.222385, 0.0139162}, constexpr double sRGB_d50[3][3] = {
{0.385336, 0.717021, 0.0971389}, {0.435859, 0.222385, 0.0139162},
{0.143023, 0.0605936, 0.713817}}; {0.385336, 0.717021, 0.0971389},
{0.143023, 0.0605936, 0.713817}
};
const double d50_sRGB[3][3] = {{3.13593293538656, -0.978702373022194, 0.0720490013929888}, constexpr double d50_sRGB[3][3] = {
{-1.61878246026431, 1.91609508555177, -0.22919049060526}, {3.13593293538656, -0.978702373022194, 0.0720490013929888},
{-0.490913888760734, 0.0334453372795315, 1.40593851447263}}; {-1.61878246026431, 1.91609508555177, -0.22919049060526},
{-0.490913888760734, 0.0334453372795315, 1.40593851447263}
};
const double adobe_d50[3][3] = {{0.6097395054954, 0.3111142944042, 0.0194773131652}, constexpr double adobe_d50[3][3] = {
{0.2052518325737, 0.6256618480686, 0.0608872306106}, {0.6097395054954, 0.3111142944042, 0.0194773131652},
{0.1492308013399, 0.0632241329247, 0.744846530711}}; {0.2052518325737, 0.6256618480686, 0.0608872306106},
const double d50_adobe[3][3] = {{1.9624959949628, -0.978762712052774, 0.0286904764959749}, {0.1492308013399, 0.0632241329247, 0.744846530711}
{-0.610587687828765,1.91614073756734, -0.140667763143042}, };
{-0.34136021627766, 0.0334501217627688, 1.34875045144924}}; constexpr double d50_adobe[3][3] = {
const double prophoto_d50[3][3] = {{0.797675, 0.288040, 0.000000}, {1.9624959949628, -0.978762712052774, 0.0286904764959749},
{0.135192, 0.711874, 0.000000}, {-0.610587687828765,1.91614073756734, -0.140667763143042},
{0.0313534,0.000086, 0.825210}}; {-0.34136021627766, 0.0334501217627688, 1.34875045144924}
const double d50_prophoto[3][3] = {{1.34594335079331, -0.544598514291158, 0}, };
{-0.255608118122657, 1.50816768465213, 0}, constexpr double prophoto_d50[3][3] = {
{-0.0511117387775285, 0.0205345459181255, 1.21181275069376}}; {0.797675, 0.288040, 0.000000},
const double widegamut_d50[3][3] = {{0.716105, 0.258187, 0.000000}, {0.135192, 0.711874, 0.000000},
{0.100930, 0.724938, 0.0517813}, {0.0313534,0.000086, 0.825210}
{0.147186, 0.0168748, 0.773429}}; };
const double d50_widegamut[3][3] = {{1.46280597103052, -0.521792197260068, 0.0349341417298585}, constexpr double d50_prophoto[3][3] = {
{-0.184062984909417, 1.44723786022891, -0.0968930022172314}, {1.34594335079331, -0.544598514291158, 0},
{-0.27436071519732, 0.0677226440980744,1.28840945122198}}; {-0.255608118122657, 1.50816768465213, 0},
const double bruce_d50[3][3] = {{0.4941607255908, 0.2521412970174, 0.0157852934504}, {-0.0511117387775285, 0.0205345459181255, 1.21181275069376}
{0.3204990468435, 0.684494580042, 0.062927176507}, };
{0.1495612990809, 0.0633643619597, 0.746498914581}}; constexpr double widegamut_d50[3][3] = {
const double d50_bruce[3][3] = {{2.65042308164152, -0.978762745761462, 0.0264609493245811}, {0.716105, 0.258187, 0.000000},
{-1.20155941925411, 1.9161402914007, -0.136115844662896}, {0.100930, 0.724938, 0.0517813},
{-0.42902228923717, 0.0334495071197919, 1.34583900936772}}; {0.147186, 0.0168748, 0.773429}
const double beta_d50[3][3] = {{0.671254, 0.303273, 0.000000}, };
{0.174583, 0.663786, 0.040701}, constexpr double d50_widegamut[3][3] = {
{0.118383, 0.0329413, 0.784509}}; {1.46280597103052, -0.521792197260068, 0.0349341417298585},
const double d50_beta[3][3] = {{1.68322591962771, -0.771023599950842, 0.0400013658754702}, {-0.184062984909417, 1.44723786022891, -0.0968930022172314},
{-0.428235060337656, 1.70655704781303, -0.0885376438040078}, {-0.27436071519732, 0.0677226440980744,1.28840945122198}
{-0.236018598193503, 0.0446902191738489,1.27236406897742}}; };
const double best_d50[3][3] = {{0.632670, 0.228457, 0.000000}, constexpr double bruce_d50[3][3] = {
{0.204556, 0.737352, 0.00951424}, {0.4941607255908, 0.2521412970174, 0.0157852934504},
{0.126995, 0.0341908, 0.815696}}; {0.3204990468435, 0.684494580042, 0.062927176507},
const double d50_best[3][3] = {{1.75525923340349, -0.544133953997468, 0.00634675299435191}, {0.1495612990809, 0.0633643619597, 0.746498914581}
{-0.483679025800866, 1.50687975713407, -0.017576175021718}, };
{-0.253000840399762, 0.0215532098817316,1.22569552576991}}; constexpr double d50_bruce[3][3] = {
{2.65042308164152, -0.978762745761462, 0.0264609493245811},
{-1.20155941925411, 1.9161402914007, -0.136115844662896},
{-0.42902228923717, 0.0334495071197919, 1.34583900936772}
};
constexpr double beta_d50[3][3] = {
{0.671254, 0.303273, 0.000000},
{0.174583, 0.663786, 0.040701},
{0.118383, 0.0329413, 0.784509}
};
constexpr double d50_beta[3][3] = {
{1.68322591962771, -0.771023599950842, 0.0400013658754702},
{-0.428235060337656, 1.70655704781303, -0.0885376438040078},
{-0.236018598193503, 0.0446902191738489,1.27236406897742}
};
constexpr double best_d50[3][3] = {
{0.632670, 0.228457, 0.000000},
{0.204556, 0.737352, 0.00951424},
{0.126995, 0.0341908, 0.815696}
};
constexpr double d50_best[3][3] = {
{1.75525923340349, -0.544133953997468, 0.00634675299435191},
{-0.483679025800866, 1.50687975713407, -0.017576175021718},
{-0.253000840399762, 0.0215532098817316,1.22569552576991}
};
*/ */
#endif #endif