Abstract profile - Gamut improvment (#6665)

* Gamut control when the chosen primaries are different from working profile

* Gamut control abstract

* Gamut label and history

* Change to Wx Wz

* Fixed crash if y primaries are set to zero

* Fomated with Astylert ImProcFunctions::workingtrc and Color::primaries_to_xyz

* Fixed black becomes green wit gamt abstract profile

* Harmonize types in color.cc

* Try to fix Multiplication result converted to larger type
This commit is contained in:
Desmis
2023-01-24 14:29:50 +01:00
committed by GitHub
parent 21afbaf90b
commit 7afdfc1e2b
10 changed files with 415 additions and 51 deletions

View File

@@ -416,6 +416,8 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
{
const TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix(params->icm.workingProfile);
double wprofprim[3][3];//store primaries to XYZ
bool gamutcontrol = params->icm.gamut;
const float toxyz[3][3] = {
{
static_cast<float>(wprof[0][0] / ((normalizeIn ? 65535.0 : 1.0))), //I have suppressed / Color::D50x
@@ -440,57 +442,64 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
if (settings->verbose) {
printf("profile not accepted\n");
}
return;
}
if (mul == -5 && gampos == 2.4 && slpos == 12.92310) {//must be change if we change settings RT sRGB
//only in this case we can shortcut..all process..no gamut control..because we reduce...leads to very small differences, but big speedup
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic, 16) if (multiThread)
#pragma omp parallel for schedule(dynamic, 16) if (multiThread)
#endif
for (int i = 0; i < ch; ++i)
for (int j = 0; j < cw; ++j) {
float r = src->r(i, j);
float g = src->g(i, j);
float b = src->b(i, j);
r = (Color::igammatab_srgb[r]) / 65535.f;
g = (Color::igammatab_srgb[g]) / 65535.f;
b = (Color::igammatab_srgb[b]) / 65535.f;
dst->r(i, j) = r;
dst->g(i, j) = g;
dst->b(i, j) = b;
}
return;
for (int i = 0; i < ch; ++i)
for (int j = 0; j < cw; ++j) {
float r = src->r(i, j);
float g = src->g(i, j);
float b = src->b(i, j);
r = (Color::igammatab_srgb[r]) / 65535.f;
g = (Color::igammatab_srgb[g]) / 65535.f;
b = (Color::igammatab_srgb[b]) / 65535.f;
dst->r(i, j) = r;
dst->g(i, j) = g;
dst->b(i, j) = b;
}
return;
}
if (mul == 1 ||(params->icm.wprim == ColorManagementParams::Primaries::DEFAULT && params->icm.will == ColorManagementParams::Illuminant::DEFAULT)) {//shortcut and speedup when no call primaries and illuminant - no gamut control...in this case be careful
if (mul == 1 || (params->icm.wprim == ColorManagementParams::Primaries::DEFAULT && params->icm.will == ColorManagementParams::Illuminant::DEFAULT)) { //shortcut and speedup when no call primaries and illuminant - no gamut control...in this case be careful
GammaValues g_a; //gamma parameters
double pwr = 1.0 / static_cast<double>(gampos);
Color::calcGamma(pwr, slpos, g_a); // call to calcGamma with selected gamma and slope
#ifdef _OPENMP
# pragma omp parallel for schedule(dynamic,16) if (multiThread)
# pragma omp parallel for schedule(dynamic,16) if (multiThread)
#endif
for (int y = 0; y < ch; ++y) {
int x = 0;
#ifdef __SSE2__
for (; x < cw - 3; x += 4) {
STVFU(dst->r(y,x), F2V(65536.f) * gammalog(LVFU(src->r(y,x)), F2V(gampos), F2V(slpos), F2V(g_a[3]), F2V(g_a[4])));
STVFU(dst->g(y,x), F2V(65536.f) * gammalog(LVFU(src->g(y,x)), F2V(gampos), F2V(slpos), F2V(g_a[3]), F2V(g_a[4])));
STVFU(dst->b(y,x), F2V(65536.f) * gammalog(LVFU(src->b(y,x)), F2V(gampos), F2V(slpos), F2V(g_a[3]), F2V(g_a[4])));
}
STVFU(dst->r(y, x), F2V(65536.f) * gammalog(LVFU(src->r(y, x)), F2V(gampos), F2V(slpos), F2V(g_a[3]), F2V(g_a[4])));
STVFU(dst->g(y, x), F2V(65536.f) * gammalog(LVFU(src->g(y, x)), F2V(gampos), F2V(slpos), F2V(g_a[3]), F2V(g_a[4])));
STVFU(dst->b(y, x), F2V(65536.f) * gammalog(LVFU(src->b(y, x)), F2V(gampos), F2V(slpos), F2V(g_a[3]), F2V(g_a[4])));
}
#endif
for (; x < cw; ++x) {
dst->r(y,x) = 65536.f * gammalog(src->r(y,x), gampos, slpos, g_a[3], g_a[4]);
dst->g(y,x) = 65536.f * gammalog(src->g(y,x), gampos, slpos, g_a[3], g_a[4]);
dst->b(y,x) = 65536.f * gammalog(src->b(y,x), gampos, slpos, g_a[3], g_a[4]);
dst->r(y, x) = 65536.f * gammalog(src->r(y, x), gampos, slpos, g_a[3], g_a[4]);
dst->g(y, x) = 65536.f * gammalog(src->g(y, x), gampos, slpos, g_a[3], g_a[4]);
dst->b(y, x) = 65536.f * gammalog(src->b(y, x), gampos, slpos, g_a[3], g_a[4]);
}
}
return;
}
float redxx = params->icm.redx;
float redyy = params->icm.redy;
@@ -498,6 +507,7 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
float bluyy = params->icm.bluy;
float grexx = params->icm.grex;
float greyy = params->icm.grey;
float epsil = 0.0001f;
if (prim == 12) {//convert datas area to xy
float redgraphx = params->icm.labgridcieALow;
@@ -519,11 +529,34 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
greyy = 0.55f * (gregraphy + 1.f) - 0.1f;
greyy = rtengine::LIM(greyy, 0.5f, 1.f);
}
//fixed crash when there is no space or too small..just a line...Possible if bx, by aligned with Gx,Gy Rx,Ry
float ac = (greyy - redyy) / (grexx - redxx);
//fix crash if user select 0 for redyy, bluyy, greyy
if (redyy == 0.f) {
redyy = epsil;
}
if (bluyy == 0.f) {
bluyy = epsil;
}
if (greyy == 0.f) {
greyy = epsil;
}
//fix crash if grexx - redxx = 0
float grered = 1.f;
grered = grexx - redxx;
if (grered == 0.f) {
grered = epsil;
}
float ac = (greyy - redyy) / grered;
float bc = greyy - ac * grexx;
float yc = ac * bluxx + bc;
if ((bluyy < yc + 0.0004f) && (bluyy > yc - 0.0004f)) {//under 0.0004 in some case crash because space too small
if ((bluyy < yc + 0.0004f) && (bluyy > yc - 0.0004f)) { //under 0.0004 in some case crash because space too small
return;
}
@@ -564,7 +597,6 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
}
case ColorManagementParams::Primaries::ACES_P0: {
profile = "ACESp0";
break;
}
@@ -593,11 +625,13 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
break;
}
}
if (settings->verbose && prim != 0) {
printf("prim=%i Profile Destination=%s\n", prim, profile.c_str());
}
if (settings->verbose && prim != 0) {
printf("prim=%i Profile Destination=%s\n", prim, profile.c_str());
}
cmsHTRANSFORM hTransform = nullptr;
if (transform) {
hTransform = transform;
} else {
@@ -622,7 +656,9 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
};
double tempv4 = 5003.;
float p[6]; //primaries
double p[6]; //primaries
double Wx = 1.0;
double Wz = 1.0;
//primaries for 10 working profiles ==> output profiles
if (profile == "WideGamut") {
@@ -633,6 +669,9 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
p[4] = 0.1570;
p[5] = 0.0180;
illum = toUnderlying(ColorManagementParams::Illuminant::D50);
Wx = 0.964295676;
Wz = 0.825104603;
} else if (profile == "Adobe RGB") {
p[0] = 0.6400; //Adobe primaries
p[1] = 0.3300;
@@ -642,6 +681,9 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
p[5] = 0.0600;
tempv4 = 6504.;
illum = toUnderlying(ColorManagementParams::Illuminant::D65);
Wx = 0.95045471;
Wz = 1.08905029;
} else if (profile == "sRGB") {
p[0] = 0.6400; // sRGB primaries
p[1] = 0.3300;
@@ -651,6 +693,9 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
p[5] = 0.0600;
tempv4 = 6504.;
illum = toUnderlying(ColorManagementParams::Illuminant::D65);
Wx = 0.95045471;
Wz = 1.08905029;
} else if (profile == "BruceRGB") {
p[0] = 0.6400; // Bruce primaries
p[1] = 0.3300;
@@ -660,7 +705,10 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
p[5] = 0.0600;
tempv4 = 6504.;
illum = toUnderlying(ColorManagementParams::Illuminant::D65);
} else if (profile == "Beta RGB") {
Wx = 0.95045471;
Wz = 1.08905029;
} else if (profile == "Beta RGB") {
p[0] = 0.6888; // Beta primaries
p[1] = 0.3112;
p[2] = 0.1986;
@@ -668,6 +716,9 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
p[4] = 0.1265;
p[5] = 0.0352;
illum = toUnderlying(ColorManagementParams::Illuminant::D50);
Wx = 0.964295676;
Wz = 0.825104603;
} else if (profile == "BestRGB") {
p[0] = 0.7347; // Best primaries
p[1] = 0.2653;
@@ -676,6 +727,9 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
p[4] = 0.1300;
p[5] = 0.0350;
illum = toUnderlying(ColorManagementParams::Illuminant::D50);
Wx = 0.964295676;
Wz = 0.825104603;
} else if (profile == "Rec2020") {
p[0] = 0.7080; // Rec2020 primaries
p[1] = 0.2920;
@@ -685,6 +739,9 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
p[5] = 0.0460;
tempv4 = 6504.;
illum = toUnderlying(ColorManagementParams::Illuminant::D65);
Wx = 0.95045471;
Wz = 1.08905029;
} else if (profile == "ACESp0") {
p[0] = 0.7347; // ACES P0 primaries
p[1] = 0.2653;
@@ -694,6 +751,9 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
p[5] = -0.0770;
tempv4 = 6004.;
illum = toUnderlying(ColorManagementParams::Illuminant::D60);
Wx = 0.952646075;
Wz = 1.008825184;
} else if (profile == "ACESp1") {
p[0] = 0.713; // ACES P1 primaries
p[1] = 0.293;
@@ -703,6 +763,9 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
p[5] = 0.044;
tempv4 = 6004.;
illum = toUnderlying(ColorManagementParams::Illuminant::D60);
Wx = 0.952646075;
Wz = 1.008825184;
} else if (profile == "ProPhoto") {
p[0] = 0.7347; //ProPhoto and default primaries
p[1] = 0.2653;
@@ -711,8 +774,11 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
p[4] = 0.0366;
p[5] = 0.0001;
illum = toUnderlying(ColorManagementParams::Illuminant::D50);
Wx = 0.964295676;
Wz = 0.825104603;
} else if (profile == "Custom") {
p[0] = redxx;
p[0] = redxx;
p[1] = redyy;
p[2] = grexx;
p[3] = greyy;
@@ -743,11 +809,13 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
gammaParams[3] = 1. / slpos;
gammaParams[5] = 0.0;
gammaParams[6] = 0.0;
// printf("ga0=%f ga1=%f ga2=%f ga3=%f ga4=%f\n", ga0, ga1, ga2, ga3, ga4);
// printf("ga0=%f ga1=%f ga2=%f ga3=%f ga4=%f\n", ga0, ga1, ga2, ga3, ga4);
// 7 parameters for smoother curves
cmsCIExyY xyD;
Glib::ustring ills = "D50";
switch (ColorManagementParams::Illuminant(illum)) {
case ColorManagementParams::Illuminant::DEFAULT:
case ColorManagementParams::Illuminant::STDA:
@@ -802,55 +870,95 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
cmsWhitePointFromTemp(&xyD, tempv4);
switch (ColorManagementParams::Illuminant(illum)) {
case ColorManagementParams::Illuminant::DEFAULT:
case ColorManagementParams::Illuminant::D55:
case ColorManagementParams::Illuminant::DEFAULT: {
break;
}
case ColorManagementParams::Illuminant::D55: {
Wx = 0.956565934;
Wz = 0.920253249;
break;
}
case ColorManagementParams::Illuminant::D80: {
Wx = 0.950095542;
Wz = 1.284213976;
break;
}
case ColorManagementParams::Illuminant::D41: {
Wx = 0.991488263;
Wz = 0.631604625;
break;
}
case ColorManagementParams::Illuminant::D50: {
xyD = {0.3457, 0.3585, 1.0}; // near LCMS values but not perfect... it's a compromise!!
Wx = 0.964295676;
Wz = 0.825104603;
break;
}
case ColorManagementParams::Illuminant::D60: {
Wx = 0.952646075;
Wz = 1.008825184;
xyD = {0.32168, 0.33767, 1.0};
break;
}
case ColorManagementParams::Illuminant::D65: {
Wx = 0.95045471;
Wz = 1.08905029;
xyD = {0.312700492, 0.329000939, 1.0};
break;
}
case ColorManagementParams::Illuminant::D120: {
Wx = 0.979182;
Wz = 1.623623;
xyD = {0.269669, 0.28078, 1.0};
break;
}
case ColorManagementParams::Illuminant::STDA: {
Wx = 1.098500393;
Wz = 0.355848714;
xyD = {0.447573, 0.407440, 1.0};
ills = "stdA 2875K";
break;
}
case ColorManagementParams::Illuminant::TUNGSTEN_2000K: {
Wx = 1.274335;
Wz = 0.145233;
xyD = {0.526591, 0.41331, 1.0};
ills = "Tungsten 2000K";
break;
}
case ColorManagementParams::Illuminant::TUNGSTEN_1500K: {
Wx = 1.489921;
Wz = 0.053826;
xyD = {0.585703, 0.393157, 1.0};
ills = "Tungsten 1500K";
break;
}
}
double wprofpri[9];
if (gamutcontrol) {
//xyz in functiuon primaries and illuminant
Color::primaries_to_xyz(p, Wx, Wz, wprofpri);
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
wprofprim[i][j] = (double) wprofpri[j * 3 + i];
//xyz in TMatrix format
}
}
}
//D41 0.377984 0.381229
//D55 0.332424 0.347426
//D80 0.293755 0.309185
@@ -869,7 +977,7 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
cmsWriteTag(oprofdef, cmsSigGreenTRCTag, GammaTRC[1]);
cmsWriteTag(oprofdef, cmsSigBlueTRCTag, GammaTRC[2]);
//to read XYZ values and illuminant
//to read XYZ values and illuminant
if (rtengine::settings->verbose) {
cmsCIEXYZ *redT = static_cast<cmsCIEXYZ*>(cmsReadTag(oprofdef, cmsSigRedMatrixColumnTag));
cmsCIEXYZ *greenT = static_cast<cmsCIEXYZ*>(cmsReadTag(oprofdef, cmsSigGreenMatrixColumnTag));
@@ -881,6 +989,7 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
}
cmsFreeToneCurve(GammaTRC[0]);
if (oprofdef) {
constexpr cmsUInt32Number flags = cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE | cmsFLAGS_BLACKPOINTCOMPENSATION | cmsFLAGS_GAMUTCHECK;
const cmsHPROFILE iprof = ICCStore::getInstance()->getXYZProfile();
@@ -889,7 +998,10 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
lcmsMutex->unlock();
}
}
if (hTransform) {
#ifdef _OPENMP
#pragma omp parallel if (multiThread)
#endif
@@ -901,19 +1013,30 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
#pragma omp for schedule(dynamic, 16) nowait
#endif
for (int i = 0; i < ch; ++i) {
for (int i = 0; i < ch; ++i)
{
float *p = pBuf.data;
for (int j = 0; j < cw; ++j) {
const float r = src->r(i, j);
const float g = src->g(i, j);
const float b = src->b(i, j);
float X = toxyz[0][0] * r + toxyz[0][1] * g + toxyz[0][2] * b;
float Y = toxyz[1][0] * r + toxyz[1][1] * g + toxyz[1][2] * b;
float Z = toxyz[2][0] * r + toxyz[2][1] * g + toxyz[2][2] * b;
*(p++) = toxyz[0][0] * r + toxyz[0][1] * g + toxyz[0][2] * b;
*(p++) = toxyz[1][0] * r + toxyz[1][1] * g + toxyz[1][2] * b;
*(p++) = toxyz[2][0] * r + toxyz[2][1] * g + toxyz[2][2] * b;
if (gamutcontrol) {
Color::gamutmap(X, Y, Z, wprofprim);//gamut control
}
*(p++) = X;
*(p++) = Y;
*(p++) = Z;
}
p = pBuf.data;
cmsDoTransform(hTransform, p, p, cw);
for (int j = 0; j < cw; ++j) {
dst->r(i, j) = *(p++) * normalize;
dst->g(i, j) = *(p++) * normalize;
@@ -921,10 +1044,12 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
}
}
}
if (!keepTransForm) {
cmsDeleteTransform(hTransform);
hTransform = nullptr;
}
transform = hTransform;
}
}