diff --git a/include/lut-calibrator/BestResult.h b/include/lut-calibrator/BestResult.h index 8a789559d..b615944f7 100644 --- a/include/lut-calibrator/BestResult.h +++ b/include/lut-calibrator/BestResult.h @@ -80,6 +80,7 @@ struct BestResult double upYLimit = 0; double downYLimit = 0; double yShift = 0; + bool isSourceP010 = false; } signal; long long int minError = MAX_CALIBRATION_ERROR; @@ -121,6 +122,7 @@ struct BestResult out << "bestResult.signal.upYLimit = " << std::to_string(signal.upYLimit) << ";" << std::endl; out << "bestResult.signal.downYLimit = " << std::to_string(signal.downYLimit) << ";" << std::endl; out << "bestResult.signal.yShift = " << std::to_string(signal.yShift) << ";" << std::endl; + out << "bestResult.signal.isSourceP010 = " << std::to_string(signal.isSourceP010) << ";" << std::endl; out << "bestResult.minError = " << std::to_string(std::round(minError * 100.0) / 30000.0) << ";" << std::endl; out << "*/" << std::endl; } diff --git a/include/lut-calibrator/ColorSpace.h b/include/lut-calibrator/ColorSpace.h index 9833e6b82..800013c3d 100644 --- a/include/lut-calibrator/ColorSpace.h +++ b/include/lut-calibrator/ColorSpace.h @@ -44,7 +44,7 @@ using namespace aliases; namespace ColorSpaceMath { - enum PRIMARIES { SRGB = 0, BT_2020, WIDE_GAMMUT }; + enum PRIMARIES { SRGB = 0, BT_2020, WIDE_GAMMUT }; QString gammaToString(HDR_GAMMA gamma); @@ -85,6 +85,8 @@ namespace ColorSpaceMath double3 bt2020_linear_to_nonlinear(double3 input); + double srgb_nonlinear_to_linear(double input); + double3 srgb_nonlinear_to_linear(double3 input); double3 srgb_linear_to_nonlinear(double3 input); diff --git a/include/lut-calibrator/LutCalibrator.h b/include/lut-calibrator/LutCalibrator.h index 816afa5da..8245bfa02 100644 --- a/include/lut-calibrator/LutCalibrator.h +++ b/include/lut-calibrator/LutCalibrator.h @@ -58,7 +58,7 @@ namespace linalg { } namespace ColorSpaceMath { - enum HDR_GAMMA { PQ = 0, HLG, sRGB, BT2020inSRGB, PQinSRGB}; + enum HDR_GAMMA { PQ = 0, HLG, sRGB, BT2020inSRGB, PQinSRGB, P010 }; } struct BestResult; diff --git a/sources/grabber/linux/v4l2/V4L2Grabber.cpp b/sources/grabber/linux/v4l2/V4L2Grabber.cpp index 9b363884b..56275355b 100644 --- a/sources/grabber/linux/v4l2/V4L2Grabber.cpp +++ b/sources/grabber/linux/v4l2/V4L2Grabber.cpp @@ -61,6 +61,21 @@ // some stuff for HDR tone mapping #define LUT_FILE_SIZE 50331648 +namespace +{ + #ifdef V4L2_PIX_FMT_P010 + #pragma message "P010 is supported on the build machine" + bool supportedP010 = true; + #else + #pragma message "P010 is NOT supported on the build machine" + bool supportedP010 = false; + #endif +}; + +#ifndef V4L2_PIX_FMT_P010 + #define V4L2_PIX_FMT_P010 v4l2_fourcc('P', '0', '1', '0') +#endif + static const V4L2Grabber::HyperHdrFormat supportedFormats[] = { { V4L2_PIX_FMT_YUYV, PixelFormat::YUYV }, @@ -68,10 +83,8 @@ static const V4L2Grabber::HyperHdrFormat supportedFormats[] = { V4L2_PIX_FMT_RGB24, PixelFormat::RGB24 }, { V4L2_PIX_FMT_YUV420, PixelFormat::I420 }, { V4L2_PIX_FMT_NV12, PixelFormat::NV12 }, - { V4L2_PIX_FMT_MJPEG, PixelFormat::MJPEG } - #ifdef V4L2_PIX_FMT_P010 - ,{ V4L2_PIX_FMT_P010, PixelFormat::P010 } - #endif + { V4L2_PIX_FMT_MJPEG, PixelFormat::MJPEG }, + { V4L2_PIX_FMT_P010, PixelFormat::P010 } }; @@ -84,6 +97,8 @@ V4L2Grabber::V4L2Grabber(const QString& device, const QString& configurationPath { // Refresh devices getV4L2devices(); + + Debug(_log, "P010 was %s on the build machine", (supportedP010) ? "supported" : "unsupported"); } QString V4L2Grabber::GetSharedLut() @@ -132,7 +147,8 @@ void V4L2Grabber::setHdrToneMappingEnabled(int mode) { Debug(_log, "setHdrToneMappingMode replacing LUT and restarting"); _V4L2WorkerManager.Stop(); - if ((_actualVideoFormat == PixelFormat::YUYV) || (_actualVideoFormat == PixelFormat::I420) || (_actualVideoFormat == PixelFormat::NV12) || (_actualVideoFormat == PixelFormat::MJPEG)) + if ((_actualVideoFormat == PixelFormat::YUYV) || (_actualVideoFormat == PixelFormat::I420) || (_actualVideoFormat == PixelFormat::NV12) + || (_actualVideoFormat == PixelFormat::P010) || (_actualVideoFormat == PixelFormat::MJPEG)) loadLutFile(PixelFormat::YUYV); else loadLutFile(PixelFormat::RGB24); @@ -985,6 +1001,16 @@ bool V4L2Grabber::init_device(QString selectedDeviceName, DevicePropertiesItem p } break; + case V4L2_PIX_FMT_P010: + { + loadLutFile(PixelFormat::YUYV); + _actualVideoFormat = PixelFormat::P010; + _frameByteSize = (props.x * props.y * 6) / 2; + _lineLength = props.x * 2; + Info(_log, "Video pixel format is set to: P010"); + } + break; + case V4L2_PIX_FMT_NV12: { loadLutFile(PixelFormat::YUYV); diff --git a/sources/grabber/windows/MF/MFGrabber.cpp b/sources/grabber/windows/MF/MFGrabber.cpp index 91c786bda..f46867ded 100644 --- a/sources/grabber/windows/MF/MFGrabber.cpp +++ b/sources/grabber/windows/MF/MFGrabber.cpp @@ -189,7 +189,7 @@ void MFGrabber::setHdrToneMappingEnabled(int mode) { Debug(_log, "setHdrToneMappingMode replacing LUT and restarting"); _MFWorkerManager.Stop(); - if ((_actualVideoFormat == PixelFormat::YUYV) || (_actualVideoFormat == PixelFormat::I420) || (_actualVideoFormat == PixelFormat::NV12) || (_actualVideoFormat == PixelFormat::MJPEG)) + if ((_actualVideoFormat == PixelFormat::YUYV) || (_actualVideoFormat == PixelFormat::I420) || (_actualVideoFormat == PixelFormat::P010) || (_actualVideoFormat == PixelFormat::NV12) || (_actualVideoFormat == PixelFormat::MJPEG)) loadLutFile(PixelFormat::YUYV); else loadLutFile(PixelFormat::RGB24); @@ -869,6 +869,14 @@ bool MFGrabber::init_device(QString selectedDeviceName, DevicePropertiesItem pro } break; + case PixelFormat::P010: + { + loadLutFile(PixelFormat::YUYV); + _frameByteSize = (6 * props.x * props.y) / 2; + _lineLength = props.x * 2; + } + break; + case PixelFormat::RGB24: { loadLutFile(PixelFormat::RGB24); diff --git a/sources/lut-calibrator/ColorSpace.cpp b/sources/lut-calibrator/ColorSpace.cpp index 663b1c8da..445fedc3e 100644 --- a/sources/lut-calibrator/ColorSpace.cpp +++ b/sources/lut-calibrator/ColorSpace.cpp @@ -76,6 +76,8 @@ namespace ColorSpaceMath return "BT2020 with sRGB TRC"; else if (gamma == HDR_GAMMA::PQinSRGB) return "PQ in SRGB"; + else if (gamma == HDR_GAMMA::P010) + return "P010"; return "UNKNOWN"; } diff --git a/sources/lut-calibrator/LutCalibrator.cpp b/sources/lut-calibrator/LutCalibrator.cpp index a24825d54..f4d1fa3fa 100644 --- a/sources/lut-calibrator/LutCalibrator.cpp +++ b/sources/lut-calibrator/LutCalibrator.cpp @@ -118,6 +118,25 @@ LutCalibrator::~LutCalibrator() Info(_log, "The calibration object is deleted"); } +static void unpackP010(double *y, double *u, double *v) +{ + if (y !=nullptr) + { + *y = *y / 1.5; + } + + for (auto chroma : { u, v }) + if (chroma != nullptr) + { + double val = (*chroma - (128.0/255.0)) / 1.5; + *chroma = (128.0 / 255.0) + val; + } +}; + +static void unpackP010(double3& yuv) +{ + unpackP010(&yuv.x, &yuv.y, &yuv.z); +}; void LutCalibrator::cancelCalibrationSafe() { @@ -302,6 +321,7 @@ void LutCalibrator::startHandler() _capturedColors.reset(); _capturedColors = std::make_shared(); + bestResult = std::make_shared(); if (setTestData()) { @@ -406,11 +426,15 @@ void LutCalibrator::handleImage(const Image& image) } auto pixelFormat = image.getOriginFormat(); - if (pixelFormat != PixelFormat::NV12 && pixelFormat != PixelFormat::MJPEG && pixelFormat != PixelFormat::YUYV) + if (pixelFormat != PixelFormat::NV12 && pixelFormat != PixelFormat::MJPEG && pixelFormat != PixelFormat::YUYV && pixelFormat != PixelFormat::P010) { - error("Only NV12/MJPEG/YUYV video format for the USB grabber and NV12 for the flatbuffers source are supported for the LUT calibration."); + error("Only NV12/MJPEG/YUYV/P010 video format for the USB grabber and NV12 for the flatbuffers source are supported for the LUT calibration."); return; } + else if (pixelFormat == PixelFormat::P010) + { + bestResult->signal.isSourceP010 = true; + } int boardIndex = -1; @@ -663,6 +687,11 @@ static double3 hdr_to_srgb(const YuvConverter* _yuvConverter, double3 yuv, const double3 srgb; bool white = true; + if (gamma == HDR_GAMMA::P010) + { + unpackP010(yuv); + } + if (gamma == HDR_GAMMA::sRGB || gamma == HDR_GAMMA::BT2020inSRGB) { CapturedColors::correctYRange(yuv, signal.yRange, signal.upYLimit, signal.downYLimit, signal.yShift); @@ -689,7 +718,7 @@ static double3 hdr_to_srgb(const YuvConverter* _yuvConverter, double3 yuv, const double3 e; - if (gamma == HDR_GAMMA::PQ) + if (gamma == HDR_GAMMA::PQ || gamma == HDR_GAMMA::P010) { e = PQ_ST2084(10000.0 / nits, a); } @@ -1094,9 +1123,13 @@ void LutCalibrator::fineTune(bool precise) sampleColors[SampleColor::LOW_GREEN] = (std::pair(sampleGreenLow.getInputYuvColors().front().first, byte2{ sampleGreenLow.U(), sampleGreenLow.V() })); sampleColors[SampleColor::LOW_BLUE] = (std::pair(sampleBlueLow.getInputYuvColors().front().first, byte2{ sampleBlueLow.U(), sampleBlueLow.V() })); - for (int gamma = (precise) ? bestResult->gamma : HDR_GAMMA::PQ; gamma <= HDR_GAMMA::PQinSRGB; gamma++) + for (int gamma = (precise) ? (bestResult->gamma) : ((bestResult->signal.isSourceP010) ? HDR_GAMMA::P010 : HDR_GAMMA::PQ); + gamma <= HDR_GAMMA::P010; gamma++) { std::vector gammasHLG; + + if (gamma == HDR_GAMMA::P010 && !bestResult->signal.isSourceP010) + continue; if (gamma == HDR_GAMMA::HLG) { @@ -1116,6 +1149,13 @@ void LutCalibrator::fineTune(bool precise) { NITS = 10000.0 * PQ_ST2084(1.0, maxLevel); } + else if (gamma == HDR_GAMMA::P010) + { + double unpackWhite = white; + unpackP010(&unpackWhite, nullptr, nullptr); + maxLevel = (unpackWhite - 16.0) / (235.0 - 16.0); + NITS = 10000.0 * PQ_ST2084(1.0, maxLevel); + } else if (gamma == HDR_GAMMA::PQinSRGB) { NITS = 10000.0 * PQ_ST2084(1.0, srgb_linear_to_nonlinear(maxLevel)); @@ -1230,11 +1270,12 @@ void LutCalibrator::calibration() { Debug(_log, "Selected nits: %f", (bestResult->gamma == HDR_GAMMA::HLG) ? 1000.0 * (1 / bestResult->nits) : bestResult->nits); } - Debug(_log, "Selected bt2020 gamma range: %i", bestResult->bt2020Range); - Debug(_log, "Selected alternative conversion of primaries: %i", bestResult->altConvert); + Debug(_log, "Selected bt2020 gamma range: %s", (bestResult->bt2020Range) ? "yes" : "no"); + Debug(_log, "Selected alternative conversion of primaries: %s", (bestResult->altConvert) ? "yes" : "no"); Debug(_log, "Selected aspect: %f %f %f", bestResult->aspect.x, bestResult->aspect.y, bestResult->aspect.z); Debug(_log, "Selected color aspect mode: %i", bestResult->coloredAspectMode); Debug(_log, "Selected color aspect: %s %s", QSTRING_CSTR(vecToString(bestResult->colorAspect.first)), QSTRING_CSTR(vecToString(bestResult->colorAspect.second))); + Debug(_log, "Selected source is P010: %s", (bestResult->signal.isSourceP010) ? "yes" : "no"); if (_debug) { @@ -1423,6 +1464,11 @@ void CreateLutWorker::run() } else { + if (bestResult->signal.isSourceP010) + { + unpackP010(yuv); + } + yuv = yuvConverter->toRgb(bestResult->signal.range, bestResult->coef, yuv); } @@ -1543,8 +1589,7 @@ void LutCalibrator::calibrate() { emit GlobalSignals::getInstance()->SignalRequestComponent(hyperhdr::Components::COMP_FLATBUFSERVER, -1, false); } - - bestResult = std::make_shared(); + _capturedColors->finilizeBoard(); @@ -1577,9 +1622,16 @@ void LutCalibrator::capturedPrimariesCorrection(ColorSpaceMath::HDR_GAMMA gamma, for (auto& c : capturedPrimaries) { - auto a = _yuvConverter->toRgb(_capturedColors->getRange(), YuvConverter::YUV_COEFS(coef), c.yuv()); + auto yuv = c.yuv(); + + if (gamma == HDR_GAMMA::P010) + { + unpackP010(yuv); + } + + auto a = _yuvConverter->toRgb(_capturedColors->getRange(), YuvConverter::YUV_COEFS(coef), yuv); - if (gamma == ColorSpaceMath::HDR_GAMMA::PQ) + if (gamma == ColorSpaceMath::HDR_GAMMA::PQ || gamma == ColorSpaceMath::HDR_GAMMA::P010) { a = PQ_ST2084(10000.0 / nits, a); } diff --git a/sources/utils/CMakeLists.txt b/sources/utils/CMakeLists.txt index 6e9bac641..42fb9dc5d 100644 --- a/sources/utils/CMakeLists.txt +++ b/sources/utils/CMakeLists.txt @@ -15,6 +15,7 @@ add_library(hyperhdr-utils OBJECT ${Utils_SOURCES}) target_link_libraries(hyperhdr-utils Qt${Qt_VERSION}::Core Qt${Qt_VERSION}::Network + linalg ) if(USE_PRECOMPILED_HEADERS AND COMMAND target_precompile_headers) diff --git a/sources/utils/FrameDecoder.cpp b/sources/utils/FrameDecoder.cpp index 320acd200..fd5013e9f 100644 --- a/sources/utils/FrameDecoder.cpp +++ b/sources/utils/FrameDecoder.cpp @@ -29,12 +29,40 @@ #include #include -//#define TAKE_SCREEN_SHOT +#include +#include + +using namespace linalg; +using namespace aliases; + +namespace +{ + std::vector lutP010_y; + std::vector lutP010_uv; + + void initP010() + { + if (lutP010_y.size() > 0) + return; + + lutP010_y.resize(1024); + lutP010_uv.resize(1024); + + for (int i = 0; i < lutP010_y.size(); ++i) + { + double val = i / 1023.0; + lutP010_y[i] = std::lround(val * 1.5 * 255.0); + } + + + for (int i = 0; i < lutP010_uv.size(); ++i) + { + double val = (i - 512.0) * 1.5; + lutP010_uv[i] = std::lround((512.0 + val) * 255.0 / 1023.0); + } + }; +}; -#ifdef TAKE_SCREEN_SHOT - #include - int screenShotTaken = 300; -#endif void FrameDecoder::processImage( int _cropLeft, int _cropRight, int _cropTop, int _cropBottom, @@ -47,7 +75,7 @@ void FrameDecoder::processImage( // validate format if (pixelFormat != PixelFormat::YUYV && pixelFormat != PixelFormat::XRGB && pixelFormat != PixelFormat::RGB24 && - pixelFormat != PixelFormat::I420 && pixelFormat != PixelFormat::NV12 && pixelFormat != PixelFormat::MJPEG) + pixelFormat != PixelFormat::I420 && pixelFormat != PixelFormat::NV12 && pixelFormat != PixelFormat::P010 && pixelFormat != PixelFormat::MJPEG) { Error(Logger::getInstance("FrameDecoder"), "Invalid pixel format given"); return; @@ -55,7 +83,7 @@ void FrameDecoder::processImage( // validate format LUT if ((pixelFormat == PixelFormat::YUYV || pixelFormat == PixelFormat::I420 || pixelFormat == PixelFormat::MJPEG || - pixelFormat == PixelFormat::NV12) && lutBuffer == NULL) + pixelFormat == PixelFormat::NV12 || pixelFormat == PixelFormat::P010) && lutBuffer == NULL) { Error(Logger::getInstance("FrameDecoder"), "Missing LUT table for YUV colorspace"); return; @@ -99,14 +127,6 @@ void FrameDecoder::processImage( } } -#ifdef TAKE_SCREEN_SHOT - if (screenShotTaken > 0 && screenShotTaken-- == 1) - { - QImage jpgImage((const uint8_t*)outputImage.rawMem(), outputImage.width(), outputImage.height(), 3 * outputImage.width(), QImage::Format_RGB888); - jpgImage.save("D:/grabber_yuv.png", "png"); - } -#endif - return; } @@ -239,6 +259,43 @@ void FrameDecoder::processImage( return; } + if (pixelFormat == PixelFormat::P010) + { + uint16_t p010[2] = {}; + + initP010(); + + auto deltaUV = (dataUV != nullptr) ? (uint8_t*)dataUV : (uint8_t*)data + lineLength * height; + for (int yDest = 0, ySource = _cropTop; yDest < outputHeight; ++ySource, ++yDest) + { + uint8_t* currentDest = destMemory + ((uint64_t)destLineSize) * yDest; + uint8_t* endDest = currentDest + destLineSize; + uint8_t* currentSource = (uint8_t*)data + (((uint64_t)lineLength * ySource) + ((uint64_t)_cropLeft)); + uint8_t* currentSourceUV = deltaUV + (((uint64_t)ySource / 2) * lineLength) + ((uint64_t)_cropLeft); + + while (currentDest < endDest) + { + memcpy(((uint32_t*)&p010), ((uint32_t*)currentSource), 4); + buffer[0] = lutP010_y[p010[0] >> 6]; + buffer[1] = lutP010_y[p010[1] >> 6]; + currentSource += 4; + memcpy(((uint32_t*)&p010), ((uint32_t*)currentSourceUV), 4); + buffer[2] = lutP010_uv[p010[0] >> 6]; + buffer[3] = lutP010_uv[p010[1] >> 6]; + currentSourceUV += 4; + + ind_lutd = LUT_INDEX(buffer[0], buffer[2], buffer[3]); + ind_lutd2 = LUT_INDEX(buffer[1], buffer[2], buffer[3]); + + *((uint32_t*)currentDest) = *((uint32_t*)(&lutBuffer[ind_lutd])); + currentDest += 3; + *((uint32_t*)currentDest) = *((uint32_t*)(&lutBuffer[ind_lutd2])); + currentDest += 3; + } + } + return; + } + if (pixelFormat == PixelFormat::NV12) { auto deltaUV = (dataUV != nullptr) ? (uint8_t*)dataUV : (uint8_t*)data + lineLength * height; @@ -265,13 +322,7 @@ void FrameDecoder::processImage( currentDest += 3; } } -#ifdef TAKE_SCREEN_SHOT - if (screenShotTaken > 0 && screenShotTaken-- == 1) - { - QImage jpgImage((const uint8_t*)outputImage.rawMem(), outputImage.width(), outputImage.height(), 3 * outputImage.width(), QImage::Format_RGB888); - jpgImage.save("D:/grabber_nv12.png", "png"); - } -#endif + return; } } @@ -286,7 +337,7 @@ void FrameDecoder::processQImage( // validate format if (pixelFormat != PixelFormat::YUYV && pixelFormat != PixelFormat::XRGB && pixelFormat != PixelFormat::RGB24 && - pixelFormat != PixelFormat::I420 && pixelFormat != PixelFormat::NV12) + pixelFormat != PixelFormat::I420 && pixelFormat != PixelFormat::NV12 && pixelFormat != PixelFormat::P010) { Error(Logger::getInstance("FrameDecoder"), "Invalid pixel format given"); return; @@ -294,7 +345,7 @@ void FrameDecoder::processQImage( // validate format LUT if ((pixelFormat == PixelFormat::YUYV || pixelFormat == PixelFormat::I420 || - pixelFormat == PixelFormat::NV12) && lutBuffer == NULL) + pixelFormat == PixelFormat::NV12 || pixelFormat == PixelFormat::P010) && lutBuffer == NULL) { Error(Logger::getInstance("FrameDecoder"), "Missing LUT table for YUV colorspace"); return; @@ -427,6 +478,38 @@ void FrameDecoder::processQImage( return; } + if (pixelFormat == PixelFormat::P010) + { + uint16_t p010[2] = {}; + initP010(); + + uint8_t* deltaUV = (dataUV != nullptr) ? (uint8_t*)dataUV : (uint8_t*)data + lineLength * height; + for (int yDest = 0, ySource = 0; yDest < outputHeight; ySource += 2, ++yDest) + { + uint8_t* currentDest = destMemory + ((uint64_t)destLineSize) * yDest; + uint8_t* endDest = currentDest + destLineSize; + uint8_t* currentSource = (uint8_t*)data + (((uint64_t)lineLength * ySource)); + uint8_t* currentSourceU = deltaUV + (((uint64_t)ySource / 2) * lineLength); + + while (currentDest < endDest) + { + memcpy(((uint16_t*)&p010), ((uint16_t*)currentSource), 2); + buffer[0] = lutP010_y[p010[0] >> 6]; + currentSource += 4; + memcpy(((uint32_t*)&p010), ((uint32_t*)currentSourceU), 4); + buffer[2] = lutP010_uv[p010[0] >> 6]; + buffer[3] = lutP010_uv[p010[1] >> 6]; + currentSourceU += 4; + + ind_lutd = LUT_INDEX(buffer[0], buffer[2], buffer[3]); + + *((uint32_t*)currentDest) = *((uint32_t*)(&lutBuffer[ind_lutd])); + currentDest += 3; + } + } + return; + } + if (pixelFormat == PixelFormat::NV12) { uint8_t* deltaUV = (dataUV != nullptr) ? (uint8_t*)dataUV : (uint8_t*)data + lineLength * height; @@ -498,13 +581,6 @@ void FrameDecoder::applyLUT(uint8_t* _source, unsigned int width, unsigned int h } } } -#ifdef TAKE_SCREEN_SHOT - if (screenShotTaken > 0 && screenShotTaken-- == 1) - { - QImage jpgImage((const uint8_t*)_source, width, height, 3 * width, QImage::Format_RGB888); - jpgImage.save("D:/grabber_mjpeg.png", "png"); - } -#endif } void FrameDecoder::processSystemImageBGRA(Image& image, int targetSizeX, int targetSizeY,