From d98288fa4e5cf548771b051af1defe1e03d1ef69 Mon Sep 17 00:00:00 2001 From: Edouard Francois <edouard.francois@technicolor.com> Date: Mon, 5 Jul 2021 16:26:20 +0000 Subject: [PATCH] JVET-V0108: Colour transform information SEI --- cfg/examples_SEI_CTI/readMe.txt | 7 + cfg/examples_SEI_CTI/seiCti_hdrPq_to_sdr1.cfg | 24 +++ cfg/examples_SEI_CTI/seiCti_hdrPq_to_sdr2.cfg | 21 ++ cfg/examples_SEI_CTI/seiCti_sdr_to_hdrPq1.cfg | 22 ++ doc/software-manual.tex | 69 +++++- source/App/DecoderApp/DecApp.cpp | 91 ++++++++ source/App/DecoderApp/DecApp.h | 3 + source/App/DecoderApp/DecAppCfg.cpp | 6 + source/App/DecoderApp/DecAppCfg.h | 3 + source/App/EncoderApp/EncApp.cpp | 18 ++ source/App/EncoderApp/EncAppCfg.cpp | 77 +++++++ source/App/EncoderApp/EncAppCfg.h | 15 ++ source/App/Parcat/parcat.cpp | 16 ++ source/Lib/CommonLib/Buffer.cpp | 66 ++++++ source/Lib/CommonLib/Buffer.h | 4 + source/Lib/CommonLib/CommonDef.h | 3 + source/Lib/CommonLib/Picture.cpp | 49 +++++ source/Lib/CommonLib/Picture.h | 9 + source/Lib/CommonLib/SEI.cpp | 3 + source/Lib/CommonLib/SEI.h | 27 +++ source/Lib/CommonLib/SEIColourTransform.cpp | 200 ++++++++++++++++++ source/Lib/CommonLib/SEIColourTransform.h | 73 +++++++ source/Lib/CommonLib/TypeDef.h | 12 +- source/Lib/DecoderLib/DecLib.cpp | 8 + source/Lib/DecoderLib/DecLib.h | 5 + source/Lib/DecoderLib/SEIread.cpp | 86 ++++++++ source/Lib/DecoderLib/SEIread.h | 3 + source/Lib/EncoderLib/EncCfg.h | 43 ++++ source/Lib/EncoderLib/EncGOP.cpp | 9 + source/Lib/EncoderLib/SEIEncoder.cpp | 27 +++ source/Lib/EncoderLib/SEIEncoder.h | 3 + source/Lib/EncoderLib/SEIwrite.cpp | 61 ++++++ source/Lib/EncoderLib/SEIwrite.h | 3 + 33 files changed, 1055 insertions(+), 11 deletions(-) create mode 100644 cfg/examples_SEI_CTI/readMe.txt create mode 100644 cfg/examples_SEI_CTI/seiCti_hdrPq_to_sdr1.cfg create mode 100644 cfg/examples_SEI_CTI/seiCti_hdrPq_to_sdr2.cfg create mode 100644 cfg/examples_SEI_CTI/seiCti_sdr_to_hdrPq1.cfg create mode 100644 source/Lib/CommonLib/SEIColourTransform.cpp create mode 100644 source/Lib/CommonLib/SEIColourTransform.h diff --git a/cfg/examples_SEI_CTI/readMe.txt b/cfg/examples_SEI_CTI/readMe.txt new file mode 100644 index 000000000..04fb12dbe --- /dev/null +++ b/cfg/examples_SEI_CTI/readMe.txt @@ -0,0 +1,7 @@ +example encoding command line +encoder -c encoder_randomaccess_vtm.cfg -c classH1.cfg -c H1_BalloonFestival.cfg -c seiCti_hdrPq_to_sdr1.cfg + -i BalloonFestival_1920x1080p_24_10b_pq_709_ct2020_420_rev1.yuv -ip 32 -fs 0 -f 33 -q 22 + -b BalloonFestival_1920x1080p_24_10b_pq_709_ct2020_420_rev1.bin -o /dev/null --InternalBitDepth=10 --OutputBitDepth=10 + +example decoding command line +decoder -b BalloonFestival_1920x1080p_24_10b_pq_709_ct2020_420_rev1.bin -o dec.yuv --SEICTIFilename=dec_cti.yuv \ No newline at end of file diff --git a/cfg/examples_SEI_CTI/seiCti_hdrPq_to_sdr1.cfg b/cfg/examples_SEI_CTI/seiCti_hdrPq_to_sdr1.cfg new file mode 100644 index 000000000..8ac807a9a --- /dev/null +++ b/cfg/examples_SEI_CTI/seiCti_hdrPq_to_sdr1.cfg @@ -0,0 +1,24 @@ +# example mapping SDR BT.2020 std range to BT.2100 HDR-PQ-like Std range +# this is provided as indicative example, but in principle the mapping curve should be dynamically tuned depending on the content + +SEICTIEnabled : 1 +SEICTIId : 1 + +SEICTISignalInfoFlag : 1 +SEICTIFullRangeFlag : 0 +SEICTIPrimaries : 9 +SEICTITransferFunction : 14 +SEICTIMatrixCoefs : 9 + +SEICTICrossCompFlag : 1 +SEICTICrossCompInferred : 1 +SEICTILut0 : 64 00 41 41 41 41 44 50 56 62 70 78 87 97 109 79 00 # Lut Y + +SEICTIChromaOffset : 0 # chroma scaling offset + +### DO NOT ADD ANYTHING BELOW THIS LINE ### +### DO NOT DELETE THE EMPTY LINE BELOW ### + + + + diff --git a/cfg/examples_SEI_CTI/seiCti_hdrPq_to_sdr2.cfg b/cfg/examples_SEI_CTI/seiCti_hdrPq_to_sdr2.cfg new file mode 100644 index 000000000..715d8756e --- /dev/null +++ b/cfg/examples_SEI_CTI/seiCti_hdrPq_to_sdr2.cfg @@ -0,0 +1,21 @@ +# example mapping BT.2100 HDR-PQ Std range to SDR-like BT.2020 std range +# this is provided as indicative example, but in principle the mapping curve should be dynamically tuned depending on the content + +SEICTIEnabled : 1 +SEICTIId : 1 + +SEICTISignalInfoFlag : 1 +SEICTIFullRangeFlag : 0 +SEICTIPrimaries : 9 +SEICTITransferFunction : 14 +SEICTIMatrixCoefs : 9 + +SEICTICrossCompFlag : 1 # cross-component scaling mode enabled or not +SEICTICrossCompInferred : 0 # chroma LUT inferred (1) or not (0) from luma LUT +SEICTINbChromaLut : 1 # nb of chroma LUT (1 or 2) +SEICTILut0 : 64 00 41 41 41 41 44 50 56 62 70 78 87 97 109 79 00 # Lut Y +SEICTILut1 : 64 56 47 47 47 48 51 56 60 66 72 78 85 93 87 70 64 # Lut chroma +SEICTIChromaOffset : 0 # chroma scaling offset + +### DO NOT ADD ANYTHING BELOW THIS LINE ### +### DO NOT DELETE THE EMPTY LINE BELOW ### diff --git a/cfg/examples_SEI_CTI/seiCti_sdr_to_hdrPq1.cfg b/cfg/examples_SEI_CTI/seiCti_sdr_to_hdrPq1.cfg new file mode 100644 index 000000000..08efe7406 --- /dev/null +++ b/cfg/examples_SEI_CTI/seiCti_sdr_to_hdrPq1.cfg @@ -0,0 +1,22 @@ +# example mapping BT.2100 HDR-PQ Std range to SDR-like BT.2020 std range +# this is provided as indicative example, but in principle the mapping curve should be dynamically tuned depending on the content + +SEICTIEnabled : 1 +SEICTIId : 1 + +SEICTISignalInfoFlag : 1 +SEICTIFullRangeFlag : 0 +SEICTIPrimaries : 9 +SEICTITransferFunction : 16 +SEICTIMatrixCoefs : 9 + +SEICTICrossCompFlag : 1 +SEICTICrossCompInferred : 1 +SEICTILut0 : 64 00 103 101 97 79 72 67 58 54 47 46 42 39 41 50 00 # Lut Y +SEICTIChromaOffset : 0 # chroma scaling offset + +### DO NOT ADD ANYTHING BELOW THIS LINE ### +### DO NOT DELETE THE EMPTY LINE BELOW ### + + + diff --git a/doc/software-manual.tex b/doc/software-manual.tex index e6c8246ac..aab013ac1 100644 --- a/doc/software-manual.tex +++ b/doc/software-manual.tex @@ -3522,7 +3522,7 @@ The table below lists the SEI messages defined for Version 1 and Range-Extension 139 & Temporal motion-constrained tile sets & Table \ref{tab:sei-tmcts} \\ 140 & Chroma resampling filter hint & Table \ref{tab:chroma-resampling-filter-hint} \\ 141 & Knee function information & Table \ref{tab:sei-knee-function} \\ - 142 & Colour remapping information & Table \ref{tab:sei-colour-remapping}\\ + 142 & Colour transform information & Table \ref{tab:sei-colour-transform}\\ 143 & Deinterlaced field identification & (Not handled)\\ 144 & Content light level info & Table \ref{tab:sei-content-light-level}\\ 147 & Alternative transfer characteristics & Table \ref{tab:sei-alternative-transfer-characteristics}\\ @@ -4207,12 +4207,63 @@ Array of output knee point. Default table can be set to the following: \end{OptionTableNoShorthand} -\begin{OptionTableNoShorthand}{Colour remapping SEI message encoder parameters}{tab:sei-colour-remapping} -\Option{SEIColourRemappingInfoFileRoot (-cri)} & -\Default{\NotSet} & -Specifies the prefix of input Colour Remapping Information file. Prefix is completed by ``_x.txt'' where x is the POC number. -The contents of the file are a list of the SEI message's syntax element names (in decoding order) immediately followed by a `:' and then the associated value. -An example file can be found in cfg/misc/example_colour_remapping_sei_encoder_0.txt. +\begin{OptionTableNoShorthand}{Colour transform information SEI message encoder parameters}{tab:sei-colour-transform} +\Option{SEICTIEnabled} & +\Default{false} & +Enables (true) or disables (false) the insertion of colour transform information (CTI) SEI message. +Examples configuration files for CTI can be found in folder cfg/examples_SEI_CTI. +\\ +\Option{SEICTIId} & +\Default{0} & +Specifies the ID of the CTI SEI message. +\\ +\Option{SEICTISignalInfoFlag} & +\Default{false} & +Enables (true) or disables (false) the insertion of output signal information after applying the colour transform. +\\ +\Option{SEICTIFullRangeFlag} & +\Default{false} & +Specifies the range (true:full, false:limited) of the output signal after applying the colour transform. +\\ +\Option{SEICTIPrimaries} & +\Default{0} & +Specifies the colour primaries of the output signal after applying the colour transform. +\\ +\Option{SEICTITransferFunction} & +\Default{0} & +Specifies the transfer function (characteristics) of the output signal after applying the colour transform. +\\ +\Option{SEICTIMatrixCoefs} & +\Default{0} & +Specifies the matrix coefficients type of the output signal after applying the colour transform. +\\ +\Option{SEICTICrossCompFlag} & +\Default{true} & +Enables (true) or disables (false) the cross-component scaling for applying the colour transform. +\\ +\Option{SEICTICrossCompInferred} & +\Default{true} & +Infers (true) or signals (false) the cross-component scaling tables for the colour transform. +\\ +\Option{SEICTINbChromaLut} & +\Default{0} & +Specifies the number of chroma tables (1 or 2) for the colour transform (only used when SEICTICrossCompInferred = false). +\\ +\Option{SEICTILut0} & +\Default{0} & +Specifies the transform table for colour component 0. +\\ +\Option{SEICTILut1} & +\Default{0} & +Specifies the transform table for colour component 1 (only used when SEICTICrossCompFlag = false). +\\ +\Option{SEICTILut2} & +\Default{0} & +Specifies the transform table for colour component 2 (only used when SEICTINbChromaLut = 2). +\\ +\Option{SEICTIChromaOffset} & +\Default{0} & +Specifies the offset to be added to the values of the cross-component scaling tables (only used when SEICTICrossCompInferred = false). \\ \end{OptionTableNoShorthand} @@ -5169,10 +5220,10 @@ has the following behaviour: When a non-empty file name is specified, information regarding any decoded SEI messages will be output to the indicated file. If the file name is '-', then stdout is used instead. \\ -\Option{SEIColourRemappingInfoFilename} & +\Option{SEICTIFilename} & %\ShortOption{\None} & \Default{\NotSet} & -Specifies that the colour remapping SEI message should be applied to the output video, with the output written to this file. +Specifies that the colour transform information (CTI) SEI message should be applied to the output video, with the output written to this file. If no value is specified, the SEI message is ignored and no mapping is applied. \\ diff --git a/source/App/DecoderApp/DecApp.cpp b/source/App/DecoderApp/DecApp.cpp index 023ab1594..5886ea266 100644 --- a/source/App/DecoderApp/DecApp.cpp +++ b/source/App/DecoderApp/DecApp.cpp @@ -417,6 +417,42 @@ uint32_t DecApp::decode() m_cVideoIOYuvReconFile[nalu.m_nuhLayerId].setBitdepthShift(channelType, reconBitdepth - fileBitdepth); } } +#if JVET_V0108 + if (!m_SEICTIFileName.empty() && !m_cVideoIOYuvSEICTIFile[nalu.m_nuhLayerId].isOpen()) + { + const BitDepths& bitDepths = pcListPic->front()->cs->sps->getBitDepths(); // use bit depths of first reconstructed picture. + for (uint32_t channelType = 0; channelType < MAX_NUM_CHANNEL_TYPE; channelType++) + { + if (m_outputBitDepth[channelType] == 0) + { + m_outputBitDepth[channelType] = bitDepths.recon[channelType]; + } + } + + if (m_packedYUVMode && (m_outputBitDepth[CH_L] != 10 && m_outputBitDepth[CH_L] != 12)) + { + EXIT("Invalid output bit-depth for packed YUV output, aborting\n"); + } + + std::string SEICTIFileName = m_SEICTIFileName; + if (m_SEICTIFileName.compare("/dev/null") && m_cDecLib.getVPS() != nullptr && m_cDecLib.getVPS()->getMaxLayers() > 1 && xIsNaluWithinTargetOutputLayerIdSet(&nalu)) + { + size_t pos = SEICTIFileName.find_last_of('.'); + if (pos != string::npos) + { + SEICTIFileName.insert(pos, std::to_string(nalu.m_nuhLayerId)); + } + else + { + SEICTIFileName.append(std::to_string(nalu.m_nuhLayerId)); + } + } + if ((m_cDecLib.getVPS() != nullptr && (m_cDecLib.getVPS()->getMaxLayers() == 1 || xIsNaluWithinTargetOutputLayerIdSet(&nalu))) || m_cDecLib.getVPS() == nullptr) + { + m_cVideoIOYuvSEICTIFile[nalu.m_nuhLayerId].open(SEICTIFileName, true, m_outputBitDepth, m_outputBitDepth, bitDepths.recon); // write mode + } + } +#endif if (!m_annotatedRegionsSEIFileName.empty()) { xOutputAnnotatedRegions(pcListPic); @@ -618,6 +654,15 @@ void DecApp::xDestroyDecLib() recFile.second.close(); } } +#if JVET_V0108 + if (!m_SEICTIFileName.empty()) + { + for (auto& recFile : m_cVideoIOYuvSEICTIFile) + { + recFile.second.close(); + } + } +#endif // destroy decoder class m_cDecLib.destroy(); @@ -776,6 +821,29 @@ void DecApp::xWriteOutput( PicList* pcListPic, uint32_t tId ) NUM_CHROMA_FORMAT, m_bClipOutputVideoToRec709Range ); } } +#if JVET_V0108 + // Perform CTI on decoded frame and write to output CTI file + if (!m_SEICTIFileName.empty()) + { + const Window& conf = pcPic->getConformanceWindow(); + const SPS* sps = pcPic->cs->sps; + ChromaFormat chromaFormatIDC = sps->getChromaFormatIdc(); + if (m_upscaledOutput) + { + m_cVideoIOYuvSEICTIFile[pcPic->layerId].writeUpscaledPicture(*sps, *pcPic->cs->pps, pcPic->getDisplayBuf(), m_outputColourSpaceConvert, m_packedYUVMode, m_upscaledOutput, NUM_CHROMA_FORMAT, m_bClipOutputVideoToRec709Range); + } + else + { + m_cVideoIOYuvSEICTIFile[pcPic->layerId].write(pcPic->getRecoBuf().get(COMPONENT_Y).width, pcPic->getRecoBuf().get(COMPONENT_Y).height, pcPic->getDisplayBuf(), + m_outputColourSpaceConvert, m_packedYUVMode, + conf.getWindowLeftOffset() * SPS::getWinUnitX(chromaFormatIDC), + conf.getWindowRightOffset() * SPS::getWinUnitX(chromaFormatIDC), + conf.getWindowTopOffset() * SPS::getWinUnitY(chromaFormatIDC), + conf.getWindowBottomOffset() * SPS::getWinUnitY(chromaFormatIDC), + NUM_CHROMA_FORMAT, m_bClipOutputVideoToRec709Range); + } + } +#endif writeLineToOutputLog(pcPic); // update POC of display order @@ -923,6 +991,29 @@ void DecApp::xFlushOutput( PicList* pcListPic, const int layerId ) NUM_CHROMA_FORMAT, m_bClipOutputVideoToRec709Range ); } } +#if JVET_V0108 + // Perform CTI on decoded frame and write to output CTI file + if (!m_SEICTIFileName.empty()) + { + const Window& conf = pcPic->getConformanceWindow(); + const SPS* sps = pcPic->cs->sps; + ChromaFormat chromaFormatIDC = sps->getChromaFormatIdc(); + if (m_upscaledOutput) + { + m_cVideoIOYuvSEICTIFile[pcPic->layerId].writeUpscaledPicture(*sps, *pcPic->cs->pps, pcPic->getDisplayBuf(), m_outputColourSpaceConvert, m_packedYUVMode, m_upscaledOutput, NUM_CHROMA_FORMAT, m_bClipOutputVideoToRec709Range); + } + else + { + m_cVideoIOYuvSEICTIFile[pcPic->layerId].write(pcPic->getRecoBuf().get(COMPONENT_Y).width, pcPic->getRecoBuf().get(COMPONENT_Y).height, pcPic->getDisplayBuf(), + m_outputColourSpaceConvert, m_packedYUVMode, + conf.getWindowLeftOffset() * SPS::getWinUnitX(chromaFormatIDC), + conf.getWindowRightOffset() * SPS::getWinUnitX(chromaFormatIDC), + conf.getWindowTopOffset() * SPS::getWinUnitY(chromaFormatIDC), + conf.getWindowBottomOffset() * SPS::getWinUnitY(chromaFormatIDC), + NUM_CHROMA_FORMAT, m_bClipOutputVideoToRec709Range); + } + } +#endif writeLineToOutputLog(pcPic); #if JVET_S0078_NOOUTPUTPRIORPICFLAG } diff --git a/source/App/DecoderApp/DecApp.h b/source/App/DecoderApp/DecApp.h index d3573294b..2b3580ab4 100644 --- a/source/App/DecoderApp/DecApp.h +++ b/source/App/DecoderApp/DecApp.h @@ -61,6 +61,9 @@ private: // class interface DecLib m_cDecLib; ///< decoder class std::unordered_map<int, VideoIOYuv> m_cVideoIOYuvReconFile; ///< reconstruction YUV class +#if JVET_V0108 + std::unordered_map<int, VideoIOYuv> m_cVideoIOYuvSEICTIFile; ///< reconstruction YUV with CTI class +#endif // for output control int m_iPOCLastDisplay; ///< last POC in display order diff --git a/source/App/DecoderApp/DecAppCfg.cpp b/source/App/DecoderApp/DecAppCfg.cpp index 0a126cb13..c4ad90ebf 100644 --- a/source/App/DecoderApp/DecAppCfg.cpp +++ b/source/App/DecoderApp/DecAppCfg.cpp @@ -96,6 +96,9 @@ bool DecAppCfg::parseCfg( int argc, char* argv[] ) ("SEINoDisplay", m_decodedNoDisplaySEIEnabled, true, "Control handling of decoded no display SEI messages") ("TarDecLayerIdSetFile,l", cfg_TargetDecLayerIdSetFile, string(""), "targetDecLayerIdSet file name. The file should include white space separated LayerId values to be decoded. Omitting the option or a value of -1 in the file decodes all layers.") ("SEIColourRemappingInfoFilename", m_colourRemapSEIFileName, string(""), "Colour Remapping YUV output file name. If empty, no remapping is applied (ignore SEI message)\n") +#if JVET_V0108 + ("SEICTIFilename", m_SEICTIFileName, string(""), "CTI YUV output file name. If empty, no Colour Transform is applied (ignore SEI message)\n") +#endif ("SEIAnnotatedRegionsInfoFilename", m_annotatedRegionsSEIFileName, string(""), "Annotated regions output file name. If empty, no object information will be saved (ignore SEI message)\n") ("OutputDecodedSEIMessagesFilename", m_outputDecodedSEIMessagesFilename, string(""), "When non empty, output decoded SEI messages to the indicated file. If file is '-', then output to stdout\n") #if JVET_S0257_DUMP_360SEI_MESSAGE @@ -258,6 +261,9 @@ DecAppCfg::DecAppCfg() , m_decodedPictureHashSEIEnabled(0) , m_decodedNoDisplaySEIEnabled(false) , m_colourRemapSEIFileName() +#if JVET_V0108 +, m_SEICTIFileName() +#endif , m_annotatedRegionsSEIFileName() , m_targetDecLayerIdSet() , m_outputDecodedSEIMessagesFilename() diff --git a/source/App/DecoderApp/DecAppCfg.h b/source/App/DecoderApp/DecAppCfg.h index bdf0db7a9..157c18cfc 100644 --- a/source/App/DecoderApp/DecAppCfg.h +++ b/source/App/DecoderApp/DecAppCfg.h @@ -72,6 +72,9 @@ protected: int m_decodedPictureHashSEIEnabled; ///< Checksum(3)/CRC(2)/MD5(1)/disable(0) acting on decoded picture hash SEI message bool m_decodedNoDisplaySEIEnabled; ///< Enable(true)/disable(false) writing only pictures that get displayed based on the no display SEI message std::string m_colourRemapSEIFileName; ///< output Colour Remapping file name +#if JVET_V0108 + std::string m_SEICTIFileName; ///< output Recon with CTI file name +#endif std::string m_annotatedRegionsSEIFileName; ///< annotated regions file name std::vector<int> m_targetDecLayerIdSet; ///< set of LayerIds to be included in the sub-bitstream extraction process. std::string m_outputDecodedSEIMessagesFilename; ///< filename to output decoded SEI messages to. If '-', then use stdout. If empty, do not output details. diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp index e66809b6d..30c4d1119 100644 --- a/source/App/EncoderApp/EncApp.cpp +++ b/source/App/EncoderApp/EncApp.cpp @@ -984,6 +984,24 @@ void EncApp::xInitLibCfg() m_cEncLib.setAmbientViewingEnvironmentSEIIlluminance (m_aveSEIAmbientIlluminance); m_cEncLib.setAmbientViewingEnvironmentSEIAmbientLightX ((uint16_t)m_aveSEIAmbientLightX); m_cEncLib.setAmbientViewingEnvironmentSEIAmbientLightY ((uint16_t)m_aveSEIAmbientLightY); +#if JVET_V0108 + // colour tranform information sei + m_cEncLib.setCtiSEIEnabled(m_ctiSEIEnabled); + m_cEncLib.setCtiSEIId(m_ctiSEIId); + m_cEncLib.setCtiSEISignalInfoFlag(m_ctiSEISignalInfoFlag); + m_cEncLib.setCtiSEIFullRangeFlag(m_ctiSEIFullRangeFlag); + m_cEncLib.setCtiSEIPrimaries(m_ctiSEIPrimaries); + m_cEncLib.setCtiSEITransferFunction(m_ctiSEITransferFunction); + m_cEncLib.setCtiSEIMatrixCoefs(m_ctiSEIMatrixCoefs); + m_cEncLib.setCtiSEICrossComponentFlag(m_ctiSEICrossComponentFlag); + m_cEncLib.setCtiSEICrossComponentInferred(m_ctiSEICrossComponentInferred); + m_cEncLib.setCtiSEINbChromaLut(m_ctiSEINumberChromaLut); + m_cEncLib.setCtiSEIChromaOffset(m_ctiSEIChromaOffset); + for (int i = 0; i < MAX_NUM_COMPONENT; i++) + { + m_cEncLib.setCtiSEILut(m_ctiSEILut[i], i); + } +#endif // content colour volume SEI m_cEncLib.setCcvSEIEnabled (m_ccvSEIEnabled); m_cEncLib.setCcvSEICancelFlag (m_ccvSEICancelFlag); diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp index cc4c25665..5e889d5d4 100644 --- a/source/App/EncoderApp/EncAppCfg.cpp +++ b/source/App/EncoderApp/EncAppCfg.cpp @@ -623,6 +623,11 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) SMultiValueInput<int> cfg_DisplayPrimariesCode (0, 50000, 6, 6, defaultPrimaryCodes, sizeof(defaultPrimaryCodes )/sizeof(int)); SMultiValueInput<int> cfg_DisplayWhitePointCode (0, 50000, 2, 2, defaultWhitePointCode, sizeof(defaultWhitePointCode)/sizeof(int)); +#if JVET_V0108 + SMultiValueInput<int16_t> cfg_SEICTILut0(0, 128, 0, MAX_CTI_LUT_SIZE + 1); + SMultiValueInput<int16_t> cfg_SEICTILut1(0, 128, 0, MAX_CTI_LUT_SIZE + 1); + SMultiValueInput<int16_t> cfg_SEICTILut2(0, 128, 0, MAX_CTI_LUT_SIZE + 1); +#endif SMultiValueInput<bool> cfg_timeCodeSeiTimeStampFlag (0, 1, 0, MAX_TIMECODE_SEI_SETS); SMultiValueInput<bool> cfg_timeCodeSeiNumUnitFieldBasedFlag(0, 1, 0, MAX_TIMECODE_SEI_SETS); SMultiValueInput<int> cfg_timeCodeSeiCountingType (0, 6, 0, MAX_TIMECODE_SEI_SETS); @@ -1413,6 +1418,23 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) ("SEIAVEAmbientIlluminance", m_aveSEIAmbientIlluminance, 100000u, "Specifies the environmental illluminance of the ambient viewing environment in units of 1/10000 lux for the ambient viewing environment SEI message") ("SEIAVEAmbientLightX", m_aveSEIAmbientLightX, 15635u, "Specifies the normalized x chromaticity coordinate of the environmental ambient light in the nominal viewing enviornment according to the CIE 1931 definition in units of 1/50000 lux for the ambient viewing enviornment SEI message") ("SEIAVEAmbientLightY", m_aveSEIAmbientLightY, 16450u, "Specifies the normalized y chromaticity coordinate of the environmental ambient light in the nominal viewing enviornment according to the CIE 1931 definition in units of 1/50000 lux for the ambient viewing enviornment SEI message") +#if JVET_V0108 +// colour tranform information SEI + ("SEICTIEnabled", m_ctiSEIEnabled, false, "Control generation of the Colour transform information SEI message") + ("SEICTIId", m_ctiSEIId, 0u, "Id of the Colour transform information SEI message") + ("SEICTISignalInfoFlag", m_ctiSEISignalInfoFlag, false, "indicates if signal information are present in the Colour transform information SEI message") + ("SEICTIFullRangeFlag", m_ctiSEIFullRangeFlag, false, "specifies signal range after applying the Colour transform information SEI message") + ("SEICTIPrimaries", m_ctiSEIPrimaries, 0u, "indicates the signal primaries after applying the Colour transform information SEI message") + ("SEICTITransferFunction", m_ctiSEITransferFunction, 0u, "indicates the signal transfer function after applying the Colour transform information SEI message") + ("SEICTIMatrixCoefs", m_ctiSEIMatrixCoefs, 0u, "indicates the signal matrix coefficients after applying the Colour transform information SEI message") + ("SEICTICrossCompFlag", m_ctiSEICrossComponentFlag, true, "Specifies if cross-component transform mode is enabled in SEI CTI") + ("SEICTICrossCompInferred", m_ctiSEICrossComponentInferred, true, "Specifies if cross-component transform LUT is inferred in SEI CTI") + ("SEICTINbChromaLut", m_ctiSEINumberChromaLut, 0u, "Specifies the number of chroma LUTs in SEI CTI") + ("SEICTIChromaOffset", m_ctiSEIChromaOffset, 0, "Specifies the chroma offset of SEI CTI") + ("SEICTILut0", cfg_SEICTILut0, cfg_SEICTILut0, "slope values for component 0 of SEI CTI") + ("SEICTILut1", cfg_SEICTILut1, cfg_SEICTILut1, "slope values for component 1 of SEI CTI") + ("SEICTILut2", cfg_SEICTILut2, cfg_SEICTILut2, "slope values for component 2 of SEI CTI") +#endif // content colour volume SEI ("SEICCVEnabled", m_ccvSEIEnabled, false, "Control generation of the Content Colour Volume SEI message") ("SEICCVCancelFlag", m_ccvSEICancelFlag, true, "Specifies the persistence of any previous content colour volume SEI message in output order.") @@ -2479,6 +2501,52 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) m_masteringDisplay.whitePoint[idx] = uint16_t((cfg_DisplayWhitePointCode.values.size() > idx) ? cfg_DisplayWhitePointCode.values[idx] : 0); } } +#if JVET_V0108 + if (m_ctiSEIEnabled) + { + CHECK(!m_ctiSEICrossComponentFlag && m_ctiSEICrossComponentInferred, "CTI CrossComponentFlag is 0, but CTI CrossComponentInferred is 1 (must be 0 for CrossComponentFlag 0)"); + CHECK(!m_ctiSEICrossComponentFlag && !m_ctiSEICrossComponentInferred && !m_ctiSEINumberChromaLut, "For CTI CrossComponentFlag = 0, CTI NumberChromaLut needs to be specified (1 or 2) "); + CHECK(m_ctiSEICrossComponentFlag && !m_ctiSEICrossComponentInferred && !m_ctiSEINumberChromaLut, "For CTI CrossComponentFlag = 1 and CrossComponentInferred = 0, CTI NumberChromaLut needs to be specified (1 or 2) "); + + CHECK(cfg_SEICTILut0.values.empty(), "SEI CTI (SEICTIEnabled) but no LUT0 specified"); + m_ctiSEILut[0].presentFlag = true; + m_ctiSEILut[0].numLutValues = (int)cfg_SEICTILut0.values.size(); + m_ctiSEILut[0].lutValues = cfg_SEICTILut0.values; + + if (!m_ctiSEICrossComponentFlag || (m_ctiSEICrossComponentFlag && !m_ctiSEICrossComponentInferred)) + { + CHECK(cfg_SEICTILut1.values.empty(), "SEI CTI LUT1 not specified"); + m_ctiSEILut[1].presentFlag = true; + m_ctiSEILut[1].numLutValues = (int)cfg_SEICTILut1.values.size(); + m_ctiSEILut[1].lutValues = cfg_SEICTILut1.values; + + if (m_ctiSEINumberChromaLut == 1) + { // Cb lut the same as Cr lut + m_ctiSEILut[2].presentFlag = true; + m_ctiSEILut[2].numLutValues = m_ctiSEILut[1].numLutValues; + m_ctiSEILut[2].lutValues = m_ctiSEILut[1].lutValues; + } + else if (m_ctiSEINumberChromaLut == 2) + { // read from cfg + CHECK(cfg_SEICTILut2.values.empty(), "SEI CTI LUT2 not specified"); + m_ctiSEILut[2].presentFlag = true; + m_ctiSEILut[2].numLutValues = (int)cfg_SEICTILut2.values.size(); + m_ctiSEILut[2].lutValues = cfg_SEICTILut2.values; + } + else + { + CHECK(m_ctiSEINumberChromaLut < 1 && m_ctiSEINumberChromaLut > 2, "Number of chroma LUTs is missing or out of range!"); + } + } + // check if lut size is power of 2 + for (int idx = 0; idx < MAX_NUM_COMPONENT; idx++) + { + int n = m_ctiSEILut[idx].numLutValues - 1; + CHECK(n > 0 && (n & (n - 1)) != 0, "Size of LUT minus 1 should be power of 2!"); + CHECK(n > MAX_CTI_LUT_SIZE, "LUT size minus 1 is larger than MAX_CTI_LUT_SIZE (64)!"); + } + } +#endif if ( m_omniViewportSEIEnabled && !m_omniViewportSEICancelFlag ) { CHECK (!( m_omniViewportSEICntMinus1 >= 0 && m_omniViewportSEICntMinus1 < 16 ), "SEIOmniViewportCntMinus1 must be in the range of 0 to 16"); @@ -3031,6 +3099,12 @@ bool EncAppCfg::xCheckParameter() if (m_updateCtrl > 0 && m_adpOption > 2) { m_adpOption -= 2; } } +#if JVET_V0108 + if (m_ctiSEIEnabled) + { + xConfirmPara(m_ctiSEINumberChromaLut < 0 || m_ctiSEINumberChromaLut > 2, "CTI number of chroma LUTs is out of range"); + } +#endif xConfirmPara( m_cbQpOffset < -12, "Min. Chroma Cb QP Offset is -12" ); xConfirmPara( m_cbQpOffset > 12, "Max. Chroma Cb QP Offset is 12" ); xConfirmPara( m_crQpOffset < -12, "Min. Chroma Cr QP Offset is -12" ); @@ -4419,6 +4493,9 @@ void EncAppCfg::xPrintParameter() msg( VERBOSE, "RPR:%d ", 0 ); } msg(VERBOSE, "TemporalFilter:%d ", m_gopBasedTemporalFilterEnabled); +#if JVET_V0108 + msg(VERBOSE, "SEI CTI:%d ", m_ctiSEIEnabled); +#endif #if EXTENSION_360_VIDEO m_ext360.outputConfigurationSummary(); #endif diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h index f4d024bef..53660e445 100644 --- a/source/App/EncoderApp/EncAppCfg.h +++ b/source/App/EncoderApp/EncAppCfg.h @@ -546,6 +546,21 @@ protected: uint32_t m_aveSEIAmbientIlluminance; uint32_t m_aveSEIAmbientLightX; uint32_t m_aveSEIAmbientLightY; +#if JVET_V0108 + // colour tranform information sei + bool m_ctiSEIEnabled; + uint32_t m_ctiSEIId; + bool m_ctiSEISignalInfoFlag; + bool m_ctiSEIFullRangeFlag; + uint32_t m_ctiSEIPrimaries; + uint32_t m_ctiSEITransferFunction; + uint32_t m_ctiSEIMatrixCoefs; + bool m_ctiSEICrossComponentFlag; + bool m_ctiSEICrossComponentInferred; + uint32_t m_ctiSEINumberChromaLut; + int m_ctiSEIChromaOffset; + LutModel m_ctiSEILut[MAX_NUM_COMPONENT]; +#endif // content colour volume sei bool m_ccvSEIEnabled; bool m_ccvSEICancelFlag; diff --git a/source/App/Parcat/parcat.cpp b/source/App/Parcat/parcat.cpp index d23035edf..db9c5d5b9 100644 --- a/source/App/Parcat/parcat.cpp +++ b/source/App/Parcat/parcat.cpp @@ -214,6 +214,9 @@ std::vector<uint8_t> filter_segment(const std::vector<uint8_t> & v, int idx, int int off = 0; int cnt = 0; bool idr_found = false; +#if JVET_V0108 + bool is_pre_sei_before_idr = true; +#endif std::vector<uint8_t> out; out.reserve(v.size()); @@ -269,6 +272,12 @@ std::vector<uint8_t> filter_segment(const std::vector<uint8_t> & v, int idx, int parameterSetManager.storePPS( pps, inp_nalu.getBitstream().getFifo() ); } +#if JVET_V0108 + if (nalu_type == NAL_UNIT_CODED_SLICE_IDR_W_RADL || nalu_type == NAL_UNIT_CODED_SLICE_IDR_N_LP) + { + is_pre_sei_before_idr = false; + } +#endif if(nalu_type == NAL_UNIT_CODED_SLICE_IDR_W_RADL || nalu_type == NAL_UNIT_CODED_SLICE_IDR_N_LP) { poc = 0; @@ -329,9 +338,16 @@ std::vector<uint8_t> filter_segment(const std::vector<uint8_t> & v, int idx, int skip_next_sei = true; idr_found = true; } +#if JVET_V0108 + if ((idx > 1 && (nalu_type == NAL_UNIT_CODED_SLICE_IDR_W_RADL || nalu_type == NAL_UNIT_CODED_SLICE_IDR_N_LP)) + || ((idx > 1 && !idr_found) && (nalu_type == NAL_UNIT_OPI || nalu_type == NAL_UNIT_DCI || nalu_type == NAL_UNIT_VPS || nalu_type == NAL_UNIT_SPS || nalu_type == NAL_UNIT_PPS || nalu_type == NAL_UNIT_PREFIX_APS || nalu_type == NAL_UNIT_SUFFIX_APS || nalu_type == NAL_UNIT_PH || nalu_type == NAL_UNIT_ACCESS_UNIT_DELIMITER)) + || (nalu_type == NAL_UNIT_SUFFIX_SEI && skip_next_sei) + || (idx > 1 && nalu_type == NAL_UNIT_PREFIX_SEI && is_pre_sei_before_idr)) +#else if ((idx > 1 && (nalu_type == NAL_UNIT_CODED_SLICE_IDR_W_RADL || nalu_type == NAL_UNIT_CODED_SLICE_IDR_N_LP)) || ((idx > 1 && !idr_found) && (nalu_type == NAL_UNIT_OPI || nalu_type == NAL_UNIT_DCI || nalu_type == NAL_UNIT_VPS || nalu_type == NAL_UNIT_SPS || nalu_type == NAL_UNIT_PPS || nalu_type == NAL_UNIT_PREFIX_APS || nalu_type == NAL_UNIT_SUFFIX_APS || nalu_type == NAL_UNIT_PH || nalu_type == NAL_UNIT_ACCESS_UNIT_DELIMITER || nalu_type == NAL_UNIT_PREFIX_SEI)) || (nalu_type == NAL_UNIT_SUFFIX_SEI && skip_next_sei)) +#endif { } else diff --git a/source/Lib/CommonLib/Buffer.cpp b/source/Lib/CommonLib/Buffer.cpp index a53adaf77..ce468f30a 100644 --- a/source/Lib/CommonLib/Buffer.cpp +++ b/source/Lib/CommonLib/Buffer.cpp @@ -484,6 +484,72 @@ void AreaBuf<Pel>::scaleSignal(const int scale, const bool dir, const ClpRng& cl } } +#if JVET_V0108 +template<> +void AreaBuf<Pel>::applyLumaCTI(std::vector<Pel>& pLUTY) +{ + Pel* dst = buf; + Pel* src = buf; + for (unsigned y = 0; y < height; y++) + { + for (unsigned x = 0; x < width; x++) + { + dst[x] = pLUTY[src[x]]; + } + dst += stride; + src += stride; + } +} + +template<> +void AreaBuf<Pel>::applyChromaCTI(Pel* bufY, int strideY, std::vector<Pel>& pLUTC, int bitDepth, ChromaFormat chrFormat, bool fwdMap) +{ + int range = 1 << bitDepth; + int offset = range / 2; + int sx = 1; + int sy = 1; + if (CHROMA_420 == chrFormat) + { + sx = 2; + sy = 2; + } + else if (CHROMA_422 == chrFormat) + { + sx = 2; + } + + Pel* dst = buf; + Pel* src = buf; + if (fwdMap) + { + for (unsigned y = 0; y < height; y++) + { + for (unsigned x = 0; x < width; x++) + { + int pelY = bufY[sy * y * strideY + sx * x]; + double scale = (double)pLUTC[pelY] / (double)(1 << CSCALE_FP_PREC); + dst[x] = Clip3((Pel)0, (Pel)(range - 1), (Pel)(offset + (double)(src[x] - offset) / scale + .5)); + } + dst += stride; + src += stride; + } + } + else + { + for (unsigned y = 0; y < height; y++) + { + for (unsigned x = 0; x < width; x++) + { + int pelY = bufY[sy * y * strideY + sx * x]; + int scal = pLUTC[pelY]; + dst[x] = Clip3(0, range - 1, ((offset << CSCALE_FP_PREC) + (src[x] - offset) * scal + (1 << (CSCALE_FP_PREC - 1))) >> CSCALE_FP_PREC); + } + dst += stride; + src += stride; + } + } +} +#endif template<> void AreaBuf<Pel>::addAvg( const AreaBuf<const Pel> &other1, const AreaBuf<const Pel> &other2, const ClpRng& clpRng) { diff --git a/source/Lib/CommonLib/Buffer.h b/source/Lib/CommonLib/Buffer.h index 331c30950..ae3d874fd 100644 --- a/source/Lib/CommonLib/Buffer.h +++ b/source/Lib/CommonLib/Buffer.h @@ -137,6 +137,10 @@ struct AreaBuf : public Size void rspSignal ( std::vector<Pel>& pLUT ); void scaleSignal ( const int scale, const bool dir , const ClpRng& clpRng); +#if JVET_V0108 + void applyLumaCTI(std::vector<Pel>& pLUTY); + void applyChromaCTI(Pel* bufY, int strideY, std::vector<Pel>& pLUTUV, int bitDepth, ChromaFormat chrFormat, bool fwdMap); +#endif T computeAvg ( ) const; T& at( const int &x, const int &y ) { return buf[y * stride + x]; } diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h index dfbf09854..c45930607 100644 --- a/source/Lib/CommonLib/CommonDef.h +++ b/source/Lib/CommonLib/CommonDef.h @@ -478,6 +478,9 @@ static const int DELTA_QP_ACT[4] = { -5, 1, 3, 1 }; static const int MAX_TSRC_RICE = 8; ///<Maximum supported TSRC Rice parameter static const int MIN_TSRC_RICE = 1; ///<Minimum supported TSRC Rice parameter #endif +#if JVET_V0108 +static const int MAX_CTI_LUT_SIZE = 64; ///<Maximum colour transform LUT size for CTI SEI +#endif // ==================================================================================================================== // Macro functions // ==================================================================================================================== diff --git a/source/Lib/CommonLib/Picture.cpp b/source/Lib/CommonLib/Picture.cpp index f11a3bfb4..eda607c94 100644 --- a/source/Lib/CommonLib/Picture.cpp +++ b/source/Lib/CommonLib/Picture.cpp @@ -64,6 +64,9 @@ Picture::Picture() precedingDRAP = false; #if JVET_U0084_EDRAP edrapRapId = -1; +#endif +#if JVET_V0108 + m_colourTranfParams = NULL; #endif nonReferencePictureFlag = false; @@ -133,6 +136,9 @@ void Picture::destroy() delete[] m_spliceIdx; m_spliceIdx = NULL; } +#if JVET_V0108 + m_invColourTransfBuf = NULL; +#endif } void Picture::createTempBuffers( const unsigned _maxCUSize ) @@ -1211,3 +1217,46 @@ void Picture::addPictureToHashMapForInter() } } } +#if JVET_V0108 +void Picture::createColourTransfProcessor(bool firstPictureInSequence, SEIColourTransformApply* ctiCharacteristics, PelStorage* ctiBuf, int width, int height, ChromaFormat fmt, int bitDepth) +{ + m_colourTranfParams = ctiCharacteristics; + m_invColourTransfBuf = ctiBuf; + if (firstPictureInSequence) + { + // Create and initialize the Colour Transform Processor + m_colourTranfParams->create(width, height, fmt, bitDepth); + + //Frame level PelStorage buffer created to apply the Colour Transform + m_invColourTransfBuf->create(UnitArea(chromaFormat, Area(0, 0, width, height))); + } +} + +PelUnitBuf Picture::getDisplayBuf() +{ + int payloadType = 0; + std::list<SEI*>::iterator message; + + for (message = SEIs.begin(); message != SEIs.end(); ++message) + { + payloadType = (*message)->payloadType(); + if (payloadType == SEI::COLOUR_TRANSFORM_INFO) + { + // re-init parameters + *m_colourTranfParams->m_pColourTransfParams = *static_cast<SEIColourTransformInfo*>(*message); + //m_colourTranfParams->m_pColourTransfParams = static_cast<SEIColourTransformInfo*>(*message); + break; + } + } + + m_invColourTransfBuf->copyFrom(getRecoBuf()); + + if (m_colourTranfParams->m_pColourTransfParams != NULL) + { + m_colourTranfParams->generateColourTransfLUTs(); + m_colourTranfParams->inverseColourTransform(m_invColourTransfBuf); + } + + return *m_invColourTransfBuf; +} +#endif \ No newline at end of file diff --git a/source/Lib/CommonLib/Picture.h b/source/Lib/CommonLib/Picture.h index 309d7b923..2fd36db40 100644 --- a/source/Lib/CommonLib/Picture.h +++ b/source/Lib/CommonLib/Picture.h @@ -48,6 +48,9 @@ #include "CodingStructure.h" #include "Hash.h" #include "MCTS.h" +#if JVET_V0108 +#include "SEIColourTransform.h" +#endif #include <deque> @@ -68,6 +71,12 @@ struct Picture : public UnitArea void createTempBuffers( const unsigned _maxCUSize ); void destroyTempBuffers(); +#if JVET_V0108 + SEIColourTransformApply* m_colourTranfParams; + PelStorage* m_invColourTransfBuf; + void createColourTransfProcessor(bool firstPictureInSequence, SEIColourTransformApply* ctiCharacteristics, PelStorage* ctiBuf, int width, int height, ChromaFormat fmt, int bitDepth); + PelUnitBuf getDisplayBuf(); +#endif PelBuf getOrigBuf(const CompArea &blk); const CPelBuf getOrigBuf(const CompArea &blk) const; diff --git a/source/Lib/CommonLib/SEI.cpp b/source/Lib/CommonLib/SEI.cpp index d0767ebb6..84aef2d0d 100644 --- a/source/Lib/CommonLib/SEI.cpp +++ b/source/Lib/CommonLib/SEI.cpp @@ -443,6 +443,9 @@ const char *SEI::getSEIMessageString(SEI::PayloadType payloadType) case SEI::CONTENT_LIGHT_LEVEL_INFO: return "Content light level information"; case SEI::AMBIENT_VIEWING_ENVIRONMENT: return "Ambient viewing environment"; case SEI::CONTENT_COLOUR_VOLUME: return "Content colour volume"; +#if JVET_V0108 + case SEI::COLOUR_TRANSFORM_INFO: return "Colour transform information"; +#endif case SEI::EQUIRECTANGULAR_PROJECTION: return "Equirectangular projection"; case SEI::SPHERE_ROTATION: return "Sphere rotation"; case SEI::REGION_WISE_PACKING: return "Region wise packing information"; diff --git a/source/Lib/CommonLib/SEI.h b/source/Lib/CommonLib/SEI.h index 7c42f62e5..2d2eab68f 100644 --- a/source/Lib/CommonLib/SEI.h +++ b/source/Lib/CommonLib/SEI.h @@ -69,6 +69,9 @@ public: DECODED_PICTURE_HASH = 132, SCALABLE_NESTING = 133, MASTERING_DISPLAY_COLOUR_VOLUME = 137, +#if JVET_V0108 + COLOUR_TRANSFORM_INFO = 142, +#endif DEPENDENT_RAP_INDICATION = 145, EQUIRECTANGULAR_PROJECTION = 150, SPHERE_ROTATION = 154, @@ -834,6 +837,30 @@ public: uint16_t m_ambientLightY; }; +#if JVET_V0108 +class SEIColourTransformInfo : public SEI +{ +public: + PayloadType payloadType() const { return COLOUR_TRANSFORM_INFO; } + SEIColourTransformInfo() { } + + virtual ~SEIColourTransformInfo() { } + + uint16_t m_id; + bool m_signalInfoFlag; + bool m_fullRangeFlag; + uint16_t m_primaries; + uint16_t m_transferFunction; + uint16_t m_matrixCoefs; + bool m_crossComponentFlag; + bool m_crossComponentInferred; + uint16_t m_numberChromaLutMinus1; + int m_chromaOffset; + uint16_t m_bitdepth; + uint16_t m_log2NumberOfPointsPerLut; + LutModel m_lut[MAX_NUM_COMPONENT]; +}; +#endif class SEIContentColourVolume : public SEI { public: diff --git a/source/Lib/CommonLib/SEIColourTransform.cpp b/source/Lib/CommonLib/SEIColourTransform.cpp new file mode 100644 index 000000000..1e463b428 --- /dev/null +++ b/source/Lib/CommonLib/SEIColourTransform.cpp @@ -0,0 +1,200 @@ +/* The copyright in this software is being made available under the BSD + * License, included below. This software may be subject to other third party + * and contributor rights, including patent rights, and no such rights are + * granted under this license. + * + * Copyright (c) 2010-2021, ITU/ISO/IEC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ITU/ISO/IEC nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + /** \file SEIColourTransform.cpp + \brief Colour transform SEI + */ + +#include "SEIColourTransform.h" + +#if JVET_V0108 +#include "SEI.h" +#include "Unit.h" +#include "Buffer.h" + +SEIColourTransformApply::SEIColourTransformApply() + : m_width (0) + , m_height (0) + , m_chromaFormat (NUM_CHROMA_FORMAT) + , m_bitDepth (0) + , m_pColourTransfParams (NULL) +{ +} + +void SEIColourTransformApply::create(uint32_t width, uint32_t height, ChromaFormat fmt, uint8_t bitDepth) +{ + m_width = width; + m_height = height; + m_chromaFormat = fmt; + m_bitDepth = bitDepth; + m_pColourTransfParams = new SEIColourTransformInfo; + m_lutSize = 1 << m_bitDepth; + for (int i = 0; i < MAX_NUM_COMPONENT; i++) + { + m_mapLut[i].resize(m_lutSize, 0); + } +} + +SEIColourTransformApply::~SEIColourTransformApply() +{ +} + +void SEIColourTransformApply::inverseColourTransform(PelStorage* transformBuf) +{ + uint8_t numComp = m_chromaFormat ? MAX_NUM_COMPONENT : 1; + PelBuf* buffY = &transformBuf->Y(); + PelBuf* buffCb = &transformBuf->Cb(); + PelBuf* buffCr = &transformBuf->Cr(); + + if (numComp == 3) + { + if (m_pColourTransfParams->m_crossComponentFlag) + { + buffCb->applyChromaCTI(buffY->buf, buffY->stride, m_mapLut[COMPONENT_Cb], m_bitDepth, m_chromaFormat, false); + buffCr->applyChromaCTI(buffY->buf, buffY->stride, m_mapLut[COMPONENT_Cr], m_bitDepth, m_chromaFormat, false); + } + else + { + buffCb->applyLumaCTI(m_mapLut[COMPONENT_Cb]); // apply direct mapping like in luma (no cross component mapping); same function, but different lut. + buffCr->applyLumaCTI(m_mapLut[COMPONENT_Cr]); + } + } + buffY->applyLumaCTI(m_mapLut[COMPONENT_Y]); +} + +void SEIColourTransformApply::generateColourTransfLUTs() +{ + uint8_t numComp = m_chromaFormat ? MAX_NUM_COMPONENT : 1; + int numPreLutPoints = 1 << m_pColourTransfParams->m_log2NumberOfPointsPerLut; + int dynamicRange = 1 << m_bitDepth; + const uint32_t orgCW = dynamicRange / numPreLutPoints; + int scalingPreLut = 1 << ( 11 - (int)floorLog2(orgCW) ); // scale-up values from cfg file (chroma preLut is scaled down in cfg) + + std::vector<Pel> pivotInPoints; + std::vector<Pel> pivotMappedPointsY(numPreLutPoints+1); + std::vector<Pel> pivotMappedPointsX(numPreLutPoints+1); + + // Create Inverse Luma LUT - same for all possible combinations of ctiCrossComp and ctiChromaLutInferred + + std::vector<int> invScale(numPreLutPoints); + + pivotInPoints = m_pColourTransfParams->m_lut[0].lutValues; + pivotMappedPointsX[0] = pivotInPoints[0]; + pivotMappedPointsY[0] = 0; + for (int j = 1; j < numPreLutPoints; j++) + { + pivotMappedPointsX[j] = pivotMappedPointsX[j - 1] + pivotInPoints[j]; + pivotMappedPointsY[j] = j * orgCW; + } + + for (int i = 0; i < numPreLutPoints; i++) + { + invScale[i] = ((int32_t)m_pColourTransfParams->m_lut[0].lutValues[i + 1] * (1 << FP_PREC) + (1 << (floorLog2(orgCW) - 1))) >> floorLog2(orgCW); + } + + for (int i = 0; i < dynamicRange; i++) + { + int idx = i / orgCW; + int tempVal = pivotMappedPointsX[idx] + ((invScale[idx] * (i - pivotMappedPointsY[idx]) + (1 << (FP_PREC - 1))) >> FP_PREC); + m_mapLut[0][i] = Clip3((Pel)0, (Pel)(dynamicRange - 1), (Pel)(tempVal)); + } + + // calculate chroma LUTs + if (m_pColourTransfParams->m_crossComponentInferred == 0) + { + for (int i = 1; i < numComp; i++) + { // loop for U and V + if (m_pColourTransfParams->m_crossComponentFlag == 1) + { + // cross-component U and V LUT + for (int j = 0; j < dynamicRange; j++) + { + int idx = j / orgCW; + int slope = scalingPreLut * (m_pColourTransfParams->m_lut[i].lutValues[idx + 1] - m_pColourTransfParams->m_lut[i].lutValues[idx]); + m_mapLut[i][j] = scalingPreLut * m_pColourTransfParams->m_lut[i].lutValues[idx] + slope * (j - pivotMappedPointsY[idx]) / orgCW; + } + } + else + { + // intra-component Chroma (U and V) LUT + // initialize pivot points + pivotInPoints = m_pColourTransfParams->m_lut[i].lutValues; + pivotMappedPointsX[0] = pivotInPoints[0]; + for (int j = 1; j <= numPreLutPoints; j++) + { + pivotMappedPointsX[j] = pivotMappedPointsX[j-1] + pivotInPoints[j]; + } + + for (int i = 0; i < numPreLutPoints; i++) + { + invScale[i] = ((int32_t)m_pColourTransfParams->m_lut[0].lutValues[i + 1] * (1 << FP_PREC) + (1 << (floorLog2(orgCW) - 1))) >> floorLog2(orgCW); + } + + for (int j = 0; j < dynamicRange; j++) + { + int idx = j / orgCW; + int tempVal = pivotMappedPointsX[idx] + ((invScale[idx] * (j - pivotMappedPointsY[idx]) + (1 << (FP_PREC - 1))) >> FP_PREC); + m_mapLut[i][j] = Clip3((Pel)0, (Pel)(dynamicRange - 1), (Pel)(tempVal)); + } + } + } + } + else + { + int chrOffset = m_pColourTransfParams->m_chromaOffset; + + std::vector<int> chromaAdjHelpLUT (numPreLutPoints); + for (int i = 0; i < numPreLutPoints; i++) + { + chromaAdjHelpLUT[i] = (m_pColourTransfParams->m_lut[0].lutValues[i + 1] == 0) ? (1 << CSCALE_FP_PREC) : ((int32_t)((m_pColourTransfParams->m_lut[0].lutValues[i + 1] + chrOffset) * (1 << FP_PREC) / orgCW)); + } + + // generate smoothed chroma LUT as done by JVET-U0078 + std::vector<int> interpLut(numPreLutPoints + 1); + for (int i = 1; i < numPreLutPoints; i++) + { + interpLut[i] = (chromaAdjHelpLUT[i] + chromaAdjHelpLUT[i - 1] + 1) / 2; + } + interpLut[0] = chromaAdjHelpLUT[0]; + interpLut[numPreLutPoints] = chromaAdjHelpLUT[numPreLutPoints - 1]; + + for (int i = 0; i < dynamicRange; i++) + { + int idx = i / orgCW; + int slope = interpLut[idx + 1] - interpLut[idx]; + m_mapLut[1][i] = interpLut[idx] + slope * (i - pivotMappedPointsY[idx]) / orgCW; + m_mapLut[2][i] = m_mapLut[1][i]; + } + } +} +#endif diff --git a/source/Lib/CommonLib/SEIColourTransform.h b/source/Lib/CommonLib/SEIColourTransform.h new file mode 100644 index 000000000..83bf8190d --- /dev/null +++ b/source/Lib/CommonLib/SEIColourTransform.h @@ -0,0 +1,73 @@ +/* The copyright in this software is being made available under the BSD + * License, included below. This software may be subject to other third party + * and contributor rights, including patent rights, and no such rights are + * granted under this license. + * + * Copyright (c) 2010-2021, ITU/ISO/IEC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ITU/ISO/IEC nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + /** \file SEIColourTransform.h + \brief Colour transform SEI + */ + +#ifndef __SEIFILMCOLOURTRANFORMAPPLY__ +#define __SEIFILMCOLOURTRANFORMAPPLY__ + +#include "CommonDef.h" +//! \ingroup CommonLib +//! \{ + +#if JVET_V0108 +struct PelStorage; +class SEIColourTransformInfo; + +class SEIColourTransformApply +{ +private: + uint32_t m_width; + uint32_t m_height; + ChromaFormat m_chromaFormat; + uint8_t m_bitDepth; + uint32_t m_lutSize; + std::vector<Pel> m_mapLut[MAX_NUM_COMPONENT]; + +public: + SEIColourTransformInfo* m_pColourTransfParams; + +public: + SEIColourTransformApply(); + virtual ~SEIColourTransformApply(); + + void create (uint32_t width, uint32_t height, ChromaFormat fmt, uint8_t bitDepth); + void inverseColourTransform (PelStorage* transformBuf); + void generateColourTransfLUTs (); + +};// END CLASS DEFINITION SEIColourTransformApply +#endif + +#endif \ No newline at end of file diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h index 4a3e499a5..1ab54fb37 100644 --- a/source/Lib/CommonLib/TypeDef.h +++ b/source/Lib/CommonLib/TypeDef.h @@ -74,6 +74,8 @@ #define JVET_V0061_SEI 1 // JVET-V0061 Display orientation SEI message +#define JVET_V0108 1 // JVET_V0108: Colour Transform Information SEI + //########### place macros to be be kept below this line ############### #define GDR_ENABLED 1 @@ -917,8 +919,14 @@ struct LFCUParam bool leftEdge; ///< indicates left edge bool topEdge; ///< indicates top edge }; - - +#if JVET_V0108 +struct LutModel +{ + bool presentFlag = false; + int numLutValues = 0; + std::vector<Pel> lutValues; +}; +#endif struct PictureHash { diff --git a/source/Lib/DecoderLib/DecLib.cpp b/source/Lib/DecoderLib/DecLib.cpp index dc03f4252..56abfb32c 100644 --- a/source/Lib/DecoderLib/DecLib.cpp +++ b/source/Lib/DecoderLib/DecLib.cpp @@ -430,6 +430,10 @@ DecLib::DecLib() , m_prevPicPOC(MAX_INT) , m_prevTid0POC(0) , m_bFirstSliceInPicture(true) +#if JVET_V0108 + , m_firstPictureInSequence(true) + , m_colourTranfParams() +#endif , m_firstSliceInBitstream(true) , m_isFirstAuInCvs( true ) , m_prevSliceSkipped(false) @@ -1684,6 +1688,10 @@ void DecLib::xActivateParameterSets( const InputNALUnit nalu ) m_pcPic->finalInit(vps, *sps, *pps, picHeader, apss, lmcsAPS, scalinglistAPS); #else m_pcPic->finalInit( vps, *sps, *pps, &m_picHeader, apss, lmcsAPS, scalinglistAPS ); +#endif +#if JVET_V0108 + m_pcPic->createColourTransfProcessor(m_firstPictureInSequence, &m_colourTranfParams, &m_invColourTransfBuf, pps->getPicWidthInLumaSamples(), pps->getPicHeightInLumaSamples(), sps->getChromaFormatIdc(), sps->getBitDepth(CHANNEL_TYPE_LUMA)); + m_firstPictureInSequence = false; #endif m_pcPic->createTempBuffers( m_pcPic->cs->pps->pcv->maxCUWidth ); m_pcPic->cs->createCoeffs((bool)m_pcPic->cs->sps->getPLTMode()); diff --git a/source/Lib/DecoderLib/DecLib.h b/source/Lib/DecoderLib/DecLib.h index 95137fbf6..4482f2a20 100644 --- a/source/Lib/DecoderLib/DecLib.h +++ b/source/Lib/DecoderLib/DecLib.h @@ -132,6 +132,11 @@ private: int m_prevPicPOC; int m_prevTid0POC; bool m_bFirstSliceInPicture; +#if JVET_V0108 + bool m_firstPictureInSequence; + SEIColourTransformApply m_colourTranfParams; + PelStorage m_invColourTransfBuf; +#endif bool m_firstSliceInSequence[MAX_VPS_LAYERS]; bool m_firstSliceInBitstream; bool m_isFirstAuInCvs; diff --git a/source/Lib/DecoderLib/SEIread.cpp b/source/Lib/DecoderLib/SEIread.cpp index 67141f6cc..252d5b773 100644 --- a/source/Lib/DecoderLib/SEIread.cpp +++ b/source/Lib/DecoderLib/SEIread.cpp @@ -330,6 +330,12 @@ void SEIReader::xReadSEImessage(SEIMessages& seis, const NalUnitType nalUnitType sei = new SEIContentColourVolume; xParseSEIContentColourVolume((SEIContentColourVolume&)*sei, payloadSize, pDecodedMessageOutputStream); break; +#if JVET_V0108 + case SEI::COLOUR_TRANSFORM_INFO: + sei = new SEIColourTransformInfo; + xParseSEIColourTransformInfo((SEIColourTransformInfo&)*sei, payloadSize, pDecodedMessageOutputStream); + break; +#endif default: for (uint32_t i = 0; i < payloadSize; i++) { @@ -1373,6 +1379,86 @@ void SEIReader::xParseSEIAmbientViewingEnvironment(SEIAmbientViewingEnvironment& sei_read_code(pDecodedMessageOutputStream, 16, code, "ambient_light_y"); sei.m_ambientLightY = (uint16_t)code; } +#if JVET_V0108 +void SEIReader::xParseSEIColourTransformInfo(SEIColourTransformInfo& sei, uint32_t payloadSize, std::ostream* pDecodedMessageOutputStream) +{ + uint32_t code; + + output_sei_message_header(sei, pDecodedMessageOutputStream, payloadSize); + + sei_read_uvlc(pDecodedMessageOutputStream, code, "colour_transform_id"); sei.m_id = code; + sei_read_flag(pDecodedMessageOutputStream, code, "colour_transform_cancel_flag"); bool colourTransformCancelFlag = code; + + if (colourTransformCancelFlag == 0) + { + sei_read_flag(pDecodedMessageOutputStream, code, "colour_transform_persistence_flag"); + sei_read_flag(pDecodedMessageOutputStream, code, "colour_transform_video_signal_info_present_flag"); sei.m_signalInfoFlag = code; + + if (sei.m_signalInfoFlag) + { + sei_read_flag(pDecodedMessageOutputStream, code, "colour_transform_full_range_flag"); sei.m_fullRangeFlag = code; + sei_read_code(pDecodedMessageOutputStream, 8, code, "colour_transform_primaries"); sei.m_primaries = code; + sei_read_code(pDecodedMessageOutputStream, 8, code, "colour_transform_transfer_function"); sei.m_transferFunction = code; + sei_read_code(pDecodedMessageOutputStream, 8, code, "colour_transform_matrix_coefficients"); sei.m_matrixCoefs = code; + } + else + { + sei.m_fullRangeFlag = 0; + sei.m_primaries = 0; + sei.m_transferFunction = 0; + sei.m_matrixCoefs = 0; + } + sei_read_code(pDecodedMessageOutputStream, 4, code, "colour_transform_bit_depth_minus8"); sei.m_bitdepth = 8+code; + sei_read_code(pDecodedMessageOutputStream, 3, code, "colour_transform_log2_number_of_points_per_lut_minus1"); sei.m_log2NumberOfPointsPerLut = code + 1; + int numLutValues = (1 << sei.m_log2NumberOfPointsPerLut) + 1; + sei_read_flag(pDecodedMessageOutputStream, code, "colour_transform_cross_comp_flag"); sei.m_crossComponentFlag = code; + sei.m_crossComponentInferred = 0; + if (sei.m_crossComponentFlag == true) + { + sei_read_flag(pDecodedMessageOutputStream, code, "colour_transform_cross_comp_inferred"); sei.m_crossComponentInferred = code; + } + for (int i = 0; i < MAX_NUM_COMPONENT; i++) { + sei.m_lut[i].lutValues.resize(numLutValues); + } + + uint16_t lutCodingLength = 2 + sei.m_bitdepth - sei.m_log2NumberOfPointsPerLut; + for (uint32_t j = 0; j < numLutValues; j++) + { + sei_read_code(pDecodedMessageOutputStream, lutCodingLength, code, "colour_transform_lut[0][i]"); + sei.m_lut[0].lutValues[j] = code; + } + sei.m_lut[0].numLutValues = numLutValues; + sei.m_lut[0].presentFlag = true; + if (sei.m_crossComponentFlag == 0 || sei.m_crossComponentInferred == 0) + { + sei_read_flag(pDecodedMessageOutputStream, code, "colour_transform_number_chroma_lut_minus1"); sei.m_numberChromaLutMinus1 = code; + for (uint32_t j = 0; j < numLutValues; j++) + { + sei_read_code(pDecodedMessageOutputStream, lutCodingLength, code, "colour_transform_lut[1][i]"); + sei.m_lut[1].lutValues[j] = code; + sei.m_lut[2].lutValues[j] = code; + } + if (sei.m_numberChromaLutMinus1 == 1) + { + for (uint32_t j = 0; j < numLutValues; j++) + { + sei_read_code(pDecodedMessageOutputStream, lutCodingLength, code, "colour_transform_lut[2][i]"); + sei.m_lut[2].lutValues[j] = code; + } + } + sei.m_lut[1].numLutValues = numLutValues; + sei.m_lut[2].numLutValues = numLutValues; + sei.m_lut[1].presentFlag = true; + sei.m_lut[2].presentFlag = true; + } + else + { + sei_read_code(pDecodedMessageOutputStream, lutCodingLength, code, "colour_transform_chroma_offset"); + sei.m_chromaOffset = code; + } + } +} +#endif void SEIReader::xParseSEIContentColourVolume(SEIContentColourVolume& sei, uint32_t payloadSize, std::ostream *pDecodedMessageOutputStream) { int i; diff --git a/source/Lib/DecoderLib/SEIread.h b/source/Lib/DecoderLib/SEIread.h index 3efec6508..ba7c81427 100644 --- a/source/Lib/DecoderLib/SEIread.h +++ b/source/Lib/DecoderLib/SEIread.h @@ -100,6 +100,9 @@ protected: #if JVET_U0084_EDRAP void xParseSEIExtendedDrapIndication (SEIExtendedDrapIndication& sei, uint32_t payloadSize, std::ostream *pDecodedMessageOutputStream); #endif +#if JVET_V0108 + void xParseSEIColourTransformInfo (SEIColourTransformInfo& sei, uint32_t payloadSize, std::ostream* pDecodedMessageOutputStream); +#endif void sei_read_scode(std::ostream *pOS, uint32_t length, int& code, const char *pSymbolName); void sei_read_code(std::ostream *pOS, uint32_t uiLength, uint32_t& ruiCode, const char *pSymbolName); diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h index b5268b8db..dbc90ab5b 100644 --- a/source/Lib/EncoderLib/EncCfg.h +++ b/source/Lib/EncoderLib/EncCfg.h @@ -660,6 +660,21 @@ protected: uint32_t m_aveSEIAmbientIlluminance; uint16_t m_aveSEIAmbientLightX; uint16_t m_aveSEIAmbientLightY; +#if JVET_V0108 + // colour tranform information sei + bool m_ctiSEIEnabled; + uint32_t m_ctiSEIId; + bool m_ctiSEISignalInfoFlag; + bool m_ctiSEIFullRangeFlag; + uint32_t m_ctiSEIPrimaries; + uint32_t m_ctiSEITransferFunction; + uint32_t m_ctiSEIMatrixCoefs; + bool m_ctiSEICrossComponentFlag; + bool m_ctiSEICrossComponentInferred; + uint32_t m_ctiSEINumberChromaLut; + int m_ctiSEIChromaOffset; + LutModel m_ctiSEILut[MAX_NUM_COMPONENT]; +#endif // ccv sei bool m_ccvSEIEnabled; bool m_ccvSEICancelFlag; @@ -1854,6 +1869,34 @@ public: uint16_t getAmbientViewingEnvironmentSEIAmbientLightX() { return m_aveSEIAmbientLightX; } void setAmbientViewingEnvironmentSEIAmbientLightY( uint16_t v ) { m_aveSEIAmbientLightY = v; } uint16_t getAmbientViewingEnvironmentSEIAmbientLightY() { return m_aveSEIAmbientLightY; } +#if JVET_V0108 + // colour tranform information sei + void setCtiSEIEnabled(bool b) { m_ctiSEIEnabled = b; } + bool getCtiSEIEnabled() { return m_ctiSEIEnabled; } + void setCtiSEIId(uint32_t b) { m_ctiSEIId = b; } + uint32_t getCtiSEIId() { return m_ctiSEIId; } + void setCtiSEISignalInfoFlag(bool b) { m_ctiSEISignalInfoFlag = b; } + bool getCtiSEISignalInfoFlag() { return m_ctiSEISignalInfoFlag; } + void setCtiSEIFullRangeFlag(bool b) { m_ctiSEIFullRangeFlag = b; } + bool getCtiSEIFullRangeFlag() { return m_ctiSEIFullRangeFlag; } + uint32_t getCtiSEIPrimaries() { return m_ctiSEIPrimaries; } + void setCtiSEIPrimaries(uint32_t v) { m_ctiSEIPrimaries = v; } + uint32_t getCtiSEITransferFunction() { return m_ctiSEITransferFunction; } + void setCtiSEITransferFunction(uint32_t v) { m_ctiSEITransferFunction = v; } + uint32_t getCtiSEIMatrixCoefs() { return m_ctiSEIMatrixCoefs; } + void setCtiSEIMatrixCoefs(uint32_t v) { m_ctiSEIMatrixCoefs = v; } + void setCtiSEICrossComponentFlag(bool b) { m_ctiSEICrossComponentFlag = b; } + bool getCtiSEICrossComponentFlag() { return m_ctiSEICrossComponentFlag; } + void setCtiSEICrossComponentInferred(bool b) { m_ctiSEICrossComponentInferred = b; } + bool getCtiSEICrossComponentInferred() { return m_ctiSEICrossComponentInferred; } + uint32_t getCtiSEINbChromaLut() { return m_ctiSEINumberChromaLut; } + void setCtiSEINbChromaLut(uint32_t v) { m_ctiSEINumberChromaLut = v; } + int getCtiSEIChromaOffset() { return m_ctiSEIChromaOffset; } + void setCtiSEIChromaOffset(int v) { m_ctiSEIChromaOffset = v; } + LutModel getCtiSEILut(int idx) { return m_ctiSEILut[idx]; } + void setCtiSEILut(LutModel& cmp, int idx) { m_ctiSEILut[idx] = cmp; } + LutModel* getCtiSEILuts() { return m_ctiSEILut; } +#endif // ccv SEI void setCcvSEIEnabled(bool b) { m_ccvSEIEnabled = b; } bool getCcvSEIEnabled() { return m_ccvSEIEnabled; } diff --git a/source/Lib/EncoderLib/EncGOP.cpp b/source/Lib/EncoderLib/EncGOP.cpp index cf74366bf..81932147c 100644 --- a/source/Lib/EncoderLib/EncGOP.cpp +++ b/source/Lib/EncoderLib/EncGOP.cpp @@ -806,6 +806,15 @@ void EncGOP::xCreateIRAPLeadingSEIMessages (SEIMessages& seiMessages, const SPS seiMessages.push_back(seiDepthRepresentationInfo); } #endif +#if JVET_V0108 + // colour transform information + if (m_pcCfg->getCtiSEIEnabled()) + { + SEIColourTransformInfo* seiCTI = new SEIColourTransformInfo; + m_seiEncoder.initSEIColourTransformInfo(seiCTI); + seiMessages.push_back(seiCTI); + } +#endif } void EncGOP::xCreatePerPictureSEIMessages (int picInGOP, SEIMessages& seiMessages, SEIMessages& nestedSeiMessages, Slice *slice) diff --git a/source/Lib/EncoderLib/SEIEncoder.cpp b/source/Lib/EncoderLib/SEIEncoder.cpp index a88724830..da179ba69 100644 --- a/source/Lib/EncoderLib/SEIEncoder.cpp +++ b/source/Lib/EncoderLib/SEIEncoder.cpp @@ -998,6 +998,33 @@ void SEIEncoder::initSEIDepthRepresentationInfo(SEIDepthRepresentationInfo *sei) } #endif +#if JVET_V0108 +void SEIEncoder::initSEIColourTransformInfo(SEIColourTransformInfo* seiCTI) +{ + CHECK(!(m_isInitialized), "Unspecified error"); + CHECK(!(seiCTI != NULL), "Unspecified error"); + + // Set SEI message parameters read from command line options + seiCTI->m_id = m_pcCfg->getCtiSEIId(); + seiCTI->m_signalInfoFlag = m_pcCfg->getCtiSEISignalInfoFlag(); + seiCTI->m_fullRangeFlag = m_pcCfg->getCtiSEIFullRangeFlag(); + seiCTI->m_primaries = m_pcCfg->getCtiSEIPrimaries(); + seiCTI->m_transferFunction = m_pcCfg->getCtiSEITransferFunction(); + seiCTI->m_matrixCoefs = m_pcCfg->getCtiSEIMatrixCoefs(); + seiCTI->m_crossComponentFlag = m_pcCfg->getCtiSEICrossComponentFlag(); + seiCTI->m_crossComponentInferred = m_pcCfg->getCtiSEICrossComponentInferred(); + seiCTI->m_numberChromaLutMinus1 = m_pcCfg->getCtiSEINbChromaLut() - 1; + seiCTI->m_chromaOffset = m_pcCfg->getCtiSEIChromaOffset(); + + seiCTI->m_bitdepth = m_pcCfg->getBitDepth(CHANNEL_TYPE_LUMA); + + for (int i = 0; i < MAX_NUM_COMPONENT; i++) { + seiCTI->m_lut[i] = m_pcCfg->getCtiSEILut(i); + } + seiCTI->m_log2NumberOfPointsPerLut = floorLog2(seiCTI->m_lut[0].numLutValues - 1); +} +#endif + void SEIEncoder::initSEISubpictureLevelInfo(SEISubpicureLevelInfo *sei, const SPS *sps) { const EncCfgParam::CfgSEISubpictureLevel &cfgSubPicLevel = m_pcCfg->getSubpicureLevelInfoSEICfg(); diff --git a/source/Lib/EncoderLib/SEIEncoder.h b/source/Lib/EncoderLib/SEIEncoder.h index 6534b2a55..6b89bc326 100644 --- a/source/Lib/EncoderLib/SEIEncoder.h +++ b/source/Lib/EncoderLib/SEIEncoder.h @@ -96,6 +96,9 @@ public: void initSEIDepthRepresentationInfo(SEIDepthRepresentationInfo *sei); #endif bool initSEIAnnotatedRegions(SEIAnnotatedRegions *sei, int currPOC); +#if JVET_V0108 + void initSEIColourTransformInfo(SEIColourTransformInfo* sei); +#endif void readAnnotatedRegionSEI(std::istream &fic, SEIAnnotatedRegions *seiAnnoRegion, bool &failed); private: EncCfg* m_pcCfg; diff --git a/source/Lib/EncoderLib/SEIwrite.cpp b/source/Lib/EncoderLib/SEIwrite.cpp index c0e834c4c..2c8dfd120 100644 --- a/source/Lib/EncoderLib/SEIwrite.cpp +++ b/source/Lib/EncoderLib/SEIwrite.cpp @@ -145,6 +145,11 @@ void SEIWriter::xWriteSEIpayloadData(OutputBitstream &bs, const SEI& sei, HRD &h case SEI::CONTENT_COLOUR_VOLUME: xWriteSEIContentColourVolume(*static_cast<const SEIContentColourVolume*>(&sei)); break; +#if JVET_V0108 + case SEI::COLOUR_TRANSFORM_INFO: + xWriteSEIColourTransformInfo(*static_cast<const SEIColourTransformInfo*>(&sei)); + break; +#endif case SEI::SUBPICTURE_LEVEL_INFO: xWriteSEISubpictureLevelInfo(*static_cast<const SEISubpicureLevelInfo*>(&sei)); break; @@ -1287,4 +1292,60 @@ void SEIWriter::xWriteSEIContentColourVolume(const SEIContentColourVolume &sei) } } +#if JVET_V0108 +void SEIWriter::xWriteSEIColourTransformInfo(const SEIColourTransformInfo& sei) +{ + bool colourTransformCancelFlag = 0; + bool colourTransformPersistenceFlag = 0; + + WRITE_UVLC(sei.m_id, "colour_transform_id"); + WRITE_FLAG(colourTransformCancelFlag, "colour_transform_cancel_flag"); + + if (colourTransformCancelFlag == 0) + { + WRITE_FLAG(colourTransformPersistenceFlag, "colour_transform_persistence_flag"); + WRITE_FLAG(sei.m_signalInfoFlag, "colour_transform_video_signal_info_present_flag"); + + if (sei.m_signalInfoFlag) + { + WRITE_FLAG(sei.m_fullRangeFlag, "colour_transform_full_range_flag"); + WRITE_CODE(sei.m_primaries, 8, "colour_transform_primaries"); + WRITE_CODE(sei.m_transferFunction, 8, "colour_transform_transfer_function"); + WRITE_CODE(sei.m_matrixCoefs, 8, "colour_transform_matrix_coefficients"); + } + WRITE_CODE(sei.m_bitdepth - 8, 4, "colour_transform_bit_depth_minus8"); + WRITE_CODE(sei.m_log2NumberOfPointsPerLut - 1, 3, "colour_transform_log2_number_of_points_per_lut_minus1"); + WRITE_FLAG(sei.m_crossComponentFlag, "colour_transform_cross_comp_flag"); + if (sei.m_crossComponentFlag) + { + WRITE_FLAG(sei.m_crossComponentInferred, "colour_transform_cross_comp_inferred"); + } + + uint16_t lutCodingLength = 2 + sei.m_bitdepth - sei.m_log2NumberOfPointsPerLut; + for (uint32_t j = 0; j < sei.m_lut[0].numLutValues; j++) + { + WRITE_CODE(sei.m_lut[0].lutValues[j], lutCodingLength, "colour_transform_lut[0][i]"); + } + if (sei.m_crossComponentFlag == 0 || sei.m_crossComponentInferred == 0) + { + WRITE_FLAG(sei.m_numberChromaLutMinus1, "colour_transform_number_chroma_lut_minus1"); + for (uint32_t j = 0; j < sei.m_lut[1].numLutValues; j++) + { + WRITE_CODE(sei.m_lut[1].lutValues[j], lutCodingLength, "colour_transform_lut[1][i]"); + } + if (sei.m_numberChromaLutMinus1 == 1) + { + for (uint32_t j = 0; j < sei.m_lut[2].numLutValues; j++) + { + WRITE_CODE(sei.m_lut[2].lutValues[j], lutCodingLength, "colour_transform_lut[2][i]"); + } + } + } + else + { + WRITE_CODE(sei.m_chromaOffset, lutCodingLength, "colour_transform_chroma_offset"); + } + } +} +#endif //! \} diff --git a/source/Lib/EncoderLib/SEIwrite.h b/source/Lib/EncoderLib/SEIwrite.h index f4cf84af5..7f6c9ba9b 100644 --- a/source/Lib/EncoderLib/SEIwrite.h +++ b/source/Lib/EncoderLib/SEIwrite.h @@ -92,6 +92,9 @@ protected: void xWriteSEIContentLightLevelInfo(const SEIContentLightLevelInfo& sei); void xWriteSEIAmbientViewingEnvironment(const SEIAmbientViewingEnvironment& sei); void xWriteSEIContentColourVolume(const SEIContentColourVolume &sei); +#if JVET_V0108 + void xWriteSEIColourTransformInfo(const SEIColourTransformInfo& sei); +#endif void xWriteSEIAnnotatedRegions (const SEIAnnotatedRegions& sei); void xWriteSEIpayloadData(OutputBitstream &bs, const SEI& sei, HRD &hrd, const uint32_t temporalId); void xWriteByteAlign(); -- GitLab