Moved the ICC TRC patching code for RTv2 profiles from ImProcFunctions::lab2rgbOut to ICCStore

See #5026
This commit is contained in:
Alberto Griggio 2018-11-25 14:23:06 +01:00
parent 23aa0562aa
commit cfc947a865
2 changed files with 125 additions and 80 deletions

View File

@ -38,6 +38,8 @@
#include "../rtgui/threadutils.h"
#include "lcms2_plugin.h"
#include "color.h"
#include "cJSON.h"
#define inkc_constant 0x696E6B43
namespace rtengine
@ -208,8 +210,85 @@ const char* wpnames[] = {"sRGB", "Adobe RGB", "ProPhoto", "WideGamut", "BruceRGB
// high g=1.3 s=3.35 for high dynamic images
//low g=2.6 s=6.9 for low contrast images
//-----------------------------------------------------------------------------
// helper functions to fix V2 profiles TRCs, used in
// rtengine::ProfileContent::toProfile()
// see https://github.com/Beep6581/RawTherapee/issues/5026
// -----------------------------------------------------------------------------
bool is_RTv2_profile(cmsHPROFILE profile)
{
if (int(cmsGetProfileVersion(profile)) != 2) {
return false;
}
const cmsMLU *mlu = static_cast<const cmsMLU *>(cmsReadTag(profile, cmsSigDeviceMfgDescTag));
if (!mlu) {
return false;
}
cmsUInt32Number sz = cmsMLUgetASCII(mlu, "en", "US", nullptr, 0);
if (!sz) {
return false;
}
std::vector<char> buf(sz);
cmsMLUgetASCII(mlu, "en", "US", &buf[0], sz);
buf.back() = 0; // sanity
return strcmp(&buf[0], "RawTherapee") == 0;
}
bool get_RT_gamma_slope(cmsHPROFILE profile, double &gammatag, double &slopetag)
{
const cmsMLU *modelDescMLU = static_cast<const cmsMLU *>(cmsReadTag(profile, cmsSigDeviceModelDescTag));
if (modelDescMLU) {
cmsUInt32Number count = cmsMLUgetWide(modelDescMLU, "en", "US", nullptr, 0);
if (count) {
std::vector<wchar_t> vbuf(count);
wchar_t *buffer = &vbuf[0];
count = cmsMLUgetWide(modelDescMLU, "en", "US", buffer, count);
Glib::ustring modelDesc;
#if __SIZEOF_WCHAR_T__ == 2
char *cModelDesc = g_utf16_to_utf8((unsigned short int*)buffer, -1, nullptr, nullptr, nullptr); // convert to utf-8 in a buffer allocated by glib
if (cModelDesc) {
modelDesc.assign(cModelDesc);
g_free(cModelDesc);
}
#else
modelDesc = utf32_to_utf8(buffer, count);
#endif
if (!modelDesc.empty()) {
std::size_t pos = modelDesc.find("g");
std::size_t posmid = modelDesc.find("s");
std::size_t posend = modelDesc.find("!");
std::string strgamma = modelDesc.substr(pos + 1, (posmid - pos));
gammatag = std::stod(strgamma.c_str());
std::string strslope = modelDesc.substr(posmid + 1, (posend - posmid));
slopetag = std::stod(strslope.c_str());
return true;
}
}
}
return false;
}
Glib::ustring get_profile_description(cmsHPROFILE profile)
{
const cmsMLU *mlu = static_cast<const cmsMLU *>(cmsReadTag(profile, cmsSigProfileDescriptionTag));
if (!mlu) {
return "";
}
cmsUInt32Number sz = cmsMLUgetASCII(mlu, "en", "US", nullptr, 0);
if (!sz) {
return "";
}
std::vector<char> buf(sz);
cmsMLUgetASCII(mlu, "en", "US", &buf[0], sz);
buf.back() = 0; // sanity
return std::string(&buf[0]);
}
} // namespace
rtengine::ProfileContent::ProfileContent() = default;
rtengine::ProfileContent::ProfileContent(const Glib::ustring& fileName)
@ -255,11 +334,52 @@ rtengine::ProfileContent::ProfileContent(cmsHPROFILE hProfile)
cmsHPROFILE rtengine::ProfileContent::toProfile() const
{
cmsHPROFILE profile = nullptr;
if (!data.empty()) {
profile = cmsOpenProfileFromMem(data.c_str(), data.size());
// if this is a V2 profile generated by RawTherapee, we rebuild the
// TRC. See https://github.com/Beep6581/RawTherapee/issues/5026 and
// the references in there
if (profile && is_RTv2_profile(profile)) {
double gammatag, slopetag;
if (get_RT_gamma_slope(profile, gammatag, slopetag)) {
constexpr double eps = 0.000000001; // not divide by zero
double pwr = 1.0 / gammatag;
double ts = slopetag;
double slope = slopetag == 0 ? eps : slopetag;
return
!data.empty()
? cmsOpenProfileFromMem(data.c_str(), data.size())
: nullptr;
GammaValues g_b; //gamma parameters
Color::calcGamma(pwr, ts, 0, g_b); // call to calcGamma with selected gamma and slope : return parameters for LCMS2
cmsFloat64Number gammaParams[7]; //gamma parameters
gammaParams[4] = g_b[3] * ts;
gammaParams[0] = gammatag;
gammaParams[1] = 1. / (1.0 + g_b[4]);
gammaParams[2] = g_b[4] / (1.0 + g_b[4]);
gammaParams[3] = 1. / slope;
gammaParams[5] = 0.0;
gammaParams[6] = 0.0;
cmsToneCurve* GammaTRC;
if (slopetag == 0.) {
//printf("gammatag=%f\n", gammatag);
GammaTRC = cmsBuildGamma(NULL, gammatag);
} else {
GammaTRC = cmsBuildParametricToneCurve(nullptr, 5, gammaParams); //5 = smoother than 4
}
cmsWriteTag(profile, cmsSigRedTRCTag, GammaTRC);
cmsWriteTag(profile, cmsSigGreenTRCTag, GammaTRC);
cmsWriteTag(profile, cmsSigBlueTRCTag, GammaTRC);
cmsFreeToneCurve(GammaTRC);
if (settings->verbose) {
std::cout << "ICCStore: rebuilt TRC for RTv2 profile " << get_profile_description(profile) << ": gamma=" << gammatag << ", slope=" << slopetag << std::endl;
}
} else if (settings->verbose) {
std::cout << "ICCStore: no gamma/slope info found for RTv2 profile " << get_profile_description(profile) << std::endl;
}
}
}
return profile;
}
const std::string& rtengine::ProfileContent::getData() const

View File

@ -303,82 +303,7 @@ Imagefloat* ImProcFunctions::lab2rgbOut(LabImage* lab, int cx, int cy, int cw, i
}
Imagefloat* image = new Imagefloat(cw, ch);
cmsHPROFILE oprof = nullptr;
oprof = ICCStore::getInstance()->getProfile(icm.outputProfile);
Glib::ustring outtest = icm.outputProfile;
std::string fileis_RTv2 = outtest.substr(0, 4);
//printf("IsRTv2=%s\n", fileis_RTv2.c_str());
if(fileis_RTv2 == "RTv2") {//Only fot ICC v2 : read tag from desc to retrieve gamma and slope save before in generate ICC v2
//due to bug in LCMS in CmsToneCurve
//printf("icmout=%s \n",icm.output.c_str());
GammaValues g_b; //gamma parameters
const double eps = 0.000000001; // not divide by zero
double gammatag = 2.4;
double slopetag = 12.92310;
cmsMLU *modelDescMLU = (cmsMLU*) (cmsReadTag(oprof, cmsSigDeviceModelDescTag));
if (modelDescMLU) {
cmsUInt32Number count = cmsMLUgetWide(modelDescMLU, "en", "US", nullptr, 0); // get buffer length first
if (count) {
wchar_t *buffer = new wchar_t[count];
count = cmsMLUgetWide(modelDescMLU, "en", "US", buffer, count); // now put the string in the buffer
Glib::ustring modelDesc;
#if __SIZEOF_WCHAR_T__ == 2
char* cModelDesc = g_utf16_to_utf8((unsigned short int*)buffer, -1, nullptr, nullptr, nullptr); // convert to utf-8 in a buffer allocated by glib
if (cModelDesc) {
modelDesc.assign(cModelDesc);
g_free(cModelDesc);
}
#else
modelDesc = utf32_to_utf8(buffer, count);
#endif
delete [] buffer;
if (!modelDesc.empty()) {
std::size_t pos = modelDesc.find("g");
std::size_t posmid = modelDesc.find("s");
std::size_t posend = modelDesc.find("!");
std::string strgamma = modelDesc.substr(pos + 1, (posmid - pos));
gammatag = std::stod(strgamma.c_str());
std::string strslope = modelDesc.substr(posmid + 1, (posend - posmid));
slopetag = std::stod(strslope.c_str());
// printf("gam=%f slo=%f\n", gammatag, slopetag);
}
} else {
printf("Error: lab2rgbOut / String length is null!\n");
}
} else {
printf("Error: lab2rgbOut / cmsReadTag/cmsSigDeviceModelDescTag failed!\n");
}
double pwr = 1.0 / gammatag;
double ts = slopetag;
double slope = slopetag == 0 ? eps : slopetag;
int mode = 0;
Color::calcGamma(pwr, ts, mode, g_b); // call to calcGamma with selected gamma and slope : return parameters for LCMS2
cmsFloat64Number gammaParams[7]; //gamma parameters
gammaParams[4] = g_b[3] * ts;
gammaParams[0] = gammatag;
gammaParams[1] = 1. / (1.0 + g_b[4]);
gammaParams[2] = g_b[4] / (1.0 + g_b[4]);
gammaParams[3] = 1. / slope;
gammaParams[5] = 0.0;
gammaParams[6] = 0.0;
cmsToneCurve* GammaTRC[3];
if(slopetag == 0.) {
//printf("gammatag=%f\n", gammatag);
GammaTRC[0] = GammaTRC[1] = GammaTRC[2] = cmsBuildGamma(NULL, gammatag);
}
else {
GammaTRC[0] = GammaTRC[1] = GammaTRC[2] = cmsBuildParametricToneCurve(nullptr, 5, gammaParams); //5 = smoother than 4
}
cmsWriteTag(oprof, cmsSigRedTRCTag, GammaTRC[0]);
cmsWriteTag(oprof, cmsSigGreenTRCTag, GammaTRC[1]);
cmsWriteTag(oprof, cmsSigBlueTRCTag, GammaTRC[2]);
cmsFreeToneCurve(GammaTRC[0]);
}
cmsHPROFILE oprof = ICCStore::getInstance()->getProfile(icm.outputProfile);
if (oprof) {
cmsUInt32Number flags = cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE;