Skip to content

Commit

Permalink
More precise calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
awawa-dev committed Sep 12, 2024
1 parent 205ebe0 commit 96544ac
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 168 deletions.
6 changes: 3 additions & 3 deletions include/lut-calibrator/LutCalibrator.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class LutCalibrator : public QObject
void SignalLutCalibrationUpdated(const QJsonObject& data);

public slots:
void incomingCommand(QString rootpath, GrabberWrapper* grabberWrapper, hyperhdr::Components defaultComp, int checksum, double saturation, double luminance, double gammaR, double gammaG, double gammaB);
void startHandler(QString rootpath, hyperhdr::Components defaultComp, bool debug);
void stopHandler();
void setVideoImage(const QString& name, const Image<ColorRgb>& image);
void setSystemImage(const QString& name, const Image<ColorRgb>& image);
Expand All @@ -96,7 +96,6 @@ public slots:
void tryHDR10();
void setupWhitePointCorrection();
bool setTestData();
void detectGamma();

void capturedPrimariesCorrection(ColorSpaceMath::HDR_GAMMA gamma, double gammaHLG, double nits, int coef, linalg::mat<double, 3, 3>& convert_bt2020_to_XYZ, linalg::mat<double, 3, 3>& convert_XYZ_to_corrected, bool printDebug = false);
bool parseTextLut2binary(const char* filename = "D:/interpolated_lut.txt", const char* outfile = "D:/lut_lin_tables.3d");
Expand All @@ -106,5 +105,6 @@ public slots:
std::shared_ptr<YuvConverter> _yuvConverter;
std::shared_ptr< BestResult> bestResult;
MemoryBuffer<uint8_t> _lut;
QString _rootPath;
QString _rootPath;
bool _debug;
};
9 changes: 2 additions & 7 deletions sources/api/HyperAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1006,17 +1006,12 @@ void HyperAPI::handleLutCalibrationCommand(const QJsonObject& message, const QSt
return;
}

int checksum = message["checksum"].toInt(-1);
double saturation = message["saturation"].toDouble(1.0);
double luminance = message["luminance"].toDouble(1.0);
double gammaR = message["gammaR"].toDouble(1.0);
double gammaG = message["gammaG"].toDouble(1.0);
double gammaB = message["gammaB"].toDouble(1.0);
bool debug = message["debug"].toBool(false);

sendSuccessReply(command, tan);

if (subcommand == "capture")
_lutCalibrator->incomingCommand(_instanceManager->getRootPath(), (_videoGrabber != nullptr) ? _videoGrabber->grabberWrapper() : nullptr, getActiveComponent(), checksum, saturation, luminance, gammaR, gammaG, gammaB);
_lutCalibrator->startHandler(_instanceManager->getRootPath(), getActiveComponent(), debug);
else
_lutCalibrator->stopHandler();
}
Expand Down
21 changes: 3 additions & 18 deletions sources/api/JSONRPC_schema/schema-lut-calibration.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,9 @@
"tan" : {
"type" : "integer"
},
"checksum": {
"type" : "integer",
"required" : true
},
"saturation": {
"type" : "number"
},
"luminance": {
"type" : "number"
},
"gammaR": {
"type" : "number"
},
"gammaG": {
"type" : "number"
},
"gammaB": {
"type" : "number"
"debug": {
"type" : "boolean",
"required" : false
}
},
"additionalProperties": false
Expand Down
10 changes: 3 additions & 7 deletions sources/lut-calibrator/ColorSpace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -411,13 +411,9 @@ namespace ColorSpaceMath

void trim01(double3& input)
{
for (int i = 0; i < 3; i++)
{
if (input[i] > 1.0)
input[i] = 1.0;
else if (input[i] < 0.0)
input[i] = 0.0;
}
input.x = std::max(0.0, std::min(input.x, 1.0));
input.y = std::max(0.0, std::min(input.y, 1.0));
input.z = std::max(0.0, std::min(input.z, 1.0));
}

QString vecToString(const double2& v)
Expand Down
210 changes: 83 additions & 127 deletions sources/lut-calibrator/LutCalibrator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ LutCalibrator::LutCalibrator()
_log = Logger::getInstance("CALIBRATOR");
_capturedColors = std::make_shared<CapturedColors>();
_yuvConverter = std::make_shared<YuvConverter>();
_debug = true;
}

void LutCalibrator::error(QString message)
Expand Down Expand Up @@ -298,9 +299,10 @@ void LutCalibrator::sendReport(QString report)
Debug(_log, REPORT_TOKEN "%s\r\n", QSTRING_CSTR(list.join("\r\n")));
}

void LutCalibrator::incomingCommand(QString rootpath, GrabberWrapper* grabberWrapper, hyperhdr::Components defaultComp, int checksum, double saturation, double luminance, double gammaR, double gammaG, double gammaB)
void LutCalibrator::startHandler(QString rootpath, hyperhdr::Components defaultComp, bool debug)
{
_rootPath = rootpath;
_debug = debug;

stopHandler();

Expand Down Expand Up @@ -626,7 +628,7 @@ linalg::vec<double, 3> LutCalibrator::hdr_to_srgb(linalg::vec<double, 3> yuv, co
{
srgb = ColorSpaceMath::from_BT2020_to_BT709(e);
}

ColorSpaceMath::trim01(srgb);
srgb = srgb_linear_to_nonlinear(srgb);

if (tryBt2020Range)
Expand Down Expand Up @@ -670,9 +672,12 @@ void LutCalibrator::scoreBoard(bool testOnly, const linalg::mat<double, 4, 4>& c
}
};

void LutCalibrator::detectGamma()
{
const auto white = _capturedColors->all[SCREEN_COLOR_DIMENSION - 1][SCREEN_COLOR_DIMENSION - 1][SCREEN_COLOR_DIMENSION - 1].Y();


void LutCalibrator::fineTune()
{
const auto MAX_IND = SCREEN_COLOR_DIMENSION - 1;
const auto white = _capturedColors->all[MAX_IND][MAX_IND][MAX_IND].Y();
double2 nits{ 0, 0 };
double maxLevel = 0;

Expand All @@ -685,142 +690,91 @@ void LutCalibrator::detectGamma()
maxLevel = white / 255.0;
}

nits[HDR_GAMMA::PQ] = 10000.0 * PQ_ST2084(1.0, maxLevel);
nits[HDR_GAMMA::PQ] = 10000.0 * PQ_ST2084(1.0, maxLevel);

for (int gamma = HDR_GAMMA::PQ; gamma <= HDR_GAMMA::HLG; gamma++)
for (double gammaHLG = 0; gammaHLG <= ((gamma == HDR_GAMMA::PQ) ? 0 : 1.2); gammaHLG += ((gammaHLG == 0) ? 1.1 : 0.1))
{
std::vector<double> gammasHLG;
if (gamma == HDR_GAMMA::PQ)
gammasHLG = { 0 };
else if (gamma == HDR_GAMMA::HLG)
gammasHLG = { 0 , 1.2, 1.1};

for (double gammaHLG : gammasHLG)
{
if (gammaHLG == 1.1 && bestResult->gammaHLG == 0)
break;

if (gamma == HDR_GAMMA::HLG)
{
nits[HDR_GAMMA::HLG] = 1 / OOTF_HLG(inverse_OETF_HLG(maxLevel), gammaHLG).x;
}
for (int coef = YuvConverter::YUV_COEFS::BT601; coef <= YuvConverter::YUV_COEFS::BT2020; coef++)
for (int coef = YuvConverter::YUV_COEFS::FCC; coef <= YuvConverter::YUV_COEFS::BT2020; coef++)
{
double3x3 convert_bt2020_to_XYZ;
double3x3 convert_XYZ_to_sRgb;
printf("Processing gamma: %s, gammaHLG: %f, coef: %s. Current best gamma: %s, gammaHLG: %f, coef: %s. Score: %lli\n",
QSTRING_CSTR(gammaToString(HDR_GAMMA(gamma))), gammaHLG, QSTRING_CSTR(_yuvConverter->coefToString(YuvConverter::YUV_COEFS(coef))),
QSTRING_CSTR(gammaToString(HDR_GAMMA(bestResult->gamma))), bestResult->gammaHLG, QSTRING_CSTR(_yuvConverter->coefToString(YuvConverter::YUV_COEFS(bestResult->coef))), bestResult->minError / 1000);

auto coefValues = _yuvConverter->getCoef(YuvConverter::YUV_COEFS(coef)) + double2(0, 0);;
auto coefMatrix = _yuvConverter->create_yuv_to_rgb_matrix(_capturedColors->getRange(), coefValues.x, coefValues.y);

capturedPrimariesCorrection(HDR_GAMMA(gamma), gammaHLG, nits[gamma], coef, convert_bt2020_to_XYZ, convert_XYZ_to_sRgb);
auto bt2020_to_sRgb = mul(convert_XYZ_to_sRgb, convert_bt2020_to_XYZ);

for (double aspectX = 0.95; aspectX <= 1.05; aspectX += 0.01)
for (double aspectY = 1.15; aspectY >= 0.98; aspectY -= 0.01)
for (double aspectZ = 1.15; aspectZ >= 0.98; aspectZ -= 0.01)
for (int altConvert = 0; altConvert <= 1; altConvert++)
for (int tryBt2020Range = 0; tryBt2020Range <= 1; tryBt2020Range++)
for (double krDelta = -0.018; krDelta <= 0.01801; krDelta += 0.002)
for (double kbDelta = -0.018; kbDelta <= 0.01801; kbDelta += 0.002)
{
long long int currentError = 0;
for (int r = SCREEN_COLOR_DIMENSION - 1; r >= 0 && currentError < bestResult->minError; r--)
for (int g = SCREEN_COLOR_DIMENSION - 1; g >= 0 && currentError < bestResult->minError; g--)
for (int b = SCREEN_COLOR_DIMENSION - 1; b >= 0 && currentError < bestResult->minError; b--)
if ((r % 2 == 0 && g % 4 == 0 && b % 4 == 0) || (r == b && b == g))
{
auto& sample = _capturedColors->all[r][g][b];
auto yuv = _yuvConverter->multiplyColorMatrix(coefMatrix, sample.yuv());
auto srgb = hdr_to_srgb(sample.yuv(), byte2{ sample.U(), sample.V() }, double3{ aspectX, aspectY, aspectZ }, coefMatrix, HDR_GAMMA(gamma), gammaHLG, nits[gamma], altConvert, bt2020_to_sRgb, tryBt2020Range);
auto SRGB = to_int3(srgb * 255.0);

currentError += sample.getSourceError(SRGB);
if (r + 2 == SCREEN_COLOR_DIMENSION && g + 2 == SCREEN_COLOR_DIMENSION && b + 2 == SCREEN_COLOR_DIMENSION && linalg::maxelem(SRGB) > 250)
currentError = bestResult->minError;
}
if (currentError < bestResult->minError)
{
bestResult->minError = currentError;
bestResult->coef = YuvConverter::YUV_COEFS(coef);
bestResult->coefDelta = double2(0, 0);
bestResult->bt2020Range = tryBt2020Range;
bestResult->altConvert = altConvert;
bestResult->aspect = double3(aspectX, aspectY, aspectZ);
bestResult->nits = nits[gamma];
bestResult->gamma = HDR_GAMMA(gamma);
bestResult->gammaHLG = gammaHLG;
}
}
}
}
};
double3x3 convert_bt2020_to_XYZ;
double3x3 convert_XYZ_to_sRgb;
double2 kDelta = double2(krDelta, kbDelta);

void LutCalibrator::fineTune()
{
const auto white = _capturedColors->all[SCREEN_COLOR_DIMENSION - 1][SCREEN_COLOR_DIMENSION - 1][SCREEN_COLOR_DIMENSION - 1].Y();
double2 nits{ 0, 0 };
double maxLevel = 0;
auto coefValues = _yuvConverter->getCoef(YuvConverter::YUV_COEFS(coef)) + kDelta;
auto coefMatrix = _yuvConverter->create_yuv_to_rgb_matrix(_capturedColors->getRange(), coefValues.x, coefValues.y);

if (_capturedColors->getRange() == YuvConverter::COLOR_RANGE::LIMITED)
{
maxLevel = (white - 16.0) / (235.0 - 16.0);
}
else
{
maxLevel = white / 255.0;
}

nits[HDR_GAMMA::PQ] = 10000.0 * PQ_ST2084(1.0, maxLevel);
int gamma = bestResult->gamma;
int altConvert = bestResult->altConvert;
int tryBt2020Range = bestResult->bt2020Range;
int coef = bestResult->coef;
capturedPrimariesCorrection(HDR_GAMMA(gamma), gammaHLG, nits[gamma], coef, convert_bt2020_to_XYZ, convert_XYZ_to_sRgb);
auto bt2020_to_sRgb = mul(convert_XYZ_to_sRgb, convert_bt2020_to_XYZ);

auto gammaHLG = bestResult->gammaHLG;
{
if (gamma == HDR_GAMMA::HLG)
{
nits[HDR_GAMMA::HLG] = 1 / OOTF_HLG(inverse_OETF_HLG(maxLevel), gammaHLG).x;
}
for (double krDelta = -0.018; krDelta <= 0.018; krDelta += 0.002)
for (double kbDelta = -0.018; kbDelta <= 0.018; kbDelta += 0.002)
{
double3x3 convert_bt2020_to_XYZ;
double3x3 convert_XYZ_to_sRgb;
double2 kDelta = double2(krDelta, kbDelta);

auto coefValues = _yuvConverter->getCoef(YuvConverter::YUV_COEFS(coef)) + kDelta;
auto coefMatrix = _yuvConverter->create_yuv_to_rgb_matrix(_capturedColors->getRange(), coefValues.x, coefValues.y);

capturedPrimariesCorrection(HDR_GAMMA(gamma), gammaHLG, nits[gamma], coef, convert_bt2020_to_XYZ, convert_XYZ_to_sRgb);
auto bt2020_to_sRgb = mul(convert_XYZ_to_sRgb, convert_bt2020_to_XYZ);

auto starterX = bestResult->aspect.x;
auto starterY = bestResult->aspect.y;
auto starterZ = bestResult->aspect.z;
for (double aspectX = starterX - 0.03; aspectX <= starterX + 0.03; aspectX += 0.005)
for (double aspectY = starterY + 0.03; aspectY >= starterY - 0.03; aspectY -= 0.005)
for (double aspectZ = starterZ + 0.03; aspectZ >= starterZ - 0.03; aspectZ -= 0.005)
{
double3 aspect(aspectX, aspectY, aspectZ);
for (int altConvert = 0; altConvert <= 1; altConvert++)
for (int tryBt2020Range = 0; tryBt2020Range <= 1; tryBt2020Range++)
for (double aspectX = 1.0; aspectX >= 0.9799; aspectX -= 0.01)
for (double aspectYZ = 1.0; aspectYZ <= 1.0901; aspectYZ += 0.005)

long long int currentError = 0;
for (int r = SCREEN_COLOR_DIMENSION - 1; r >= 0 && currentError < bestResult->minError; r--)
for (int g = SCREEN_COLOR_DIMENSION - 1; g >= 0 && currentError < bestResult->minError; g--)
for (int b = SCREEN_COLOR_DIMENSION - 1; b >= 0 && currentError < bestResult->minError; b--)
if ((r % 2 == 0 && g % 4 == 0 && b % 4 == 0) || (r == b && b == g))
{
auto& sample = _capturedColors->all[r][g][b];
auto yuv = _yuvConverter->multiplyColorMatrix(coefMatrix, sample.yuv());
auto srgb = hdr_to_srgb(sample.yuv(), byte2{ sample.U(), sample.V() }, aspect, coefMatrix, HDR_GAMMA(gamma), gammaHLG, nits[gamma], altConvert, bt2020_to_sRgb, tryBt2020Range);
auto SRGB = to_int3(srgb * 255.0);
currentError += sample.getSourceError(SRGB);

if (r + 2 == SCREEN_COLOR_DIMENSION && g + 2 == SCREEN_COLOR_DIMENSION && b + 2 == SCREEN_COLOR_DIMENSION && linalg::maxelem(SRGB) > 250)
currentError = bestResult->minError;
double3 aspect(aspectX, aspectYZ, aspectYZ);

long long int currentError = 0;
for (int rr = MAX_IND; rr >= 0 && currentError < bestResult->minError; rr--)
for (int gg = MAX_IND; gg >= 0 && currentError < bestResult->minError; gg--)
for (int bb = MAX_IND; bb >= 0 && currentError < bestResult->minError; bb--)
{
int r = (rr == MAX_IND) ? MAX_IND - 1 : ((rr == MAX_IND - 1) ? MAX_IND : rr);
int g = (gg == MAX_IND) ? MAX_IND - 1 : ((gg == MAX_IND - 1) ? MAX_IND : gg);
int b = (bb == MAX_IND) ? MAX_IND - 1 : ((bb == MAX_IND - 1) ? MAX_IND : bb);

if ((r % 2 == 0 && g % 4 == 0 && b % 4 == 0) || (r == b && b == g))
{
auto& sample = _capturedColors->all[r][g][b];
auto yuv = _yuvConverter->multiplyColorMatrix(coefMatrix, sample.yuv());
auto srgb = hdr_to_srgb(sample.yuv(), byte2{ sample.U(), sample.V() }, aspect, coefMatrix, HDR_GAMMA(gamma), gammaHLG, nits[gamma], altConvert, bt2020_to_sRgb, tryBt2020Range);
auto SRGB = to_int3(srgb * 255.0);


currentError += sample.getSourceError(SRGB);

if ((r + 2 == SCREEN_COLOR_DIMENSION && g + 2 == SCREEN_COLOR_DIMENSION && b + 2 == SCREEN_COLOR_DIMENSION &&
(SRGB.x > 248 || SRGB.x < 232)))
currentError = bestResult->minError;
}
}
if (currentError < bestResult->minError)
{
bestResult->minError = currentError;
bestResult->coef = YuvConverter::YUV_COEFS(coef);
bestResult->coefDelta = kDelta;
bestResult->bt2020Range = tryBt2020Range;
bestResult->altConvert = altConvert;
bestResult->aspect = aspect;
bestResult->nits = nits[gamma];
bestResult->gamma = HDR_GAMMA(gamma);
bestResult->gammaHLG = gammaHLG;
}
}

if (currentError < bestResult->minError)
{
bestResult->minError = currentError;
bestResult->coef = YuvConverter::YUV_COEFS(coef);
bestResult->coefDelta = kDelta;
bestResult->bt2020Range = tryBt2020Range;
bestResult->altConvert = altConvert;
bestResult->aspect = aspect;
bestResult->nits = nits[gamma];
bestResult->gamma = HDR_GAMMA(gamma);
bestResult->gammaHLG = gammaHLG;
}
}
}
}
}
}
Expand All @@ -832,9 +786,7 @@ void LutCalibrator::tryHDR10()
const int SCALE = SCREEN_COLOR_DIMENSION - 1;
const auto white = _capturedColors->all[SCALE][SCALE][SCALE].Y();

detectGamma();

fineTune();
fineTune();

double3x3 convert_bt2020_to_XYZ;
double3x3 convert_XYZ_to_sRgb;
Expand All @@ -847,7 +799,9 @@ void LutCalibrator::tryHDR10()

scoreBoard(false, coefMatrix, bestResult->gamma, bestResult->gammaHLG, bestResult->nits, bestResult->aspect, bestResult->bt2020Range, bestResult->altConvert, bt2020_to_sRgb, bestResult->minError, currentError);


Debug(_log, "Score: %f", bestResult->minError / 1000.0);

Debug(_log, "Selected coef: %s", QSTRING_CSTR( _yuvConverter->coefToString(bestResult->coef)));
Debug(_log, "selected coef delta: %f %f", bestResult->coefDelta.x, bestResult->coefDelta.y);
Debug(_log, "Selected EOTF: %s", QSTRING_CSTR(ColorSpaceMath::gammaToString(bestResult->gamma)));
Expand Down Expand Up @@ -1058,6 +1012,8 @@ bool LutCalibrator::setTestData()
{
std::vector<std::vector<std::vector<std::vector<int>>>> testData;

// asssign your test data from calibration_captured_yuv.txt to testData here

if (testData.size() != SCREEN_COLOR_DIMENSION ||
testData[0].size() != SCREEN_COLOR_DIMENSION ||
testData[0][0].size() != SCREEN_COLOR_DIMENSION ||
Expand Down
Loading

0 comments on commit 96544ac

Please sign in to comment.