From d2fcb9faf4301eae034b4d53ab874bfb211781c7 Mon Sep 17 00:00:00 2001 From: Yue Li <yue.li@bytedance.com> Date: Fri, 3 Feb 2023 16:18:37 -0800 Subject: [PATCH] integrate JVET-AC0177 (EE1-1.7: Deep In-Loop Filter with Additional Input Information) --- source/App/DecoderApp/DecApp.cpp | 3 + source/App/DecoderApp/DecAppCfg.cpp | 3 + source/App/DecoderApp/DecAppCfg.h | 3 + source/App/EncoderApp/EncApp.cpp | 6 + source/App/EncoderApp/EncAppCfg.cpp | 4 + source/App/EncoderApp/EncAppCfg.h | 6 + source/Lib/CommonLib/CommonDef.h | 3 + source/Lib/CommonLib/NNFilterSet1.cpp | 137 ++++++++++- source/Lib/CommonLib/NNFilterSet1.h | 7 + source/Lib/CommonLib/NNInference.h | 222 ++++++++++++++++++ source/Lib/CommonLib/Slice.h | 7 + source/Lib/CommonLib/TypeDef.h | 11 +- source/Lib/DecoderLib/DecLib.cpp | 4 + source/Lib/DecoderLib/DecLib.h | 15 +- source/Lib/DecoderLib/VLCReader.cpp | 3 + source/Lib/EncoderLib/EncCfg.h | 14 ++ source/Lib/EncoderLib/EncGOP.cpp | 4 + source/Lib/EncoderLib/EncLib.cpp | 3 + source/Lib/EncoderLib/VLCWriter.cpp | 3 + .../AdditionalLumaInter/Conversion/convert.py | 27 +++ .../AdditionalLumaInter/Conversion/net.py | 99 ++++++++ .../AdditionalLumaInter/Training/dataset.py | 117 +++++++++ .../AdditionalLumaInter/Training/main.py | 142 +++++++++++ .../AdditionalLumaInter/Training/net.py | 99 ++++++++ .../AdditionalLumaInter/Training/train.sh | 3 + .../AdditionalLumaInter/instruction.pdf | Bin 0 -> 53251 bytes 26 files changed, 928 insertions(+), 17 deletions(-) create mode 100755 training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Conversion/convert.py create mode 100644 training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Conversion/net.py create mode 100644 training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Training/dataset.py create mode 100755 training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Training/main.py create mode 100644 training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Training/net.py create mode 100755 training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Training/train.sh create mode 100644 training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/instruction.pdf diff --git a/source/App/DecoderApp/DecApp.cpp b/source/App/DecoderApp/DecApp.cpp index d3296a34f7..d1031389e4 100644 --- a/source/App/DecoderApp/DecApp.cpp +++ b/source/App/DecoderApp/DecApp.cpp @@ -620,6 +620,9 @@ void DecApp::xCreateDecLib() m_cDecLib.setNnlfSet1InterChromaModelName (m_nnlfSet1InterChromaModelName); m_cDecLib.setNnlfSet1IntraLumaModelName (m_nnlfSet1IntraLumaModelName); m_cDecLib.setNnlfSet1IntraChromaModelName (m_nnlfSet1IntraChromaModelName); +#if JVET_AC0177_MULTI_FRAME + m_cDecLib.setNnlfSet1AlternativeInterLumaModelName (m_nnlfSet1AlternativeInterLumaModelName); +#endif #endif if (!m_outputDecodedSEIMessagesFilename.empty()) diff --git a/source/App/DecoderApp/DecAppCfg.cpp b/source/App/DecoderApp/DecAppCfg.cpp index 027d4a3e58..8202fbc76c 100644 --- a/source/App/DecoderApp/DecAppCfg.cpp +++ b/source/App/DecoderApp/DecAppCfg.cpp @@ -90,6 +90,9 @@ bool DecAppCfg::parseCfg( int argc, char* argv[] ) ( "NnlfSet1InterChromaModel", m_nnlfSet1InterChromaModelName, string("models/NnlfSet1_ChromaCNNFilter_InterSlice_int16.sadl"), "NnlfSet1 inter chroma model name") ( "NnlfSet1IntraLumaModel", m_nnlfSet1IntraLumaModelName, string("models/NnlfSet1_LumaCNNFilter_IntraSlice_int16.sadl"), "NnlfSet1 intra luma model name") ( "NnlfSet1IntraChromaModel", m_nnlfSet1IntraChromaModelName, string("models/NnlfSet1_ChromaCNNFilter_IntraSlice_int16.sadl"), "NnlfSet1 intra chroma model name") +#if JVET_AC0177_MULTI_FRAME + ( "NnlfSet1AlternativeInterLumaModel", m_nnlfSet1AlternativeInterLumaModelName, string("models/NnlfSet1_LumaCNNFilter_InterSlice_MultiframePrior_Tid345_int16.sadl"), "NnlfSet1 alternative inter luma model name") +#endif #endif ("OplFile,-opl", m_oplFilename , string(""), "opl-file name without extension for conformance testing\n") diff --git a/source/App/DecoderApp/DecAppCfg.h b/source/App/DecoderApp/DecAppCfg.h index 7dbb765f40..e8a5dad494 100644 --- a/source/App/DecoderApp/DecAppCfg.h +++ b/source/App/DecoderApp/DecAppCfg.h @@ -72,6 +72,9 @@ protected: std::string m_nnlfSet1InterChromaModelName; ///<inter chroma nnlf set1 model std::string m_nnlfSet1IntraLumaModelName; ///<intra luma nnlf set1 model std::string m_nnlfSet1IntraChromaModelName; ///<inra chroma nnlf set1 model +#if JVET_AC0177_MULTI_FRAME + std::string m_nnlfSet1AlternativeInterLumaModelName; ///<alternative inter luma nnlf set1 model +#endif #endif int m_iSkipFrame; ///< counter for frames prior to the random access point to skip diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp index 03f3e3cca1..d39de5428f 100644 --- a/source/App/EncoderApp/EncApp.cpp +++ b/source/App/EncoderApp/EncApp.cpp @@ -248,6 +248,9 @@ void EncApp::xInitLibCfg() m_cEncLib.setNnlfSet1InterChromaModelName (m_nnlfSet1InterChromaModelName); m_cEncLib.setNnlfSet1IntraLumaModelName (m_nnlfSet1IntraLumaModelName); m_cEncLib.setNnlfSet1IntraChromaModelName (m_nnlfSet1IntraChromaModelName); +#if JVET_AC0177_MULTI_FRAME + m_cEncLib.setNnlfSet1AlternativeInterLumaModelName (m_nnlfSet1AlternativeInterLumaModelName); +#endif #endif #if JVET_AB0068_RD m_cEncLib.setUseEncNnlfOpt (m_encNnlfOpt); @@ -1087,6 +1090,9 @@ void EncApp::xInitLibCfg() m_cEncLib.setNnlfSet1InferSizeBase (m_nnlfSet1InferSizeBase); m_cEncLib.setNnlfSet1InferSizeExtension (m_nnlfSet1InferSizeExtension); m_cEncLib.setNnlfSet1MaxNumParams (m_nnlfSet1MaxNumParams); +#if JVET_AC0177_MULTI_FRAME + m_cEncLib.setNnlfSet1UseMultiframe (m_nnlfSet1Multiframe); +#endif #endif m_cEncLib.setLmcs ( m_lmcsEnabled ); m_cEncLib.setReshapeSignalType ( m_reshapeSignalType ); diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp index 8ba5c3cf29..629b53b137 100644 --- a/source/App/EncoderApp/EncAppCfg.cpp +++ b/source/App/EncoderApp/EncAppCfg.cpp @@ -1443,6 +1443,10 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) ( "NnlfSet1InterChromaModel", m_nnlfSet1InterChromaModelName, string("models/NnlfSet1_ChromaCNNFilter_InterSlice_int16.sadl"), "NnlfSet1 inter chroma model name") ( "NnlfSet1IntraLumaModel", m_nnlfSet1IntraLumaModelName, string("models/NnlfSet1_LumaCNNFilter_IntraSlice_int16.sadl"), "NnlfSet1 intra luma model name") ( "NnlfSet1IntraChromaModel", m_nnlfSet1IntraChromaModelName, string("models/NnlfSet1_ChromaCNNFilter_IntraSlice_int16.sadl"), "NnlfSet1 intra chroma model name") +#if JVET_AC0177_MULTI_FRAME + ( "NnlfSet1AlternativeInterLumaModel", m_nnlfSet1AlternativeInterLumaModelName, string("models/NnlfSet1_LumaCNNFilter_InterSlice_MultiframePrior_Tid345_int16.sadl"), "NnlfSet1 alternative inter luma model name") + ( "NnlfSet1Multiframe", m_nnlfSet1Multiframe, false, "Input multiple frames in NN-based loop filter set 1" ) +#endif #endif #if JVET_AB0068_RD ( "EncNnlfOpt", m_encNnlfOpt, false, "Encoder optimization with NN-based loop filter") diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h index 5d59c2ab36..ba685eced6 100644 --- a/source/App/EncoderApp/EncAppCfg.h +++ b/source/App/EncoderApp/EncAppCfg.h @@ -94,6 +94,9 @@ protected: std::string m_nnlfSet1InterChromaModelName; ///<inter chroma nnlf set1 model std::string m_nnlfSet1IntraLumaModelName; ///<intra luma nnlf set1 model std::string m_nnlfSet1IntraChromaModelName; ///<inra chroma nnlf set1 model +#if JVET_AC0177_MULTI_FRAME + std::string m_nnlfSet1AlternativeInterLumaModelName; ///<alternative inter luma nnlf set1 model +#endif #endif #if JVET_AB0068_RD std::string m_rdoCnnlfInterLumaModelName; ///<inter luma cnnlf model @@ -733,6 +736,9 @@ protected: unsigned m_nnlfSet1InferSizeBase; unsigned m_nnlfSet1InferSizeExtension; unsigned m_nnlfSet1MaxNumParams; +#if JVET_AC0177_MULTI_FRAME + bool m_nnlfSet1Multiframe; +#endif #endif #if JVET_AB0068_RD diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h index a8c42e17eb..c037e9e3d5 100644 --- a/source/Lib/CommonLib/CommonDef.h +++ b/source/Lib/CommonLib/CommonDef.h @@ -198,6 +198,9 @@ static const int NNQPOFFSET[QP_OFFSET_NUM] = { -5, 5 }; #if NN_FILTERING_SET_1 static const int NN_INPUT_PRECISION= 13; static const int NN_OUTPUT_PRECISION= 13; +#if JVET_AC0177_MULTI_FRAME +static const int MINIMUM_TID_ENABLING_TEMPORAL_INPUTS = 3; // JVET-AC0177: minimum temporal layer id enabling temporal inputs +#endif #endif diff --git a/source/Lib/CommonLib/NNFilterSet1.cpp b/source/Lib/CommonLib/NNFilterSet1.cpp index 4cac99ccf4..e2606d203c 100644 --- a/source/Lib/CommonLib/NNFilterSet1.cpp +++ b/source/Lib/CommonLib/NNFilterSet1.cpp @@ -80,12 +80,19 @@ void NNFilterSet1::create( const int picWidth, const int picHeight, const Chroma } #endif } +#if JVET_AC0177_MULTI_FRAME +void NNFilterSet1::init(std::string interLuma, std::string interChroma, std::string intraLuma, std::string intraChroma, std::string alternativeInterLuma) +#else void NNFilterSet1::init(std::string interLuma, std::string interChroma, std::string intraLuma, std::string intraChroma) +#endif { m_interLuma = interLuma; m_interChroma = interChroma; m_intraLuma = intraLuma; m_intraChroma = intraChroma; +#if JVET_AC0177_MULTI_FRAME + m_alternativeInterLuma = alternativeInterLuma; +#endif } void NNFilterSet1::destroy() { @@ -108,6 +115,9 @@ struct ModelData { vector<sadl::Tensor<T>> inputs; int hor,ver; bool luma,inter; +#if JVET_AC0177_MULTI_FRAME + bool alternative; +#endif }; template<typename T> @@ -120,12 +130,20 @@ static std::vector<ModelData<T>> initSpace() { static std::vector<ModelData<TypeSadl>> models=initSpace<TypeSadl>(); template<typename T> +#if JVET_AC0177_MULTI_FRAME +static ModelData<T> &getModel(int ver, int hor, bool luma, bool inter, const string modelName, bool alternative=false) +#else static ModelData<T> &getModel(int ver, int hor, bool luma, bool inter, const string modelName) +#endif { ModelData<T> *ptr = nullptr; for(auto &m: models) { +#if JVET_AC0177_MULTI_FRAME + if (m.luma == luma && m.inter == inter && m.alternative == alternative) +#else if (m.luma == luma && m.inter == inter) +#endif { ptr = &m; break; @@ -151,6 +169,9 @@ static ModelData<T> &getModel(int ver, int hor, bool luma, bool inter, const str m.inter = inter; m.ver = 0; m.hor = 0; +#if JVET_AC0177_MULTI_FRAME + m.alternative = alternative; +#endif } ModelData<T> &m = *ptr; if (m.ver != ver || m.hor != hor) @@ -279,6 +300,60 @@ void extractOutputsLuma (Picture* pic, sadl::Model<T> &m, PelStorage& tempBuf, P } } +#if JVET_AC0177_FLIP_INPUT +template<typename T> +void extractOutputsLuma (Picture* pic, sadl::Model<T> &m, PelStorage& tempBuf, PelStorage& tempScaledBuf, UnitArea inferArea, int extLeft, int extRight, int extTop, int extBottom, bool inter, bool flip) +{ +#if NN_FIXED_POINT_IMPLEMENTATION + int log2InputScale = 10; + int log2OutputScale = 10; + int shiftInput = NN_OUTPUT_PRECISION - log2InputScale; + int shiftOutput = NN_OUTPUT_PRECISION - log2OutputScale; + int offset = (1 << shiftOutput) / 2; +#else + double inputScale = 1024; + double outputScale = 1024; +#endif + auto output = m.result(0); + PelBuf bufDst = tempBuf.getBuf(inferArea).get(COMPONENT_Y); + +#if SCALE_NN_RESIDUE + PelBuf bufScaledDst = tempScaledBuf.getBuf(inferArea).get(COMPONENT_Y); +#endif + + int hor = inferArea.lwidth(); + int ver = inferArea.lheight(); + + PelBuf bufRec = pic->getRecBeforeDbfBuf(inferArea).get(COMPONENT_Y); + + for (int c = 0; c < 4; c++) // output includes 4 sub images + { + for (int y = 0; y < ver >> 1; y++) + { + for (int x = 0; x < hor >> 1; x++) + { + int yy = (y << 1) + c / 2; + int xx = (x << 1) + c % 2; + NNInference::geometricTransform(ver, hor, yy, xx, flip); + if (xx < extLeft || yy < extTop || xx >= hor - extRight || yy >= ver - extBottom) + { + continue; + } +#if NN_FIXED_POINT_IMPLEMENTATION + int out = ( output(0, y, x, c) + (bufRec.at(xx, yy) << shiftInput) + offset) >> shiftOutput; +#else + int out = ( output(0, y, x, c) + bufRec.at(xx, yy) / inputScale ) * outputScale + 0.5; +#endif + bufDst.at(xx, yy) = Pel(Clip3<int>( 0, 1023, out)); + + #if SCALE_NN_RESIDUE + bufScaledDst.at(xx, yy) = Pel(Clip3<int>(0, 1023 << NN_RESIDUE_ADDITIONAL_SHIFT, out * (1 << NN_RESIDUE_ADDITIONAL_SHIFT) ) ); + #endif + } + } + } +} +#endif template<typename T> void extractOutputsChroma (Picture* pic, sadl::Model<T> &m, PelStorage& tempBuf, PelStorage& tempScaledBuf, UnitArea inferArea, int extLeft, int extRight, int extTop, int extBottom, bool inter) { @@ -353,12 +428,17 @@ void NNFilterSet1::cnnFilterLumaBlock(Picture* pic, UnitArea inferArea, int extL if (border_to_skip>0) sadl::Tensor<float>::skip_border = true; // get model +#if JVET_AC0177_MULTI_FRAME + bool alternative = pic->slices[0]->getSPS()->getNnlfSet1MultiframeEnabledFlag() && inter && pic->slices[0]->getTLayer() >= MINIMUM_TID_ENABLING_TEMPORAL_INPUTS ? true : false; + // get model + ModelData<T> &m = getModel<T>(inferArea.lheight(), inferArea.lwidth(), true, inter, inter ? (alternative ? m_alternativeInterLuma : m_interLuma) : m_intraLuma, alternative); +#else ModelData<T> &m = getModel<T>(inferArea.lheight(), inferArea.lwidth(), true, inter, inter ? m_interLuma : m_intraLuma); +#endif sadl::Model<T> &model = m.model; // get inputs vector<sadl::Tensor<T>> &inputs = m.inputs; - int seqQp = pic->slices[0]->getPPS()->getPicInitQPMinus26() + 26; int sliceQp = pic->slices[0]->getSliceQp(); int delta = inter ? paramIdx * 5 : paramIdx * 2; @@ -366,20 +446,47 @@ void NNFilterSet1::cnnFilterLumaBlock(Picture* pic, UnitArea inferArea, int extL { delta = 5 - delta; } +#if JVET_AC0177_FLIP_INPUT + bool flip = pic->slices[0]->getSPS()->getNnlfSet1MultiframeEnabledFlag() && (paramIdx == 2) ? true : false; + if (flip) + { + delta = 0; + } +#endif int qp = inter ? seqQp - delta : sliceQp - delta; std::vector<InputData> listInputData; #if JVET_AC0089_COMBINE_INTRA_INTER - InputData inputRec = { NN_INPUT_REC, 0, inputScale, NN_INPUT_PRECISION - log2InputScale, true, false }; - InputData inputPred = { NN_INPUT_PRED, 1, inputScale, NN_INPUT_PRECISION - log2InputScale, true, false }; - InputData inputBs = { NN_INPUT_BS, 2, inputScale, NN_INPUT_PRECISION - log2InputScale, true, false }; - InputData inputIpb = { NN_INPUT_IPB, 3, 1, NN_INPUT_PRECISION, true, false }; - InputData inputQp = { NN_INPUT_LOCAL_QP, 4, qpScale, NN_INPUT_PRECISION - log2QpScale, true, false }; - listInputData.push_back(inputRec); - listInputData.push_back(inputPred); - listInputData.push_back(inputBs); - listInputData.push_back(inputIpb); - listInputData.push_back(inputQp); +#if JVET_AC0177_MULTI_FRAME + if (alternative) + { + InputData inputRec = {NN_INPUT_REC, 0, inputScale, NN_INPUT_PRECISION - log2InputScale, true, false}; + InputData inputPred = {NN_INPUT_PRED, 1, inputScale, NN_INPUT_PRECISION - log2InputScale, true, false}; + InputData inputRefList0 = {NN_INPUT_REF_LIST_0, 2, inputScale, NN_INPUT_PRECISION - log2InputScale, true, false}; + InputData inputRefList1 = {NN_INPUT_REF_LIST_1, 3, inputScale, NN_INPUT_PRECISION - log2InputScale, true, false}; + InputData inputQp = {NN_INPUT_LOCAL_QP, 4, qpScale, NN_INPUT_PRECISION - log2QpScale, true, false}; + listInputData.push_back(inputRec); + listInputData.push_back(inputPred); + listInputData.push_back(inputRefList0); + listInputData.push_back(inputRefList1); + listInputData.push_back(inputQp); + } + else + { +#endif + InputData inputRec = { NN_INPUT_REC, 0, inputScale, NN_INPUT_PRECISION - log2InputScale, true, false }; + InputData inputPred = { NN_INPUT_PRED, 1, inputScale, NN_INPUT_PRECISION - log2InputScale, true, false }; + InputData inputBs = { NN_INPUT_BS, 2, inputScale, NN_INPUT_PRECISION - log2InputScale, true, false }; + InputData inputIpb = { NN_INPUT_IPB, 3, 1, NN_INPUT_PRECISION, true, false }; + InputData inputQp = { NN_INPUT_LOCAL_QP, 4, qpScale, NN_INPUT_PRECISION - log2QpScale, true, false }; + listInputData.push_back(inputRec); + listInputData.push_back(inputPred); + listInputData.push_back(inputBs); + listInputData.push_back(inputIpb); + listInputData.push_back(inputQp); +#if JVET_AC0177_MULTI_FRAME + } +#endif #else if (inter) { @@ -414,11 +521,19 @@ void NNFilterSet1::cnnFilterLumaBlock(Picture* pic, UnitArea inferArea, int extL listInputData.push_back(inputQp); } #endif +#if JVET_AC0177_FLIP_INPUT + NNInference::prepareInputs<T>(pic, inferArea, inputs, -1, qp, -1, listInputData, flip); +#else NNInference::prepareInputs<T>(pic, inferArea, inputs, -1, qp, -1, listInputData); +#endif NNInference::infer<T>(model, inputs); // get outputs +#if JVET_AC0177_FLIP_INPUT + extractOutputsLuma(pic, model, m_tempBuf[paramIdx], m_tempScaledBuf[paramIdx], inferArea, extLeft, extRight, extTop, extBottom, inter, flip); +#else extractOutputsLuma(pic, model, m_tempBuf[paramIdx], m_tempScaledBuf[paramIdx], inferArea, extLeft, extRight, extTop, extBottom, inter); +#endif } diff --git a/source/Lib/CommonLib/NNFilterSet1.h b/source/Lib/CommonLib/NNFilterSet1.h index b97214a683..6a6dfa4e63 100644 --- a/source/Lib/CommonLib/NNFilterSet1.h +++ b/source/Lib/CommonLib/NNFilterSet1.h @@ -53,6 +53,9 @@ public: std::vector<PelStorage> m_tempBuf; std::string m_interLuma, m_interChroma, m_intraLuma, m_intraChroma; +#if JVET_AC0177_MULTI_FRAME + std::string m_alternativeInterLuma; +#endif #if SCALE_NN_RESIDUE std::vector<PelStorage> m_tempScaledBuf; #endif @@ -63,7 +66,11 @@ public: void scaleResidualBlock(Picture *pic, UnitArea inferArea, int paramIdx, ComponentID compID); #endif void create(const int picWidth, const int picHeight, const ChromaFormat format, const int nnlfSet1NumParams); +#if JVET_AC0177_MULTI_FRAME + void init(std::string interLuma, std::string interChroma, std::string intraLuma, std::string intraChroma, std::string alternativeInterLuma); +#else void init(std::string interLuma, std::string interChroma, std::string intraLuma, std::string intraChroma); +#endif void destroy(); }; diff --git a/source/Lib/CommonLib/NNInference.h b/source/Lib/CommonLib/NNInference.h index ed40b60636..84ffa13b63 100644 --- a/source/Lib/CommonLib/NNInference.h +++ b/source/Lib/CommonLib/NNInference.h @@ -135,6 +135,90 @@ public: } #endif } +#if JVET_AC0177_FLIP_INPUT + static void geometricTransform (int ver, int hor, int& y, int& x, bool flip) + { + x = flip ? hor - 1 - x : x; + } + template<typename T> + static void fillInputFromBuf (Picture* pic, UnitArea inferArea, sadl::Tensor<T> &input, PelUnitBuf buf, bool luma, bool chroma, double scale, int shift, bool flip) + { + PelBuf bufY, bufCb, bufCr; + + if (luma) + { + bufY = buf.get(COMPONENT_Y); + } + if (chroma) + { + bufCb = buf.get(COMPONENT_Cb); + bufCr = buf.get(COMPONENT_Cr); + } + + int hor, ver; + if (luma) + { + hor = inferArea.lwidth(); + ver = inferArea.lheight(); + } + else + { + hor = inferArea.lwidth() >> 1; + ver = inferArea.lheight() >> 1; + } +#if NN_FIXED_POINT_IMPLEMENTATION + for (int y = 0; y < ver; y++) + { + for (int x = 0; x < hor; x++) + { + int yT = y; + int xT = x; + geometricTransform(ver, hor, yT, xT, flip); + if (luma && !chroma) + { + input(0, yT, xT, 0) = bufY.at(x, y) << shift; + } + else if (!luma && chroma) + { + input(0, yT, xT, 0) = bufCb.at(x, y) << shift; + input(0, yT, xT, 1) = bufCr.at(x, y) << shift; + } + else if (luma && chroma) + { + input(0, yT, xT, 0) = bufY.at(x, y) << shift; + input(0, yT, xT, 1) = bufCb.at(x >> 1, y >> 1) << shift; + input(0, yT, xT, 2) = bufCr.at(x >> 1, y >> 1) << shift; + } + } + } +#else + for (int y = 0; y < ver; y++) + { + for (int x = 0; x < hor; x++) + { + int yT = y; + int xT = x; + geometricTransform(ver, hor, yT, xT, flip); + if (luma && !chroma) + { + input(0, yT, xT, 0) = bufY.at(x, y) / scale; + } + else if (!luma && chroma) + { + input(0, yT, xT, 0) = bufCb.at(x, y) / scale; + input(0, yT, xT, 1) = bufCr.at(x, y) / scale; + } + else if (luma && chroma) + { + input(0, yT, xT, 0) = bufY.at(x, y) / scale; + input(0, yT, xT, 1) = bufCb.at(x >> 1, y >> 1) / scale; + input(0, yT, xT, 2) = bufCr.at(x >> 1, y >> 1) / scale; + } + } + } +#endif + } +#endif template<typename T> static void fillInputFromConstant (Picture* pic, UnitArea inferArea, sadl::Tensor<T> &input, int c, bool luma, double scale, int shift) { @@ -242,6 +326,87 @@ public: } #endif } +#if JVET_AC0177_FLIP_INPUT + template<typename T> + static void fillInputFromBufIpb(Picture *pic, UnitArea inferArea, sadl::Tensor<T> &input, PelUnitBuf buf, bool luma, + bool chroma, double scale, int shift, bool flip) + { + PelBuf bufY, bufCb, bufCr; + + if (luma) + { + bufY = buf.get(COMPONENT_Y); + } + if (chroma) + { + bufCb = buf.get(COMPONENT_Cb); + bufCr = buf.get(COMPONENT_Cr); + } + + int hor, ver; + if (luma) + { + hor = inferArea.lwidth(); + ver = inferArea.lheight(); + } + else + { + hor = inferArea.lwidth() >> 1; + ver = inferArea.lheight() >> 1; + } +#if NN_FIXED_POINT_IMPLEMENTATION + for (int yy = 0; yy < ver; yy++) + { + for (int xx = 0; xx < hor; xx++) + { + int yT = yy; + int xT = xx; + geometricTransform(ver, hor, yT, xT, flip); + if (luma && !chroma) + { + input(0, yT, xT, 0) = ((bufY.at(xx, yy) >> 1) + ((bufY.at(xx, yy) >> 1) == 0) - 1) << (shift - 1); + } + else if (!luma && chroma) + { + input(0, yT, xT, 0) = ((bufCb.at(xx, yy) >> 1) + ((bufCb.at(xx, yy) >> 1) == 0) - 1) << (shift - 1); + input(0, yT, xT, 1) = ((bufCr.at(xx, yy) >> 1) + ((bufCr.at(xx, yy) >> 1) == 0) - 1) << (shift - 1); + } + else if (luma && chroma) + { + input(0, yT, xT, 0) = ((bufY.at(xx, yy) >> 1) + ((bufY.at(xx, yy) >> 1) == 0) - 1) << (shift - 1); + input(0, yT, xT, 1) = ((bufCb.at(xx, yy) >> 1) + ((bufCb.at(xx, yy) >> 1) == 0) - 1) << (shift - 1); + input(0, yT, xT, 2) = ((bufCr.at(xx, yy) >> 1) + ((bufCr.at(xx, yy) >> 1) == 0) - 1) << (shift - 1); + } + } + } +#else + for (int yy = 0; yy < ver; yy++) + { + for (int xx = 0; xx < hor; xx++) + { + int yT = yy; + int xT = xx; + geometricTransform(ver, hor, yT, xT, flip); + if (luma && !chroma) + { + input(0, yT, xT, 0) = ((bufY.at(xx, yy) >> 1) + ((bufY.at(xx, yy) >> 1) == 0) - 1) / 2.0; + } + else if (!luma && chroma) + { + input(0, yT, xT, 0) = ((bufCb.at(xx, yy) >> 1) + ((bufCb.at(xx, yy) >> 1) == 0) - 1) / 2.0; + input(0, yT, xT, 1) = ((bufCr.at(xx, yy) >> 1) + ((bufCr.at(xx, yy) >> 1) == 0) - 1) / 2.0; + } + else if (luma && chroma) + { + input(0, yT, xT, 0) = ((bufY.at(xx, yy) >> 1) + ((bufY.at(xx, yy) >> 1) == 0) - 1) / 2.0; + input(0, yT, xT, 1) = ((bufCb.at(xx, yy) >> 1) + ((bufCb.at(xx, yy) >> 1) == 0) - 1) / 2.0; + input(0, yT, xT, 2) = ((bufCr.at(xx, yy) >> 1) + ((bufCr.at(xx, yy) >> 1) == 0) - 1) / 2.0; + } + } + } +#endif + } +#endif #endif template<typename T> @@ -272,6 +437,14 @@ public: case NN_INPUT_SLICE_TYPE: fillInputFromConstant<T>(pic, inferArea, inputs[inputData.index], sliceType, inputData.luma, inputData.scale, inputData.shift); break; +#if JVET_AC0177_MULTI_FRAME + case NN_INPUT_REF_LIST_0: + fillInputFromBuf<T>(pic, inferArea, inputs[inputData.index], pic->slices[0]->getRefPic(REF_PIC_LIST_0, 0)->getRecoBuf(inferArea), inputData.luma, inputData.chroma, inputData.scale, inputData.shift); + break; + case NN_INPUT_REF_LIST_1: + fillInputFromBuf<T>(pic, inferArea, inputs[inputData.index], pic->slices[0]->getRefPic(REF_PIC_LIST_1, 0)->getRecoBuf(inferArea), inputData.luma, inputData.chroma, inputData.scale, inputData.shift); + break; +#endif #if JVET_AC0089_COMBINE_INTRA_INTER case NN_INPUT_IPB: fillInputFromBufIpb<T>(pic, inferArea, inputs[inputData.index], pic->getBlockPredModeBuf(inferArea), inputData.luma, inputData.chroma, inputData.scale, inputData.shift); @@ -283,6 +456,55 @@ public: } } } +#if JVET_AC0177_FLIP_INPUT + template<typename T> + static void prepareInputs (Picture* pic, UnitArea inferArea, std::vector<sadl::Tensor<T>> &inputs, int globalQp, int localQp, int sliceType, const std::vector<InputData> &listInputData, bool flip) + { + for (auto inputData : listInputData) + { + switch (inputData.nnInputType) + { + case NN_INPUT_REC: + fillInputFromBuf<T>(pic, inferArea, inputs[inputData.index], pic->getRecBeforeDbfBuf(inferArea), inputData.luma, inputData.chroma, inputData.scale, inputData.shift, flip); + break; + case NN_INPUT_PRED: + fillInputFromBuf<T>(pic, inferArea, inputs[inputData.index], pic->getPredBufCustom(inferArea), inputData.luma, inputData.chroma, inputData.scale, inputData.shift, flip); + break; + case NN_INPUT_PARTITION: + fillInputFromBuf<T>(pic, inferArea, inputs[inputData.index], pic->getCuAverageBuf(inferArea), inputData.luma, inputData.chroma, inputData.scale, inputData.shift, flip); + break; + case NN_INPUT_BS: + fillInputFromBuf<T>(pic, inferArea, inputs[inputData.index], pic->getBsMapBuf(inferArea), inputData.luma, inputData.chroma, inputData.scale, inputData.shift, flip); + break; + case NN_INPUT_GLOBAL_QP: + fillInputFromConstant<T>(pic, inferArea, inputs[inputData.index], globalQp, inputData.luma, inputData.scale, inputData.shift); + break; + case NN_INPUT_LOCAL_QP: + fillInputFromConstant<T>(pic, inferArea, inputs[inputData.index], localQp, inputData.luma, inputData.scale, inputData.shift); + break; + case NN_INPUT_SLICE_TYPE: + fillInputFromConstant<T>(pic, inferArea, inputs[inputData.index], sliceType, inputData.luma, inputData.scale, inputData.shift); + break; +#if JVET_AC0177_MULTI_FRAME + case NN_INPUT_REF_LIST_0: + fillInputFromBuf<T>(pic, inferArea, inputs[inputData.index], pic->slices[0]->getRefPic(REF_PIC_LIST_0, 0)->getRecoBuf(inferArea), inputData.luma, inputData.chroma, inputData.scale, inputData.shift, flip); + break; + case NN_INPUT_REF_LIST_1: + fillInputFromBuf<T>(pic, inferArea, inputs[inputData.index], pic->slices[0]->getRefPic(REF_PIC_LIST_1, 0)->getRecoBuf(inferArea), inputData.luma, inputData.chroma, inputData.scale, inputData.shift, flip); + break; +#endif +#if JVET_AC0089_COMBINE_INTRA_INTER + case NN_INPUT_IPB: + fillInputFromBufIpb<T>(pic, inferArea, inputs[inputData.index], pic->getBlockPredModeBuf(inferArea), inputData.luma, inputData.chroma, inputData.scale, inputData.shift, flip); + break; +#endif + default: + THROW("invalid input data"); + break; + } + } + } +#endif template<typename T> static void infer(sadl::Model<T> &model, std::vector<sadl::Tensor<T>> &inputs); }; diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h index 231684dbf7..20eb48bd31 100644 --- a/source/Lib/CommonLib/Slice.h +++ b/source/Lib/CommonLib/Slice.h @@ -1492,6 +1492,9 @@ private: unsigned m_nnlfSet1InferSize[MAX_NUM_CNNLF_INFER_GRANULARITY]; unsigned m_nnlfSet1InferSizeExtension; unsigned m_nnlfSet1MaxNumParams; +#if JVET_AC0177_MULTI_FRAME + bool m_nnlfSet1MultiframeEnabledFlag; +#endif #endif bool m_wrapAroundEnabledFlag; unsigned m_IBCFlag; @@ -1765,6 +1768,10 @@ public: #if NN_FILTERING_SET_1 bool getNnlfSet1EnabledFlag() const { return m_nnlfSet1EnabledFlag; } void setNnlfSet1EnabledFlag( bool b ) { m_nnlfSet1EnabledFlag = b; } +#if JVET_AC0177_MULTI_FRAME + bool getNnlfSet1MultiframeEnabledFlag() const { return m_nnlfSet1MultiframeEnabledFlag; } + void setNnlfSet1MultiframeEnabledFlag( bool b ) { m_nnlfSet1MultiframeEnabledFlag = b; } +#endif #endif void setJointCbCrEnabledFlag(bool bVal) { m_JointCbCrEnabledFlag = bVal; } bool getJointCbCrEnabledFlag() const { return m_JointCbCrEnabledFlag; } diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h index 9e968498f7..1de614434f 100644 --- a/source/Lib/CommonLib/TypeDef.h +++ b/source/Lib/CommonLib/TypeDef.h @@ -122,10 +122,10 @@ using TypeSadl = float; #define BYPASS_INTER_SLICE 0 // only used for training data generation #define JVET_AC0089_COMBINE_INTRA_INTER 1 // JVET-AC0089: EE1-1.5.3 Use combined inter/intra models. Luma model uses IPB input. Chroma model does not use IPB input. - +#define JVET_AC0177_MULTI_FRAME 1 // JVET-AC0177: EE1-1.7: Deep In-Loop Filter with Additional Input Information +#define JVET_AC0177_FLIP_INPUT 1 // JVET-AC0177: flip input and output of NN filter model #endif - #define JVET_AB0068_RD 1 // JVET-AB0068: EE1-1.6: RDO Considering Deep In-Loop Filtering //########### place macros to be removed in next cycle below this line ############### @@ -519,8 +519,15 @@ enum NNInputType NN_INPUT_LOCAL_QP = 5, NN_INPUT_SLICE_TYPE = 6, #if JVET_AC0089_NNVC_USE_BPM_INFO +#if JVET_AC0177_MULTI_FRAME + NN_INPUT_IPB = 7, + NN_INPUT_REF_LIST_0 = 8, + NN_INPUT_REF_LIST_1 = 9, + MAX_NUM_NN_INPUT = 10 +#else NN_INPUT_IPB = 7, MAX_NUM_NN_INPUT = 8 +#endif #else MAX_NUM_NN_INPUT = 7 #endif diff --git a/source/Lib/DecoderLib/DecLib.cpp b/source/Lib/DecoderLib/DecLib.cpp index 67eb276970..75f3a1decf 100644 --- a/source/Lib/DecoderLib/DecLib.cpp +++ b/source/Lib/DecoderLib/DecLib.cpp @@ -651,7 +651,11 @@ void DecLib::executeLoopFilters() if (cs.sps->getNnlfSet1EnabledFlag()) { m_pcNNFilterSet1.create(cs.pcv->lumaWidth, cs.pcv->lumaHeight, cs.pcv->chrFormat, cs.sps->getNnlfSet1MaxNumParams()); +#if JVET_AC0177_MULTI_FRAME + m_pcNNFilterSet1.init(getNnlfSet1InterLumaModelName(), getNnlfSet1InterChromaModelName(), getNnlfSet1IntraLumaModelName(), getNnlfSet1IntraChromaModelName(), getNnlfSet1AlternativeInterLumaModelName()); +#else m_pcNNFilterSet1.init(getNnlfSet1InterLumaModelName(), getNnlfSet1InterChromaModelName(), getNnlfSet1IntraLumaModelName(), getNnlfSet1IntraChromaModelName()); +#endif } #endif diff --git a/source/Lib/DecoderLib/DecLib.h b/source/Lib/DecoderLib/DecLib.h index 3c4e673400..f85c4d2b8c 100644 --- a/source/Lib/DecoderLib/DecLib.h +++ b/source/Lib/DecoderLib/DecLib.h @@ -77,10 +77,13 @@ class DecLib { private: #if NN_FILTERING_SET_1 - std::string m_nnlfSet1InterLumaModelName; ///<inter luma nnlfSet1 model - std::string m_nnlfSet1InterChromaModelName; ///<inter chroma nnlfSet1 model - std::string m_nnlfSet1IntraLumaModelName; ///<intra luma nnlfSet1 model - std::string m_nnlfSet1IntraChromaModelName; ///<inra chroma nnlfSet1 model + std::string m_nnlfSet1InterLumaModelName; ///<inter luma nnlf set1 model + std::string m_nnlfSet1InterChromaModelName; ///<inter chroma nnlf set1 model + std::string m_nnlfSet1IntraLumaModelName; ///<intra luma nnlf set1 model + std::string m_nnlfSet1IntraChromaModelName; ///<inra chroma nnlf set1 model +#if JVET_AC0177_MULTI_FRAME + std::string m_nnlfSet1AlternativeInterLumaModelName; ///<alternative inter luma nnlf set1 model +#endif #endif int m_iMaxRefPicNum; bool m_isFirstGeneralHrd; @@ -252,6 +255,10 @@ public: void setNnlfSet1InterChromaModelName(std::string s) { m_nnlfSet1InterChromaModelName = s; } void setNnlfSet1IntraLumaModelName(std::string s) { m_nnlfSet1IntraLumaModelName = s; } void setNnlfSet1IntraChromaModelName(std::string s) { m_nnlfSet1IntraChromaModelName = s; } +#if JVET_AC0177_MULTI_FRAME + std::string getNnlfSet1AlternativeInterLumaModelName() { return m_nnlfSet1AlternativeInterLumaModelName; } + void setNnlfSet1AlternativeInterLumaModelName(std::string s) { m_nnlfSet1AlternativeInterLumaModelName = s; } +#endif #endif void setDecodedPictureHashSEIEnabled(int enabled) { m_decodedPictureHashSEIEnabled=enabled; } diff --git a/source/Lib/DecoderLib/VLCReader.cpp b/source/Lib/DecoderLib/VLCReader.cpp index fbeccce480..143c495112 100644 --- a/source/Lib/DecoderLib/VLCReader.cpp +++ b/source/Lib/DecoderLib/VLCReader.cpp @@ -1748,6 +1748,9 @@ void HLSyntaxReader::parseSPS(SPS* pcSPS) pcSPS->setNnlfSet1InferSizeExtension ( uiCode ); READ_UVLC( uiCode, "sps_nnlf_set1_max_num_params" ); pcSPS->setNnlfSet1MaxNumParams (uiCode ); +#if JVET_AC0177_MULTI_FRAME + READ_FLAG( uiCode, "sps_nnlf_set1_multi_frame_enabled_flag" ); pcSPS->setNnlfSet1MultiframeEnabledFlag ( uiCode ? true : false ); +#endif } #endif diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h index 016d0fad20..3ff3f7d294 100644 --- a/source/Lib/EncoderLib/EncCfg.h +++ b/source/Lib/EncoderLib/EncCfg.h @@ -163,6 +163,9 @@ protected: std::string m_nnlfSet1InterChromaModelName; ///<inter chroma nnlf set1 model std::string m_nnlfSet1IntraLumaModelName; ///<intra luma nnlf set1 model std::string m_nnlfSet1IntraChromaModelName; ///<inra chroma nnlf set1 model +#if JVET_AC0177_MULTI_FRAME + std::string m_nnlfSet1AlternativeInterLumaModelName; ///<alternative inter luma nnlf set1 model +#endif #endif #if JVET_AB0068_RD bool m_encNnlfOpt; @@ -776,6 +779,9 @@ protected: unsigned m_nnlfSet1InferSizeBase; unsigned m_nnlfSet1InferSizeExtension; unsigned m_nnlfSet1MaxNumParams; +#if JVET_AC0177_MULTI_FRAME + bool m_nnlfSet1Multiframe; +#endif #endif #if JVET_O0756_CALCULATE_HDRMETRICS double m_whitePointDeltaE[hdrtoolslib::NB_REF_WHITE]; @@ -842,6 +848,10 @@ public: void setNnlfSet1InterChromaModelName(std::string s) { m_nnlfSet1InterChromaModelName = s; } void setNnlfSet1IntraLumaModelName(std::string s) { m_nnlfSet1IntraLumaModelName = s; } void setNnlfSet1IntraChromaModelName(std::string s) { m_nnlfSet1IntraChromaModelName = s; } +#if JVET_AC0177_MULTI_FRAME + std::string getNnlfSet1AlternativeInterLumaModelName() { return m_nnlfSet1AlternativeInterLumaModelName; } + void setNnlfSet1AlternativeInterLumaModelName(std::string s) { m_nnlfSet1AlternativeInterLumaModelName = s; } +#endif #endif #if JVET_AB0068_RD std::string getRdoCnnlfInterLumaModelName() { return m_rdoCnnlfInterLumaModelName; } @@ -2043,6 +2053,10 @@ public: unsigned getNnlfSet1InferSizeExtension() const { return m_nnlfSet1InferSizeExtension; } void setNnlfSet1MaxNumParams( unsigned s ) { m_nnlfSet1MaxNumParams = s; } unsigned getNnlfSet1MaxNumParams() const { return m_nnlfSet1MaxNumParams; } +#if JVET_AC0177_MULTI_FRAME + void setNnlfSet1UseMultiframe( bool b ) { m_nnlfSet1Multiframe = b; } + bool getNnlfSet1UseMultiframe() const { return m_nnlfSet1Multiframe; } +#endif #endif #if JVET_O0756_CALCULATE_HDRMETRICS void setWhitePointDeltaE( uint32_t index, double value ) { m_whitePointDeltaE[ index ] = value; } diff --git a/source/Lib/EncoderLib/EncGOP.cpp b/source/Lib/EncoderLib/EncGOP.cpp index 609e63118a..996fc82098 100644 --- a/source/Lib/EncoderLib/EncGOP.cpp +++ b/source/Lib/EncoderLib/EncGOP.cpp @@ -3106,7 +3106,11 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic, #if NN_FILTERING_SET_1 if ( cs.sps->getNnlfSet1EnabledFlag() ) { +#if JVET_AC0177_MULTI_FRAME + m_pcNNFilterSet1.init(m_pcEncLib->getNnlfSet1InterLumaModelName(), m_pcEncLib->getNnlfSet1InterChromaModelName(), m_pcEncLib->getNnlfSet1IntraLumaModelName(), m_pcEncLib->getNnlfSet1IntraChromaModelName(), m_pcEncLib->getNnlfSet1AlternativeInterLumaModelName()); +#else m_pcNNFilterSet1.init(m_pcEncLib->getNnlfSet1InterLumaModelName(), m_pcEncLib->getNnlfSet1InterChromaModelName(), m_pcEncLib->getNnlfSet1IntraLumaModelName(), m_pcEncLib->getNnlfSet1IntraChromaModelName()); +#endif m_pcNNFilterSet1.initCABACEstimator( m_pcEncLib->getCABACEncoder(), m_pcEncLib->getCtxCache(), pcSlice ); m_pcNNFilterSet1.cnnFilterEncoder(pcPic, pcSlice->getLambdas()); } diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp index 2421b93953..6523da53b8 100644 --- a/source/Lib/EncoderLib/EncLib.cpp +++ b/source/Lib/EncoderLib/EncLib.cpp @@ -1456,6 +1456,9 @@ void EncLib::xInitSPS( SPS& sps ) { sps.setNnlfSet1MaxNumParams(m_nnlfSet1MaxNumParams); } +#if JVET_AC0177_MULTI_FRAME + sps.setNnlfSet1MultiframeEnabledFlag(m_nnlfSet1Multiframe); +#endif } #endif diff --git a/source/Lib/EncoderLib/VLCWriter.cpp b/source/Lib/EncoderLib/VLCWriter.cpp index 10dba722dd..aad5d30b34 100644 --- a/source/Lib/EncoderLib/VLCWriter.cpp +++ b/source/Lib/EncoderLib/VLCWriter.cpp @@ -1022,6 +1022,9 @@ void HLSWriter::codeSPS( const SPS* pcSPS ) WRITE_UVLC( pcSPS->getNnlfSet1InferSize(CNNLF_INFER_GRANULARITY_BASE), "sps_nnlf_set1_infer_size_base" ); WRITE_UVLC( pcSPS->getNnlfSet1InferSizeExtension(), "sps_nnlf_set1_infer_size_extension" ); WRITE_UVLC( pcSPS->getNnlfSet1MaxNumParams(), "sps_nnlf_set1_max_num_params" ); +#if JVET_AC0177_MULTI_FRAME + WRITE_FLAG( pcSPS->getNnlfSet1MultiframeEnabledFlag(), "sps_nnlf_set1_multi_frame_enabled_flag" ); +#endif } #endif diff --git a/training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Conversion/convert.py b/training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Conversion/convert.py new file mode 100755 index 0000000000..941e42578f --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Conversion/convert.py @@ -0,0 +1,27 @@ +import torch +import torch.nn as nn +from net import ConditionalNet +import numpy as np +import os + +# input +yuv = np.ones((1, 1, 32, 32), dtype=np.float32) +pred = np.ones((1, 1, 32, 32), dtype=np.float32) +forw = np.ones((1, 1, 32, 32), dtype=np.float32) +bacw = np.ones((1, 1, 32, 32), dtype=np.float32) +qp = np.ones((1, 1, 32, 32), dtype=np.float32) + +# model +# model = nn.DataParallel(ConditionalNet(96, 8)) # if model is trained on multiple GPUs +model = ConditionalNet(96, 8) # if model is trained with single GPU +state = torch.load('50.ckpt', map_location=torch.device('cpu')) +model.load_state_dict(state) + +dummy_input = (torch.from_numpy(yuv), torch.from_numpy(pred), torch.from_numpy(forw), torch.from_numpy(bacw), torch.from_numpy(qp)) +torch.onnx.export(model.module, dummy_input, "NnlfSet1_LumaCNNFilter_InterSlice_MultiframePrior_Tid345_int16.onnx") + + + + + + diff --git a/training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Conversion/net.py b/training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Conversion/net.py new file mode 100644 index 0000000000..f859b403e4 --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Conversion/net.py @@ -0,0 +1,99 @@ +import torch +import torch.nn as nn + + +def conv3x3(in_channels, out_channels, stride=1, padding=1): + return nn.Conv2d(in_channels, out_channels, kernel_size=3, + stride=stride, padding=padding) + + +def conv1x1(in_channels, out_channels, stride=1, padding=0): + return nn.Conv2d(in_channels, out_channels, kernel_size=1, + stride=stride, padding=padding) + + +# Conv3x3 + PReLU +class conv3x3_f(nn.Module): + def __init__(self, in_channels, out_channels, stride=1): + super(conv3x3_f, self).__init__() + self.conv = conv3x3(in_channels, out_channels, stride) + self.relu = nn.PReLU() + + def forward(self, x): + x = self.conv(x) + x = self.relu(x) + return x + + +# Conv1x1 + PReLU +class conv1x1_f(nn.Module): + def __init__(self, in_channels, out_channels, stride=1): + super(conv1x1_f, self).__init__() + self.conv = conv1x1(in_channels, out_channels, stride) + self.relu = nn.PReLU() + + def forward(self, x): + x = self.conv(x) + x = self.relu(x) + return x + + +# Residual Block +class ResidualBlock(nn.Module): + def __init__(self, in_channels, out_channels): + super(ResidualBlock, self).__init__() + self.conv1 = conv3x3(in_channels, out_channels) + self.relu = nn.PReLU() + self.conv2 = conv3x3(out_channels, out_channels) + + def forward(self, x): + out = self.conv1(x) + out = self.relu(out) + out = self.conv2(out) + return out + + +class ConditionalNet(nn.Module): + def __init__(self, f, rbn): + super(ConditionalNet, self).__init__() + self.rbn = rbn + self.convRec = conv3x3_f(1, f) + self.convPred = conv3x3_f(1, f) + self.convTemp = conv3x3_f(2, f) + self.convQp = conv3x3_f(1, f) + self.fuse = conv1x1_f(4 * f, f) + self.transitionH = conv3x3_f(f, f, 2) + self.backbone = nn.ModuleList([ResidualBlock(f, f)]) + for _ in range(self.rbn - 1): + self.backbone.append(ResidualBlock(f, f)) + self.last_layer = nn.Sequential( + nn.Conv2d( + in_channels=f, + out_channels=f, + kernel_size=3, + stride=1, + padding=1), + nn.PReLU(), + nn.Conv2d( + in_channels=f, + out_channels=4, + kernel_size=3, + stride=1, + padding=1), + # nn.PixelShuffle(2) + ) + + def forward(self, rec, pred, forw, bacw, qp): + rec_f = self.convRec(rec) + pred_f = self.convPred(pred) + temp_f = self.convTemp(torch.cat((forw, bacw), 1)) + qp_f = self.convQp(qp) + xh = torch.cat((rec_f, pred_f, temp_f, qp_f), 1) + xh = self.fuse(xh) + x = self.transitionH(xh) + for i in range(self.rbn): + x = self.backbone[i](x) + x + # output + x = self.last_layer(x) + # x = x + rec + return x diff --git a/training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Training/dataset.py b/training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Training/dataset.py new file mode 100644 index 0000000000..972f36d414 --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Training/dataset.py @@ -0,0 +1,117 @@ +from __future__ import print_function, division +import torch +import numpy as np +from torch.utils.data import Dataset, DataLoader +from torchvision import transforms, utils +import json +import matplotlib.pyplot as plt +import torchvision.transforms.functional as F + + +class CnnlfDataset(Dataset): + """MultiType Tree partition prediction dataset.""" + + def __init__(self, data, transform=None): + """ + Args: + blk_size: block size. + data: dataset bin file. There should be a json description file in the same folder named + data.json + transform (callable, optional): Optional transform to be applied + on a sample. + """ + self.transform = transform + + with open(data+'.json') as file: + description = json.loads(file.read()) + + self.data_file = data + self.patch_size = description['patch_size'] + self.border_size = description['border_size'] + self.components = dict(zip(description['components'], range(len(description['components'])))) + self.nb_comp = len(self.components) + self.len = description['nb_patches'] + self.block_size = self.patch_size + 2 * self.border_size + self.block_volume = self.block_size * self.block_size * self.nb_comp + + def __len__(self): + return self.len + + def __getitem__(self, idx): + if torch.is_tensor(idx): + idx = idx.tolist() + with open(self.data_file) as file: + block = np.fromfile(file, dtype='float32', count=self.block_volume, offset=self.block_volume * idx * 4).\ + reshape((self.block_size, self.block_size, self.nb_comp)) + + org = block[:, :, self.components['org_Y']] + rec = block[:, :, self.components['rec_before_dbf_Y']] + pred = block[:, :, self.components['pred_Y']] + forw = block[:, :, self.components['ref_list_0_Y']] + bacw = block[:, :, self.components['ref_list_1_Y']] + qp = block[:, :, self.components['qp_base']] + training_sample = {'org': org, 'rec': rec, 'pred': pred, 'forw': forw, 'bacw': bacw, 'qp': qp} + + if self.transform: + training_sample = self.transform(training_sample) + + return training_sample + + +class ToTensor(object): + """Convert ndarrays in sample to Tensors.""" + + def __call__(self, sample): + org, rec, pred, forw, bacw, qp = sample['org'], sample['rec'], sample['pred'], sample['forw'], sample['bacw'], sample['qp'] + + # swap color axis because + # numpy image: H x W x C + # torch image: C X H X W + org = org[np.newaxis, :, :] + rec = rec[np.newaxis, :, :] + pred = pred[np.newaxis, :, :] + forw = forw[np.newaxis, :, :] + bacw = bacw[np.newaxis, :, :] + qp = qp[np.newaxis, :, :] + return {'org': torch.from_numpy(org), + 'rec': torch.from_numpy(rec), + 'pred': torch.from_numpy(pred), + 'forw': torch.from_numpy(forw), + 'bacw': torch.from_numpy(bacw), + 'qp': torch.from_numpy(qp)} + + +def preprocess(sample, device): + org, rec, pred = sample['org'].to(device), sample['rec'].to(device), sample['pred'].to(device) + forw, bacw, qp = sample['forw'].to(device), sample['bacw'].to(device), sample['qp'].to(device) + + if torch.rand(1) < 0.5: + org[qp[:, 0, 0, 0] > 30.0/64.0, :, :, :] = F.hflip(org[qp[:, 0, 0, 0] > 30.0/64.0, :, :, :]) + rec[qp[:, 0, 0, 0] > 30.0/64.0, :, :, :] = F.hflip(rec[qp[:, 0, 0, 0] > 30.0/64.0, :, :, :]) + pred[qp[:, 0, 0, 0] > 30.0/64.0, :, :, :] = F.hflip(pred[qp[:, 0, 0, 0] > 30.0/64.0, :, :, :]) + forw[qp[:, 0, 0, 0] > 30.0/64.0, :, :, :] = F.hflip(forw[qp[:, 0, 0, 0] > 30.0/64.0, :, :, :]) + bacw[qp[:, 0, 0, 0] > 30.0/64.0, :, :, :] = F.hflip(bacw[qp[:, 0, 0, 0] > 30.0/64.0, :, :, :]) + if torch.rand(1) < 0.5: + org[qp[:, 0, 0, 0] > 30.0/64.0, :, :, :] = F.vflip(org[qp[:, 0, 0, 0] > 30.0/64.0, :, :, :]) + rec[qp[:, 0, 0, 0] > 30.0/64.0, :, :, :] = F.vflip(rec[qp[:, 0, 0, 0] > 30.0/64.0, :, :, :]) + pred[qp[:, 0, 0, 0] > 30.0/64.0, :, :, :] = F.vflip(pred[qp[:, 0, 0, 0] > 30.0/64.0, :, :, :]) + forw[qp[:, 0, 0, 0] > 30.0/64.0, :, :, :] = F.vflip(forw[qp[:, 0, 0, 0] > 30.0/64.0, :, :, :]) + bacw[qp[:, 0, 0, 0] > 30.0/64.0, :, :, :] = F.vflip(bacw[qp[:, 0, 0, 0] > 30.0/64.0, :, :, :]) + + return {'org': org, 'rec': rec, 'pred': pred, 'forw': forw, 'bacw': bacw, 'qp': qp} + + +def display_batch(batch): + fig, ax = plt.subplots(nrows=8, ncols=8) + i = 0 + for row in ax: + for col in row: + col.imshow(batch[i, 0, :, :], cmap='gray', vmin=0., vmax=1.) + i += 1 + plt.show() + + +def get_loader(data, batch_size, shuffle, num_workers=4): + dataset = CnnlfDataset(data=data, transform=transforms.Compose([ToTensor()])) + data_loader = DataLoader(dataset, batch_size=batch_size, shuffle=shuffle, num_workers=num_workers) + return data_loader diff --git a/training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Training/main.py b/training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Training/main.py new file mode 100755 index 0000000000..e5c6704d40 --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Training/main.py @@ -0,0 +1,142 @@ +import argparse +import torch +import torch.nn as nn +from net import ConditionalNet +from dataset import get_loader, display_batch, preprocess +from math import log10 + + +# learning policy +def adjust_learning_rate(optimizer, decay_rate): + """Sets the learning rate to the initial LR decayed by 10 every 30 epochs""" + print('update learing rate') + for param_group in optimizer.param_groups: + param_group['lr'] *= decay_rate + + +def train(opt): + # Device configuration + if torch.cuda.is_available(): + device = torch.device('cuda') + else: + device = torch.device('cpu') + # load training and validation data + train_loader = get_loader(data=opt.train_data, batch_size=opt.train_batch_size, shuffle=True, + num_workers=opt.num_workers) + if opt.validation_data: + validation_loader = get_loader(data=opt.validation_data, batch_size=opt.validation_batch_size, shuffle=False, + num_workers=opt.num_workers) + # Construct network + CnnlfNet = ConditionalNet(opt.feature_maps, opt.rbn) + + if torch.cuda.device_count() > 1: + print("Let's use", torch.cuda.device_count(), "GPUs!") + # dim = 0 [30, xxx] -> [10, ...], [10, ...], [10, ...] on 3 GPUs + CnnlfNet = nn.DataParallel(CnnlfNet) + CnnlfNet.to(device) + + if opt.pretrained_model: + CnnlfNet.load_state_dict(torch.load(opt.pretrained_model, map_location=device)) + + optimizer = torch.optim.Adam(CnnlfNet.parameters(), lr=opt.lr, weight_decay=opt.weight_decay) + + # Loss and optimizer + criterion = nn.MSELoss() + if opt.MSE == 1: + print('Loss function: MSE') + criterionB = nn.MSELoss() + else: + print('Loss function: SAD') + criterionB = nn.L1Loss() + + # Train the model + step_per_epoch = len(train_loader) + for epoch in range(0, opt.epoches): + print('Epoch {}'.format(epoch)) + for i, sample_t in enumerate(train_loader): + global_step = epoch * step_per_epoch + i + 1 + if global_step == opt.decay_epoch1 * step_per_epoch or global_step == opt.decay_epoch2 * step_per_epoch: + adjust_learning_rate(optimizer, opt.decay_rate) + if global_step == opt.mse_epoch * step_per_epoch: + criterionB = nn.MSELoss() + adjust_learning_rate(optimizer, opt.decay_rate) + sample_t = preprocess(sample_t, device) + org, rec, pred = sample_t['org'], sample_t['rec'], sample_t['pred'] + forw, bacw, qp = sample_t['forw'], sample_t['bacw'], sample_t['qp'] + # Forward pass + outputs = CnnlfNet(rec, pred, forw, bacw, qp) + lossB = criterionB(outputs, org) + loss = criterion(outputs, org) + + # Backward and optimize + optimizer.zero_grad() + lossB.backward() + optimizer.step() + if global_step % opt.loss_interval == 0: + print('Global Step {}, Loss: {:.4f}'.format(global_step, loss.item())) + print('PSNR of the model on a batch: {}'.format(10 * log10(1 / loss.item()))) + if opt.validation_data and global_step % opt.validation_interval == 0: + # validate the model + with torch.no_grad(): + avg_psnr_ref = 0 + avg_psnr_val = 0 + _len = 0 + for sample_v in validation_loader: + sample_v = preprocess(sample_v, device) + org, rec, pred = sample_v['org'], sample_v['rec'], sample_v['pred'] + forw, bacw, qp = sample_v['forw'], sample_v['bacw'], sample_v['qp'] + qp = sample_v['qp'].to(device) + outputs = CnnlfNet(rec, pred, forw, bacw, qp) + loss_ref = criterion(rec, org) + loss_val = criterion(outputs, org) + if loss_ref.item() == 0: + continue + _len += 1 + avg_psnr_ref += 10 * log10(1 / loss_ref.item()) + avg_psnr_val += 10 * log10(1 / loss_val.item()) + print('PSNR of the anchor on validation: {}'.format(avg_psnr_ref / _len)) + print('PSNR of the model on validation: {}'.format(avg_psnr_val / _len)) + if (epoch + 1) % opt.checkpoint_interval == 0: + # Save the model checkpoint + torch.save(CnnlfNet.state_dict(), str(epoch+1) + '.ckpt') + + +if __name__ == '__main__': + # parse arguments + parser = argparse.ArgumentParser() + + # training/validation data + parser.add_argument('--num_workers', type=int, default=4, help='number of workers') + parser.add_argument('--train_batch_size', type=int, default=64, help='train batch size') + parser.add_argument('--validation_batch_size', type=int, default=64, help='validation batch size') + parser.add_argument('--train_data', type=str, default='luma_data.bin', help='training data') + parser.add_argument('--validation_data', type=str, default='', help='validation data') + + # network + parser.add_argument('--feature_maps', type=int, default=96, help='number of feature maps') + parser.add_argument('--rbn', type=int, default=8, help='number of residual blocks') + + # loss + parser.add_argument('--weight_decay', type=float, default=1e-8, help='weight decay') + parser.add_argument('--MSE', type=int, default=0, help='loss function, default=SAD') + + # optimizer configurations + parser.add_argument('--lr', type=float, default=1e-4, help='learning rate, default=0.0001') + parser.add_argument('--decay_epoch1', type=int, default=20, help='first milestone to decay lr') + parser.add_argument('--decay_epoch2', type=int, default=30, help='second milestone to decay lr') + parser.add_argument('--mse_epoch', type=int, default=40, help='switch to the mse loss and decrease learning rate at mse_epoch') + parser.add_argument('--decay_rate', type=float, default=0.1, help='the multiplier to decay lr') + parser.add_argument('--epoches', type=int, default=50, help='number of epoches to train for') + + # dump configurations + parser.add_argument('--loss_interval', type=int, default=1000, help='number of iteration to print loss') + parser.add_argument('--validation_interval', type=int, default=10000, help='number of iteration to validate') + parser.add_argument('--checkpoint_interval', type=int, default=10, help='number of epochs to save checkpoint') + + # finetune + parser.add_argument('--pretrained_model', type=str, default="", help='pretrained model') + + options = parser.parse_args() + print(options) + train(options) + diff --git a/training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Training/net.py b/training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Training/net.py new file mode 100644 index 0000000000..7dd8089a75 --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Training/net.py @@ -0,0 +1,99 @@ +import torch +import torch.nn as nn + + +def conv3x3(in_channels, out_channels, stride=1, padding=1): + return nn.Conv2d(in_channels, out_channels, kernel_size=3, + stride=stride, padding=padding) + + +def conv1x1(in_channels, out_channels, stride=1, padding=0): + return nn.Conv2d(in_channels, out_channels, kernel_size=1, + stride=stride, padding=padding) + + +# Conv3x3 + PReLU +class conv3x3_f(nn.Module): + def __init__(self, in_channels, out_channels, stride=1): + super(conv3x3_f, self).__init__() + self.conv = conv3x3(in_channels, out_channels, stride) + self.relu = nn.PReLU() + + def forward(self, x): + x = self.conv(x) + x = self.relu(x) + return x + + +# Conv1x1 + PReLU +class conv1x1_f(nn.Module): + def __init__(self, in_channels, out_channels, stride=1): + super(conv1x1_f, self).__init__() + self.conv = conv1x1(in_channels, out_channels, stride) + self.relu = nn.PReLU() + + def forward(self, x): + x = self.conv(x) + x = self.relu(x) + return x + + +# Residual Block +class ResidualBlock(nn.Module): + def __init__(self, in_channels, out_channels): + super(ResidualBlock, self).__init__() + self.conv1 = conv3x3(in_channels, out_channels) + self.relu = nn.PReLU() + self.conv2 = conv3x3(out_channels, out_channels) + + def forward(self, x): + out = self.conv1(x) + out = self.relu(out) + out = self.conv2(out) + return out + + +class ConditionalNet(nn.Module): + def __init__(self, f, rbn): + super(ConditionalNet, self).__init__() + self.rbn = rbn + self.convRec = conv3x3_f(1, f) + self.convPred = conv3x3_f(1, f) + self.convTemp = conv3x3_f(2, f) + self.convQp = conv3x3_f(1, f) + self.fuse = conv1x1_f(4 * f, f) + self.transitionH = conv3x3_f(f, f, 2) + self.backbone = nn.ModuleList([ResidualBlock(f, f)]) + for _ in range(self.rbn - 1): + self.backbone.append(ResidualBlock(f, f)) + self.last_layer = nn.Sequential( + nn.Conv2d( + in_channels=f, + out_channels=f, + kernel_size=3, + stride=1, + padding=1), + nn.PReLU(), + nn.Conv2d( + in_channels=f, + out_channels=4, + kernel_size=3, + stride=1, + padding=1), + nn.PixelShuffle(2) + ) + + def forward(self, rec, pred, forw, bacw, qp): + rec_f = self.convRec(rec) + pred_f = self.convPred(pred) + temp_f = self.convTemp(torch.cat((forw, bacw), 1)) + qp_f = self.convQp(qp) + xh = torch.cat((rec_f, pred_f, temp_f, qp_f), 1) + xh = self.fuse(xh) + x = self.transitionH(xh) + for i in range(self.rbn): + x = self.backbone[i](x) + x + # output + x = self.last_layer(x) + x = x + rec + return x diff --git a/training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Training/train.sh b/training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Training/train.sh new file mode 100755 index 0000000000..ae190efa0a --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/Training/train.sh @@ -0,0 +1,3 @@ +#!/bin/bash +python3 main.py + diff --git a/training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/instruction.pdf b/training/training_scripts/Nn_Filtering_Set_1/Scripts/AdditionalLumaInter/instruction.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5b3626c1366eded884276de65c7f8860fe8f6177 GIT binary patch literal 53251 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'}TfA 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 literal 0 HcmV?d00001 -- GitLab