From d0fab1e06744d6a91a3d23bd1856fd7d6e3dcdac Mon Sep 17 00:00:00 2001 From: Jay Shingala <jay.shingala@ittiam.com> Date: Wed, 10 May 2023 12:30:08 +0000 Subject: [PATCH] Merge EE1-1.1 code changes (JVET-AD0156) --- .gitattributes | 3 + CMakeLists.txt | 3 +- README.md | 15 + cfg/nn-based/NnlfOption_3.cfg | 1 + models/NnlfSetLC/LC_float_model0.sadl | 3 + models/NnlfSetLC/LC_float_model1.sadl | 3 + models/NnlfSetLC/LC_float_model2.sadl | 3 + models/NnlfSetLC/LC_float_model3.sadl | 3 + models/NnlfSetLC/LC_int16_model0.sadl | 3 + models/NnlfSetLC/LC_int16_model1.sadl | 3 + models/NnlfSetLC/LC_int16_model2.sadl | 3 + models/NnlfSetLC/LC_int16_model3.sadl | 3 + .../App/BitstreamExtractorApp/CMakeLists.txt | 2 +- source/App/DecoderApp/DecAppCfg.cpp | 5 + source/App/EncoderApp/EncApp.cpp | 4 + source/App/EncoderApp/EncAppCfg.cpp | 24 +- source/App/EncoderApp/EncAppCfg.h | 4 + source/Lib/CommonLib/CMakeLists.txt | 3 + source/Lib/CommonLib/CodingStatistics.h | 3 + source/Lib/CommonLib/CodingStructure.cpp | 29 +- source/Lib/CommonLib/CodingStructure.h | 13 + source/Lib/CommonLib/CommonDef.h | 8 + source/Lib/CommonLib/Contexts.cpp | 12 + source/Lib/CommonLib/Contexts.h | 3 + source/Lib/CommonLib/NNFilterSetLC.cpp | 463 ++++++++ source/Lib/CommonLib/NNFilterSetLC.h | 86 ++ source/Lib/CommonLib/NNInference.cpp | 1 - source/Lib/CommonLib/Picture.cpp | 10 +- source/Lib/CommonLib/Picture.h | 34 +- source/Lib/CommonLib/Rom.cpp | 8 + source/Lib/CommonLib/Rom.h | 6 + source/Lib/CommonLib/Slice.h | 32 + source/Lib/CommonLib/TypeDef.h | 18 + source/Lib/DecoderLib/CABACReader.cpp | 36 + source/Lib/DecoderLib/DecLib.cpp | 23 + source/Lib/DecoderLib/DecLib.h | 9 + source/Lib/DecoderLib/DecSlice.cpp | 3 + source/Lib/DecoderLib/VLCReader.cpp | 42 +- source/Lib/EncoderLib/CABACWriter.cpp | 18 +- source/Lib/EncoderLib/CABACWriter.h | 36 + source/Lib/EncoderLib/CMakeLists.txt | 3 + source/Lib/EncoderLib/EncCfg.h | 8 + source/Lib/EncoderLib/EncGOP.cpp | 102 ++ source/Lib/EncoderLib/EncGOP.h | 9 + source/Lib/EncoderLib/EncLib.cpp | 4 + source/Lib/EncoderLib/EncNNFilterSet1.cpp | 4 +- source/Lib/EncoderLib/EncNNFilterSetLC.cpp | 523 +++++++++ source/Lib/EncoderLib/EncNNFilterSetLC.h | 72 ++ source/Lib/EncoderLib/VLCWriter.cpp | 26 +- .../BVIDVC_streamsplit.csv | 621 ++++++++++ .../1_generate_raw_data/bvidvc_mp4_2_yuv.py | 35 + .../1_generate_raw_data/div2k_gt_datagen.py | 209 ++++ .../1_generate_raw_data/div2k_png_2_yuv.py | 55 + .../1_generate_raw_data/gt_bvidvc_datagen.py | 186 +++ .../gt_bvidvc_parallel_runs.py | 44 + .../encode_streams.py | 91 ++ .../training_data_gen.py | 271 +++++ .../training_data_gen_parallel.py | 89 ++ .../3_training/AI_LC-CPDF_model0_cfg.json | 3 + .../3_training/AI_LC-CPDF_model1_cfg.json | 3 + .../3_training/AI_LC-baseline_cfg.json | 3 + .../3_training/NNLF_data_loader.py | 129 +++ .../3_training/NNLF_models.py | 1024 +++++++++++++++++ .../RA_LC-CPDF_model2_cfg_stage1.json | 3 + .../RA_LC-CPDF_model2_cfg_stage2.json | 3 + .../RA_LC-CPDF_model3_cfg_stage1.json | 3 + .../RA_LC-CPDF_model3_cfg_stage2.json | 3 + .../3_training/train_NNLF.py | 223 ++++ .../sadl_conversion_utilities/norm_auto.py | 529 +++++++++ .../sadl_conversion_utilities/q_factor_gen.py | 106 ++ .../sadl_mod_src_files/layer_const.h | 154 +++ .../sadl_mod_src_files/layer_conv2d_1x1.h | 444 +++++++ .../sadl_mod_src_files/layer_conv2d_3x3.h | 787 +++++++++++++ .../sadl_mod_src_files/options.h | 99 ++ .../sadl_mod_src_files/tensor.h | 649 +++++++++++ .../4_conversions/tf2_onnx.py | 65 ++ .../Nn_Filtering_Set_LC/README.pdf | Bin 0 -> 336384 bytes .../Nn_Filtering_Set_LC/requirements.txt | 5 + 78 files changed, 7551 insertions(+), 17 deletions(-) create mode 100644 cfg/nn-based/NnlfOption_3.cfg create mode 100644 models/NnlfSetLC/LC_float_model0.sadl create mode 100644 models/NnlfSetLC/LC_float_model1.sadl create mode 100644 models/NnlfSetLC/LC_float_model2.sadl create mode 100644 models/NnlfSetLC/LC_float_model3.sadl create mode 100644 models/NnlfSetLC/LC_int16_model0.sadl create mode 100644 models/NnlfSetLC/LC_int16_model1.sadl create mode 100644 models/NnlfSetLC/LC_int16_model2.sadl create mode 100644 models/NnlfSetLC/LC_int16_model3.sadl create mode 100644 source/Lib/CommonLib/NNFilterSetLC.cpp create mode 100644 source/Lib/CommonLib/NNFilterSetLC.h create mode 100644 source/Lib/EncoderLib/EncNNFilterSetLC.cpp create mode 100644 source/Lib/EncoderLib/EncNNFilterSetLC.h create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/BVIDVC_streamsplit.csv create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/bvidvc_mp4_2_yuv.py create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/div2k_gt_datagen.py create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/div2k_png_2_yuv.py create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/gt_bvidvc_datagen.py create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/gt_bvidvc_parallel_runs.py create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/2_generate_compression_data/encode_streams.py create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/2_generate_compression_data/training_data_gen.py create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/2_generate_compression_data/training_data_gen_parallel.py create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/3_training/AI_LC-CPDF_model0_cfg.json create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/3_training/AI_LC-CPDF_model1_cfg.json create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/3_training/AI_LC-baseline_cfg.json create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/3_training/NNLF_data_loader.py create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/3_training/NNLF_models.py create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/3_training/RA_LC-CPDF_model2_cfg_stage1.json create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/3_training/RA_LC-CPDF_model2_cfg_stage2.json create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/3_training/RA_LC-CPDF_model3_cfg_stage1.json create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/3_training/RA_LC-CPDF_model3_cfg_stage2.json create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/3_training/train_NNLF.py create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/norm_auto.py create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/q_factor_gen.py create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/sadl_mod_src_files/layer_const.h create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/sadl_mod_src_files/layer_conv2d_1x1.h create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/sadl_mod_src_files/layer_conv2d_3x3.h create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/sadl_mod_src_files/options.h create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/sadl_mod_src_files/tensor.h create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/4_conversions/tf2_onnx.py create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/README.pdf create mode 100644 training/training_scripts/Nn_Filtering_Set_LC/requirements.txt diff --git a/.gitattributes b/.gitattributes index d09fb877fe..2403eed923 100644 --- a/.gitattributes +++ b/.gitattributes @@ -509,3 +509,6 @@ models/NnlfSet1_CombinedIntraInter/NnlfSet1_LumaCNNFilter_float.sadl filter=lfs models/NnlfSet1_CombinedIntraInter/NnlfSet1_LumaCNNFilter_int16.sadl filter=lfs diff=lfs merge=lfs -text models/NnlfSet1_AdditionalInter/NnlfSet1_LumaCNNFilter_InterSlice_MultipleFrames_int16.sadl filter=lfs diff=lfs merge=lfs -text models/NnlfSet1_AdditionalInter/NnlfSet1_LumaCNNFilter_InterSlice_MultipleFrames_float.sadl filter=lfs diff=lfs merge=lfs -text +models/NnlfSetLC/*.sadl filter=lfs diff=lfs merge=lfs -text +models/NnlfSetLC/* filter=lfs diff=lfs merge=lfs -text +training/training_scripts/Nn_Filtering_Set_LC/3_training/*.json filter=lfs diff=lfs merge=lfs -text diff --git a/CMakeLists.txt b/CMakeLists.txt index 09fb01a630..6500b68903 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,8 +76,9 @@ endif() # bb_enable_warnings( gcc warnings-as-errors -Wno-sign-compare ) # bb_enable_warnings( gcc -Wno-unused-variable ) # bb_enable_warnings( gcc-4.8 warnings-as-errors -Wno-unused-variable ) +# -Wno-ignored-attributes is for tensorflow # for gcc 8.2: -bb_enable_warnings( gcc warnings-as-errors -Wno-sign-compare -Wno-class-memaccess) +bb_enable_warnings( gcc warnings-as-errors -Wno-sign-compare -Wno-class-memaccess -Wno-ignored-attributes) if( XCODE ) bb_enable_warnings( clang warnings-as-errors diff --git a/README.md b/README.md index 3d54053ab7..58d2a3a3d9 100644 --- a/README.md +++ b/README.md @@ -399,6 +399,21 @@ To specify model paths, use e.g. following command lines. Note that model paths --NnlfSet1IntraLumaModel="models/NnlfSet1_LumaCNNFilter_IntraSlice_int16.sadl" --NnlfSet1IntraChromaModel="models/NnlfSet1_ChromaCNNFilter_IntraSlice_int16.sadl" +LC NN-based loop filter set +---------------------------------------------- +To activate LC(low complexity) NN-based loop filter set, use --NnlfOption=3, or equivalently -c cfg/nn-based/NnlfOption\_3.cfg + +The LC NNLF consists of the CP Decomposed & Fused (CPDF) LC models proposed in JVET-AD0156. + +The LC NNLF models are realized in SADL float and int16 frameworks. This can be set with the help of the macro ``NN_FIXED_POINT_IMPLEMENTATION`` in the file ``source/Lib/CommonLib/TypeDef.h``. The macro needs to be set to either 0 or 1 for float or int16 respectively. + +To specify LC model paths, refer to following examples in command line. Note that model paths should be specified at both encoder and decoder. The path needs to be set based on the framework that the software is built for. + +SADL float: ``--LCModelPath=models/NnlfSetLC/LC_float_model0.sadl,models/NnlfSetLC/LC_float_model1.sadl,models/NnlfSetLC/LC_float_model2.sadl,models/NnlfSetLC/LC_float_model3.sadl`` + +SADL int16: ``--LCModelPath=models/NnlfSetLC/LC_int16_model0.sadl,models/NnlfSetLC/LC_int16_model1.sadl,models/NnlfSetLC/LC_int16_model2.sadl,models/NnlfSetLC/LC_int16_model3.sadl`` + +The training and model conversion to SADL details can be found at ``training/training_scripts/Nn_Filtering_Set_LC/README.pdf`` NN-based loop filter encoder optimization ---------------------------------------------- diff --git a/cfg/nn-based/NnlfOption_3.cfg b/cfg/nn-based/NnlfOption_3.cfg new file mode 100644 index 0000000000..3972a8fad5 --- /dev/null +++ b/cfg/nn-based/NnlfOption_3.cfg @@ -0,0 +1 @@ +NnlfOption : 3 # NN-based loop filter, 0: disable NN filter; 1: Use NN-based loop filter set 0; 2: Use NN-based loop filter set 1; 3: Use LC NN-based loop filter set \ No newline at end of file diff --git a/models/NnlfSetLC/LC_float_model0.sadl b/models/NnlfSetLC/LC_float_model0.sadl new file mode 100644 index 0000000000..3b3df55f24 --- /dev/null +++ b/models/NnlfSetLC/LC_float_model0.sadl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a0cf99d11bbe0ed193e4208cdc14c359761c6379e0d1066a0c3942febf49ed4 +size 223231 diff --git a/models/NnlfSetLC/LC_float_model1.sadl b/models/NnlfSetLC/LC_float_model1.sadl new file mode 100644 index 0000000000..ef608515a3 --- /dev/null +++ b/models/NnlfSetLC/LC_float_model1.sadl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a99a35d468751f7a93666214f19b3e017c47150c2611f1758288390a90bdc6c0 +size 223231 diff --git a/models/NnlfSetLC/LC_float_model2.sadl b/models/NnlfSetLC/LC_float_model2.sadl new file mode 100644 index 0000000000..7369ce56bc --- /dev/null +++ b/models/NnlfSetLC/LC_float_model2.sadl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9c21fbc1b175f0e578a8fa76b4c52772813a121516b8fdb2689445284f301694 +size 223231 diff --git a/models/NnlfSetLC/LC_float_model3.sadl b/models/NnlfSetLC/LC_float_model3.sadl new file mode 100644 index 0000000000..210a7cda50 --- /dev/null +++ b/models/NnlfSetLC/LC_float_model3.sadl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e344d52eb539c41d64373a3914677c38c2e74539f19b52ef54949372ebbb458c +size 223231 diff --git a/models/NnlfSetLC/LC_int16_model0.sadl b/models/NnlfSetLC/LC_int16_model0.sadl new file mode 100644 index 0000000000..64d2bc4028 --- /dev/null +++ b/models/NnlfSetLC/LC_int16_model0.sadl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dde1b10c41e8bd9762ca9cfce03017fa92e93f758cdf36c6eda55f08b146a137 +size 118787 diff --git a/models/NnlfSetLC/LC_int16_model1.sadl b/models/NnlfSetLC/LC_int16_model1.sadl new file mode 100644 index 0000000000..1ed9897b64 --- /dev/null +++ b/models/NnlfSetLC/LC_int16_model1.sadl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e686d36830af5da326bf096de283bf485d607b436bb4d8701176cc92b0127abc +size 118787 diff --git a/models/NnlfSetLC/LC_int16_model2.sadl b/models/NnlfSetLC/LC_int16_model2.sadl new file mode 100644 index 0000000000..ec59bd45d6 --- /dev/null +++ b/models/NnlfSetLC/LC_int16_model2.sadl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27b2de502f26a2f245cd4b13eecd89c7f87efbc2b1789df7e84386f468da46a6 +size 118787 diff --git a/models/NnlfSetLC/LC_int16_model3.sadl b/models/NnlfSetLC/LC_int16_model3.sadl new file mode 100644 index 0000000000..2ed315c714 --- /dev/null +++ b/models/NnlfSetLC/LC_int16_model3.sadl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:711be695081f0d0ad815de49c8cd592f14ce57ed458cc2bf4a40af7954bf4a9a +size 118787 diff --git a/source/App/BitstreamExtractorApp/CMakeLists.txt b/source/App/BitstreamExtractorApp/CMakeLists.txt index 52458f61d3..cf7dacaa28 100644 --- a/source/App/BitstreamExtractorApp/CMakeLists.txt +++ b/source/App/BitstreamExtractorApp/CMakeLists.txt @@ -60,7 +60,7 @@ if( CMAKE_COMPILER_IS_GNUCC AND BUILD_STATIC ) target_compile_definitions( ${EXE_NAME} PUBLIC ENABLE_WPP_STATIC_LINK=1 ) endif() -target_link_libraries( ${EXE_NAME} CommonLib EncoderLib DecoderLib Utilities Threads::Threads ${ADDITIONAL_LIBS} ) +target_link_libraries( ${EXE_NAME} CommonLib EncoderLib DecoderLib Utilities Threads::Threads ${ADDITIONAL_LIBS}) # lldb custom data formatters if( XCODE ) diff --git a/source/App/DecoderApp/DecAppCfg.cpp b/source/App/DecoderApp/DecAppCfg.cpp index 21dcb5fc5e..23f6143ac5 100644 --- a/source/App/DecoderApp/DecAppCfg.cpp +++ b/source/App/DecoderApp/DecAppCfg.cpp @@ -106,6 +106,11 @@ bool DecAppCfg::parseCfg( int argc, char* argv[] ) ("NnpfModelPath", m_nnpfModelPath, string(""), "paths to post-filter models: <path_0>,[<path_1>,...,<path_n>]\n") ("NnpfReconFile", m_postReconFileName, string(""), "path to post-filtered reconstruction") #endif + +#if NN_FILTERING_SET_LC + ( "LCModelPath", m_lcModelPath, default_lc_model_path, "LC model path\n") +#endif + ("OplFile,-opl", m_oplFilename , string(""), "opl-file name without extension for conformance testing\n") #if ENABLE_SIMD_OPT diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp index 6ed57bdb3f..25a0edcb26 100644 --- a/source/App/EncoderApp/EncApp.cpp +++ b/source/App/EncoderApp/EncApp.cpp @@ -1118,6 +1118,10 @@ void EncApp::xInitLibCfg() m_cEncLib.setUseNnlfSet0 (m_nnlfSet0); #endif +#if NN_FILTERING_SET_LC + m_cEncLib.setUseNnlfSetLC (m_nnlfSetLC); +#endif + m_cEncLib.setUseALF ( m_alf ); #if JVET_T0064 m_cEncLib.setALFStrength (m_alfStrength); diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp index a35d50009d..4feda3d1a1 100644 --- a/source/App/EncoderApp/EncAppCfg.cpp +++ b/source/App/EncoderApp/EncAppCfg.cpp @@ -736,6 +736,9 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) ("c", po::parseConfigFile, "configuration file name") #if NN_FILTERING_SET_0 ("ModelPath,-mp", m_ModelPath, default_model_path, "model path\n") +#endif +#if NN_FILTERING_SET_LC + ( "LCModelPath", m_lcModelPath, default_lc_model_path, "LC model path\n") #endif ("WarnUnknowParameter,w", warnUnknowParameter, 0, "warn for unknown configuration parameters instead of failing") ("isSDR", sdr, false, "compatibility") @@ -1156,7 +1159,7 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) ("GolombRiceParameterAdaptation", m_persistentRiceAdaptationEnabledFlag, false, "Enable the adaptation of the Golomb-Rice parameter over the course of each slice") ("AlignCABACBeforeBypass", m_cabacBypassAlignmentEnabledFlag, false, "Align the CABAC engine to a defined fraction of a bit prior to coding bypass data. Must be 1 in high bit rate profile, 0 otherwise") #if NN_COMMON_SPS - ("NnlfOption", m_nnlfOption, 0, "NN-based in-loop filter option (0:disable nnlf, 1: enable nnlf-0, 2: enable nnlf-1)") + ("NnlfOption", m_nnlfOption, 0, "NN-based in-loop filter option (0:disable nnlf, 1: enable nnlf-0, 2: enable nnlf-1, 3: enable nnlf-LC)") #endif ("SAO", m_bUseSAO, true, "Enable Sample Adaptive Offset") ("TestSAODisableAtPictureLevel", m_bTestSAODisableAtPictureLevel, false, "Enables the testing of disabling SAO at the picture level after having analysed all blocks") @@ -1521,6 +1524,11 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) #if NN_FILTERING_SET_0 ("NnlfSet0", m_nnlfSet0, true, "NN-based loop filter set 0\n") #endif + +#if NN_FILTERING_SET_LC + ("NnlfSetLC", m_nnlfSetLC, true, "NN-based loop filter set 2\n") +#endif + ("TemporalFilter", m_gopBasedTemporalFilterEnabled, false, "Enable GOP based temporal filter. Disabled per default") ("TemporalFilterFutureReference", m_gopBasedTemporalFilterFutureReference, true, "Enable referencing of future frames in the GOP based temporal filter. This is typically disabled for Low Delay configurations.") ("TemporalFilterStrengthFrame*", m_gopBasedTemporalFilterStrengths, std::map<int, double>(), "Strength for every * frame in GOP based temporal filter, where * is an integer. E.g. --TemporalFilterStrengthFrame8 0.95 will enable GOP based temporal filter at every 8th frame with strength 0.95.") @@ -2444,12 +2452,15 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) } #if NN_COMMON_SPS - CHECK(m_nnlfOption != 0 && m_nnlfOption != 1 && m_nnlfOption != 2, "Undefined NN-based in-loop filter option"); + CHECK(m_nnlfOption != 0 && m_nnlfOption != 1 && m_nnlfOption != 2 && m_nnlfOption != 3, "Undefined NN-based in-loop filter option"); #if NN_FILTERING_SET_0 m_nnlfSet0 = true; #endif #if NN_FILTERING_SET_1 m_nnlfSet1 = true; +#endif +#if NN_FILTERING_SET_LC + m_nnlfSetLC = true; #endif if (m_nnlfOption == 0) { @@ -2459,6 +2470,9 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) #endif #if NN_FILTERING_SET_1 m_nnlfSet1 = false; +#endif +#if NN_FILTERING_SET_LC + m_nnlfSetLC = false; #endif } else @@ -2471,6 +2485,9 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) #if NN_FILTERING_SET_1 m_nnlfSet1 = m_nnlfSet == 1 ? true : false; m_encDbOpt = m_nnlfSet1 ? true : m_encDbOpt; +#endif +#if NN_FILTERING_SET_LC + m_nnlfSetLC = m_nnlfSet == 2 ? true : false; #endif } #endif @@ -4228,6 +4245,9 @@ void EncAppCfg::xPrintParameter() #if NN_FILTERING_SET_1 msg( VERBOSE, "NNLFSET1:%d ", (m_nnlfSet1)?(1):(0)); #endif +#if NN_FILTERING_SET_LC + msg( VERBOSE, "NnlfSetLC:%d ", m_nnlfSetLC?1:0); +#endif #if JVET_AB0068_AC0328_NNLF_RDO msg( VERBOSE, "EncNnlfOpt:%d ", m_encNnlfOpt ? 1 : 0); #endif diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h index eda98c8ff6..7208274c91 100644 --- a/source/App/EncoderApp/EncAppCfg.h +++ b/source/App/EncoderApp/EncAppCfg.h @@ -758,6 +758,10 @@ protected: bool m_nnlfSet0; ///< CNN Loop Filter #endif +#if NN_FILTERING_SET_LC + bool m_nnlfSetLC; ///< LC CNN Loop Filter +#endif + bool m_alf; ///< Adaptive Loop Filter #if JVET_T0064 double m_alfStrength; diff --git a/source/Lib/CommonLib/CMakeLists.txt b/source/Lib/CommonLib/CMakeLists.txt index 1feff1e963..515dea4847 100644 --- a/source/Lib/CommonLib/CMakeLists.txt +++ b/source/Lib/CommonLib/CMakeLists.txt @@ -111,6 +111,7 @@ if( MSVC ) set_property( SOURCE NNInference.cpp APPEND PROPERTY COMPILE_FLAGS "/arch:AVX2 -DNDEBUG=1 ") set_property( SOURCE NNFilterSet0.cpp APPEND PROPERTY COMPILE_FLAGS "/arch:AVX2 -DNDEBUG=1 ") set_property( SOURCE NNFilterSet1.cpp APPEND PROPERTY COMPILE_FLAGS "/arch:AVX2 -DNDEBUG=1 ") + set_property( SOURCE NNFilterSetLC.cpp APPEND PROPERTY COMPILE_FLAGS "/arch:AVX2 -DNDEBUG=1 ") elseif( UNIX OR MINGW ) if( NNLF_BUILD_WITH_AVX512 STREQUAL "1" ) set_property( SOURCE NNSuperResolution.cpp APPEND PROPERTY COMPILE_FLAGS "-DNDEBUG=1 -mavx512f -mavx512bw -ffast-math") @@ -118,12 +119,14 @@ elseif( UNIX OR MINGW ) set_property( SOURCE NNInference.cpp APPEND PROPERTY COMPILE_FLAGS "-DNDEBUG=1 -mavx512f -mavx512bw -ffast-math") set_property( SOURCE NNFilterSet0.cpp APPEND PROPERTY COMPILE_FLAGS "-DNDEBUG=1 -mavx512f -mavx512bw -ffast-math") set_property( SOURCE NNFilterSet1.cpp APPEND PROPERTY COMPILE_FLAGS "-DNDEBUG=1 -mavx512f -mavx512bw -ffast-math") + set_property( SOURCE NNFilterSetLC.cpp APPEND PROPERTY COMPILE_FLAGS "-DNDEBUG=1 -mavx512f -mavx512bw -ffast-math") else() set_property( SOURCE NNSuperResolution.cpp APPEND PROPERTY COMPILE_FLAGS "-DNDEBUG=1 -mavx2 -ffast-math") set_property( SOURCE NNPostFilter.cpp APPEND PROPERTY COMPILE_FLAGS "-DNDEBUG=1 -mavx2 -ffast-math") set_property( SOURCE NNInference.cpp APPEND PROPERTY COMPILE_FLAGS "-DNDEBUG=1 -mavx2 -ffast-math") set_property( SOURCE NNFilterSet0.cpp APPEND PROPERTY COMPILE_FLAGS "-DNDEBUG=1 -mavx2 -ffast-math") set_property( SOURCE NNFilterSet1.cpp APPEND PROPERTY COMPILE_FLAGS "-DNDEBUG=1 -mavx2 -ffast-math") + set_property( SOURCE NNFilterSetLC.cpp APPEND PROPERTY COMPILE_FLAGS "-DNDEBUG=1 -mavx2 -ffast-math") endif() endif() if(UNIX) diff --git a/source/Lib/CommonLib/CodingStatistics.h b/source/Lib/CommonLib/CodingStatistics.h index 0cde637774..2e08daf208 100644 --- a/source/Lib/CommonLib/CodingStatistics.h +++ b/source/Lib/CommonLib/CodingStatistics.h @@ -103,6 +103,9 @@ enum CodingStatisticsType STATS__CABAC_BITS__LFNST, STATS__CABAC_BITS__ALF, +#if NN_FILTERING_SET_LC + STATS__CABAC_BITS__LCNNLF, +#endif STATS__CABAC_TRM_BITS, STATS__CABAC_FIXED_BITS, STATS__BYTE_ALIGNMENT_BITS, diff --git a/source/Lib/CommonLib/CodingStructure.cpp b/source/Lib/CommonLib/CodingStructure.cpp index 92b4741aa8..7a744617d3 100644 --- a/source/Lib/CommonLib/CodingStructure.cpp +++ b/source/Lib/CommonLib/CodingStructure.cpp @@ -1118,6 +1118,16 @@ void CodingStructure::rebindPicBufs() { m_nnPostFiltered.destroy(); } +#endif +#if NN_FILTERING_SET_LC + if (!picture->M_BUFS(0, PIC_DEC_LCNN_FILTERED).bufs.empty()) + { + m_lcnn_filtered.createFromBuf(picture->M_BUFS(0, PIC_DEC_LCNN_FILTERED)); + } + else + { + m_lcnn_filtered.destroy(); + } #endif if( pcv->isEncoder ) { @@ -1618,6 +1628,12 @@ const CPelBuf CodingStructure::getResiBuf(const CompArea &blk) const { r PelUnitBuf CodingStructure::getResiBuf(const UnitArea &unit) { return getBuf(unit, PIC_RESIDUAL); } const CPelUnitBuf CodingStructure::getResiBuf(const UnitArea &unit) const { return getBuf(unit, PIC_RESIDUAL); } +#if NN_FILTERING_SET_LC + PelBuf CodingStructure::getLCnnlfBuf(const CompArea &blk) { return getBuf(blk, PIC_DEC_LCNN_FILTERED); } +const CPelBuf CodingStructure::getLCnnlfBuf(const CompArea &blk) const { return getBuf(blk, PIC_DEC_LCNN_FILTERED); } + PelUnitBuf CodingStructure::getLCnnlfBuf(const UnitArea &unit) { return getBuf(unit, PIC_DEC_LCNN_FILTERED); } +const CPelUnitBuf CodingStructure::getLCnnlfBuf(const UnitArea &unit) const { return getBuf(unit, PIC_DEC_LCNN_FILTERED); } +#endif PelBuf CodingStructure::getRecoBuf(const CompArea &blk) { return getBuf(blk, PIC_RECONSTRUCTION); } const CPelBuf CodingStructure::getRecoBuf(const CompArea &blk) const { return getBuf(blk, PIC_RECONSTRUCTION); } PelUnitBuf CodingStructure::getRecoBuf(const UnitArea &unit) { return getBuf(unit, PIC_RECONSTRUCTION); } @@ -1659,8 +1675,12 @@ PelBuf CodingStructure::getBuf( const CompArea &blk, const PictureType &type ) const ComponentID compID = blk.compID; -#if JVET_AC0055_NN_POST_FILTERING +#if JVET_AC0055_NN_POST_FILTERING && NN_FILTERING_SET_LC + PelStorage* buf = type == PIC_PREDICTION ? &m_pred : ( type == PIC_RESIDUAL ? &m_resi : ( type == PIC_RECONSTRUCTION ? &m_reco : ( type == PIC_ORG_RESI ? &m_orgr : (type == PIC_NN_POST_FILTERED ? &m_nnPostFiltered : (type == PIC_DEC_LCNN_FILTERED ? &m_lcnn_filtered : nullptr ) ) ) ) ); +#elif JVET_AC0055_NN_POST_FILTERING PelStorage* buf = type == PIC_PREDICTION ? &m_pred : ( type == PIC_RESIDUAL ? &m_resi : ( type == PIC_RECONSTRUCTION ? &m_reco : ( type == PIC_ORG_RESI ? &m_orgr : (type == PIC_NN_POST_FILTERED ? &m_nnPostFiltered : nullptr ) ) ) ); +#elif NN_FILTERING_SET_LC + PelStorage* buf = type == PIC_PREDICTION ? &m_pred : ( type == PIC_RESIDUAL ? &m_resi : ( type == PIC_RECONSTRUCTION ? &m_reco : ( type == PIC_ORG_RESI ? &m_orgr : (type == PIC_DEC_LCNN_FILTERED ? &m_lcnn_filtered : nullptr ) ) ) ); #else PelStorage* buf = type == PIC_PREDICTION ? &m_pred : ( type == PIC_RESIDUAL ? &m_resi : ( type == PIC_RECONSTRUCTION ? &m_reco : ( type == PIC_ORG_RESI ? &m_orgr : nullptr ) ) ); #endif @@ -1702,9 +1722,12 @@ const CPelBuf CodingStructure::getBuf( const CompArea &blk, const PictureType &t } const ComponentID compID = blk.compID; - -#if JVET_AC0055_NN_POST_FILTERING +#if JVET_AC0055_NN_POST_FILTERING && NN_FILTERING_SET_LC + const PelStorage* buf = type == PIC_PREDICTION ? &m_pred : ( type == PIC_RESIDUAL ? &m_resi : ( type == PIC_RECONSTRUCTION ? &m_reco : ( type == PIC_ORG_RESI ? &m_orgr : ( type == PIC_NN_POST_FILTERED ? &m_nnPostFiltered : ( type == PIC_DEC_LCNN_FILTERED ? &m_lcnn_filtered : nullptr ) ) ) ) ); +#elif JVET_AC0055_NN_POST_FILTERING const PelStorage* buf = type == PIC_PREDICTION ? &m_pred : ( type == PIC_RESIDUAL ? &m_resi : ( type == PIC_RECONSTRUCTION ? &m_reco : ( type == PIC_ORG_RESI ? &m_orgr : ( type == PIC_NN_POST_FILTERED ? &m_nnPostFiltered : nullptr ) ) ) ); +#elif NN_FILTERING_SET_LC + const PelStorage* buf = type == PIC_PREDICTION ? &m_pred : ( type == PIC_RESIDUAL ? &m_resi : ( type == PIC_RECONSTRUCTION ? &m_reco : ( type == PIC_ORG_RESI ? &m_orgr : ( type == PIC_DEC_LCNN_FILTERED ? &m_lcnn_filtered : nullptr ) ) ) ); #else const PelStorage* buf = type == PIC_PREDICTION ? &m_pred : ( type == PIC_RESIDUAL ? &m_resi : ( type == PIC_RECONSTRUCTION ? &m_reco : ( type == PIC_ORG_RESI ? &m_orgr : nullptr ) ) ); #endif diff --git a/source/Lib/CommonLib/CodingStructure.h b/source/Lib/CommonLib/CodingStructure.h index ca6ab60882..8522dba625 100644 --- a/source/Lib/CommonLib/CodingStructure.h +++ b/source/Lib/CommonLib/CodingStructure.h @@ -68,6 +68,9 @@ enum PictureType #if NNVC_USE_BS PIC_BS_MAP, #endif +#if NN_FILTERING_SET_LC + PIC_DEC_LCNN_FILTERED, +#endif #if NNVC_USE_PRED PIC_PREDICTION_CUSTOM, #endif @@ -266,6 +269,9 @@ private: #if JVET_AC0055_NN_POST_FILTERING PelStorage m_nnPostFiltered; #endif +#if NN_FILTERING_SET_LC + PelStorage m_lcnn_filtered; +#endif TCoeff *m_coeffs [ MAX_NUM_COMPONENT ]; Pel *m_pcmbuf [ MAX_NUM_COMPONENT ]; @@ -357,6 +363,13 @@ public: PelUnitBuf getResiBuf() { return m_resi; } const CPelUnitBuf getResiBuf() const { return m_resi; } +#if NN_FILTERING_SET_LC + PelBuf getLCnnlfBuf(const CompArea &blk); + const CPelBuf getLCnnlfBuf(const CompArea &blk) const; + PelUnitBuf getLCnnlfBuf(const UnitArea &unit); + const CPelUnitBuf getLCnnlfBuf(const UnitArea &unit) const; +#endif + // org-resi buffer PelBuf getOrgResiBuf(const ComponentID &compID) { return m_orgr.get(compID); } const CPelBuf getOrgResiBuf(const ComponentID &compID) const { return m_orgr.get(compID); } diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h index 945ae33f84..167ada2a5b 100644 --- a/source/Lib/CommonLib/CommonDef.h +++ b/source/Lib/CommonLib/CommonDef.h @@ -120,6 +120,14 @@ typedef enum AFFINE_MODEL_NUM } EAffineModel; +#if NN_FILTERING_SET_LC +enum LCnnlfMode +{ + LCNNLF_OFF, + LCNNLF_ALL_ON, + LCNNLF_CTU_ONOFF +}; +#endif static const int AFFINE_ME_LIST_SIZE = 4; static const int AFFINE_ME_LIST_SIZE_LD = 3; static const double AFFINE_ME_LIST_MVP_TH = 1.0; diff --git a/source/Lib/CommonLib/Contexts.cpp b/source/Lib/CommonLib/Contexts.cpp index 876a59fd1f..8cb56bc500 100644 --- a/source/Lib/CommonLib/Contexts.cpp +++ b/source/Lib/CommonLib/Contexts.cpp @@ -828,6 +828,18 @@ const CtxSet ContextSetCfg::nnlfSet1ParamIdx = ContextSetCfg::addCtxSet }); #endif +#if NN_FILTERING_SET_LC +const CtxSet ContextSetCfg::ctbLCnnlfFlag = +{ + ContextSetCfg::addCtxSet + ({ + { 33, 52, 46, 25, 61, 54, 25, 61, 54, }, + { 13, 23, 46, 4, 61, 54, 19, 46, 54, }, + { 62, 39, 39, 54, 39, 39, 31, 39, 39, }, + { 0, 0, 0, 4, 0, 0, 1, 0, 0, }, + }) +}; +#endif const CtxSet ContextSetCfg::ctbAlfAlternative = ContextSetCfg::addCtxSet ({ { 11, 26, }, diff --git a/source/Lib/CommonLib/Contexts.h b/source/Lib/CommonLib/Contexts.h index aedbfa3ac5..1f6814389b 100644 --- a/source/Lib/CommonLib/Contexts.h +++ b/source/Lib/CommonLib/Contexts.h @@ -265,6 +265,9 @@ public: static const CtxSet BcwIdx; #if NN_FILTERING_SET_0 static const CtxSet ctbCnnlfFlag; +#endif +#if NN_FILTERING_SET_LC + static const CtxSet ctbLCnnlfFlag; #endif static const CtxSet ctbAlfFlag; static const CtxSet ctbAlfAlternative; diff --git a/source/Lib/CommonLib/NNFilterSetLC.cpp b/source/Lib/CommonLib/NNFilterSetLC.cpp new file mode 100644 index 0000000000..8e435343f5 --- /dev/null +++ b/source/Lib/CommonLib/NNFilterSetLC.cpp @@ -0,0 +1,463 @@ +/* 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-2023, 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 NNFilterSetLC.cpp + \brief cnn loop filter class +*/ +#include "NNFilterSetLC.h" + +#define HAVE_INTTYPES_H 1 +#define __STDC_FORMAT_MACROS +#include <sadl/model.h> +#include "NNInference.h" + +#if NN_FILTERING_SET_LC +#include "CodingStructure.h" +#include "Picture.h" + +NNFilterSetLC::NNFilterSetLC() +{ + for(auto &p: m_Module) p=std::make_unique<sadl::Model<TypeSadl>>(); + + for (int compIdx = 0; compIdx < MAX_NUM_COMPONENT; compIdx++) + { + m_ctuEnableFlag[compIdx] = nullptr; + } + m_loadFlag = false; +} + +NNFilterSetLC::~NNFilterSetLC() = default; + +void NNFilterSetLC::initNumCTUs(int m_numCTUsInPic) +{ + m_numCtusInFrame = m_numCTUsInPic; +} +void NNFilterSetLC::destroy() +{ + for (int compIdx = 0; compIdx < MAX_NUM_COMPONENT; compIdx++) + { + if (m_ctuEnableFlag[compIdx]) + { + delete[] m_ctuEnableFlag[compIdx]; + m_ctuEnableFlag[compIdx] = nullptr; + } + } +} + + +void NNFilterSetLC::filterPicture( PelUnitBuf in, PelUnitBuf bs_map, PelUnitBuf out, int model_selector /*=MAX_INT*/, int qp /*=MAX_INT*/) +{ + if(m_ModelPaths.empty()) + { + out.copyFrom(in); + return; + } + int blockSize = 64; + + int boundary_ext = BOUNDARY_EXT; + int boundary_ext_interleave = boundary_ext/2; + + PelBuf bsMapBufY = bs_map.get(COMPONENT_Y); + PelBuf bsMapBufCb = bs_map.get(COMPONENT_Cb); + PelBuf bsMapBufCr = bs_map.get(COMPONENT_Cr); + + PelBuf reconBufY = in.get(COMPONENT_Y); + PelBuf reconBufCb = in.get(COMPONENT_Cb); + PelBuf reconBufCr = in.get(COMPONENT_Cr); + PelBuf nnFilteredBufY = out.get(COMPONENT_Y); + PelBuf nnFilteredBufCb = out.get(COMPONENT_Cb); + PelBuf nnFilteredBufCr = out.get(COMPONENT_Cr); + + int pic_w = reconBufY.width; + int pic_h = reconBufY.height; + +#if NN_FIXED_POINT_IMPLEMENTATION + const int org_quantizers_in = ORG_QUANTIZERS_IN; + const int sadl_quantizers_in = SADL_QUANTIZERS_IN; + const int out_quantizers_in = OUT_QUANTIZERS_IN; + const int in_left_shift = sadl_quantizers_in - org_quantizers_in; + const int out_right_shift = out_quantizers_in - org_quantizers_in; +#else + float in_maxValue = 1023; +#endif + + sadl::Model<TypeSadl>& model = *m_Module[model_selector]; + + + sadl::Tensor<TypeSadl> map_cu; + map_cu = m_Input[model_selector][0]; + for( int y = 0; y < pic_h; y+=blockSize*2) + { + for( int x = 0; x < pic_w; x+=blockSize*2) + { + for( int i = - boundary_ext_interleave; i < blockSize + boundary_ext_interleave; ++i) + { + for( int j = - boundary_ext_interleave; j < blockSize + boundary_ext_interleave; ++j) + { + int pos_x = std::max(0, std::min( x/2 + j, pic_w / 2 - 1 )); + int pos_y = std::max(0, std::min( y/2 + i, pic_h / 2 - 1 )); + + int pos_x_luma_tl = std::max(0, std::min(x + j*2, pic_w - 1)); + int pos_y_luma_tl = std::max(0, std::min(y + i*2, pic_h - 1)); + int pos_x_luma_br = std::max(0, std::min(pos_x_luma_tl + 1, pic_w - 1)); + int pos_y_luma_br = std::max(0, std::min(pos_y_luma_tl + 1, pic_h - 1)); + +#if NN_FIXED_POINT_IMPLEMENTATION + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 0 ) = reconBufY.at( pos_x_luma_tl, pos_y_luma_tl) << in_left_shift; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 1 ) = reconBufY.at( pos_x_luma_br, pos_y_luma_tl) << in_left_shift; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 2 ) = reconBufY.at( pos_x_luma_tl, pos_y_luma_br) << in_left_shift; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 3 ) = reconBufY.at( pos_x_luma_br, pos_y_luma_br) << in_left_shift; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 4 ) = reconBufCb.at( pos_x, pos_y) << in_left_shift; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 5 ) = reconBufCr.at( pos_x, pos_y) << in_left_shift; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 6 ) = round(std::pow(2, (qp-42.)/6.) * std::pow(2, sadl_quantizers_in)); + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 7 ) = bsMapBufY.at( pos_x_luma_tl, pos_y_luma_tl) << in_left_shift; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 8 ) = bsMapBufCb.at( pos_x, pos_y) << in_left_shift; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 9 ) = bsMapBufCr.at( pos_x, pos_y) << in_left_shift; +#else + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 0 ) = reconBufY.at( pos_x_luma_tl, pos_y_luma_tl) / in_maxValue; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 1 ) = reconBufY.at( pos_x_luma_br, pos_y_luma_tl) / in_maxValue; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 2 ) = reconBufY.at( pos_x_luma_tl, pos_y_luma_br) / in_maxValue; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 3 ) = reconBufY.at( pos_x_luma_br, pos_y_luma_br) / in_maxValue; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 4 ) = reconBufCb.at( pos_x, pos_y) / in_maxValue; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 5 ) = reconBufCr.at( pos_x, pos_y) / in_maxValue; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 6 ) = std::pow(2, (qp-42.)/6.); + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 7 ) = bsMapBufY.at( pos_x_luma_tl, pos_y_luma_tl) / in_maxValue; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 8 ) = bsMapBufCb.at( pos_x, pos_y) / in_maxValue; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 9 ) = bsMapBufCr.at( pos_x, pos_y) / in_maxValue; +#endif + } + } + + m_Input[model_selector][0] = map_cu; + NNInference::infer<TypeSadl>(model, m_Input[model_selector]); + sadl::Tensor<TypeSadl> map_out = model.result(0); + for( int i = 0; i < blockSize; ++i) + { + for( int j = 0; j < blockSize; ++j) + { + int pos_x = x/2 + j; + int pos_y = y/2 + i; + if( pos_x >= pic_w/2 || pos_y >= pic_h / 2) + { + continue; + } + int i_slice = i + boundary_ext_interleave; + int j_slice = j + boundary_ext_interleave; +#if NN_FIXED_POINT_IMPLEMENTATION + nnFilteredBufY.at(pos_x * 2, pos_y * 2) = ( int(map_out(0, i_slice, j_slice, 0) + (map_cu(0, i_slice, j_slice, 0) << (out_right_shift - in_left_shift)))) * (1 << (NN_RESIDUE_ADDITIONAL_SHIFT - out_right_shift)); + nnFilteredBufY.at(pos_x * 2 + 1, pos_y * 2) = ( int(map_out(0, i_slice, j_slice, 1) + (map_cu(0, i_slice, j_slice, 1) << (out_right_shift - in_left_shift)))) * (1 << (NN_RESIDUE_ADDITIONAL_SHIFT - out_right_shift)); + nnFilteredBufY.at(pos_x * 2, pos_y * 2 + 1) = ( int(map_out(0, i_slice, j_slice, 2) + (map_cu(0, i_slice, j_slice, 2) << (out_right_shift - in_left_shift)))) * (1 << (NN_RESIDUE_ADDITIONAL_SHIFT - out_right_shift)); + nnFilteredBufY.at(pos_x * 2 + 1, pos_y * 2 + 1) = ( int(map_out(0, i_slice, j_slice, 3) + (map_cu(0, i_slice, j_slice, 3) << (out_right_shift - in_left_shift)))) * (1 << (NN_RESIDUE_ADDITIONAL_SHIFT - out_right_shift)); + nnFilteredBufCb.at(pos_x, pos_y) = ( int(map_out(0, i_slice, j_slice, 4) + (map_cu(0, i_slice, j_slice, 4) << (out_right_shift - in_left_shift)))) * (1 << (NN_RESIDUE_ADDITIONAL_SHIFT - out_right_shift)); + nnFilteredBufCr.at(pos_x, pos_y) = ( int(map_out(0, i_slice, j_slice, 5) + (map_cu(0, i_slice, j_slice, 5) << (out_right_shift - in_left_shift)))) * (1 << (NN_RESIDUE_ADDITIONAL_SHIFT - out_right_shift)); +#else + nnFilteredBufY.at(pos_x * 2, pos_y * 2) = int( (map_out(0, i_slice, j_slice, 0) + map_cu(0, i_slice, j_slice, 0)) * in_maxValue * (1 << NN_RESIDUE_ADDITIONAL_SHIFT) + 0.5); + nnFilteredBufY.at(pos_x * 2 + 1, pos_y * 2) = int( (map_out(0, i_slice, j_slice, 1) + map_cu(0, i_slice, j_slice, 1)) * in_maxValue * (1 << NN_RESIDUE_ADDITIONAL_SHIFT) + 0.5); + nnFilteredBufY.at(pos_x * 2, pos_y * 2 + 1) = int( (map_out(0, i_slice, j_slice, 2) + map_cu(0, i_slice, j_slice, 2)) * in_maxValue * (1 << NN_RESIDUE_ADDITIONAL_SHIFT) + 0.5); + nnFilteredBufY.at(pos_x * 2 + 1, pos_y * 2 + 1) = int( (map_out(0, i_slice, j_slice, 3) + map_cu(0, i_slice, j_slice, 3)) * in_maxValue * (1 << NN_RESIDUE_ADDITIONAL_SHIFT) + 0.5); + nnFilteredBufCb.at(pos_x, pos_y) = int( (map_out(0, i_slice, j_slice, 4) + map_cu(0, i_slice, j_slice, 4)) * in_maxValue * (1 << NN_RESIDUE_ADDITIONAL_SHIFT) + 0.5); + nnFilteredBufCr.at(pos_x, pos_y) = int( (map_out(0, i_slice, j_slice, 5) + map_cu(0, i_slice, j_slice, 5)) * in_maxValue * (1 << NN_RESIDUE_ADDITIONAL_SHIFT) + 0.5); +#endif + } + } + } + } + + +} + + +void NNFilterSetLC::filterPictureDecoder(CodingStructure& cs, PelUnitBuf in, PelUnitBuf out, PelUnitBuf recon, int model_selector ) +{ + if(m_ModelPaths.empty()) + { + out.copyFrom(in); + return; + } + int blockSize = 64; + int boundary_ext = BOUNDARY_EXT; + int boundary_ext_interleave = boundary_ext/2; + PelBuf inBufY = in.get(COMPONENT_Y); + PelBuf inBufCb = in.get(COMPONENT_Cb); + PelBuf inBufCr = in.get(COMPONENT_Cr); + PelBuf bsMapBufY = cs.picture->getBsMapBuf().get(COMPONENT_Y); + PelBuf bsMapBufCb = cs.picture->getBsMapBuf().get(COMPONENT_Cb); + PelBuf bsMapBufCr = cs.picture->getBsMapBuf().get(COMPONENT_Cr); + PelBuf nnFilteredBufY = out.get(COMPONENT_Y); + PelBuf nnFilteredBufCb = out.get(COMPONENT_Cb); + PelBuf nnFilteredBufCr = out.get(COMPONENT_Cr); + PelBuf reconBufY = recon.get(COMPONENT_Y); + PelBuf reconBufCb = recon.get(COMPONENT_Cb); + PelBuf reconBufCr = recon.get(COMPONENT_Cr); + + // get CTU enable flags + for( int compIdx = 0; compIdx < MAX_NUM_COMPONENT; compIdx++ ) + { + m_ctuEnableFlag[compIdx] = cs.picture->getLCnnlfCtuEnableFlag( compIdx ); + } + + int pic_w = inBufY.width; + int pic_h = inBufY.height; + +#if NN_FIXED_POINT_IMPLEMENTATION + const int org_quantizers_in = ORG_QUANTIZERS_IN; + const int sadl_quantizers_in = SADL_QUANTIZERS_IN; + const int out_quantizers_in = OUT_QUANTIZERS_IN; + const int in_left_shift = sadl_quantizers_in - org_quantizers_in; + const int out_right_shift = out_quantizers_in - org_quantizers_in; +#else + float in_maxValue = 1023; +#endif + + sadl::Model<TypeSadl>& model = *m_Module[model_selector]; + sadl::Tensor<TypeSadl> map_cu; + map_cu = m_Input[model_selector][0]; + + int ctuIdx = 0; + for (int y = 0; y < pic_h; y += blockSize * 2) + { + for (int x = 0; x < pic_w; x += blockSize * 2) + { + for( int i = - boundary_ext_interleave; i < blockSize + boundary_ext_interleave; ++i) + { + for( int j = - boundary_ext_interleave; j < blockSize + boundary_ext_interleave; ++j) + { + int pos_x = std::max(0, std::min(x / 2 + j, pic_w / 2 - 1)); + int pos_y = std::max(0, std::min(y / 2 + i, pic_h / 2 - 1)); + + int pos_x_luma_tl = std::max(0, std::min(x + j * 2, pic_w - 1)); + int pos_y_luma_tl = std::max(0, std::min(y + i * 2, pic_h - 1)); + int pos_x_luma_br = std::max(0, std::min(pos_x_luma_tl + 1, pic_w - 1)); + int pos_y_luma_br = std::max(0, std::min(pos_y_luma_tl + 1, pic_h - 1)); +#if NN_FIXED_POINT_IMPLEMENTATION + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 0) = inBufY.at(pos_x_luma_tl, pos_y_luma_tl) << in_left_shift; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 1) = inBufY.at(pos_x_luma_br, pos_y_luma_tl) << in_left_shift; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 2) = inBufY.at(pos_x_luma_tl, pos_y_luma_br) << in_left_shift; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 3) = inBufY.at(pos_x_luma_br, pos_y_luma_br) << in_left_shift; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 4) = inBufCb.at(pos_x, pos_y) << in_left_shift; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 5) = inBufCr.at(pos_x, pos_y) << in_left_shift; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 6 ) = round(std::pow(2, (cs.slice->getSliceQp() -42.)/6.) * std::pow(2, sadl_quantizers_in)); + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 7 ) = bsMapBufY.at( pos_x_luma_tl, pos_y_luma_tl) << in_left_shift; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 8 ) = bsMapBufCb.at( pos_x, pos_y) << in_left_shift; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 9 ) = bsMapBufCr.at( pos_x, pos_y) << in_left_shift; +#else + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 0) = inBufY.at(pos_x_luma_tl, pos_y_luma_tl) / in_maxValue; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 1) = inBufY.at(pos_x_luma_br, pos_y_luma_tl) / in_maxValue; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 2) = inBufY.at(pos_x_luma_tl, pos_y_luma_br) / in_maxValue; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 3) = inBufY.at(pos_x_luma_br, pos_y_luma_br) / in_maxValue; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 4) = inBufCb.at(pos_x, pos_y) / in_maxValue; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 5) = inBufCr.at(pos_x, pos_y) / in_maxValue; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 6 ) = std::pow(2, (cs.slice->getSliceQp() -42.)/6.); + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 7 ) = bsMapBufY.at( pos_x_luma_tl, pos_y_luma_tl) / in_maxValue; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 8 ) = bsMapBufCb.at( pos_x, pos_y) / in_maxValue; + map_cu(0, boundary_ext_interleave + i, boundary_ext_interleave + j, 9 ) = bsMapBufCr.at( pos_x, pos_y) / in_maxValue; +#endif + } + } + m_Input[model_selector][0] = map_cu; + NNInference::infer<TypeSadl>(model, m_Input[model_selector]); + sadl::Tensor<TypeSadl> map_out = model.result(0); + for (int i = 0; i < blockSize; ++i) + { + for (int j = 0; j < blockSize; ++j) + { + int pos_x = x / 2 + j; + int pos_y = y / 2 + i; + if (pos_x >= pic_w / 2 || pos_y >= pic_h / 2) + { + continue; + } + int i_slice = i + boundary_ext_interleave; + int j_slice = j + boundary_ext_interleave; + if (m_ctuEnableFlag[COMPONENT_Y][ctuIdx]) + { +#if NN_FIXED_POINT_IMPLEMENTATION + nnFilteredBufY.at(pos_x * 2, pos_y * 2) = + (int(map_out(0, i_slice, j_slice, 0) + (map_cu(0, i_slice, j_slice, 0) << (out_right_shift - in_left_shift)))) * (1 << (NN_RESIDUE_ADDITIONAL_SHIFT - out_right_shift)); + nnFilteredBufY.at(pos_x * 2 + 1, pos_y * 2) = + (int(map_out(0, i_slice, j_slice, 1) + (map_cu(0, i_slice, j_slice, 1) << (out_right_shift - in_left_shift)))) * (1 << (NN_RESIDUE_ADDITIONAL_SHIFT - out_right_shift)); + nnFilteredBufY.at(pos_x * 2, pos_y * 2 + 1) = + (int(map_out(0, i_slice, j_slice, 2) + (map_cu(0, i_slice, j_slice, 2) << (out_right_shift - in_left_shift)))) * (1 << (NN_RESIDUE_ADDITIONAL_SHIFT - out_right_shift)); + nnFilteredBufY.at(pos_x * 2 + 1, pos_y * 2 + 1) = + (int(map_out(0, i_slice, j_slice, 3) + (map_cu(0, i_slice, j_slice, 3) << (out_right_shift - in_left_shift)))) * (1 << (NN_RESIDUE_ADDITIONAL_SHIFT - out_right_shift)); +#else + nnFilteredBufY.at(pos_x * 2, pos_y * 2) = + int((map_out(0, i_slice, j_slice, 0) + map_cu(0, i_slice, j_slice, 0)) * in_maxValue * (1 << NN_RESIDUE_ADDITIONAL_SHIFT) + 0.5); + nnFilteredBufY.at(pos_x * 2 + 1, pos_y * 2) = + int((map_out(0, i_slice, j_slice, 1) + map_cu(0, i_slice, j_slice, 1)) * in_maxValue * (1 << NN_RESIDUE_ADDITIONAL_SHIFT) + 0.5); + nnFilteredBufY.at(pos_x * 2, pos_y * 2 + 1) = + int((map_out(0, i_slice, j_slice, 2) + map_cu(0, i_slice, j_slice, 2)) * in_maxValue * (1 << NN_RESIDUE_ADDITIONAL_SHIFT) + 0.5); + nnFilteredBufY.at(pos_x * 2 + 1, pos_y * 2 + 1) = + int((map_out(0, i_slice, j_slice, 3) + map_cu(0, i_slice, j_slice, 3)) * in_maxValue * (1 << NN_RESIDUE_ADDITIONAL_SHIFT) + 0.5); +#endif + } + else + { + nnFilteredBufY.at(pos_x * 2, pos_y * 2) = reconBufY.at(pos_x * 2, pos_y * 2) << NN_RESIDUE_ADDITIONAL_SHIFT; + nnFilteredBufY.at(pos_x * 2 + 1, pos_y * 2) = reconBufY.at(pos_x * 2 + 1, pos_y * 2) << NN_RESIDUE_ADDITIONAL_SHIFT; + nnFilteredBufY.at(pos_x * 2, pos_y * 2 + 1) = reconBufY.at(pos_x * 2, pos_y * 2 + 1) << NN_RESIDUE_ADDITIONAL_SHIFT; + nnFilteredBufY.at(pos_x * 2 + 1, pos_y * 2 + 1) = reconBufY.at(pos_x * 2 + 1, pos_y * 2 + 1) << NN_RESIDUE_ADDITIONAL_SHIFT; + } + if (m_ctuEnableFlag[COMPONENT_Cb][ctuIdx]) + { +#if NN_FIXED_POINT_IMPLEMENTATION + nnFilteredBufCb.at(pos_x, pos_y) = + (int(map_out(0, i_slice, j_slice, 4) + (map_cu(0, i_slice, j_slice, 4) << (out_right_shift - in_left_shift)))) * (1 << (NN_RESIDUE_ADDITIONAL_SHIFT - out_right_shift)); +#else + nnFilteredBufCb.at(pos_x, pos_y) = + int((map_out(0, i_slice, j_slice, 4) + map_cu(0, i_slice, j_slice, 4)) * in_maxValue * (1 << NN_RESIDUE_ADDITIONAL_SHIFT) + 0.5); +#endif + } + else + { + nnFilteredBufCb.at(pos_x, pos_y) = reconBufCb.at(pos_x, pos_y) << NN_RESIDUE_ADDITIONAL_SHIFT; + } + if (m_ctuEnableFlag[COMPONENT_Cr][ctuIdx]) + { +#if NN_FIXED_POINT_IMPLEMENTATION + nnFilteredBufCr.at(pos_x, pos_y) = + (int(map_out(0, i_slice, j_slice, 5) + (map_cu(0, i_slice, j_slice, 5) << (out_right_shift - in_left_shift)))) * (1 << (NN_RESIDUE_ADDITIONAL_SHIFT - out_right_shift)); +#else + nnFilteredBufCr.at(pos_x, pos_y) = + int((map_out(0, i_slice, j_slice, 5) + map_cu(0, i_slice, j_slice, 5)) * in_maxValue * (1 << NN_RESIDUE_ADDITIONAL_SHIFT) + 0.5); +#endif + } + else + { + nnFilteredBufCr.at(pos_x, pos_y) = reconBufCr.at(pos_x, pos_y) << NN_RESIDUE_ADDITIONAL_SHIFT; + } + } + } + + + CHECK(ctuIdx >= m_numCtusInFrame, "ctuIdx should be less than no. of CTUs in frame"); + ctuIdx++; + } + } +} + + +void NNFilterSetLC::scaleResidue(PelUnitBuf recoBuf, PelUnitBuf filteredBuf, PicHeader * ph) +{ + PelBuf reconBufY = recoBuf.get(COMPONENT_Y); + PelBuf reconBufCb = recoBuf.get(COMPONENT_Cb); + PelBuf reconBufCr = recoBuf.get(COMPONENT_Cr); + PelBuf nnFilteredBufY = filteredBuf.get(COMPONENT_Y); + PelBuf nnFilteredBufCb = filteredBuf.get(COMPONENT_Cb); + PelBuf nnFilteredBufCr = filteredBuf.get(COMPONENT_Cr); + + int pic_w = reconBufY.width; + int pic_h = reconBufY.height; + + const int scaleY = ph->getLCNnScale(COMPONENT_Y); + const int scaleCb = ph->getLCNnScale(COMPONENT_Cb); + const int scaleCr = ph->getLCNnScale(COMPONENT_Cr); + + int shiftY = LCNN_RESIDUE_SCALE_SHIFT + NN_RESIDUE_ADDITIONAL_SHIFT; + int shiftCb = LCNN_RESIDUE_SCALE_SHIFT + NN_RESIDUE_ADDITIONAL_SHIFT; + int shiftCr = LCNN_RESIDUE_SCALE_SHIFT + NN_RESIDUE_ADDITIONAL_SHIFT; + + int offsetY = (1 << shiftY)/2 ; + int offsetCb = (1 << shiftCb)/2; + int offsetCr = (1 << shiftCr)/2; + + for (int y = 0; y < pic_h; y++) + { + for (int x = 0; x < pic_w; x++) + { + nnFilteredBufY.at(x, y) = Clip3(0, 1023, reconBufY.at(x, y) + (((nnFilteredBufY.at(x, y) - (reconBufY.at(x, y) << NN_RESIDUE_ADDITIONAL_SHIFT)) * scaleY + offsetY ) >> shiftY)); + } + } + + for (int y = 0; y < pic_h/2; y++) + { + for (int x = 0; x < pic_w/2; x++) + { + nnFilteredBufCb.at(x, y) = Clip3(0, 1023, reconBufCb.at(x, y) + (((nnFilteredBufCb.at(x, y) - (reconBufCb.at(x, y) << NN_RESIDUE_ADDITIONAL_SHIFT)) * scaleCb + offsetCb ) >> shiftCb)); + nnFilteredBufCr.at(x, y) = Clip3(0, 1023, reconBufCr.at(x, y) + (((nnFilteredBufCr.at(x, y) - (reconBufCr.at(x, y) << NN_RESIDUE_ADDITIONAL_SHIFT)) * scaleCr + offsetCr ) >> shiftCr)); + } + } +} + +bool NNFilterSetLC::loadModel( std::string model_path ) +{ + if(m_loadFlag) + { + return true; + } + size_t len = model_path.size(); + size_t pos = 0; + size_t idx = 0; + std::string one_path; + auto remained_path = model_path; + do + { + auto pos_delimiter = model_path.find(",", pos); + one_path = ""; + + if(pos_delimiter == std::string::npos) + { + one_path = model_path.substr(pos); + pos = len; + } + else + { + one_path = model_path.substr(pos, pos_delimiter - pos); + pos = pos_delimiter + 1; + } + + if(one_path.size()) + { + sadl::Model<TypeSadl>& model = *m_Module[idx]; + ifstream file(one_path, ios::binary); + if (!model.load(file)) { + cerr << "[ERROR] Unable to read model " << one_path << endl; + exit(-1); + } + + m_Input[idx] = model.getInputsTemplate(); + + if (!model.init(m_Input[idx])) { + cerr << "[ERROR] issue during initialization" << endl; + exit(-1); + } + m_ModelPaths.push_back(one_path); + idx++; + } + }while( pos < len); + + m_loadFlag = true; + return true; +} + +#endif \ No newline at end of file diff --git a/source/Lib/CommonLib/NNFilterSetLC.h b/source/Lib/CommonLib/NNFilterSetLC.h new file mode 100644 index 0000000000..329728f3fc --- /dev/null +++ b/source/Lib/CommonLib/NNFilterSetLC.h @@ -0,0 +1,86 @@ +/* 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-2023, 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 NNFilterSetLC.h + \brief cnn loop filter class (header) +*/ + +#ifndef __NNFILTERSETLC__ +#define __NNFILTERSETLC__ + +#include "CommonDef.h" + +#if NN_FILTERING_SET_LC + +#include "Unit.h" +#include "Picture.h" +#include <fstream> + +using namespace std; + +// fwd +namespace sadl { +template<typename T> class Model; +template<typename T> class Tensor; +} + +class NNFilterSetLC +{ +public: + NNFilterSetLC(); + virtual ~NNFilterSetLC(); + + void filterPicture( PelUnitBuf in, PelUnitBuf bs_map, PelUnitBuf out, int model_selector = MAX_INT, int qp = MAX_INT); + + void filterPictureDecoder(CodingStructure& cs, PelUnitBuf in, PelUnitBuf out, PelUnitBuf recon, int model_selector = MAX_INT); + + void initNumCTUs(int m_numCTUsInPic); + void destroy(); + + void scaleResidue(PelUnitBuf recoBuf, PelUnitBuf filteredBuf, PicHeader *ph); + bool loadModel( std::string model_path ); + +public: + int m_NumCoresToUse = 1; + bool m_loadFlag; + std::vector<std::string> m_ModelPaths; + uint8_t *m_ctuEnableFlag[MAX_NUM_COMPONENT]; + uint16_t m_numCtusInFrame; + +protected: + std::unique_ptr<sadl::Model<TypeSadl>> m_Module[4]; + std::vector<sadl::Tensor<TypeSadl>> m_Input[4]; +}; +#endif + +#endif diff --git a/source/Lib/CommonLib/NNInference.cpp b/source/Lib/CommonLib/NNInference.cpp index bb606123f6..b72eff039d 100644 --- a/source/Lib/CommonLib/NNInference.cpp +++ b/source/Lib/CommonLib/NNInference.cpp @@ -52,7 +52,6 @@ void NNInference::infer(sadl::Model<int16_t> &model, std::vector<sadl::Tensor<in exit(-1); } } - #else template<> void NNInference::infer(sadl::Model<float> &model, std::vector<sadl::Tensor<float>> &inputs) diff --git a/source/Lib/CommonLib/Picture.cpp b/source/Lib/CommonLib/Picture.cpp index 3067e43e17..5c1a94cec3 100644 --- a/source/Lib/CommonLib/Picture.cpp +++ b/source/Lib/CommonLib/Picture.cpp @@ -209,6 +209,9 @@ void Picture::create( const ChromaFormat &_chromaFormat, const Size &size, const const Area a = Area( Position(), size ); M_BUFS( 0, PIC_RECONSTRUCTION ).create( _chromaFormat, a, _maxCUSize, margin, MEMORY_ALIGN_DEF_SIZE ); M_BUFS( 0, PIC_RECON_WRAP ).create( _chromaFormat, a, _maxCUSize, margin, MEMORY_ALIGN_DEF_SIZE ); +#if NN_FILTERING_SET_LC + M_BUFS( 0, PIC_DEC_LCNN_FILTERED ).create( _chromaFormat, a, _maxCUSize, margin, MEMORY_ALIGN_DEF_SIZE ); +#endif #if NNVC_USE_PARTITION_AS_CU_AVERAGE M_BUFS( 0, PIC_PARTITION_CU_AVERAGE ).create( _chromaFormat, a, _maxCUSize, margin, MEMORY_ALIGN_DEF_SIZE ); #endif @@ -396,7 +399,12 @@ const CPelUnitBuf Picture::getBsMapBuf(const UnitArea &unit) const PelBuf Picture::getBsMapBuf(const CompArea &blk) { return getBuf(blk, PIC_BS_MAP); } const CPelBuf Picture::getBsMapBuf(const CompArea &blk) const { return getBuf(blk, PIC_BS_MAP); } #endif - +#if NN_FILTERING_SET_LC + PelBuf Picture::getLCnnlfBuf(const ComponentID compID) { return getBuf(compID, PIC_DEC_LCNN_FILTERED); } +const CPelBuf Picture::getLCnnlfBuf(const ComponentID compID) const { return getBuf(compID, PIC_DEC_LCNN_FILTERED); } + PelUnitBuf Picture::getLCnnlfBuf() { return M_BUFS(0, PIC_DEC_LCNN_FILTERED); } +const CPelUnitBuf Picture::getLCnnlfBuf() const { return M_BUFS(0, PIC_DEC_LCNN_FILTERED); } +#endif #if NNVC_USE_REC_BEFORE_DBF PelBuf Picture::getRecBeforeDbfBuf(const ComponentID compID, bool /*wrap*/) { return getBuf(compID, PIC_REC_BEFORE_DBF); } PelUnitBuf Picture::getRecBeforeDbfBuf(bool /*wrap*/) { return M_BUFS(scheduler.getSplitPicId(), PIC_REC_BEFORE_DBF); } diff --git a/source/Lib/CommonLib/Picture.h b/source/Lib/CommonLib/Picture.h index f43fc75677..a8ffbd6c55 100644 --- a/source/Lib/CommonLib/Picture.h +++ b/source/Lib/CommonLib/Picture.h @@ -136,7 +136,12 @@ struct Picture : public UnitArea const CPelBuf getResiBuf(const CompArea &blk) const; PelUnitBuf getResiBuf(const UnitArea &unit); const CPelUnitBuf getResiBuf(const UnitArea &unit) const; - +#if NN_FILTERING_SET_LC + PelBuf getLCnnlfBuf(const ComponentID compID); + const CPelBuf getLCnnlfBuf(const ComponentID compID) const; + PelUnitBuf getLCnnlfBuf(); + const CPelUnitBuf getLCnnlfBuf() const; +#endif #if NNVC_USE_PARTITION_AS_CU_AVERAGE PelBuf getCuAverageBuf(const ComponentID compID, bool wrap=false); PelUnitBuf getCuAverageBuf(bool wrap=false); @@ -444,6 +449,33 @@ public: } #endif +#if NN_FILTERING_SET_LC + std::vector<uint8_t> m_LCnnlfCtuEnableFlag[MAX_NUM_COMPONENT]; + uint8_t* getLCnnlfCtuEnableFlag(int compIdx) { return m_LCnnlfCtuEnableFlag[compIdx].data(); } + std::vector<uint8_t>* getLCnnlfCtuEnableFlag() { return m_LCnnlfCtuEnableFlag; } + + void backupLCnnlfCtuEnableFlag( std::vector<uint8_t> *backup) { + for( auto i = 0; i < MAX_NUM_COMPONENT; ++i) + { + backup[i] = m_LCnnlfCtuEnableFlag[i]; + } + } + void restoreLCnnLFCtuEnableFlag( std::vector<uint8_t> *backup) { + for( auto i = 0; i < MAX_NUM_COMPONENT; ++i) + { + m_LCnnlfCtuEnableFlag[i] = backup[i]; + } + } + + void resizeLCnnlfCtuEnableFlag( int numEntries ) + { + for( int compIdx = 0; compIdx < MAX_NUM_COMPONENT; compIdx++ ) + { + m_LCnnlfCtuEnableFlag[compIdx].resize(numEntries); + std::fill( m_LCnnlfCtuEnableFlag[compIdx].begin(), m_LCnnlfCtuEnableFlag[compIdx].end(), 0 ); + } + } +#endif }; int calcAndPrintHashStatus(const CPelUnitBuf& pic, const class SEIDecodedPictureHash* pictureHashSEI, const BitDepths &bitDepths, const MsgLevel msgl); diff --git a/source/Lib/CommonLib/Rom.cpp b/source/Lib/CommonLib/Rom.cpp index 43b2a4a6fb..6c8d9f3799 100644 --- a/source/Lib/CommonLib/Rom.cpp +++ b/source/Lib/CommonLib/Rom.cpp @@ -49,6 +49,14 @@ std::string default_model_path = "./models/"; #endif +#if NN_FILTERING_SET_LC +std::string m_lcModelPath; +#if !NN_FIXED_POINT_IMPLEMENTATION +std::string default_lc_model_path = "models/NnlfSetLC/LC_float_model0.sadl,models/NnlfSetLC/LC_float_model1.sadl,models/NnlfSetLC/LC_float_model2.sadl,models/NnlfSetLC/LC_float_model3.sadl"; +#else +std::string default_lc_model_path = "models/NnlfSetLC/LC_int16_model0.sadl,models/NnlfSetLC/LC_int16_model1.sadl,models/NnlfSetLC/LC_int16_model2.sadl,models/NnlfSetLC/LC_int16_model3.sadl"; +#endif +#endif // ==================================================================================================================== // Initialize / destroy functions // ==================================================================================================================== diff --git a/source/Lib/CommonLib/Rom.h b/source/Lib/CommonLib/Rom.h index d56ab592d1..4cb3cfa218 100644 --- a/source/Lib/CommonLib/Rom.h +++ b/source/Lib/CommonLib/Rom.h @@ -52,6 +52,12 @@ extern std::string default_model_path; #endif +#if NN_FILTERING_SET_LC +// extern std::string g_LCModelPath; +extern std::string m_lcModelPath; +extern std::string default_lc_model_path; +#endif + // ==================================================================================================================== // Initialize / destroy functions // ==================================================================================================================== diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h index f05c0cb436..edeed538f6 100644 --- a/source/Lib/CommonLib/Slice.h +++ b/source/Lib/CommonLib/Slice.h @@ -1485,6 +1485,10 @@ private: bool m_nnlfSet0EnabledFlag; #endif +#if NN_FILTERING_SET_LC + bool m_nnlfSetLCEnabledFlag; +#endif + bool m_alfEnabledFlag; bool m_ccalfEnabledFlag; #if NN_FILTERING_SET_1 @@ -1777,6 +1781,10 @@ public: bool getNnlfSet1MultiframeEnabledFlag() const { return m_nnlfSet1MultiframeEnabledFlag; } void setNnlfSet1MultiframeEnabledFlag( bool b ) { m_nnlfSet1MultiframeEnabledFlag = b; } #endif +#endif +#if NN_FILTERING_SET_LC + bool getNnlfSetLCEnabledFlag() const { return m_nnlfSetLCEnabledFlag; } + void setNnlfSetLCEnabledFlag( bool b ) { m_nnlfSetLCEnabledFlag = b; } #endif void setJointCbCrEnabledFlag(bool bVal) { m_JointCbCrEnabledFlag = bVal; } bool getJointCbCrEnabledFlag() const { return m_JointCbCrEnabledFlag; } @@ -2431,6 +2439,12 @@ private: bool m_ccalfEnabledFlag[MAX_NUM_COMPONENT]; int m_ccalfCbApsId; int m_ccalfCrApsId; + +#if NN_FILTERING_SET_LC + int lc_nn_scale[MAX_NUM_COMPONENT]; + int lc_modelIdx; +#endif + bool m_deblockingFilterOverrideFlag; //!< deblocking filter override controls enabled bool m_deblockingFilterDisable; //!< deblocking filter disabled flag int m_deblockingFilterBetaOffsetDiv2; //!< beta offset for deblocking filter @@ -2550,6 +2564,14 @@ public: void setCcAlfEnabledFlag(ComponentID compId, bool b) { m_ccalfEnabledFlag[compId] = b; } bool getCcAlfEnabledFlag(ComponentID compId) const { return m_ccalfEnabledFlag[compId]; } +#if NN_FILTERING_SET_LC + void setLCNnScale(int sc, ComponentID id) { lc_nn_scale[id] = sc; } + int getLCNnScale(ComponentID id) const { return lc_nn_scale[id]; } + + void setLCNnModelIdx(int idx) { lc_modelIdx = idx; } + int getLCNnModelIdx() const { return lc_modelIdx; } +#endif + void setCcAlfCbApsId(int i) { m_ccalfCbApsId = i; } int getCcAlfCbApsId() const { return m_ccalfCbApsId; } void setCcAlfCrApsId(int i) { m_ccalfCrApsId = i; } @@ -2703,6 +2725,10 @@ private: bool m_biDirPred; int m_symRefIdx[2]; +#if NN_FILTERING_SET_LC + uint8_t m_LCnnlfSliceModeIdc[3]; +#endif + // Data int m_iSliceQpDelta; int m_iSliceChromaQpDelta[MAX_NUM_COMPONENT+1]; @@ -2859,6 +2885,12 @@ public: bool getPendingRasInit() const { return m_pendingRasInit; } void setPendingRasInit( bool val ) { m_pendingRasInit = val; } +#if NN_FILTERING_SET_LC + void setLCnnlfSliceModeIdc( uint8_t s, int compIdx ) { m_LCnnlfSliceModeIdc[compIdx] = s; } + uint8_t getLCnnlfSliceModeIdc( int compIdx ) { return m_LCnnlfSliceModeIdc[compIdx]; } + uint8_t* getLCnnlfSliceModeIdc() { return m_LCnnlfSliceModeIdc; } +#endif + void setLmcsEnabledFlag(bool b) { m_lmcsEnabledFlag = b; } bool getLmcsEnabledFlag() { return m_lmcsEnabledFlag; } const bool getLmcsEnabledFlag() const { return m_lmcsEnabledFlag; } diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h index c3cae8ce23..d42f174076 100644 --- a/source/Lib/CommonLib/TypeDef.h +++ b/source/Lib/CommonLib/TypeDef.h @@ -128,6 +128,24 @@ using TypeSadl = float; #endif #define JVET_AC0055_NN_POST_FILTERING 1 // JVET-AC0055: EE1-1.11: Content-adaptive post-filter +// nn filter set low complexity +#define NN_FILTERING_SET_LC 1 +// options set for lc NNLF +#if NN_FILTERING_SET_LC +#define REDUCE_MODEL_RD_I 1 + +// Constants +#define NN_LUMA_MODEL_NUMBER 4 +#define LCNN_MODEL_IDX_BITS 2 +#define LCNN_RESIDUE_SCALE_SHIFT 8 +#define BOUNDARY_EXT 8 + +#if NN_FIXED_POINT_IMPLEMENTATION +#define ORG_QUANTIZERS_IN 10 +#define SADL_QUANTIZERS_IN 11 +#define OUT_QUANTIZERS_IN 13 +#endif +#endif //########### place macros to be removed in next cycle below this line ############### diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp index 717d84b9e1..46f10c6e57 100644 --- a/source/Lib/DecoderLib/CABACReader.cpp +++ b/source/Lib/DecoderLib/CABACReader.cpp @@ -150,6 +150,42 @@ void CABACReader::coding_tree_unit( CodingStructure& cs, const UnitArea& area, i } #endif +#if NN_FILTERING_SET_LC + if( cs.sps->getNnlfSetLCEnabledFlag() && ctuRsAddr == 0) + { + uint32_t numCTUs = cs.pcv->sizeInCtus; + int frame_width_in_ctus = cs.pcv->widthInCtus; + for( int compIdx = 0; compIdx < MAX_NUM_COMPONENT; compIdx++ ) + { + uint8_t* ctbLCnnlfFlag = cs.slice->getPic()->getLCnnlfCtuEnableFlag( compIdx ); + const uint8_t *sliceModeIdc = cs.slice->getLCnnlfSliceModeIdc(); + for( int ctuIdx = 0; ctuIdx < numCTUs; ctuIdx++ ) + { + if (sliceModeIdc[compIdx] == LCNNLF_OFF) + { + ctbLCnnlfFlag[ctuIdx] = LCNNLF_OFF; + } + else if(sliceModeIdc[compIdx] == LCNNLF_ALL_ON) + { + ctbLCnnlfFlag[ctuIdx] = LCNNLF_ALL_ON; + } + else + { + CHECK(sliceModeIdc[compIdx] != LCNNLF_CTU_ONOFF, "slice mode idc should be LCNNLF_CTU_ONOFF"); + /* code */ + int ctxInc = 0; + const int leftCTUAddr = (ctuIdx%frame_width_in_ctus > 0) ? ctuIdx - 1 : -1; + const int aboveCTUAddr = (ctuIdx >= frame_width_in_ctus) ? ctuIdx - frame_width_in_ctus : -1; + if( leftCTUAddr >= 0 ) ctxInc += ((ctbLCnnlfFlag[leftCTUAddr] != LCNNLF_OFF) ? 1 : 0); + if( aboveCTUAddr >= 0 ) ctxInc += ((ctbLCnnlfFlag[aboveCTUAddr] != LCNNLF_OFF) ? 1 : 0); + RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET(STATS__CABAC_BITS__LCNNLF); + ctbLCnnlfFlag[ctuIdx] = m_BinDecoder.decodeBin( Ctx::ctbLCnnlfFlag( compIdx * MAX_NUM_COMPONENT + ctxInc) ); + } + } + } + } +#endif + sao( cs, ctuRsAddr ); #if NN_FILTERING_SET_1 if ( cs.sps->getNnlfSet1EnabledFlag() && ctuRsAddr == 0) diff --git a/source/Lib/DecoderLib/DecLib.cpp b/source/Lib/DecoderLib/DecLib.cpp index 81c8c8ab1f..fec65cd617 100644 --- a/source/Lib/DecoderLib/DecLib.cpp +++ b/source/Lib/DecoderLib/DecLib.cpp @@ -733,7 +733,30 @@ void DecLib::executeLoopFilters() m_pcNNFilterSet1.cnnFilter(m_pcPic); } #endif +#if NN_FILTERING_SET_LC + if (cs.sps->getNnlfSetLCEnabledFlag()) + { + if( !m_lcModelPath.empty() ) + { + m_lcNNLF.loadModel( m_lcModelPath ); + } + const int iPicWidth = cs.pps->getPicWidthInLumaSamples(); + const int iPicHeight = cs.pps->getPicHeightInLumaSamples(); + const unsigned int maxCUSize = cs.pps->pcv->maxCUWidth; + const int frameWidthInCtus = (iPicWidth % maxCUSize) ? iPicWidth / maxCUSize + 1 : iPicWidth / maxCUSize; + const int frameHeightInCtus = (iPicHeight % maxCUSize) ? iPicHeight / maxCUSize + 1 : iPicHeight / maxCUSize; + const int numCtusInFrame = frameWidthInCtus * frameHeightInCtus; + m_lcNNLF.initNumCTUs(numCtusInFrame); +#if NNVC_USE_REC_BEFORE_DBF + m_lcNNLF.filterPictureDecoder(cs, m_pcPic->getRecBeforeDbfBuf(), m_pcPic->getLCnnlfBuf(), m_pcPic->getRecoBuf(), m_pcPic->cs->picHeader->getLCNnModelIdx()); +#else + m_lcNNLF.filterPictureDecoder(cs, m_pcPic->getRecoBuf(), m_pcPic->getLCnnlfBuf(), m_pcPic->getRecoBuf(), m_pcPic->cs->picHeader->getLCNnModelIdx()); +#endif + m_lcNNLF.scaleResidue(m_pcPic->getRecoBuf(), m_pcPic->getLCnnlfBuf(), m_pcPic->cs->picHeader); + m_pcPic->getRecoBuf().copyFrom(m_pcPic->getLCnnlfBuf()); + } +#endif if( cs.sps->getSAOEnabledFlag() ) { m_cSAO.SAOProcess( cs, cs.picture->getSAO() ); diff --git a/source/Lib/DecoderLib/DecLib.h b/source/Lib/DecoderLib/DecLib.h index f5ae58e499..7808500919 100644 --- a/source/Lib/DecoderLib/DecLib.h +++ b/source/Lib/DecoderLib/DecLib.h @@ -66,6 +66,11 @@ #if JVET_AC0055_NN_POST_FILTERING #include "CommonLib/NNPostFilter.h" #endif + +#if NN_FILTERING_SET_LC +#include "CommonLib/NNFilterSetLC.h" +#endif + class InputNALUnit; //! \ingroup DecoderLib @@ -148,6 +153,10 @@ private: #endif #if JVET_AC0055_NN_POST_FILTERING NNPostFilter m_nnPostFilter; +#endif +#if NN_FILTERING_SET_LC + NNFilterSetLC m_lcNNLF; + // std::string m_lcModelPath; ///< LC model path #endif // decoder side RD cost computation RdCost m_cRdCost; ///< RD cost computation class diff --git a/source/Lib/DecoderLib/DecSlice.cpp b/source/Lib/DecoderLib/DecSlice.cpp index af13a30ab3..e5d4dae6a3 100644 --- a/source/Lib/DecoderLib/DecSlice.cpp +++ b/source/Lib/DecoderLib/DecSlice.cpp @@ -102,6 +102,9 @@ void DecSlice::decompressSlice( Slice* slice, InputBitstream* bitstream, int deb if (slice->getFirstCtuRsAddrInSlice() == 0) { +#if NN_FILTERING_SET_LC + cs.picture->resizeLCnnlfCtuEnableFlag( cs.pcv->sizeInCtus ); +#endif cs.picture->resizeAlfCtuEnableFlag( cs.pcv->sizeInCtus ); cs.picture->resizeAlfCtbFilterIndex(cs.pcv->sizeInCtus); cs.picture->resizeAlfCtuAlternative( cs.pcv->sizeInCtus ); diff --git a/source/Lib/DecoderLib/VLCReader.cpp b/source/Lib/DecoderLib/VLCReader.cpp index 5115b4a4de..197c2f8fc4 100644 --- a/source/Lib/DecoderLib/VLCReader.cpp +++ b/source/Lib/DecoderLib/VLCReader.cpp @@ -1725,7 +1725,7 @@ void HLSyntaxReader::parseSPS(SPS* pcSPS) READ_FLAG(uiCode, "sps_nnlf_enabled_flag"); pcSPS->setNnlfEnabledFlag ( uiCode ? true : false ); if (pcSPS->getNnlfEnabledFlag()) { - READ_FLAG(uiCode, "sps_nnlf_set"); pcSPS->setNnlfSet ( uiCode); + READ_UVLC(uiCode, "sps_nnlf_set"); pcSPS->setNnlfSet ( uiCode); } #if NN_FILTERING_SET_0 pcSPS->setNnlfSet0EnabledFlag(pcSPS->getNnlfEnabledFlag() && pcSPS->getNnlfSet() == 0 ? true : false); @@ -1733,6 +1733,9 @@ void HLSyntaxReader::parseSPS(SPS* pcSPS) #if NN_FILTERING_SET_1 pcSPS->setNnlfSet1EnabledFlag(pcSPS->getNnlfEnabledFlag() && pcSPS->getNnlfSet() == 1 ? true : false); #endif +#if NN_FILTERING_SET_LC + pcSPS->setNnlfSetLCEnabledFlag(pcSPS->getNnlfEnabledFlag() && pcSPS->getNnlfSet() == 2 ? true : false); +#endif #endif #if NN_FILTERING_SET_1 @@ -3332,7 +3335,27 @@ void HLSyntaxReader::parsePictureHeader( PicHeader* picHeader, ParameterSetManag picHeader->setSaoEnabledFlag(CHANNEL_TYPE_CHROMA, false); } +#if NN_FILTERING_SET_LC + if (sps->getNnlfSetLCEnabledFlag()) + { + READ_SCODE(LCNN_RESIDUE_SCALE_SHIFT+1, iCode, "nnScale_Y"); + picHeader->setLCNnScale(iCode + (1 << LCNN_RESIDUE_SCALE_SHIFT), COMPONENT_Y); + READ_SCODE(LCNN_RESIDUE_SCALE_SHIFT+1, iCode, "nnScale_Cb"); + picHeader->setLCNnScale(iCode + (1 << LCNN_RESIDUE_SCALE_SHIFT), COMPONENT_Cb); + READ_SCODE(LCNN_RESIDUE_SCALE_SHIFT+1, iCode, "nnScale_Cr"); + picHeader->setLCNnScale(iCode + (1 << LCNN_RESIDUE_SCALE_SHIFT), COMPONENT_Cr); + if(LCNN_MODEL_IDX_BITS > 0) + { + READ_CODE(LCNN_MODEL_IDX_BITS, uiCode, "nn_filter_idx"); + picHeader->setLCNnModelIdx(uiCode); + } + else + { + picHeader->setLCNnModelIdx(0); + } + } +#endif // deblocking filter controls if (pps->getDeblockingFilterControlPresentFlag()) @@ -4320,6 +4343,23 @@ void HLSyntaxReader::parseSliceHeader (Slice* pcSlice, PicHeader* picHeader, Par } } +#if NN_FILTERING_SET_LC + if (sps->getNnlfSetLCEnabledFlag()) + { + for( int compIdx = 0; compIdx < MAX_NUM_COMPONENT; compIdx++ ) + { + READ_FLAG( uiCode, "slice_cnnlf_mode_idc" ); + //fprintf(stderr, "slice_cnn_mode_idc =%d \n", uiCode); + pcSlice->setLCnnlfSliceModeIdc( (uiCode == 1) ? LCNNLF_CTU_ONOFF : LCNNLF_OFF, compIdx ); + if( pcSlice->getLCnnlfSliceModeIdc( compIdx) != LCNNLF_OFF ) + { + READ_FLAG( uiCode, "slice_cnnlf_luma_slice_all_on_flag" ); + //fprintf(stderr, "slice_cnn_luma_slice_all_on_flag =%d \n", uiCode); + pcSlice->setLCnnlfSliceModeIdc( (uiCode == 1) ? LCNNLF_ALL_ON : LCNNLF_CTU_ONOFF, compIdx ); + } + } + } +#endif if (pps->getDeblockingFilterControlPresentFlag()) { diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp index 48bad45e0a..630326eaf2 100644 --- a/source/Lib/EncoderLib/CABACWriter.cpp +++ b/source/Lib/EncoderLib/CABACWriter.cpp @@ -50,7 +50,7 @@ //! \ingroup EncoderLib //! \{ - +#if !NN_FILTERING_SET_LC void CABACWriter::initCtxModels( const Slice& slice ) { int qp = slice.getSliceQp(); @@ -62,7 +62,7 @@ void CABACWriter::initCtxModels( const Slice& slice ) } m_BinEncoder.reset( qp, (int)sliceType ); } - +#endif template <class BinProbModel> @@ -171,7 +171,19 @@ void CABACWriter::coding_tree_unit( CodingStructure& cs, const UnitArea& area, i } } #endif - +#if NN_FILTERING_SET_LC + if(cs.sps->getNnlfSetLCEnabledFlag() && ctuRsAddr == 0 && !skipSao) + { + for( int compIdx = 0; compIdx < MAX_NUM_COMPONENT; compIdx++ ) + { + uint32_t numCTUs = cs.pcv->sizeInCtus; + int frame_width_in_ctus = cs.pcv->widthInCtus; + uint8_t* ctbCnnFilterFlag = cs.slice->getPic()->getLCnnlfCtuEnableFlag( compIdx); + uint8_t* sliceModeIdc = cs.slice->getLCnnlfSliceModeIdc(); + codeLCnnlfCtuEnableFlags(numCTUs, frame_width_in_ctus, ctbCnnFilterFlag, ComponentID(compIdx), sliceModeIdc) ; + } + } +#endif if( !skipSao ) { sao( *cs.slice, ctuRsAddr ); diff --git a/source/Lib/EncoderLib/CABACWriter.h b/source/Lib/EncoderLib/CABACWriter.h index dbdb6f95f2..6ce4355632 100644 --- a/source/Lib/EncoderLib/CABACWriter.h +++ b/source/Lib/EncoderLib/CABACWriter.h @@ -59,7 +59,21 @@ public: virtual ~CABACWriter() {} public: +#if NN_FILTERING_SET_LC + void initCtxModels( const Slice& slice ) + { + int qp = slice.getSliceQp(); + SliceType sliceType = slice.getSliceType(); + SliceType encCABACTableIdx = slice.getEncCABACTableIdx(); + if( !slice.isIntra() && (encCABACTableIdx==B_SLICE || encCABACTableIdx==P_SLICE) && slice.getPPS()->getCabacInitPresentFlag() ) + { + sliceType = encCABACTableIdx; + } + m_BinEncoder.reset( qp, (int)sliceType ); + } +#else void initCtxModels ( const Slice& slice ); +#endif void setEncCu(EncCu* pcEncCu) { m_EncCu = pcEncCu; } SliceType getCtxInitId ( const Slice& slice ); void initBitstream ( OutputBitstream* bitstream ) { m_Bitstream = bitstream; m_BinEncoder.init( m_Bitstream ); } @@ -180,6 +194,28 @@ public: void codeNnlfSet1ParamIdx ( CodingStructure& cs, uint32_t ctuRsAddr, const int chal ); #endif +#if NN_FILTERING_SET_LC + // void codeLCnnlfCtuEnableFlags (uint32_t numCTUs, int frame_width_in_ctus, uint8_t* ctbCnnFilterFlag, ComponentID compID, uint8_t *cnnlfSliceModeIdc); + void codeLCnnlfCtuEnableFlags(uint32_t numCTUs, int frame_width_in_ctus, uint8_t* ctbCnnFilterFlag, ComponentID compID, uint8_t *cnnlfSliceModeIdc) + { + if (cnnlfSliceModeIdc[compID] == LCNNLF_OFF || cnnlfSliceModeIdc[compID] == LCNNLF_ALL_ON) + { + return; + } + else + { + for( int ctuIdx = 0; ctuIdx < numCTUs; ctuIdx++ ) + { + int ctxInc = 0; + const int leftCTUAddr = (ctuIdx%frame_width_in_ctus > 0) ? ctuIdx - 1 : -1; + const int aboveCTUAddr = (ctuIdx >= frame_width_in_ctus) ? ctuIdx - frame_width_in_ctus : -1; + if( leftCTUAddr >= 0 ) ctxInc += ((ctbCnnFilterFlag[leftCTUAddr] != LCNNLF_OFF) ? 1 : 0); + if( aboveCTUAddr >= 0 ) ctxInc += ((ctbCnnFilterFlag[aboveCTUAddr] != LCNNLF_OFF) ? 1 : 0); + m_BinEncoder.encodeBin( ctbCnnFilterFlag[ctuIdx], Ctx::ctbLCnnlfFlag( compID * MAX_NUM_COMPONENT + ctxInc ) ); + } + } + }; +#endif private: void unary_max_symbol ( unsigned symbol, unsigned ctxId0, unsigned ctxIdN, unsigned maxSymbol ); void unary_max_eqprob ( unsigned symbol, unsigned maxSymbol ); diff --git a/source/Lib/EncoderLib/CMakeLists.txt b/source/Lib/EncoderLib/CMakeLists.txt index a87d2b518a..d463cd590d 100644 --- a/source/Lib/EncoderLib/CMakeLists.txt +++ b/source/Lib/EncoderLib/CMakeLists.txt @@ -65,15 +65,18 @@ if( MSVC ) set_property( SOURCE EncNNFilter.cpp APPEND PROPERTY COMPILE_FLAGS "/arch:AVX2 -DNDEBUG=1 ") set_property( SOURCE EncNNFilterSet0.cpp APPEND PROPERTY COMPILE_FLAGS "/arch:AVX2 -DNDEBUG=1 ") set_property( SOURCE EncNNFilterSet1.cpp APPEND PROPERTY COMPILE_FLAGS "/arch:AVX2 -DNDEBUG=1 ") + set_property( SOURCE EncNNFilterSetLC.cpp APPEND PROPERTY COMPILE_FLAGS "/arch:AVX2 -DNDEBUG=1 ") elseif( UNIX OR MINGW ) if( NNLF_BUILD_WITH_AVX512 STREQUAL "1" ) set_property( SOURCE EncNNFilter.cpp APPEND PROPERTY COMPILE_FLAGS "-DNDEBUG=1 -mavx512f -mavx512bw -ffast-math") set_property( SOURCE EncNNFilterSet0.cpp APPEND PROPERTY COMPILE_FLAGS "-DNDEBUG=1 -mavx512f -mavx512bw -ffast-math") set_property( SOURCE EncNNFilterSet1.cpp APPEND PROPERTY COMPILE_FLAGS "-DNDEBUG=1 -mavx512f -mavx512bw -ffast-math") + set_property( SOURCE EncNNFilterSetLC.cpp APPEND PROPERTY COMPILE_FLAGS "-DNDEBUG=1 -mavx512f -mavx512bw -ffast-math") else() set_property( SOURCE EncNNFilter.cpp APPEND PROPERTY COMPILE_FLAGS "-DNDEBUG=1 -mavx2 -ffast-math") set_property( SOURCE EncNNFilterSet0.cpp APPEND PROPERTY COMPILE_FLAGS "-DNDEBUG=1 -mavx2 -ffast-math") set_property( SOURCE EncNNFilterSet1.cpp APPEND PROPERTY COMPILE_FLAGS "-DNDEBUG=1 -mavx2 -ffast-math") + set_property( SOURCE EncNNFilterSetLC.cpp APPEND PROPERTY COMPILE_FLAGS "-DNDEBUG=1 -mavx2 -ffast-math") endif() endif() diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h index 85403fd4ef..d422fb0a9b 100644 --- a/source/Lib/EncoderLib/EncCfg.h +++ b/source/Lib/EncoderLib/EncCfg.h @@ -826,6 +826,9 @@ protected: #if JVET_AC0055_NN_POST_FILTERING bool m_nnpf; #endif +#if NN_FILTERING_SET_LC + bool m_nnlfSetLC; +#endif #if JVET_O0756_CALCULATE_HDRMETRICS double m_whitePointDeltaE[hdrtoolslib::NB_REF_WHITE]; double m_maxSampleValue; @@ -2184,6 +2187,11 @@ public: bool getNnlfSet1UseMultiframe() const { return m_nnlfSet1Multiframe; } #endif #endif + +#if NN_FILTERING_SET_LC + void setUseNnlfSetLC(bool b) { m_nnlfSetLC = b; } +#endif + #if JVET_AC0055_NN_POST_FILTERING void setUseNnpf(bool b) { m_nnpf = b; } bool getUseNnpf() const { return m_nnpf; } diff --git a/source/Lib/EncoderLib/EncGOP.cpp b/source/Lib/EncoderLib/EncGOP.cpp index 41edbe1523..24f228a436 100644 --- a/source/Lib/EncoderLib/EncGOP.cpp +++ b/source/Lib/EncoderLib/EncGOP.cpp @@ -2811,6 +2811,11 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic, pcPic->resizeNnlfSet1ParamIdx( pcPic->cs->pcv->sizeInNnlfSet1InferSize ); #endif +#if NN_FILTERING_SET_LC + pcPic->resizeLCnnlfCtuEnableFlag( numberOfCtusInFrame ); + m_pcLCnnlf.create(m_pcEncLib->getCABACEncoder(), m_pcEncLib->getCtxCache(), pcSlice, m_pcCfg->getBitDepth()); +#endif + bool decPic = false; bool encPic = false; // test if we can skip the picture entirely or decode instead of encoding @@ -3165,6 +3170,103 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic, } #endif +#if NN_FILTERING_SET_LC + if ( cs.sps->getNnlfSetLCEnabledFlag() ) + { + if( !m_lcModelPath.empty() ) + { + m_pcLCnnlf.loadModel( m_lcModelPath ); + } + + double best_pic_cost = MAX_DOUBLE; + double curr_pic_cost = MAX_DOUBLE; + + int best_idx = -1; + + int nn_bestScale[MAX_NUM_COMPONENT]{MAX_INT}; + uint8_t cnnFilterSliceModeIdc[3]; + std::vector<uint8_t> cnnFilterCtuEnableFlag[MAX_NUM_COMPONENT]; + if(m_pcLCnnlf.m_cCnnTmpBufModelSelect.bufs.empty()) + { + CPelUnitBuf recoBuf = pcPic->getRecoBuf(); + ChromaFormat chromaFormat = recoBuf.chromaFormat; + Size sz = recoBuf.Y(); + Area area = Area( Position(0,0), sz); + m_pcLCnnlf.m_cCnnTmpBufModelSelect.create(chromaFormat, area, 0, 0, MEMORY_ALIGN_DEF_SIZE); + } + int start_idx = 0; + int one_after_end_idx = (int) m_pcLCnnlf.m_ModelPaths.size(); + + #if REDUCE_MODEL_RD_I + if(cs.slice->getSliceType() == I_SLICE) + one_after_end_idx /= 2; + #endif + + for( int modelIdx = start_idx; modelIdx < one_after_end_idx; modelIdx += 1) + { + const int iPicWidth = cs.pps->getPicWidthInLumaSamples(); + const int iPicHeight = cs.pps->getPicHeightInLumaSamples(); + const unsigned int maxCUSize = cs.pps->pcv->maxCUWidth; + const int frameWidthInCtus = (iPicWidth % maxCUSize) ? iPicWidth / maxCUSize + 1 : iPicWidth / maxCUSize; + const int frameHeightInCtus = (iPicHeight % maxCUSize) ? iPicHeight / maxCUSize + 1 : iPicHeight / maxCUSize; + const int numCtusInFrame = frameWidthInCtus * frameHeightInCtus; + m_pcLCnnlf.initNumCTUs(numCtusInFrame); + + PelUnitBuf origBuf = (pcSlice->getSPS()->getUseLmcs() || m_pcCfg->getGopBasedTemporalFilterEnabled()) + ? pcPic->getTrueOrigBuf() + : pcPic->getOrigBuf(); +#if NNVC_USE_REC_BEFORE_DBF + m_pcLCnnlf.filterPicture(pcPic->getRecBeforeDbfBuf(), pcPic->getBsMapBuf(), pcPic->getLCnnlfBuf(), modelIdx, cs.slice->getSliceQp()); +#else + m_pcLCnnlf.filterPicture(pcPic->getRecoBuf(), pcPic->getBsMapBuf(), pcPic->getLCnnlfBuf(), modelIdx, cs.slice->getSliceQp()); +#endif + m_pcLCnnlf.m_ctuEnableFlag[COMPONENT_Y] = cs.picture->getLCnnlfCtuEnableFlag(COMPONENT_Y); + m_pcLCnnlf.m_ctuEnableFlag[COMPONENT_Cb] = cs.picture->getLCnnlfCtuEnableFlag(COMPONENT_Cb); + m_pcLCnnlf.m_ctuEnableFlag[COMPONENT_Cr] = cs.picture->getLCnnlfCtuEnableFlag(COMPONENT_Cr); + + for (int idx = 0; idx < numCtusInFrame; idx++) + { + m_pcLCnnlf.m_ctuEnableFlag[COMPONENT_Y][idx] = 1; + m_pcLCnnlf.m_ctuEnableFlag[COMPONENT_Cb][idx] = 1; + m_pcLCnnlf.m_ctuEnableFlag[COMPONENT_Cr][idx] = 1; + } + // pcPic->getLCnnlfBuf().copyFrom(pcPic->getNnFilteredBackupBuf()); + m_pcLCnnlf.deriveScale(origBuf, pcPic->getRecoBuf(), pcPic->getLCnnlfBuf(), picHeader); + m_pcLCnnlf.scaleResidue(pcPic->getRecoBuf(), pcPic->getLCnnlfBuf(), picHeader); + uint8_t cnnSliceModeIdc[3] = { LCNNLF_OFF, LCNNLF_OFF, LCNNLF_OFF }; + curr_pic_cost = m_pcLCnnlf.filterPictureRD(cs, pcPic->getRecoBuf(), pcPic->getLCnnlfBuf(), origBuf, pcSlice->getLambdas(), cnnSliceModeIdc); + for( int s = 0; s < uiNumSliceSegments; s++ ) + { + pcPic->slices[s]->setLCnnlfSliceModeIdc( cnnSliceModeIdc[0], COMPONENT_Y ); + pcPic->slices[s]->setLCnnlfSliceModeIdc( cnnSliceModeIdc[1], COMPONENT_Cb ); + pcPic->slices[s]->setLCnnlfSliceModeIdc( cnnSliceModeIdc[2], COMPONENT_Cr ); + } + + if(curr_pic_cost < best_pic_cost) + { + best_idx = modelIdx; + best_pic_cost = curr_pic_cost; + m_pcLCnnlf.m_cCnnTmpBufModelSelect.copyFrom(pcPic->getLCnnlfBuf()); + for( auto i = 0; i < MAX_NUM_COMPONENT; ++i) + { + nn_bestScale[i] = pcSlice->getPicHeader()->getLCNnScale((ComponentID)i); + } + memcpy(cnnFilterSliceModeIdc, pcSlice->getLCnnlfSliceModeIdc(), sizeof(cnnFilterSliceModeIdc)); + cs.picture->backupLCnnlfCtuEnableFlag(cnnFilterCtuEnableFlag); + } + } // for( modelIdx ) + picHeader->setLCNnModelIdx(best_idx); + pcPic->getLCnnlfBuf().copyFrom(m_pcLCnnlf.m_cCnnTmpBufModelSelect); + for( auto i = 0; i < MAX_NUM_COMPONENT; ++i) + { + pcPic->cs->picHeader->setLCNnScale(nn_bestScale[i], (ComponentID)i); + } + memcpy(pcSlice->getLCnnlfSliceModeIdc(), cnnFilterSliceModeIdc, sizeof(cnnFilterSliceModeIdc)); + cs.picture->restoreLCnnLFCtuEnableFlag(cnnFilterCtuEnableFlag); + pcPic->getRecoBuf().copyFrom(pcPic->getLCnnlfBuf()); + } +#endif + if( pcSlice->getSPS()->getSAOEnabledFlag() ) { bool sliceEnabled[MAX_NUM_COMPONENT]; diff --git a/source/Lib/EncoderLib/EncGOP.h b/source/Lib/EncoderLib/EncGOP.h index d8519fff1d..d93c8417b1 100644 --- a/source/Lib/EncoderLib/EncGOP.h +++ b/source/Lib/EncoderLib/EncGOP.h @@ -88,6 +88,11 @@ #if JVET_AC0055_NN_POST_FILTERING #include "EncoderLib/EncNNPostFilter.h" #endif + +#if NN_FILTERING_SET_LC +#include "EncoderLib/EncNNFilterSetLC.h" +#endif + //! \ingroup EncoderLib //! \{ @@ -184,6 +189,10 @@ private: EncNNFilterSet0* m_pcCNNLF; #endif +#if NN_FILTERING_SET_LC + EncNNFilterSetLC m_pcLCnnlf; +#endif + EncReshape* m_pcReshaper; RateCtrl* m_pcRateCtrl; // indicate sequence first diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp index 94c2a9af12..643871c1db 100644 --- a/source/Lib/EncoderLib/EncLib.cpp +++ b/source/Lib/EncoderLib/EncLib.cpp @@ -1601,6 +1601,10 @@ void EncLib::xInitSPS( SPS& sps ) } #endif +#if NN_FILTERING_SET_LC + sps.setNnlfSetLCEnabledFlag(m_nnlfSetLC); +#endif + if (sps.getVuiParametersPresentFlag()) { VUI* pcVUI = sps.getVuiParameters(); diff --git a/source/Lib/EncoderLib/EncNNFilterSet1.cpp b/source/Lib/EncoderLib/EncNNFilterSet1.cpp index 3bf6468621..298d368a56 100644 --- a/source/Lib/EncoderLib/EncNNFilterSet1.cpp +++ b/source/Lib/EncoderLib/EncNNFilterSet1.cpp @@ -160,7 +160,7 @@ void EncNNFilterSet1::cnnFilterPicture(Picture* pic, int numParams) if (chal == 0) { #if NN_FIXED_POINT_IMPLEMENTATION - cnnFilterLumaBlock<int16_t>(pic, extBlock, extLeft, extRight, extTop, extBottom, paramIdx, pcSlice->getSliceType() != I_SLICE); + cnnFilterLumaBlock<TypeSadl>(pic, extBlock, extLeft, extRight, extTop, extBottom, paramIdx, pcSlice->getSliceType() != I_SLICE); #else cnnFilterLumaBlock<float>(pic, extBlock, extLeft, extRight, extTop, extBottom, paramIdx, pcSlice->getSliceType() != I_SLICE); #endif @@ -168,7 +168,7 @@ void EncNNFilterSet1::cnnFilterPicture(Picture* pic, int numParams) else { #if NN_FIXED_POINT_IMPLEMENTATION - cnnFilterChromaBlock<int16_t>(pic, extBlock, extLeft >> 1, extRight >> 1, extTop >> 1, extBottom >> 1, paramIdx, pcSlice->getSliceType() != I_SLICE); + cnnFilterChromaBlock<TypeSadl>(pic, extBlock, extLeft >> 1, extRight >> 1, extTop >> 1, extBottom >> 1, paramIdx, pcSlice->getSliceType() != I_SLICE); #else cnnFilterChromaBlock<float>(pic, extBlock, extLeft >> 1, extRight >> 1, extTop >> 1, extBottom >> 1, paramIdx, pcSlice->getSliceType() != I_SLICE); #endif diff --git a/source/Lib/EncoderLib/EncNNFilterSetLC.cpp b/source/Lib/EncoderLib/EncNNFilterSetLC.cpp new file mode 100644 index 0000000000..b3be05f5cb --- /dev/null +++ b/source/Lib/EncoderLib/EncNNFilterSetLC.cpp @@ -0,0 +1,523 @@ +/* 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-2023, 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 NNFilterSetLC.cpp + \brief Encoder low complexity cnn loop filter +*/ + +#define HAVE_INTTYPES_H 1 +#define __STDC_FORMAT_MACROS +#include "CABACWriter.h" +#include <sadl/model.h> +#include "CommonLib/NNInference.h" +#include "CommonLib/NNFilterSetLC.h" +#include "EncNNFilterSetLC.h" + +#if NN_FILTERING_SET_LC +#include "CodingStructure.h" +#include "Picture.h" + +#define CnnFilterCtx( c ) SubCtx( Ctx::ctbLCnnlfFlag, c ) + +EncNNFilterSetLC::EncNNFilterSetLC() + : m_CABACEstimator(nullptr) +{ +} + + +void EncNNFilterSetLC::create(CABACEncoder* cabacEncoder, CtxCache* ctxCache, Slice* pcSlice, const int inputBitDepth[MAX_NUM_CHANNEL_TYPE]) +{ + m_CABACEstimator = cabacEncoder->getCABACEstimator( pcSlice->getSPS() ); + m_CtxCache = ctxCache; + m_CABACEstimator->initCtxModels ( *pcSlice ); + m_CABACEstimator->resetBits(); + std::memcpy( m_inputBitDepth, inputBitDepth, sizeof( m_inputBitDepth ) ); +} + + +#define FRAME_RD_Y_MULTIPLIER 2.0 + +double EncNNFilterSetLC::filterPictureRD(CodingStructure &cs, PelUnitBuf in, PelUnitBuf out, PelUnitBuf original, const double *lambdas, uint8_t *cnnlfSliceModeIdc) +{ + int blockSize = 64; + PelBuf reconBufY = in.get(COMPONENT_Y); + PelBuf reconBufCb = in.get(COMPONENT_Cb); + PelBuf reconBufCr = in.get(COMPONENT_Cr); + PelBuf nnFilteredBufY = out.get(COMPONENT_Y); + PelBuf nnFilteredBufCb = out.get(COMPONENT_Cb); + PelBuf nnFilteredBufCr = out.get(COMPONENT_Cr); + + PelBuf OriginalBufY = original.get(COMPONENT_Y); + PelBuf OriginalBufCb = original.get(COMPONENT_Cb); + PelBuf OriginalBufCr = original.get(COMPONENT_Cr); + + int shiftLuma = 2 * DISTORTION_PRECISION_ADJUSTMENT(m_inputBitDepth[CHANNEL_TYPE_LUMA]); + int shiftChroma = 2 * DISTORTION_PRECISION_ADJUSTMENT(m_inputBitDepth[CHANNEL_TYPE_CHROMA]); + m_lambda[COMPONENT_Y] = lambdas[COMPONENT_Y] * double(1 << shiftLuma); + m_lambda[COMPONENT_Cb] = lambdas[COMPONENT_Cb] * double(1 << shiftChroma); + m_lambda[COMPONENT_Cr] = lambdas[COMPONENT_Cr] * double(1 << shiftChroma); + + int pic_w = reconBufY.width; + int pic_h = reconBufY.height; + + int ctuIdx = 0; + /* Frame level SSD params */ + + /* Luma (Y) */ + double recFrmSSDY = 0; // All Off + double nnFltFrmSSDY = 0; // All On + double bestFrmSSDY = 0; //On/Off, tracks the best SSD for each CTU + + /* Chroma (Cb) */ + double recFrmSSDCb = 0; // All Off + double nnFltFrmSSDCb = 0; // All On + double bestFrmSSDCb = 0; //On/Off, tracks the best SSD for each CTU + + /* Chroma (Cr) */ + double recFrmSSDCr = 0; // All Off + double nnFltFrmSSDCr = 0; // All On + double bestFrmSSDCr = 0; //On/Off, tracks the best SSD for each CTU + + for (int y = 0; y < pic_h; y += blockSize * 2) + { + for (int x = 0; x < pic_w; x += blockSize * 2) + { + uint32_t recBLKSSDY = 0; + uint32_t nnFltBLKSSDY = 0; + uint32_t recBLKSSDCb = 0; + uint32_t nnFltBLKSSDCb = 0; + uint32_t recBLKSSDCr = 0; + uint32_t nnFltBLKSSDCr = 0; + + for (int i = 0; i < blockSize; ++i) + { + for (int j = 0; j < blockSize; ++j) + { + int pos_x = x / 2 + j; + int pos_y = y / 2 + i; + if (pos_x >= pic_w / 2 || pos_y >= pic_h / 2) + { + continue; + } + // Luma SSD + + recBLKSSDY += (reconBufY.at(pos_x * 2, pos_y * 2) - OriginalBufY.at(pos_x * 2, pos_y * 2)) + * (reconBufY.at(pos_x * 2, pos_y * 2) + - OriginalBufY.at(pos_x * 2, pos_y * 2)); // SSD , sum of squared difference + nnFltBLKSSDY += (nnFilteredBufY.at(pos_x * 2, pos_y * 2) - OriginalBufY.at(pos_x * 2, pos_y * 2)) + * (nnFilteredBufY.at(pos_x * 2, pos_y * 2) - OriginalBufY.at(pos_x * 2, pos_y * 2)); + recBLKSSDY += (reconBufY.at(pos_x * 2 + 1, pos_y * 2) - OriginalBufY.at(pos_x * 2 + 1, pos_y * 2)) + * (reconBufY.at(pos_x * 2 + 1, pos_y * 2) + - OriginalBufY.at(pos_x * 2 + 1, pos_y * 2)); // SSD , sum of squared difference + nnFltBLKSSDY += (nnFilteredBufY.at(pos_x * 2 + 1, pos_y * 2) - OriginalBufY.at(pos_x * 2 + 1, pos_y * 2)) + * (nnFilteredBufY.at(pos_x * 2 + 1, pos_y * 2) - OriginalBufY.at(pos_x * 2 + 1, pos_y * 2)); + recBLKSSDY += (reconBufY.at(pos_x * 2, pos_y * 2 + 1) - OriginalBufY.at(pos_x * 2, pos_y * 2 + 1)) + * (reconBufY.at(pos_x * 2, pos_y * 2 + 1) + - OriginalBufY.at(pos_x * 2, pos_y * 2 + 1)); // SSD , sum of squared difference + nnFltBLKSSDY += (nnFilteredBufY.at(pos_x * 2, pos_y * 2 + 1) - OriginalBufY.at(pos_x * 2, pos_y * 2 + 1)) + * (nnFilteredBufY.at(pos_x * 2, pos_y * 2 + 1) - OriginalBufY.at(pos_x * 2, pos_y * 2 + 1)); + recBLKSSDY += (reconBufY.at(pos_x * 2 + 1, pos_y * 2 + 1) - OriginalBufY.at(pos_x * 2 + 1, pos_y * 2 + 1)) + * (reconBufY.at(pos_x * 2 + 1, pos_y * 2 + 1) + - OriginalBufY.at(pos_x * 2 + 1, pos_y * 2 + 1)); // SSD , sum of squared difference + nnFltBLKSSDY += + (nnFilteredBufY.at(pos_x * 2 + 1, pos_y * 2 + 1) - OriginalBufY.at(pos_x * 2 + 1, pos_y * 2 + 1)) + * (nnFilteredBufY.at(pos_x * 2 + 1, pos_y * 2 + 1) - OriginalBufY.at(pos_x * 2 + 1, pos_y * 2 + 1)); + + // Chroma SSD + recBLKSSDCb += + (reconBufCb.at(pos_x, pos_y) - OriginalBufCb.at(pos_x, pos_y)) + * (reconBufCb.at(pos_x, pos_y) - OriginalBufCb.at(pos_x, pos_y)); // SSD , sum of squared difference + nnFltBLKSSDCb += (nnFilteredBufCb.at(pos_x, pos_y) - OriginalBufCb.at(pos_x, pos_y)) + * (nnFilteredBufCb.at(pos_x, pos_y) - OriginalBufCb.at(pos_x, pos_y)); + + recBLKSSDCr += + (reconBufCr.at(pos_x, pos_y) - OriginalBufCr.at(pos_x, pos_y)) + * (reconBufCr.at(pos_x, pos_y) - OriginalBufCr.at(pos_x, pos_y)); // SSD , sum of squared difference + nnFltBLKSSDCr += (nnFilteredBufCr.at(pos_x, pos_y) - OriginalBufCr.at(pos_x, pos_y)) + * (nnFilteredBufCr.at(pos_x, pos_y) - OriginalBufCr.at(pos_x, pos_y)); + } + } + /* AVMBKJ + 1) get the RD for all off + 2) get the RD for all ON + 3) get the RD for ON/OFF */ + recFrmSSDY += recBLKSSDY; + recFrmSSDCb += recBLKSSDCb; + recFrmSSDCr += recBLKSSDCr; + + nnFltFrmSSDY += nnFltBLKSSDY; + nnFltFrmSSDCb += nnFltBLKSSDCb; + nnFltFrmSSDCr += nnFltBLKSSDCr; + + if (recBLKSSDY <= nnFltBLKSSDY) + { + bestFrmSSDY += recBLKSSDY; + m_ctuEnableFlag[COMPONENT_Y][ctuIdx] = 0; + + } + else + { + bestFrmSSDY += nnFltBLKSSDY; + m_ctuEnableFlag[COMPONENT_Y][ctuIdx] = 1; + } + + if (recBLKSSDCb <= nnFltBLKSSDCb) + { + bestFrmSSDCb += recBLKSSDCb; + m_ctuEnableFlag[COMPONENT_Cb][ctuIdx] = 0; + } + else + { + bestFrmSSDCb += nnFltBLKSSDCb; + m_ctuEnableFlag[COMPONENT_Cb][ctuIdx] = 1; + } + if (recBLKSSDCr <= nnFltBLKSSDCr) + { + bestFrmSSDCr += recBLKSSDCr; + m_ctuEnableFlag[COMPONENT_Cr][ctuIdx] = 0; + } + else + { + bestFrmSSDCr += nnFltBLKSSDCr; + m_ctuEnableFlag[COMPONENT_Cr][ctuIdx] = 1; + } + CHECK(ctuIdx >= m_numCtusInFrame, "ctuIdx should be less than no. of CTUs in frame"); + ctuIdx++; + } + } + /* slice level decision */ + /* calculate RD for all off case, All off case consumes 1 bit at frame level*/ + + + /* Luma case */ + double rate_all_off_Y = 1; + double costAllOff_Y = recFrmSSDY + m_lambda[COMPONENT_Y] * rate_all_off_Y; + double rate_all_on_Y = 2; // All on needs 2 bits for signalling at frame level + double costAllOn_Y = nnFltFrmSSDY + m_lambda[COMPONENT_Y] * rate_all_on_Y; + double bestPicCost = 0.0; + + TempCtx ctxStart( m_CtxCache, CnnFilterCtx( m_CABACEstimator->getCtx() ) ); + m_CABACEstimator->getCtx() = CnnFilterCtx( ctxStart ); + m_CABACEstimator->resetBits(); + uint32_t numCTUs = cs.pcv->sizeInCtus; + int frame_width_in_ctus = cs.pcv->widthInCtus; + uint8_t* ctbCnnFilterFlag = cs.slice->getPic()->getLCnnlfCtuEnableFlag( COMPONENT_Y); + cnnlfSliceModeIdc[COMPONENT_Y] = LCNNLF_CTU_ONOFF; /* set this to get rate for transmitting CTU bits */ + m_CABACEstimator->codeLCnnlfCtuEnableFlags(numCTUs, frame_width_in_ctus, ctbCnnFilterFlag, ComponentID(COMPONENT_Y), cnnlfSliceModeIdc); + + + double cost_on_off_Y = bestFrmSSDY + m_lambda[COMPONENT_Y] * (rate_all_on_Y + (FracBitsScale * (double)m_CABACEstimator->getEstFracBits())); + + if (costAllOff_Y <= costAllOn_Y && costAllOff_Y <= cost_on_off_Y) + { + cnnlfSliceModeIdc[COMPONENT_Y] = LCNNLF_OFF; + for( int ctuIdx = 0; ctuIdx < numCTUs; ctuIdx++ ) + { + m_ctuEnableFlag[COMPONENT_Y][ctuIdx] = 0; + } + bestPicCost += costAllOff_Y * (FRAME_RD_Y_MULTIPLIER); + } + else if(costAllOn_Y <= costAllOff_Y && costAllOn_Y <= cost_on_off_Y) + { + cnnlfSliceModeIdc[COMPONENT_Y] = LCNNLF_ALL_ON; + for( int ctuIdx = 0; ctuIdx < numCTUs; ctuIdx++ ) + { + m_ctuEnableFlag[COMPONENT_Y][ctuIdx] = 1; + } + bestPicCost += costAllOn_Y * (FRAME_RD_Y_MULTIPLIER); + } + else + { + cnnlfSliceModeIdc[COMPONENT_Y] = LCNNLF_CTU_ONOFF; + /* CTU flags shall reamin the same as was set before */ + bestPicCost += cost_on_off_Y * (FRAME_RD_Y_MULTIPLIER); + } + + /* Cb case */ + double rate_all_off_Cb = 1; + double costAllOff_Cb = recFrmSSDCb + m_lambda[COMPONENT_Cb] * rate_all_off_Cb; + double rate_all_on_Cb = 2; // All on needs 2 bits for signalling at frame level + double costAllOn_Cb = nnFltFrmSSDCb + m_lambda[COMPONENT_Cb] * rate_all_on_Cb; + + m_CABACEstimator->getCtx() = CnnFilterCtx( ctxStart ); + m_CABACEstimator->resetBits(); + + ctbCnnFilterFlag = cs.slice->getPic()->getLCnnlfCtuEnableFlag( COMPONENT_Cb); + cnnlfSliceModeIdc[COMPONENT_Cb] = LCNNLF_CTU_ONOFF; + m_CABACEstimator->codeLCnnlfCtuEnableFlags(numCTUs, frame_width_in_ctus, ctbCnnFilterFlag, ComponentID(COMPONENT_Cb), cnnlfSliceModeIdc); + + double cost_on_off_Cb = bestFrmSSDCb + m_lambda[COMPONENT_Cb] * (rate_all_on_Cb + (FracBitsScale * (double)m_CABACEstimator->getEstFracBits())); + + if (costAllOff_Cb <= costAllOn_Cb && costAllOff_Cb <= cost_on_off_Cb) + { + cnnlfSliceModeIdc[COMPONENT_Cb] = LCNNLF_OFF; + for( int ctuIdx = 0; ctuIdx < numCTUs; ctuIdx++ ) + { + m_ctuEnableFlag[COMPONENT_Cb][ctuIdx] = 0; + } + bestPicCost += costAllOff_Cb; + + } + else if(costAllOn_Cb <= costAllOff_Cb && costAllOn_Cb <= cost_on_off_Cb) + { + cnnlfSliceModeIdc[COMPONENT_Cb] = LCNNLF_ALL_ON; + for( int ctuIdx = 0; ctuIdx < numCTUs; ctuIdx++ ) + { + m_ctuEnableFlag[COMPONENT_Cb][ctuIdx] = 1; + } + bestPicCost += costAllOn_Cb; + } + else + { + cnnlfSliceModeIdc[COMPONENT_Cb] = LCNNLF_CTU_ONOFF; + bestPicCost += cost_on_off_Cb; + } + + + /* Cr case */ + double rate_all_off_Cr = 1; + double costAllOff_Cr = recFrmSSDCr + m_lambda[COMPONENT_Cr] * rate_all_off_Cr; + double rate_all_on_Cr = 2; // All on needs 2 bits for signalling at frame level + double costAllOn_Cr = nnFltFrmSSDCr + m_lambda[COMPONENT_Cr] * rate_all_on_Cr; + + m_CABACEstimator->getCtx() = CnnFilterCtx( ctxStart ); + m_CABACEstimator->resetBits(); + ctbCnnFilterFlag = cs.slice->getPic()->getLCnnlfCtuEnableFlag( COMPONENT_Cr); + cnnlfSliceModeIdc[COMPONENT_Cr] = LCNNLF_CTU_ONOFF; /* set this to get rate for transmitting CTU bits */ + m_CABACEstimator->codeLCnnlfCtuEnableFlags(numCTUs, frame_width_in_ctus, ctbCnnFilterFlag, ComponentID(COMPONENT_Cr), cnnlfSliceModeIdc); + double cost_on_off_Cr = bestFrmSSDCr + m_lambda[COMPONENT_Cr] * (rate_all_on_Cr + (FracBitsScale * (double)m_CABACEstimator->getEstFracBits())); + + if (costAllOff_Cr <= costAllOn_Cr && costAllOff_Cr <= cost_on_off_Cr) + { + cnnlfSliceModeIdc[COMPONENT_Cr] = LCNNLF_OFF; + for( int ctuIdx = 0; ctuIdx < numCTUs; ctuIdx++ ) + { + m_ctuEnableFlag[COMPONENT_Cr][ctuIdx] = 0; + } + bestPicCost += costAllOff_Cr; + /* reset buf later on */ + } + else if(costAllOn_Cr <= costAllOff_Cr && costAllOn_Cr <= cost_on_off_Cr) + { + cnnlfSliceModeIdc[COMPONENT_Cr] = LCNNLF_ALL_ON; + for( int ctuIdx = 0; ctuIdx < numCTUs; ctuIdx++ ) + { + m_ctuEnableFlag[COMPONENT_Cr][ctuIdx] = 1; + } + bestPicCost += costAllOn_Cr; + } + else + { + cnnlfSliceModeIdc[COMPONENT_Cr] = LCNNLF_CTU_ONOFF; + bestPicCost += cost_on_off_Cr; + } + + /* reset the buffers over here */ + ctuIdx = 0; + for( int y = 0; y < pic_h; y += blockSize * 2) + { + for( int x = 0; x < pic_w; x+=blockSize*2) + { + for (int i = 0; i < blockSize; ++i) + { + for (int j = 0; j < blockSize; ++j) + { + int pos_x = x / 2 + j; + int pos_y = y / 2 + i; + if (pos_x >= pic_w / 2 || pos_y >= pic_h / 2) + { + continue; + } + + if (m_ctuEnableFlag[COMPONENT_Y][ctuIdx] == 0) + { + nnFilteredBufY.at(pos_x * 2, pos_y * 2) = reconBufY.at(pos_x * 2, pos_y * 2); + nnFilteredBufY.at(pos_x * 2 + 1, pos_y * 2) = reconBufY.at(pos_x * 2 + 1, pos_y * 2); + nnFilteredBufY.at(pos_x * 2, pos_y * 2 + 1) = reconBufY.at(pos_x * 2, pos_y * 2 + 1); + nnFilteredBufY.at(pos_x * 2 + 1, pos_y * 2 + 1) = reconBufY.at(pos_x * 2 + 1, pos_y * 2 + 1); + } + if (m_ctuEnableFlag[COMPONENT_Cb][ctuIdx] == 0) + { + nnFilteredBufCb.at(pos_x, pos_y) = reconBufCb.at(pos_x, pos_y); + } + if (m_ctuEnableFlag[COMPONENT_Cr][ctuIdx] == 0) + { + nnFilteredBufCr.at(pos_x, pos_y) = reconBufCr.at(pos_x, pos_y); + } + } + } + ctuIdx++; + } + } + return bestPicCost; + +} + + +#define NN_SCALE_STABLIZING_FACTOR (0.1 * (1<< NN_RESIDUE_ADDITIONAL_SHIFT)) +#define NN_RESIDUE_SCALE_DEVIATION_UP_BOUND 1.25 +#define NN_RESIDUE_SCALE_DEVIATION_BOT_BOUND 0.0625 + +void EncNNFilterSetLC::deriveScale(PelUnitBuf origBuf, PelUnitBuf recoBuf, PelUnitBuf filteredBuf, PicHeader *ph) +{ + PelBuf origBufY = origBuf.get(COMPONENT_Y); + PelBuf origBufCb = origBuf.get(COMPONENT_Cb); + PelBuf origBufCr = origBuf.get(COMPONENT_Cr); + PelBuf reconBufY = recoBuf.get(COMPONENT_Y); + PelBuf reconBufCb = recoBuf.get(COMPONENT_Cb); + PelBuf reconBufCr = recoBuf.get(COMPONENT_Cr); + PelBuf nnFilteredBufY = filteredBuf.get(COMPONENT_Y); + PelBuf nnFilteredBufCb = filteredBuf.get(COMPONENT_Cb); + PelBuf nnFilteredBufCr = filteredBuf.get(COMPONENT_Cr); + + int pic_w = reconBufY.width; + int pic_h = reconBufY.height; + int pic_wh = pic_w*pic_h; + + int scaleY = 0, scaleCb = 0, scaleCr = 0; + int shiftY = LCNN_RESIDUE_SCALE_SHIFT; + int shiftCb = LCNN_RESIDUE_SCALE_SHIFT; + int shiftCr = LCNN_RESIDUE_SCALE_SHIFT; + + int scaleUpBoundY = int(NN_RESIDUE_SCALE_DEVIATION_UP_BOUND * (1 << shiftY)); + int scaleLowBoundY = int(NN_RESIDUE_SCALE_DEVIATION_BOT_BOUND * (1 << shiftY)); + + int scaleUpBoundC = int(NN_RESIDUE_SCALE_DEVIATION_UP_BOUND * (1 << shiftCb)); + int scaleLowBoundC = int(NN_RESIDUE_SCALE_DEVIATION_BOT_BOUND * (1 << shiftCb)); + + + double selfMulti[MAX_NUM_COMPONENT] = {0., 0., 0.}; + double crossMulti[MAX_NUM_COMPONENT] = {0., 0., 0.}; + double sumOriResi[MAX_NUM_COMPONENT] = {0., 0., 0.}; + double sumNnResi[MAX_NUM_COMPONENT] = {0., 0., 0.}; + + int blockSize = 64; + int ctuStride = (pic_w + 2 * blockSize - 1) / (2 * blockSize); + + for (int y = 0; y < pic_h; y++) + { + for (int x = 0; x < pic_w; x++) + { + int ctuIdx = (y / (2 * blockSize)) * ctuStride + x / (2 * blockSize); + if (m_ctuEnableFlag[COMPONENT_Y][ctuIdx] == 1) + { + int oriResi = (origBufY.at(x, y) - reconBufY.at(x, y)) << NN_RESIDUE_ADDITIONAL_SHIFT; + int nnResi = nnFilteredBufY.at(x, y) - (reconBufY.at(x, y) << NN_RESIDUE_ADDITIONAL_SHIFT); + + selfMulti[COMPONENT_Y] += nnResi * nnResi; + crossMulti[COMPONENT_Y] += nnResi * oriResi; + sumOriResi[COMPONENT_Y] += oriResi; + sumNnResi[COMPONENT_Y] += nnResi; + } + } + } + + scaleY = int(((pic_wh * crossMulti[COMPONENT_Y] - sumOriResi[COMPONENT_Y] * sumNnResi[COMPONENT_Y] + pic_wh * pic_wh * NN_SCALE_STABLIZING_FACTOR) + / (pic_wh * selfMulti[COMPONENT_Y] - sumNnResi[COMPONENT_Y] * sumNnResi[COMPONENT_Y] + pic_wh * pic_wh * NN_SCALE_STABLIZING_FACTOR)) + * (1 << shiftY) + 0.5); + + if (scaleY > scaleUpBoundY) + { + scaleY = scaleUpBoundY; + } + if (scaleY < scaleLowBoundY) + { + scaleY = scaleLowBoundY; + } + + for (int y = 0; y < pic_h/2; y++) + { + for (int x = 0; x < pic_w/2; x++) + { + int ctuIdx = (y / blockSize) * ctuStride + x / blockSize; + if (m_ctuEnableFlag[COMPONENT_Cb][ctuIdx] == 1) + { + int oriResiCb = (origBufCb.at(x, y) - reconBufCb.at(x, y)) << NN_RESIDUE_ADDITIONAL_SHIFT; + int nnResiCb = nnFilteredBufCb.at(x, y) - (reconBufCb.at(x, y) << NN_RESIDUE_ADDITIONAL_SHIFT); + + selfMulti[COMPONENT_Cb] += nnResiCb*nnResiCb; + crossMulti[COMPONENT_Cb] += nnResiCb*oriResiCb; + sumOriResi[COMPONENT_Cb] += oriResiCb; + sumNnResi[COMPONENT_Cb] += nnResiCb; + } + + if (m_ctuEnableFlag[COMPONENT_Cr][ctuIdx] == 1) + { + int oriResiCr = (origBufCr.at(x, y) - reconBufCr.at(x, y)) << NN_RESIDUE_ADDITIONAL_SHIFT; + int nnResiCr = nnFilteredBufCr.at(x, y) - (reconBufCr.at(x, y) << NN_RESIDUE_ADDITIONAL_SHIFT); + + + selfMulti[COMPONENT_Cr] += nnResiCr*nnResiCr; + crossMulti[COMPONENT_Cr] += nnResiCr*oriResiCr; + sumOriResi[COMPONENT_Cr] += oriResiCr; + sumNnResi[COMPONENT_Cr] += nnResiCr; + } + } + } + + scaleCb = int(((pic_wh / 4 * crossMulti[COMPONENT_Cb] - sumOriResi[COMPONENT_Cb] * sumNnResi[COMPONENT_Cb] + pic_wh / 4 * pic_wh / 4 * NN_SCALE_STABLIZING_FACTOR) + / (pic_wh / 4 * selfMulti[COMPONENT_Cb] - sumNnResi[COMPONENT_Cb] * sumNnResi[COMPONENT_Cb] + pic_wh / 4 * pic_wh / 4 * NN_SCALE_STABLIZING_FACTOR)) + * (1 << shiftCb) + 0.5); + + if (scaleCb > scaleUpBoundC) + { + scaleCb = scaleUpBoundC; + } + if (scaleCb < scaleLowBoundC) + { + scaleCb = scaleLowBoundC; + } + + + scaleCr = int(((pic_wh / 4 * crossMulti[COMPONENT_Cr] - sumOriResi[COMPONENT_Cr] * sumNnResi[COMPONENT_Cr] + pic_wh / 4 * pic_wh / 4 * NN_SCALE_STABLIZING_FACTOR) + / (pic_wh / 4 * selfMulti[COMPONENT_Cr] - sumNnResi[COMPONENT_Cr] * sumNnResi[COMPONENT_Cr] + pic_wh / 4 * pic_wh / 4 * NN_SCALE_STABLIZING_FACTOR)) + * (1 << shiftCr) + 0.5); + + if (scaleCr > scaleUpBoundC) + { + scaleCr = scaleUpBoundC; + } + if (scaleCr < scaleLowBoundC) + { + scaleCr = scaleLowBoundC; + } + + ph->setLCNnScale(scaleY, COMPONENT_Y); + + ph->setLCNnScale(scaleCb, COMPONENT_Cb); + + ph->setLCNnScale(scaleCr, COMPONENT_Cr); +} + + +#endif \ No newline at end of file diff --git a/source/Lib/EncoderLib/EncNNFilterSetLC.h b/source/Lib/EncoderLib/EncNNFilterSetLC.h new file mode 100644 index 0000000000..fb95374781 --- /dev/null +++ b/source/Lib/EncoderLib/EncNNFilterSetLC.h @@ -0,0 +1,72 @@ +/* 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-2023, 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 EncNNFilterSetLC.h + \brief Encoder low complexity cnn loop filter class (header) +*/ + +#ifndef __ENCNNFILTERSETLC__ +#define __ENCNNFILTERSETLC__ + +#include "CommonDef.h" + +#if NN_FILTERING_SET_LC + +#include "CommonLib/NNFilterSetLC.h" +#include "CABACWriter.h" + + +class EncNNFilterSetLC : public NNFilterSetLC +{ +public: + EncNNFilterSetLC(); + virtual ~EncNNFilterSetLC() {} + + + double filterPictureRD(CodingStructure &cs, PelUnitBuf in, PelUnitBuf out, PelUnitBuf original, const double *lambdas, uint8_t *cnnlfSliceModeIdc); + void create(CABACEncoder* cabacEncoder, CtxCache* ctxCache, Slice* pcSlice, const int inputBitDepth[MAX_NUM_CHANNEL_TYPE]); + void deriveScale(PelUnitBuf origBuf, PelUnitBuf recoBuf, PelUnitBuf filteredBuf, PicHeader *ph); + +public: + + CABACWriter* m_CABACEstimator; + CtxCache* m_CtxCache; + const double FracBitsScale = 1.0 / double(1 << SCALE_BITS); + double m_lambda[MAX_NUM_COMPONENT]; + PelStorage m_cCnnTmpBufModelSelect; + int m_inputBitDepth[MAX_NUM_CHANNEL_TYPE]; + +}; +#endif + +#endif diff --git a/source/Lib/EncoderLib/VLCWriter.cpp b/source/Lib/EncoderLib/VLCWriter.cpp index e59ac4bfe2..28c3869c90 100644 --- a/source/Lib/EncoderLib/VLCWriter.cpp +++ b/source/Lib/EncoderLib/VLCWriter.cpp @@ -1009,7 +1009,7 @@ void HLSWriter::codeSPS( const SPS* pcSPS ) WRITE_FLAG(pcSPS->getNnlfEnabledFlag(), "sps_nnlf_enabled_flag"); if (pcSPS->getNnlfEnabledFlag()) { - WRITE_FLAG(pcSPS->getNnlfSet(), "sps_nnlf_set"); + WRITE_UVLC(pcSPS->getNnlfSet(), "sps_nnlf_set"); } #endif @@ -2103,6 +2103,16 @@ WRITE_FLAG(picHeader->getGdrOrIrapPicFlag(), "ph_gdr_or_irap_pic_flag"); picHeader->setSaoEnabledFlag(CHANNEL_TYPE_CHROMA, false); } +#if NN_FILTERING_SET_LC + if(sps->getNnlfSetLCEnabledFlag()) + { + WRITE_SCODE(picHeader->getLCNnScale(COMPONENT_Y) - (1 << LCNN_RESIDUE_SCALE_SHIFT), LCNN_RESIDUE_SCALE_SHIFT+1, "nnScale_Y"); + WRITE_SCODE(picHeader->getLCNnScale(COMPONENT_Cb) - (1 << LCNN_RESIDUE_SCALE_SHIFT), LCNN_RESIDUE_SCALE_SHIFT+1, "nnScale_Cb"); + WRITE_SCODE(picHeader->getLCNnScale(COMPONENT_Cr) - (1 << LCNN_RESIDUE_SCALE_SHIFT), LCNN_RESIDUE_SCALE_SHIFT+1, "nnScale_Cr"); + if(LCNN_MODEL_IDX_BITS > 0) + WRITE_CODE(picHeader->getLCNnModelIdx(),LCNN_MODEL_IDX_BITS, "nn_filter_idx"); + } +#endif // deblocking filter controls @@ -2588,6 +2598,20 @@ void HLSWriter::codeSliceHeader ( Slice* pcSlice, PicHeader *picHeader ) } } +#if NN_FILTERING_SET_LC + if (pcSlice->getSPS()->getNnlfSetLCEnabledFlag()) + { + for( int compIdx = 0; compIdx < MAX_NUM_COMPONENT; compIdx++ ) + { + WRITE_FLAG( (pcSlice->getLCnnlfSliceModeIdc(compIdx) == LCNNLF_OFF) ? 0 : 1, "slice_cnnlf_mode_idc" ); + + if( pcSlice->getLCnnlfSliceModeIdc( compIdx ) != LCNNLF_OFF ) + { + WRITE_FLAG( (pcSlice->getLCnnlfSliceModeIdc( compIdx ) == LCNNLF_ALL_ON) ? 1 : 0, "slice_cnnlf_luma_slice_all_on_flag" ); + } + } + } +#endif if (pcSlice->getPPS()->getDeblockingFilterControlPresentFlag()) { diff --git a/training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/BVIDVC_streamsplit.csv b/training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/BVIDVC_streamsplit.csv new file mode 100644 index 0000000000..b93643865e --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/BVIDVC_streamsplit.csv @@ -0,0 +1,621 @@ +TestVectors +class_a/train/ABangkokMarketVidevo_3840x2176_25fps_10bit_420.yuv +class_a/train/ABricksBushesStaticBVITexture_3840x2176_120fps_10bit_420.yuv +class_a/train/ABuildingRoofS4IRIS_3840x2176_24fps_10bit_420.yuv +class_a/train/ACityScapesS1IRIS_3840x2176_24fps_10bit_420.yuv +class_a/train/ACityScapesS2IRIS_3840x2176_24fps_10bit_420.yuv +class_a/train/AColourfulKoreanLanternsVidevo_3840x2176_50fps_10bit_420.yuv +class_a/train/AHongKongIslandVidevo_3840x2176_25fps_10bit_420.yuv +class_a/train/AHongKongMarket1Videvo_3840x2176_25fps_10bit_420.yuv +class_a/train/AHorseStaringS1Videvo_3840x2176_50fps_10bit_420.yuv +class_a/train/AJoggersS2BVIHFR_3840x2176_120fps_10bit_420.yuv +class_a/train/ALakeYonseiUniversity_3840x2176_30fps_10bit_420.yuv +class_a/train/AMyeongDongVidevo_3840x2176_25fps_10bit_420.yuv +class_a/train/ASeasideWalkIRIS_3840x2176_24fps_10bit_420.yuv +class_a/train/AStreetDancerS3IRIS_3840x2176_24fps_10bit_420.yuv +class_a/train/ATaiChiHongKongS1Videvo_3840x2176_25fps_10bit_420.yuv +class_a/train/ATaksinBridgeVidevo_3840x2176_23fps_10bit_420.yuv +class_a/train/ATrafficonTasksinBridgeVidevo_3840x2176_25fps_10bit_420.yuv +class_a/train/ATreeWillsBVITexture_3840x2176_120fps_10bit_420.yuv +class_a/validation/ABricksLeavesBVITexture_3840x2176_120fps_10bit_420.yuv +class_a/validation/AColourfulRugsMoroccoVidevo_3840x2176_50fps_10bit_420.yuv +class_b/train1/BAdvertisingMassagesBangkokVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train1/BAmericanFootballS2Harmonics_1920x1088_60fps_10bit_420.yuv +class_b/train1/BAmericanFootballS4Harmonics_1920x1088_60fps_10bit_420.yuv +class_b/train1/BAnimalsS11Harmonics_1920x1088_60fps_10bit_420.yuv +class_b/train1/BAnimalsS1Harmonics_1920x1088_60fps_10bit_420.yuv +class_b/train1/BBangkokMarketVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train1/BBasketballGoalScoredS2Videvo_1920x1088_25fps_10bit_420.yuv +class_b/train1/BBasketballS1YonseiUniversity_1920x1088_30fps_10bit_420.yuv +class_b/train1/BBasketballS2YonseiUniversity_1920x1088_30fps_10bit_420.yuv +class_b/train1/BBasketballS3YonseiUniversity_1920x1088_30fps_10bit_420.yuv +class_b/train1/BBoatsChaoPhrayaRiverVidevo_1920x1088_23fps_10bit_420.yuv +class_b/train1/BBobbleheadBVIHFR_1920x1088_120fps_10bit_420.yuv +class_b/train1/BBookcaseBVITexture_1920x1088_120fps_10bit_420.yuv +class_b/train1/BBoxingPracticeHarmonics_1920x1088_60fps_10bit_420.yuv +class_b/train1/BBricksBushesStaticBVITexture_1920x1088_120fps_10bit_420.yuv +class_b/train1/BBricksLeavesBVITexture_1920x1088_120fps_10bit_420.yuv +class_b/train1/BBricksTiltingBVITexture_1920x1088_120fps_10bit_420.yuv +class_b/train1/BBubblesPitcherS1BVITexture_1920x1088_120fps_10bit_420.yuv +class_b/train1/BBuildingRoofS1IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train1/BBuildingRoofS2IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train1/BBuildingRoofS3IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train1/BBuildingRoofS4IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train1/BBuntingHangingAcrossHongKongVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train1/BBusyHongKongStreetVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train1/BCalmingWaterBVITexture_1920x1088_120fps_10bit_420.yuv +class_b/train1/BCarpetPanAverageBVITexture_1920x1088_120fps_10bit_420.yuv +class_b/train1/BCatchBVIHFR_1920x1088_120fps_10bit_420.yuv +class_b/train1/BCeramicsandSpicesMoroccoVidevo_1920x1088_50fps_10bit_420.yuv +class_b/train1/BCharactersYonseiUniversity_1920x1088_30fps_10bit_420.yuv +class_b/train1/BChristmasPresentsIRIS_1920x1088_24fps_10bit_420.yuv +class_b/train1/BCityScapesS1IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train1/BCityScapesS2IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train1/BCityScapesS3IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train1/BCityStreetS1IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train1/BCityStreetS3IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train1/BCityStreetS4IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train1/BCityStreetS5IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train1/BCityStreetS6IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train1/BCityStreetS7IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train1/BCloseUpBasketballSceneVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train1/BCloudsStaticBVITexture_1920x1088_120fps_10bit_420.yuv +class_b/train1/BColourfulDecorationWatPhoVidevo_1920x1088_50fps_10bit_420.yuv +class_b/train1/BColourfulKoreanLanternsVidevo_1920x1088_50fps_10bit_420.yuv +class_b/train1/BColourfulPaperLanternsVidevo_1920x1088_50fps_10bit_420.yuv +class_b/train1/BColourfulRugsMoroccoVidevo_1920x1088_50fps_10bit_420.yuv +class_b/train1/BCostaRicaS3Harmonics_1920x1088_60fps_10bit_420.yuv +class_b/train1/BCrosswalkHarmonics_1920x1088_60fps_10bit_420.yuv +class_b/train1/BCrosswalkHongKong2S1Videvo_1920x1088_25fps_10bit_420.yuv +class_b/train1/BCrosswalkHongKong2S2Videvo_1920x1088_25fps_10bit_420.yuv +class_b/train1/BCrosswalkHongKongVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train1/BCrowdRunMCLV_1920x1088_25fps_10bit_420.yuv +class_b/train1/BCyclistS1BVIHFR_1920x1088_120fps_10bit_420.yuv +class_b/train1/BCyclistVeniceBeachBoardwalkVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train1/BDollsScene1YonseiUniversity_1920x1088_30fps_10bit_420.yuv +class_b/train1/BDollsScene2YonseiUniversity_1920x1088_30fps_10bit_420.yuv +class_b/train1/BDowntownHongKongVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train1/BDrivingPOVHarmonics_1920x1088_60fps_10bit_420.yuv +class_b/train1/BDropsOnWaterBVITexture_1920x1088_120fps_10bit_420.yuv +class_b/train1/BElFuenteMaskLIVENetFlix_1920x1088_24fps_10bit_420.yuv +class_b/train1/BEnteringHongKongStallS1Videvo_1920x1088_25fps_10bit_420.yuv +class_b/train1/BFerrisWheelTurningVidevo_1920x1088_50fps_10bit_420.yuv +class_b/train1/BFireS18Mitch_1920x1088_24fps_10bit_420.yuv +class_b/train1/BFireS21Mitch_1920x1088_24fps_10bit_420.yuv +class_b/train1/BFireS71Mitch_1920x1088_24fps_10bit_420.yuv +class_b/train1/BFirewoodS2IRIS_1920x1088_25fps_10bit_420.yuv +class_b/train1/BFitnessIRIS_1920x1088_24fps_10bit_420.yuv +class_b/train1/BFjordsS1Harmonics_1920x1088_60fps_10bit_420.yuv +class_b/train1/BFlowerChapelS1IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train1/BFlowerChapelS2IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train1/BFlyingCountrysideDareful_1920x1088_29fps_10bit_420.yuv +class_b/train1/BFlyingThroughLAStreetVidevo_1920x1088_23fps_10bit_420.yuv +class_b/train1/BFungusZoomBVITexture_1920x1088_120fps_10bit_420.yuv +class_b/train1/BGrassBVITexture_1920x1088_120fps_10bit_420.yuv +class_b/train1/BHamsterBVIHFR_1920x1088_120fps_10bit_420.yuv +class_b/train1/BHarleyDavidsonIRIS_1920x1088_24fps_10bit_420.yuv +class_b/train1/BHongKongIslandVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train1/BHongKongMarket1Videvo_1920x1088_25fps_10bit_420.yuv +class_b/train1/BHongKongMarket3S1Videvo_1920x1088_25fps_10bit_420.yuv +class_b/train1/BHongKongMarket3S2Videvo_1920x1088_25fps_10bit_420.yuv +class_b/train1/BHongKongMarket4S1Videvo_1920x1088_25fps_10bit_420.yuv +class_b/train1/BHongKongMarket4S2Videvo_1920x1088_25fps_10bit_420.yuv +class_b/train1/BHongKongS1Harmonics_1920x1088_60fps_10bit_420.yuv +class_b/train1/BHongKongS2Harmonics_1920x1088_60fps_10bit_420.yuv +class_b/train1/BHongKongS3Harmonics_1920x1088_60fps_10bit_420.yuv +class_b/train1/BHorseDrawnCarriagesVidevo_1920x1088_50fps_10bit_420.yuv +class_b/train1/BHorseStaringS1Videvo_1920x1088_50fps_10bit_420.yuv +class_b/train1/BHorseStaringS2Videvo_1920x1088_50fps_10bit_420.yuv +class_b/train1/BJockeyHarmonics_1920x1088_120fps_10bit_420.yuv +class_b/train1/BJoggersS1BVIHFR_1920x1088_120fps_10bit_420.yuv +class_b/train1/BJoggersS2BVIHFR_1920x1088_120fps_10bit_420.yuv +class_b/train2/BKartingIRIS_1920x1088_24fps_10bit_420.yuv +class_b/train2/BKoraDrumsVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BLampLeavesBVITexture_1920x1088_120fps_10bit_420.yuv +class_b/train2/BLeaves1BVITexture_1920x1088_120fps_10bit_420.yuv +class_b/train2/BLeaves3BVITexture_1920x1088_120fps_10bit_420.yuv +class_b/train2/BLowLevelShotAlongHongKongVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BLungshanTempleS1Videvo_1920x1088_50fps_10bit_420.yuv +class_b/train2/BLungshanTempleS2Videvo_1920x1088_50fps_10bit_420.yuv +class_b/train2/BManMoTempleVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BManStandinginProduceTruckVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BManWalkingThroughBangkokVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BMaplesS1YonseiUniversity_1920x1088_30fps_10bit_420.yuv +class_b/train2/BMaplesS2YonseiUniversity_1920x1088_30fps_10bit_420.yuv +class_b/train2/BMirabellParkS1IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train2/BMirabellParkS2IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train2/BMobileHarmonics_1920x1088_60fps_10bit_420.yuv +class_b/train2/BMoroccanCeramicsShopVidevo_1920x1088_50fps_10bit_420.yuv +class_b/train2/BMoroccanSlippersVidevo_1920x1088_50fps_10bit_420.yuv +class_b/train2/BMuralPaintingVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BMyeongDongVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BNewYorkStreetDareful_1920x1088_30fps_10bit_420.yuv +class_b/train2/BPaintingTiltingBVITexture_1920x1088_120fps_10bit_420.yuv +class_b/train2/BParkViolinMCLJCV_1920x1088_25fps_10bit_420.yuv +class_b/train2/BPedestriansSeoulatDawnVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BPeopleWalkingS1IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train2/BPillowsTransBVITexture_1920x1088_120fps_10bit_420.yuv +class_b/train2/BPlasmaFreeBVITexture_1920x1088_120fps_10bit_420.yuv +class_b/train2/BPresentsChristmasTreeDareful_1920x1088_29fps_10bit_420.yuv +class_b/train2/BReadySetGoS2TampereUniversity_1920x1088_120fps_10bit_420.yuv +class_b/train2/BResidentialBuildingSJTU_1920x1088_60fps_10bit_420.yuv +class_b/train2/BRollerCoaster2Netflix_1920x1088_60fps_10bit_420.yuv +class_b/train2/BRunnersSJTU_1920x1088_60fps_10bit_420.yuv +class_b/train2/BRuralSetupIRIS_1920x1088_24fps_10bit_420.yuv +class_b/train2/BRuralSetupS2IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train2/BScarfSJTU_1920x1088_60fps_10bit_420.yuv +class_b/train2/BSeasideWalkIRIS_1920x1088_24fps_10bit_420.yuv +class_b/train2/BSeekingMCLV_1920x1088_25fps_10bit_420.yuv +class_b/train2/BSeoulCanalatDawnVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BShoppingCentreVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BSignboardBoatLIVENetFlix_1920x1088_30fps_10bit_420.yuv +class_b/train2/BSkyscraperBangkokVidevo_1920x1088_23fps_10bit_420.yuv +class_b/train2/BSmokeClearBVITexture_1920x1088_120fps_10bit_420.yuv +class_b/train2/BSquareAndTimelapseHarmonics_1920x1088_60fps_10bit_420.yuv +class_b/train2/BSquareS1IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train2/BSquareS2IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train2/BStreetArtVidevo_1920x1088_30fps_10bit_420.yuv +class_b/train2/BStreetDancerS2IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train2/BStreetDancerS3IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train2/BStreetDancerS4IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train2/BStreetDancerS5IRIS_1920x1088_24fps_10bit_420.yuv +class_b/train2/BStreetsOfIndiaS1Harmonics_1920x1088_60fps_10bit_420.yuv +class_b/train2/BStreetsOfIndiaS2Harmonics_1920x1088_60fps_10bit_420.yuv +class_b/train2/BStreetsOfIndiaS3Harmonics_1920x1088_60fps_10bit_420.yuv +class_b/train2/BTaiChiHongKongS1Videvo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BTaiChiHongKongS2Videvo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BTaipeiCityRooftops8Videvo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BTaipeiCityRooftopsS1Videvo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BTaipeiCityRooftopsS2Videvo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BTaksinBridgeVidevo_1920x1088_23fps_10bit_420.yuv +class_b/train2/BTallBuildingsSJTU_1920x1088_60fps_10bit_420.yuv +class_b/train2/BTennisMCLV_1920x1088_24fps_10bit_420.yuv +class_b/train2/BToddlerFountain2Netflix_1920x1088_60fps_10bit_420.yuv +class_b/train2/BTouristsSatOutsideVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BToyCalendarHarmonics_1920x1088_60fps_10bit_420.yuv +class_b/train2/BTrackingDownHongKongSideVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BTrackingPastRestaurantVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BTrackingPastStallHongKongVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BTraditionalIndonesianKecakVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BTrafficandBuildingSJTU_1920x1088_60fps_10bit_420.yuv +class_b/train2/BTrafficFlowSJTU_1920x1088_60fps_10bit_420.yuv +class_b/train2/BTrafficonTasksinBridgeVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BTreeWillsBVITexture_1920x1088_120fps_10bit_420.yuv +class_b/train2/BTruckIRIS_1920x1088_24fps_10bit_420.yuv +class_b/train2/BTunnelFlagS1Harmonics_1920x1088_60fps_10bit_420.yuv +class_b/train2/BUnloadingVegetablesVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BVegetableMarketS1LIVENetFlix_1920x1088_30fps_10bit_420.yuv +class_b/train2/BVegetableMarketS2LIVENetFlix_1920x1088_30fps_10bit_420.yuv +class_b/train2/BVegetableMarketS3LIVENetFlix_1920x1088_30fps_10bit_420.yuv +class_b/train2/BVegetableMarketS4LIVENetFlix_1920x1088_30fps_10bit_420.yuv +class_b/train2/BVeniceS1Harmonics_1920x1088_60fps_10bit_420.yuv +class_b/train2/BVeniceS2Harmonics_1920x1088_60fps_10bit_420.yuv +class_b/train2/BVeniceSceneIRIS_1920x1088_24fps_10bit_420.yuv +class_b/train2/BWalkingDownKhaoStreetVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BWalkingThroughFootbridgeVidevo_1920x1088_25fps_10bit_420.yuv +class_b/train2/BWaterS65Mitch_1920x1088_24fps_10bit_420.yuv +class_b/train2/BWaterS81Mitch_1920x1088_24fps_10bit_420.yuv +class_b/train2/BWatPhoTempleVidevo_1920x1088_50fps_10bit_420.yuv +class_b/train2/BWoodSJTU_1920x1088_60fps_10bit_420.yuv +class_b/train2/BWovenVidevo_1920x1088_25fps_10bit_420.yuv +class_b/validation/BAmericanFootballS3Harmonics_1920x1088_60fps_10bit_420.yuv +class_b/validation/BBasketballGoalScoredS1Videvo_1920x1088_25fps_10bit_420.yuv +class_b/validation/BChristmasRoomDareful_1920x1088_29fps_10bit_420.yuv +class_b/validation/BChurchInsideMCLJCV_1920x1088_30fps_10bit_420.yuv +class_b/validation/BConstructionS2YonseiUniversity_1920x1088_30fps_10bit_420.yuv +class_b/validation/BEnteringHongKongStallS2Videvo_1920x1088_25fps_10bit_420.yuv +class_b/validation/BFirewoodS1IRIS_1920x1088_24fps_10bit_420.yuv +class_b/validation/BFlagShootTUMSVT_1920x1088_50fps_10bit_420.yuv +class_b/validation/BFlyingMountainsDareful_1920x1088_29fps_10bit_420.yuv +class_b/validation/BGrazTowerIRIS_1920x1088_24fps_10bit_420.yuv +class_b/validation/BHongKongMarket2Videvo_1920x1088_25fps_10bit_420.yuv +class_b/validation/BLakeYonseiUniversity_1920x1088_30fps_10bit_420.yuv +class_b/validation/BLaundryHangingOverHongKongVidevo_1920x1088_25fps_10bit_420.yuv +class_b/validation/BMyanmarS4Harmonics_1920x1088_60fps_10bit_420.yuv +class_b/validation/BMyanmarS6Harmonics_1920x1088_60fps_10bit_420.yuv +class_b/validation/BOrangeBuntingoverHongKongVidevo_1920x1088_25fps_10bit_420.yuv +class_b/validation/BPersonRunningOutsideVidevo_1920x1088_50fps_10bit_420.yuv +class_b/validation/BSmokeS45Mitch_1920x1088_24fps_10bit_420.yuv +class_b/validation/BSparklerBVIHFR_1920x1088_120fps_10bit_420.yuv +class_b/validation/BStreetDancerS1IRIS_1920x1088_24fps_10bit_420.yuv +class_b/validation/BWalkingDownNorthRodeoVidevo_1920x1088_25fps_10bit_420.yuv +class_c/train/CAdvertisingMassagesBangkokVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CAmericanFootballS2Harmonics_960x544_60fps_10bit_420.yuv +class_c/train/CAmericanFootballS3Harmonics_960x544_60fps_10bit_420.yuv +class_c/train/CAnimalsS11Harmonics_960x544_60fps_10bit_420.yuv +class_c/train/CAnimalsS1Harmonics_960x544_60fps_10bit_420.yuv +class_c/train/CBangkokMarketVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CBasketballGoalScoredS1Videvo_960x544_25fps_10bit_420.yuv +class_c/train/CBasketballGoalScoredS2Videvo_960x544_25fps_10bit_420.yuv +class_c/train/CBasketballS2YonseiUniversity_960x544_30fps_10bit_420.yuv +class_c/train/CBasketballS3YonseiUniversity_960x544_30fps_10bit_420.yuv +class_c/train/CBoatsChaoPhrayaRiverVidevo_960x544_23fps_10bit_420.yuv +class_c/train/CBobbleheadBVIHFR_960x544_120fps_10bit_420.yuv +class_c/train/CBookcaseBVITexture_960x544_120fps_10bit_420.yuv +class_c/train/CBoxingPracticeHarmonics_960x544_60fps_10bit_420.yuv +class_c/train/CBricksBushesStaticBVITexture_960x544_120fps_10bit_420.yuv +class_c/train/CBricksLeavesBVITexture_960x544_120fps_10bit_420.yuv +class_c/train/CBricksTiltingBVITexture_960x544_120fps_10bit_420.yuv +class_c/train/CBubblesPitcherS1BVITexture_960x544_120fps_10bit_420.yuv +class_c/train/CBuildingRoofS1IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CBuildingRoofS2IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CBuildingRoofS3IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CBuntingHangingAcrossHongKongVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CBusyHongKongStreetVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CCalmingWaterBVITexture_960x544_120fps_10bit_420.yuv +class_c/train/CCarpetPanAverageBVITexture_960x544_120fps_10bit_420.yuv +class_c/train/CCatchBVIHFR_960x544_120fps_10bit_420.yuv +class_c/train/CCeramicsandSpicesMoroccoVidevo_960x544_50fps_10bit_420.yuv +class_c/train/CChristmasPresentsIRIS_960x544_24fps_10bit_420.yuv +class_c/train/CChristmasRoomDareful_960x544_29fps_10bit_420.yuv +class_c/train/CChurchInsideMCLJCV_960x544_30fps_10bit_420.yuv +class_c/train/CCityScapesS2IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CCityScapesS3IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CCityStreetS1IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CCityStreetS3IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CCityStreetS4IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CCityStreetS5IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CCityStreetS6IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CCityStreetS7IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CCloseUpBasketballSceneVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CCloudsStaticBVITexture_960x544_120fps_10bit_420.yuv +class_c/train/CColourfulDecorationWatPhoVidevo_960x544_50fps_10bit_420.yuv +class_c/train/CColourfulKoreanLanternsVidevo_960x544_50fps_10bit_420.yuv +class_c/train/CColourfulPaperLanternsVidevo_960x544_50fps_10bit_420.yuv +class_c/train/CColourfulRugsMoroccoVidevo_960x544_50fps_10bit_420.yuv +class_c/train/CConstructionS2YonseiUniversity_960x544_30fps_10bit_420.yuv +class_c/train/CCostaRicaS3Harmonics_960x544_60fps_10bit_420.yuv +class_c/train/CCrosswalkHarmonics_960x544_60fps_10bit_420.yuv +class_c/train/CCrosswalkHongKong2S2Videvo_960x544_25fps_10bit_420.yuv +class_c/train/CCrosswalkHongKongVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CCrowdRunMCLV_960x544_25fps_10bit_420.yuv +class_c/train/CCyclistS1BVIHFR_960x544_120fps_10bit_420.yuv +class_c/train/CCyclistVeniceBeachBoardwalkVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CDollsScene1YonseiUniversity_960x544_30fps_10bit_420.yuv +class_c/train/CDollsScene2YonseiUniversity_960x544_30fps_10bit_420.yuv +class_c/train/CDowntownHongKongVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CDrivingPOVHarmonics_960x544_60fps_10bit_420.yuv +class_c/train/CDropsOnWaterBVITexture_960x544_120fps_10bit_420.yuv +class_c/train/CElFuenteMaskLIVENetFlix_960x544_24fps_10bit_420.yuv +class_c/train/CEnteringHongKongStallS1Videvo_960x544_25fps_10bit_420.yuv +class_c/train/CEnteringHongKongStallS2Videvo_960x544_25fps_10bit_420.yuv +class_c/train/CFerrisWheelTurningVidevo_960x544_50fps_10bit_420.yuv +class_c/train/CFireS18Mitch_960x544_24fps_10bit_420.yuv +class_c/train/CFireS21Mitch_960x544_24fps_10bit_420.yuv +class_c/train/CFirewoodS1IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CFirewoodS2IRIS_960x544_25fps_10bit_420.yuv +class_c/train/CFjordsS1Harmonics_960x544_60fps_10bit_420.yuv +class_c/train/CFlagShootTUMSVT_960x544_50fps_10bit_420.yuv +class_c/train/CFlowerChapelS1IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CFlowerChapelS2IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CFlyingCountrysideDareful_960x544_29fps_10bit_420.yuv +class_c/train/CFlyingMountainsDareful_960x544_29fps_10bit_420.yuv +class_c/train/CFlyingThroughLAStreetVidevo_960x544_23fps_10bit_420.yuv +class_c/train/CFungusZoomBVITexture_960x544_120fps_10bit_420.yuv +class_c/train/CGrassBVITexture_960x544_120fps_10bit_420.yuv +class_c/train/CGrazTowerIRIS_960x544_24fps_10bit_420.yuv +class_c/train/CHamsterBVIHFR_960x544_120fps_10bit_420.yuv +class_c/train/CHarleyDavidsonIRIS_960x544_24fps_10bit_420.yuv +class_c/train/CHongKongIslandVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CHongKongMarket1Videvo_960x544_25fps_10bit_420.yuv +class_c/train/CHongKongMarket2Videvo_960x544_25fps_10bit_420.yuv +class_c/train/CHongKongMarket3S1Videvo_960x544_25fps_10bit_420.yuv +class_c/train/CHongKongMarket3S2Videvo_960x544_25fps_10bit_420.yuv +class_c/train/CHongKongMarket4S2Videvo_960x544_25fps_10bit_420.yuv +class_c/train/CHongKongS1Harmonics_960x544_60fps_10bit_420.yuv +class_c/train/CHongKongS2Harmonics_960x544_60fps_10bit_420.yuv +class_c/train/CHongKongS3Harmonics_960x544_60fps_10bit_420.yuv +class_c/train/CHorseDrawnCarriagesVidevo_960x544_50fps_10bit_420.yuv +class_c/train/CHorseStaringS1Videvo_960x544_50fps_10bit_420.yuv +class_c/train/CJockeyHarmonics_960x544_120fps_10bit_420.yuv +class_c/train/CJoggersS1BVIHFR_960x544_120fps_10bit_420.yuv +class_c/train/CJoggersS2BVIHFR_960x544_120fps_10bit_420.yuv +class_c/train/CKoraDrumsVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CLakeYonseiUniversity_960x544_30fps_10bit_420.yuv +class_c/train/CLampLeavesBVITexture_960x544_120fps_10bit_420.yuv +class_c/train/CLeaves1BVITexture_960x544_120fps_10bit_420.yuv +class_c/train/CLowLevelShotAlongHongKongVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CLungshanTempleS1Videvo_960x544_50fps_10bit_420.yuv +class_c/train/CLungshanTempleS2Videvo_960x544_50fps_10bit_420.yuv +class_c/train/CManMoTempleVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CManStandinginProduceTruckVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CManWalkingThroughBangkokVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CMaplesS2YonseiUniversity_960x544_30fps_10bit_420.yuv +class_c/train/CMirabellParkS1IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CMirabellParkS2IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CMobileHarmonics_960x544_60fps_10bit_420.yuv +class_c/train/CMoroccanCeramicsShopVidevo_960x544_50fps_10bit_420.yuv +class_c/train/CMoroccanSlippersVidevo_960x544_50fps_10bit_420.yuv +class_c/train/CMuralPaintingVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CMyanmarS4Harmonics_960x544_60fps_10bit_420.yuv +class_c/train/CMyanmarS6Harmonics_960x544_60fps_10bit_420.yuv +class_c/train/CMyeongDongVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CNewYorkStreetDareful_960x544_30fps_10bit_420.yuv +class_c/train/COrangeBuntingoverHongKongVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CPedestriansSeoulatDawnVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CPeopleWalkingS1IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CPersonRunningOutsideVidevo_960x544_50fps_10bit_420.yuv +class_c/train/CPillowsTransBVITexture_960x544_120fps_10bit_420.yuv +class_c/train/CPlasmaFreeBVITexture_960x544_120fps_10bit_420.yuv +class_c/train/CReadySetGoS2TampereUniversity_960x544_120fps_10bit_420.yuv +class_c/train/CResidentialBuildingSJTU_960x544_60fps_10bit_420.yuv +class_c/train/CRollerCoaster2Netflix_960x544_60fps_10bit_420.yuv +class_c/train/CRunnersSJTU_960x544_60fps_10bit_420.yuv +class_c/train/CRuralSetupIRIS_960x544_24fps_10bit_420.yuv +class_c/train/CRuralSetupS2IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CScarfSJTU_960x544_60fps_10bit_420.yuv +class_c/train/CSeasideWalkIRIS_960x544_24fps_10bit_420.yuv +class_c/train/CSeekingMCLV_960x544_25fps_10bit_420.yuv +class_c/train/CShoppingCentreVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CSignboardBoatLIVENetFlix_960x544_30fps_10bit_420.yuv +class_c/train/CSkyscraperBangkokVidevo_960x544_23fps_10bit_420.yuv +class_c/train/CSmokeClearBVITexture_960x544_120fps_10bit_420.yuv +class_c/train/CSmokeS45Mitch_960x544_24fps_10bit_420.yuv +class_c/train/CSparklerBVIHFR_960x544_120fps_10bit_420.yuv +class_c/train/CSquareAndTimelapseHarmonics_960x544_60fps_10bit_420.yuv +class_c/train/CSquareS1IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CSquareS2IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CStreetArtVidevo_960x544_30fps_10bit_420.yuv +class_c/train/CStreetDancerS1IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CStreetDancerS2IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CStreetDancerS3IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CStreetDancerS4IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CStreetDancerS5IRIS_960x544_24fps_10bit_420.yuv +class_c/train/CStreetsOfIndiaS1Harmonics_960x544_60fps_10bit_420.yuv +class_c/train/CStreetsOfIndiaS2Harmonics_960x544_60fps_10bit_420.yuv +class_c/train/CStreetsOfIndiaS3Harmonics_960x544_60fps_10bit_420.yuv +class_c/train/CTaiChiHongKongS1Videvo_960x544_25fps_10bit_420.yuv +class_c/train/CTaiChiHongKongS2Videvo_960x544_25fps_10bit_420.yuv +class_c/train/CTaipeiCityRooftops8Videvo_960x544_25fps_10bit_420.yuv +class_c/train/CTaipeiCityRooftopsS1Videvo_960x544_25fps_10bit_420.yuv +class_c/train/CTaipeiCityRooftopsS2Videvo_960x544_25fps_10bit_420.yuv +class_c/train/CTaksinBridgeVidevo_960x544_23fps_10bit_420.yuv +class_c/train/CTallBuildingsSJTU_960x544_60fps_10bit_420.yuv +class_c/train/CTennisMCLV_960x544_24fps_10bit_420.yuv +class_c/train/CTouristsSatOutsideVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CTrackingPastRestaurantVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CTrackingPastStallHongKongVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CTraditionalIndonesianKecakVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CTrafficandBuildingSJTU_960x544_60fps_10bit_420.yuv +class_c/train/CTrafficFlowSJTU_960x544_60fps_10bit_420.yuv +class_c/train/CTreeWillsBVITexture_960x544_120fps_10bit_420.yuv +class_c/train/CTruckIRIS_960x544_24fps_10bit_420.yuv +class_c/train/CTunnelFlagS1Harmonics_960x544_60fps_10bit_420.yuv +class_c/train/CUnloadingVegetablesVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CVegetableMarketS1LIVENetFlix_960x544_30fps_10bit_420.yuv +class_c/train/CVegetableMarketS3LIVENetFlix_960x544_30fps_10bit_420.yuv +class_c/train/CVegetableMarketS4LIVENetFlix_960x544_30fps_10bit_420.yuv +class_c/train/CVeniceS1Harmonics_960x544_60fps_10bit_420.yuv +class_c/train/CVeniceS2Harmonics_960x544_60fps_10bit_420.yuv +class_c/train/CWalkingDownKhaoStreetVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CWalkingDownNorthRodeoVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CWalkingThroughFootbridgeVidevo_960x544_25fps_10bit_420.yuv +class_c/train/CWaterS65Mitch_960x544_24fps_10bit_420.yuv +class_c/train/CWaterS81Mitch_960x544_24fps_10bit_420.yuv +class_c/train/CWoodSJTU_960x544_60fps_10bit_420.yuv +class_c/train/CWovenVidevo_960x544_25fps_10bit_420.yuv +class_c/validation/CAmericanFootballS4Harmonics_960x544_60fps_10bit_420.yuv +class_c/validation/CBasketballS1YonseiUniversity_960x544_30fps_10bit_420.yuv +class_c/validation/CBuildingRoofS4IRIS_960x544_24fps_10bit_420.yuv +class_c/validation/CCharactersYonseiUniversity_960x544_30fps_10bit_420.yuv +class_c/validation/CCityScapesS1IRIS_960x544_24fps_10bit_420.yuv +class_c/validation/CCrosswalkHongKong2S1Videvo_960x544_25fps_10bit_420.yuv +class_c/validation/CFireS71Mitch_960x544_24fps_10bit_420.yuv +class_c/validation/CFitnessIRIS_960x544_24fps_10bit_420.yuv +class_c/validation/CHongKongMarket4S1Videvo_960x544_25fps_10bit_420.yuv +class_c/validation/CHorseStaringS2Videvo_960x544_50fps_10bit_420.yuv +class_c/validation/CKartingIRIS_960x544_24fps_10bit_420.yuv +class_c/validation/CLaundryHangingOverHongKongVidevo_960x544_25fps_10bit_420.yuv +class_c/validation/CLeaves3BVITexture_960x544_120fps_10bit_420.yuv +class_c/validation/CMaplesS1YonseiUniversity_960x544_30fps_10bit_420.yuv +class_c/validation/CPaintingTiltingBVITexture_960x544_120fps_10bit_420.yuv +class_c/validation/CParkViolinMCLJCV_960x544_25fps_10bit_420.yuv +class_c/validation/CPresentsChristmasTreeDareful_960x544_29fps_10bit_420.yuv +class_c/validation/CSeoulCanalatDawnVidevo_960x544_25fps_10bit_420.yuv +class_c/validation/CToddlerFountain2Netflix_960x544_60fps_10bit_420.yuv +class_c/validation/CToyCalendarHarmonics_960x544_60fps_10bit_420.yuv +class_c/validation/CTrackingDownHongKongSideVidevo_960x544_25fps_10bit_420.yuv +class_c/validation/CTrafficonTasksinBridgeVidevo_960x544_25fps_10bit_420.yuv +class_c/validation/CVegetableMarketS2LIVENetFlix_960x544_30fps_10bit_420.yuv +class_c/validation/CVeniceSceneIRIS_960x544_24fps_10bit_420.yuv +class_c/validation/CWatPhoTempleVidevo_960x544_50fps_10bit_420.yuv +class_d/train/DAdvertisingMassagesBangkokVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DAmericanFootballS3Harmonics_480x272_60fps_10bit_420.yuv +class_d/train/DAmericanFootballS4Harmonics_480x272_60fps_10bit_420.yuv +class_d/train/DAnimalsS11Harmonics_480x272_60fps_10bit_420.yuv +class_d/train/DAnimalsS1Harmonics_480x272_60fps_10bit_420.yuv +class_d/train/DBangkokMarketVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DBasketballGoalScoredS1Videvo_480x272_25fps_10bit_420.yuv +class_d/train/DBasketballGoalScoredS2Videvo_480x272_25fps_10bit_420.yuv +class_d/train/DBasketballS1YonseiUniversity_480x272_30fps_10bit_420.yuv +class_d/train/DBasketballS2YonseiUniversity_480x272_30fps_10bit_420.yuv +class_d/train/DBasketballS3YonseiUniversity_480x272_30fps_10bit_420.yuv +class_d/train/DBoatsChaoPhrayaRiverVidevo_480x272_23fps_10bit_420.yuv +class_d/train/DBobbleheadBVIHFR_480x272_120fps_10bit_420.yuv +class_d/train/DBookcaseBVITexture_480x272_120fps_10bit_420.yuv +class_d/train/DBoxingPracticeHarmonics_480x272_60fps_10bit_420.yuv +class_d/train/DBricksBushesStaticBVITexture_480x272_120fps_10bit_420.yuv +class_d/train/DBricksLeavesBVITexture_480x272_120fps_10bit_420.yuv +class_d/train/DBricksTiltingBVITexture_480x272_120fps_10bit_420.yuv +class_d/train/DBubblesPitcherS1BVITexture_480x272_120fps_10bit_420.yuv +class_d/train/DBuildingRoofS1IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DBuildingRoofS2IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DBuildingRoofS4IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DBuntingHangingAcrossHongKongVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DBusyHongKongStreetVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DCalmingWaterBVITexture_480x272_120fps_10bit_420.yuv +class_d/train/DCarpetPanAverageBVITexture_480x272_120fps_10bit_420.yuv +class_d/train/DCatchBVIHFR_480x272_120fps_10bit_420.yuv +class_d/train/DCeramicsandSpicesMoroccoVidevo_480x272_50fps_10bit_420.yuv +class_d/train/DCharactersYonseiUniversity_480x272_30fps_10bit_420.yuv +class_d/train/DChristmasPresentsIRIS_480x272_24fps_10bit_420.yuv +class_d/train/DChristmasRoomDareful_480x272_29fps_10bit_420.yuv +class_d/train/DChurchInsideMCLJCV_480x272_30fps_10bit_420.yuv +class_d/train/DCityScapesS1IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DCityScapesS2IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DCityScapesS3IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DCityStreetS1IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DCityStreetS3IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DCityStreetS4IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DCityStreetS5IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DCityStreetS6IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DCityStreetS7IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DCloseUpBasketballSceneVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DCloudsStaticBVITexture_480x272_120fps_10bit_420.yuv +class_d/train/DColourfulDecorationWatPhoVidevo_480x272_50fps_10bit_420.yuv +class_d/train/DColourfulKoreanLanternsVidevo_480x272_50fps_10bit_420.yuv +class_d/train/DColourfulPaperLanternsVidevo_480x272_50fps_10bit_420.yuv +class_d/train/DColourfulRugsMoroccoVidevo_480x272_50fps_10bit_420.yuv +class_d/train/DConstructionS2YonseiUniversity_480x272_30fps_10bit_420.yuv +class_d/train/DCostaRicaS3Harmonics_480x272_60fps_10bit_420.yuv +class_d/train/DCrosswalkHarmonics_480x272_60fps_10bit_420.yuv +class_d/train/DCrosswalkHongKongVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DCrowdRunMCLV_480x272_25fps_10bit_420.yuv +class_d/train/DCyclistS1BVIHFR_480x272_120fps_10bit_420.yuv +class_d/train/DCyclistVeniceBeachBoardwalkVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DDollsScene1YonseiUniversity_480x272_30fps_10bit_420.yuv +class_d/train/DDollsScene2YonseiUniversity_480x272_30fps_10bit_420.yuv +class_d/train/DDowntownHongKongVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DDropsOnWaterBVITexture_480x272_120fps_10bit_420.yuv +class_d/train/DElFuenteMaskLIVENetFlix_480x272_24fps_10bit_420.yuv +class_d/train/DEnteringHongKongStallS1Videvo_480x272_25fps_10bit_420.yuv +class_d/train/DEnteringHongKongStallS2Videvo_480x272_25fps_10bit_420.yuv +class_d/train/DFerrisWheelTurningVidevo_480x272_50fps_10bit_420.yuv +class_d/train/DFireS18Mitch_480x272_24fps_10bit_420.yuv +class_d/train/DFireS21Mitch_480x272_24fps_10bit_420.yuv +class_d/train/DFireS71Mitch_480x272_24fps_10bit_420.yuv +class_d/train/DFirewoodS1IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DFirewoodS2IRIS_480x272_25fps_10bit_420.yuv +class_d/train/DFitnessIRIS_480x272_24fps_10bit_420.yuv +class_d/train/DFjordsS1Harmonics_480x272_60fps_10bit_420.yuv +class_d/train/DFlagShootTUMSVT_480x272_50fps_10bit_420.yuv +class_d/train/DFlowerChapelS1IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DFlowerChapelS2IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DFlyingCountrysideDareful_480x272_29fps_10bit_420.yuv +class_d/train/DFlyingMountainsDareful_480x272_29fps_10bit_420.yuv +class_d/train/DFlyingThroughLAStreetVidevo_480x272_23fps_10bit_420.yuv +class_d/train/DFungusZoomBVITexture_480x272_120fps_10bit_420.yuv +class_d/train/DGrassBVITexture_480x272_120fps_10bit_420.yuv +class_d/train/DGrazTowerIRIS_480x272_24fps_10bit_420.yuv +class_d/train/DHamsterBVIHFR_480x272_120fps_10bit_420.yuv +class_d/train/DHarleyDavidsonIRIS_480x272_24fps_10bit_420.yuv +class_d/train/DHongKongIslandVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DHongKongMarket1Videvo_480x272_25fps_10bit_420.yuv +class_d/train/DHongKongMarket2Videvo_480x272_25fps_10bit_420.yuv +class_d/train/DHongKongMarket3S1Videvo_480x272_25fps_10bit_420.yuv +class_d/train/DHongKongMarket3S2Videvo_480x272_25fps_10bit_420.yuv +class_d/train/DHongKongMarket4S1Videvo_480x272_25fps_10bit_420.yuv +class_d/train/DHongKongMarket4S2Videvo_480x272_25fps_10bit_420.yuv +class_d/train/DHongKongS2Harmonics_480x272_60fps_10bit_420.yuv +class_d/train/DHongKongS3Harmonics_480x272_60fps_10bit_420.yuv +class_d/train/DHorseDrawnCarriagesVidevo_480x272_50fps_10bit_420.yuv +class_d/train/DHorseStaringS1Videvo_480x272_50fps_10bit_420.yuv +class_d/train/DHorseStaringS2Videvo_480x272_50fps_10bit_420.yuv +class_d/train/DJockeyHarmonics_480x272_120fps_10bit_420.yuv +class_d/train/DJoggersS1BVIHFR_480x272_120fps_10bit_420.yuv +class_d/train/DJoggersS2BVIHFR_480x272_120fps_10bit_420.yuv +class_d/train/DKartingIRIS_480x272_24fps_10bit_420.yuv +class_d/train/DKoraDrumsVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DLakeYonseiUniversity_480x272_30fps_10bit_420.yuv +class_d/train/DLampLeavesBVITexture_480x272_120fps_10bit_420.yuv +class_d/train/DLaundryHangingOverHongKongVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DLeaves1BVITexture_480x272_120fps_10bit_420.yuv +class_d/train/DLeaves3BVITexture_480x272_120fps_10bit_420.yuv +class_d/train/DLowLevelShotAlongHongKongVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DLungshanTempleS1Videvo_480x272_50fps_10bit_420.yuv +class_d/train/DLungshanTempleS2Videvo_480x272_50fps_10bit_420.yuv +class_d/train/DManMoTempleVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DManStandinginProduceTruckVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DManWalkingThroughBangkokVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DMaplesS2YonseiUniversity_480x272_30fps_10bit_420.yuv +class_d/train/DMirabellParkS1IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DMobileHarmonics_480x272_60fps_10bit_420.yuv +class_d/train/DMoroccanCeramicsShopVidevo_480x272_50fps_10bit_420.yuv +class_d/train/DMoroccanSlippersVidevo_480x272_50fps_10bit_420.yuv +class_d/train/DMuralPaintingVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DMyanmarS4Harmonics_480x272_60fps_10bit_420.yuv +class_d/train/DMyanmarS6Harmonics_480x272_60fps_10bit_420.yuv +class_d/train/DMyeongDongVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DNewYorkStreetDareful_480x272_30fps_10bit_420.yuv +class_d/train/DOrangeBuntingoverHongKongVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DPaintingTiltingBVITexture_480x272_120fps_10bit_420.yuv +class_d/train/DParkViolinMCLJCV_480x272_25fps_10bit_420.yuv +class_d/train/DPedestriansSeoulatDawnVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DPeopleWalkingS1IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DPersonRunningOutsideVidevo_480x272_50fps_10bit_420.yuv +class_d/train/DPillowsTransBVITexture_480x272_120fps_10bit_420.yuv +class_d/train/DPlasmaFreeBVITexture_480x272_120fps_10bit_420.yuv +class_d/train/DPresentsChristmasTreeDareful_480x272_29fps_10bit_420.yuv +class_d/train/DReadySetGoS2TampereUniversity_480x272_120fps_10bit_420.yuv +class_d/train/DResidentialBuildingSJTU_480x272_60fps_10bit_420.yuv +class_d/train/DRollerCoaster2Netflix_480x272_60fps_10bit_420.yuv +class_d/train/DRunnersSJTU_480x272_60fps_10bit_420.yuv +class_d/train/DRuralSetupIRIS_480x272_24fps_10bit_420.yuv +class_d/train/DSeasideWalkIRIS_480x272_24fps_10bit_420.yuv +class_d/train/DSeekingMCLV_480x272_25fps_10bit_420.yuv +class_d/train/DSeoulCanalatDawnVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DShoppingCentreVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DSignboardBoatLIVENetFlix_480x272_30fps_10bit_420.yuv +class_d/train/DSkyscraperBangkokVidevo_480x272_23fps_10bit_420.yuv +class_d/train/DSmokeClearBVITexture_480x272_120fps_10bit_420.yuv +class_d/train/DSmokeS45Mitch_480x272_24fps_10bit_420.yuv +class_d/train/DSparklerBVIHFR_480x272_120fps_10bit_420.yuv +class_d/train/DSquareAndTimelapseHarmonics_480x272_60fps_10bit_420.yuv +class_d/train/DSquareS1IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DSquareS2IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DStreetArtVidevo_480x272_30fps_10bit_420.yuv +class_d/train/DStreetDancerS1IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DStreetDancerS2IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DStreetDancerS3IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DStreetDancerS4IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DStreetDancerS5IRIS_480x272_24fps_10bit_420.yuv +class_d/train/DStreetsOfIndiaS1Harmonics_480x272_60fps_10bit_420.yuv +class_d/train/DStreetsOfIndiaS2Harmonics_480x272_60fps_10bit_420.yuv +class_d/train/DStreetsOfIndiaS3Harmonics_480x272_60fps_10bit_420.yuv +class_d/train/DTaiChiHongKongS1Videvo_480x272_25fps_10bit_420.yuv +class_d/train/DTaiChiHongKongS2Videvo_480x272_25fps_10bit_420.yuv +class_d/train/DTaipeiCityRooftops8Videvo_480x272_25fps_10bit_420.yuv +class_d/train/DTaipeiCityRooftopsS1Videvo_480x272_25fps_10bit_420.yuv +class_d/train/DTaipeiCityRooftopsS2Videvo_480x272_25fps_10bit_420.yuv +class_d/train/DTaksinBridgeVidevo_480x272_23fps_10bit_420.yuv +class_d/train/DTallBuildingsSJTU_480x272_60fps_10bit_420.yuv +class_d/train/DTennisMCLV_480x272_24fps_10bit_420.yuv +class_d/train/DToddlerFountain2Netflix_480x272_60fps_10bit_420.yuv +class_d/train/DToyCalendarHarmonics_480x272_60fps_10bit_420.yuv +class_d/train/DTrackingPastRestaurantVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DTraditionalIndonesianKecakVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DTrafficandBuildingSJTU_480x272_60fps_10bit_420.yuv +class_d/train/DTrafficFlowSJTU_480x272_60fps_10bit_420.yuv +class_d/train/DTrafficonTasksinBridgeVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DTreeWillsBVITexture_480x272_120fps_10bit_420.yuv +class_d/train/DTruckIRIS_480x272_24fps_10bit_420.yuv +class_d/train/DTunnelFlagS1Harmonics_480x272_60fps_10bit_420.yuv +class_d/train/DUnloadingVegetablesVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DVegetableMarketS1LIVENetFlix_480x272_30fps_10bit_420.yuv +class_d/train/DVegetableMarketS2LIVENetFlix_480x272_30fps_10bit_420.yuv +class_d/train/DVegetableMarketS4LIVENetFlix_480x272_30fps_10bit_420.yuv +class_d/train/DVeniceS1Harmonics_480x272_60fps_10bit_420.yuv +class_d/train/DVeniceS2Harmonics_480x272_60fps_10bit_420.yuv +class_d/train/DVeniceSceneIRIS_480x272_24fps_10bit_420.yuv +class_d/train/DWalkingDownKhaoStreetVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DWalkingThroughFootbridgeVidevo_480x272_25fps_10bit_420.yuv +class_d/train/DWaterS81Mitch_480x272_24fps_10bit_420.yuv +class_d/train/DWatPhoTempleVidevo_480x272_50fps_10bit_420.yuv +class_d/train/DWoodSJTU_480x272_60fps_10bit_420.yuv +class_d/train/DWovenVidevo_480x272_25fps_10bit_420.yuv +class_d/validation/DAmericanFootballS2Harmonics_480x272_60fps_10bit_420.yuv +class_d/validation/DBuildingRoofS3IRIS_480x272_24fps_10bit_420.yuv +class_d/validation/DCrosswalkHongKong2S1Videvo_480x272_25fps_10bit_420.yuv +class_d/validation/DCrosswalkHongKong2S2Videvo_480x272_25fps_10bit_420.yuv +class_d/validation/DDrivingPOVHarmonics_480x272_60fps_10bit_420.yuv +class_d/validation/DHongKongS1Harmonics_480x272_60fps_10bit_420.yuv +class_d/validation/DMaplesS1YonseiUniversity_480x272_30fps_10bit_420.yuv +class_d/validation/DMirabellParkS2IRIS_480x272_24fps_10bit_420.yuv +class_d/validation/DRuralSetupS2IRIS_480x272_24fps_10bit_420.yuv +class_d/validation/DScarfSJTU_480x272_60fps_10bit_420.yuv +class_d/validation/DTouristsSatOutsideVidevo_480x272_25fps_10bit_420.yuv +class_d/validation/DTrackingDownHongKongSideVidevo_480x272_25fps_10bit_420.yuv +class_d/validation/DTrackingPastStallHongKongVidevo_480x272_25fps_10bit_420.yuv +class_d/validation/DVegetableMarketS3LIVENetFlix_480x272_30fps_10bit_420.yuv +class_d/validation/DWalkingDownNorthRodeoVidevo_480x272_25fps_10bit_420.yuv +class_d/validation/DWaterS65Mitch_480x272_24fps_10bit_420.yuv diff --git a/training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/bvidvc_mp4_2_yuv.py b/training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/bvidvc_mp4_2_yuv.py new file mode 100644 index 0000000000..529d2e581f --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/bvidvc_mp4_2_yuv.py @@ -0,0 +1,35 @@ +import os +import argparse +import subprocess + +def parse_arguments(): + parser = argparse.ArgumentParser() + parser.add_argument('--input_dir', type=str, + help="Path to the folder where png files are present", + required=True) + parser.add_argument('--out_dir', type=str, + help="Path to the directory where outputs to be stored", + required=True) + return vars(parser.parse_args()) + + +if __name__ == "__main__": + + args = parse_arguments() + + in_dir = args['input_dir'] + out_dir = args['out_dir'] + + if not os.path.exists(out_dir): + os.makedirs(out_dir) + + for inp in os.listdir(in_dir): + input_path = os.path.join(in_dir, inp) + interm_yuv = None + + if os.path.splitext(inp)[1] == ".mp4": + yuv_file = os.path.join(out_dir, os.path.splitext(os.path.basename(inp))[0] + ".yuv") + decompr_cmd = " ".join(["ffmpeg -v error", "-i", input_path, "-pix_fmt", "yuv420p10le", "-y", + yuv_file]) + + status = os.system(decompr_cmd) \ No newline at end of file diff --git a/training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/div2k_gt_datagen.py b/training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/div2k_gt_datagen.py new file mode 100644 index 0000000000..2d884cad8d --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/div2k_gt_datagen.py @@ -0,0 +1,209 @@ +import os +import argparse +import subprocess +import numpy as np +import math + +def str2bool(v): + if isinstance(v, bool): + return v + if v.lower() in ('yes', 'y', 'true', 't', '1'): + return True + elif v.lower() in ('no', 'n', 'false', 'f', '0'): + return False + else: + raise argparse.ArgumentTypeError('Boolean value expected.') + +def parse_arguments(): + parser = argparse.ArgumentParser() + parser.add_argument('--input_dir', type=str, + help="Path to the folder where png files are present", + required=True) + parser.add_argument('--out_dir', type=str, + help="Path to the directory where outputs to be stored", + required=True) + parser.add_argument('--ctu_size', type=int, + help="CTU size that is used for training NNLF, default=128", + default=128, required=False) + parser.add_argument('--pad_size', type=int, + help="Padding size across one side of the CTU, default=0", + default=0, required=False) + parser.add_argument('--del_inter_res', type=str2bool, + help="Do you want to delete the intermediate yuv, default=True", + default=True, required=False) + parser.add_argument('--bit_depth', type=int, + help="Bit depth of the input files, Default=8", + default=8, required=False) + return vars(parser.parse_args()) + +class VideoCaptureYUV: + def __init__(self, filename, size, bitdepth): + self.height, self.width = size + self.bitdepth = bitdepth + self.f = open(filename, 'rb') + self.frame_len = int(self.width * self.height * 3 / 2) + self.data_type = np.uint8 + if self.bitdepth > 8: + self.frame_len *= 2 + self.data_type = np.uint16 + self.luma_shape = (self.height, self.width) + self.chroma_shape = (int(self.height/2), int(self.width/2)) + self.cb_offset = self.height * self.width + self.cr_offset = self.height * self.width + int(self.height * self.width / 4) + def read_raw(self): + try: + raw = self.f.read(self.frame_len) + yuv = np.frombuffer(raw, dtype=self.data_type) + + + y = yuv[0 : self.cb_offset].reshape(self.luma_shape) + u = yuv[self.cb_offset : self.cr_offset].reshape(self.chroma_shape) + v = yuv[self.cr_offset : ].reshape(self.chroma_shape) + except Exception as e: + print(str(e)) + return False, None, None, None + return True,y,u,v + +#Padding size and block size of luma is 2 times of chroma +def pad_n_ext2mul_blksz(y,u,v,ctu_sz,pad_sz): + pad_lh = math.ceil(y.shape[0]/(2*ctu_sz)) * 2 * ctu_sz + 4 * pad_sz + pad_lw = math.ceil(y.shape[1]/(2*ctu_sz)) * 2 * ctu_sz + 4 * pad_sz + pad_ch = int(pad_lh/2) + pad_cw = int(pad_lw/2) + y_pad = np.pad(y, pad_width=pad_sz*2, mode='edge') + u_pad = np.pad(u, pad_width=pad_sz, mode='edge') + v_pad = np.pad(v, pad_width=pad_sz, mode='edge') + + for i in range(y_pad.shape[0], pad_lh): + y_pad = np.append(y_pad, y_pad[-1, :].reshape(1, y_pad.shape[1]), 0) + for i in range(y_pad.shape[1], pad_lw): + y_pad = np.append(y_pad, y_pad[:, -1].reshape(y_pad.shape[0],1), 1) + + for i in range(u_pad.shape[0], pad_ch): + u_pad = np.append(u_pad, u_pad[-1, :].reshape(1, u_pad.shape[1]), 0) + v_pad = np.append(v_pad, v_pad[-1, :].reshape(1, v_pad.shape[1]), 0) + for i in range(u_pad.shape[1], pad_cw): + u_pad = np.append(u_pad, u_pad[:, -1].reshape(u_pad.shape[0],1), 1) + v_pad = np.append(v_pad, v_pad[:, -1].reshape(v_pad.shape[0],1), 1) + + return y_pad, u_pad, v_pad + +def pad_n_dump_ctus(args, inter_yuv_file): + + inter_yuv_basename = "_".join(os.path.splitext("DIV2K" + os.path.basename(inter_yuv_file))[0].split("_")[:2]) + #CTU size and padding size taken here are for Chroma + ctu_sz = int(args['ctu_size']/2) + pad_sz = int(args['pad_size']/2) + bitdepth = args['bit_depth'] + + output_dir = args['out_dir'] + if not os.path.exists(output_dir): + os.makedirs(output_dir) + + [width, height] = list(map(int, inter_yuv_basename.split("_")[1].split("x"))) + img_size = (height, width) + + + cap_orig = VideoCaptureYUV(inter_yuv_file, img_size, bitdepth) + frm_cnt = 0 + while 1: + + ret, y, u, v = cap_orig.read_raw() + if not ret: + print("Reached end of sequence, num of frames =", frm_cnt) + break + + y_pad, u_pad, v_pad = pad_n_ext2mul_blksz(y, u, v, ctu_sz, pad_sz) + + #Padded width and height + pad_lh = y_pad.shape[0] + pad_lw = y_pad.shape[1] + + for y in range(pad_sz*2, pad_lh-pad_sz*2, 2*ctu_sz): + for x in range(pad_sz*2, pad_lw-pad_sz*2, 2*ctu_sz): + + #Luma offsets + y_y_startoffset = y - pad_sz * 2 + y_y_endoffset = y + 2 * (ctu_sz + pad_sz) + y_x_startoffset = x - pad_sz * 2 + y_x_endoffset = x + 2 * (ctu_sz + pad_sz) + out_file_name = "_".join([inter_yuv_basename, + str(frm_cnt), + str(y_y_startoffset)+ "-" + str(y_x_startoffset)]) + ".yuv" + if os.path.exists(out_file_name): + continue + + y_orig = y_pad[y_y_startoffset:y_y_endoffset, y_x_startoffset:y_x_endoffset].flatten() + + #Chroma Offsets + uv_y_startoffset = int(y/2) - pad_sz + uv_y_endoffset = int(y/2) + ctu_sz + pad_sz + uv_x_startoffset = int(x/2) - pad_sz + uv_x_endoffset = int(x/2) + ctu_sz + pad_sz + u_orig = u_pad[uv_y_startoffset:uv_y_endoffset, uv_x_startoffset:uv_x_endoffset].flatten() + v_orig = v_pad[uv_y_startoffset:uv_y_endoffset, uv_x_startoffset:uv_x_endoffset].flatten() + + yuv_orig = np.concatenate((y_orig, u_orig, v_orig)) + + out_file_name = "_".join([inter_yuv_basename, + str(frm_cnt), + str(y_x_startoffset)+ "-" + str(y_y_startoffset)]) + ".yuv" + binary_file = open(os.path.join(args['out_dir'], out_file_name), "wb") + + binary_file.write(yuv_orig.tobytes()) + binary_file.close() + + frm_cnt += 1 + + return + +def get_resolution(file): + + ffmpeg_wt_cmd = " ".join(["ffprobe -v error -select_streams v:0", + "-show_entries stream=width", + "-print_format csv", + "-i", file]) + ffmpeg_ht_cmd = " ".join(["ffprobe -v error -select_streams v:0", + "-show_entries stream=height", + "-print_format csv", + "-i", file]) + + width = int(subprocess.getoutput(ffmpeg_wt_cmd).split(',')[1]) + height = int(subprocess.getoutput(ffmpeg_ht_cmd).split(',')[1]) + + return width,height + +if __name__=="__main__": + + args = parse_arguments() + + in_dir = args['input_dir'] + out_dir = args['out_dir'] + + for inp in os.listdir(in_dir): + input_path = os.path.join(in_dir, inp) + interm_yuv = None + + if os.path.splitext(inp)[1] == ".yuv": + interm_yuv = input_path + pad_n_dump_ctus(args, interm_yuv) + + elif os.path.splitext(inp)[1] == ".png": + width, height = get_resolution(input_path) + outfile_prefix = "-".join(os.path.splitext(inp)[0].split("_")) #converting '_' to '-' + outfile = "_".join([outfile_prefix, str(width)+"x"+str(height), "8bit", "420"]) + interm_yuv = os.path.join(out_dir, outfile+".yuv") + + ffmpeg_cmd = " ".join(["ffmpeg", "-v error", "-i", input_path, + "-pix_fmt", "yuv420p", interm_yuv]) + status = os.system(ffmpeg_cmd) + if status: + print("Error while converting "+ input_path+" to yuv") + + pad_n_dump_ctus(args, interm_yuv) + + if args['del_inter_res']: + os.remove(interm_yuv) + + else: + print("Error: Unsupported input type") diff --git a/training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/div2k_png_2_yuv.py b/training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/div2k_png_2_yuv.py new file mode 100644 index 0000000000..d575edab21 --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/div2k_png_2_yuv.py @@ -0,0 +1,55 @@ +import os +import argparse +import subprocess + +def parse_arguments(): + parser = argparse.ArgumentParser() + parser.add_argument('--input_dir', type=str, + help="Path to the folder where png files are present", + required=True) + parser.add_argument('--out_dir', type=str, + help="Path to the directory where outputs to be stored", + required=True) + return vars(parser.parse_args()) + +def get_resolution(file): + + ffmpeg_wt_cmd = " ".join(["ffprobe -v error -select_streams v:0", + "-show_entries stream=width", + "-print_format csv", + "-i", file]) + ffmpeg_ht_cmd = " ".join(["ffprobe -v error -select_streams v:0", + "-show_entries stream=height", + "-print_format csv", + "-i", file]) + + width = int(subprocess.getoutput(ffmpeg_wt_cmd).split(',')[1]) + height = int(subprocess.getoutput(ffmpeg_ht_cmd).split(',')[1]) + + return width,height + +if __name__=="__main__": + + args = parse_arguments() + + in_dir = args['input_dir'] + out_dir = args['out_dir'] + + for inp in os.listdir(in_dir): + input_path = os.path.join(in_dir, inp) + interm_yuv = None + + if os.path.splitext(inp)[1] == ".png": + width, height = get_resolution(input_path) + outfile_prefix = "-".join(os.path.splitext(inp)[0].split("_")) #converting '_' to '-' + outfile = "_".join([outfile_prefix, str(width)+"x"+str(height), "8bit", "420"]) + interm_yuv = os.path.join(out_dir, outfile+".yuv") + + ffmpeg_cmd = " ".join(["ffmpeg", "-v error", "-i", input_path, + "-pix_fmt", "yuv420p", interm_yuv]) + status = os.system(ffmpeg_cmd) + if status: + print("Error while converting "+ input_path+" to yuv") + + else: + print("Error: Unsupported input type") diff --git a/training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/gt_bvidvc_datagen.py b/training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/gt_bvidvc_datagen.py new file mode 100644 index 0000000000..112109ba0d --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/gt_bvidvc_datagen.py @@ -0,0 +1,186 @@ +import os +import numpy as np +import math +import argparse +from sys import stderr + +def str2bool(v): + if isinstance(v, bool): + return v + if v.lower() in ('yes', 'y', 'true', 't', '1'): + return True + elif v.lower() in ('no', 'n', 'false', 'f', '0'): + return False + else: + raise argparse.ArgumentTypeError('Boolean value expected.') + +def parse_arguments(): + parser = argparse.ArgumentParser() + parser.add_argument('--dump_ctus', type=str2bool, + help="Whether to dump ctus, default=True", + default = True, required=False) + parser.add_argument('--stream', type=str, + help="Path to the input stream/yuv", required=False) + parser.add_argument('--ctu_size', type=int, + help="CTU size that is used for training NNLF, default=128", + default=128, required=False) + parser.add_argument('--pad_size', type=int, + help="Padding size across one side of the CTU, default=0", + default=0, required=False) + parser.add_argument('--out_dir', type=str, + help="Path to the directory where outputs should be stored", + required=False) + parser.add_argument('--del_recon_bs', type=str2bool, + help="Do you want to delete the intermediate yuvs, default=True", + default=True, required=False) + parser.add_argument('--bit_depth', type=int, + help="Bit depth of the input, Default=10", + default=10, required=False) + return vars(parser.parse_args()) + +class VideoCaptureYUV: + def __init__(self, filename, size, bitdepth): + self.height, self.width = size + self.bitdepth = bitdepth + self.f = open(filename, 'rb') + self.frame_len = int(self.width * self.height * 3 / 2) + self.data_type = np.uint8 + if self.bitdepth > 8: + self.frame_len *= 2 + self.data_type = np.uint16 + self.luma_shape = (self.height, self.width) + self.chroma_shape = (int(self.height/2), int(self.width/2)) + self.cb_offset = self.height * self.width + self.cr_offset = self.height * self.width + int(self.height * self.width / 4) + def read_raw(self): + try: + raw = self.f.read(self.frame_len) + yuv = np.frombuffer(raw, dtype=self.data_type) + + + y = yuv[0 : self.cb_offset].reshape(self.luma_shape) + u = yuv[self.cb_offset : self.cr_offset].reshape(self.chroma_shape) + v = yuv[self.cr_offset : ].reshape(self.chroma_shape) + except Exception as e: + print(str(e)) + return False, None, None, None + return True,y,u,v + +#Padding size and block size of luma is 2 times of chroma +def pad_n_ext2mul_blksz(y,u,v,ctu_sz,pad_sz): + pad_lh = math.ceil(y.shape[0]/(2*ctu_sz)) * 2 * ctu_sz + 4 * pad_sz + pad_lw = math.ceil(y.shape[1]/(2*ctu_sz)) * 2 * ctu_sz + 4 * pad_sz + pad_ch = int(pad_lh/2) + pad_cw = int(pad_lw/2) + y_pad = np.pad(y, pad_width=pad_sz*2, mode='edge') + u_pad = np.pad(u, pad_width=pad_sz, mode='edge') + v_pad = np.pad(v, pad_width=pad_sz, mode='edge') + + for i in range(y_pad.shape[0], pad_lh): + y_pad = np.append(y_pad, y_pad[-1, :].reshape(1, y_pad.shape[1]), 0) + for i in range(y_pad.shape[1], pad_lw): + y_pad = np.append(y_pad, y_pad[:, -1].reshape(y_pad.shape[0],1), 1) + + for i in range(u_pad.shape[0], pad_ch): + u_pad = np.append(u_pad, u_pad[-1, :].reshape(1, u_pad.shape[1]), 0) + v_pad = np.append(v_pad, v_pad[-1, :].reshape(1, v_pad.shape[1]), 0) + for i in range(u_pad.shape[1], pad_cw): + u_pad = np.append(u_pad, u_pad[:, -1].reshape(u_pad.shape[0],1), 1) + v_pad = np.append(v_pad, v_pad[:, -1].reshape(v_pad.shape[0],1), 1) + + return y_pad, u_pad, v_pad + + +def pad_n_dump_ctus(args, inter_yuv_file): + + inter_yuv_basename = "_".join(os.path.splitext(os.path.basename(inter_yuv_file))[0].split("_")[:2]) + #CTU size and padding size taken here are for Chroma + ctu_sz = int(args['ctu_size']/2) + pad_sz = int(args['pad_size']/2) + bitdepth = args['bit_depth'] + + output_dir = args['out_dir'] + if not os.path.exists(output_dir): + os.makedirs(output_dir) + + [width, height] = list(map(int, inter_yuv_basename.split("_")[1].split("x"))) + img_size = (height, width) + + + cap_orig = VideoCaptureYUV(inter_yuv_file, img_size, bitdepth) + frm_cnt = 0 + while 1: + + ret, y, u, v = cap_orig.read_raw() + if not ret: + print("Reached end of sequence, num of frames =", frm_cnt) + break + + y_pad, u_pad, v_pad = pad_n_ext2mul_blksz(y, u, v, ctu_sz, pad_sz) + + #Padded width and height + pad_lh = y_pad.shape[0] + pad_lw = y_pad.shape[1] + + for y in range(pad_sz*2, pad_lh-pad_sz*2, 2*ctu_sz): + for x in range(pad_sz*2, pad_lw-pad_sz*2, 2*ctu_sz): + + #Luma offsets + y_y_startoffset = y - pad_sz * 2 + y_y_endoffset = y + 2 * (ctu_sz + pad_sz) + y_x_startoffset = x - pad_sz * 2 + y_x_endoffset = x + 2 * (ctu_sz + pad_sz) + out_file_name = "_".join([inter_yuv_basename, + str(frm_cnt), + str(y_y_startoffset)+ "-" + str(y_x_startoffset)]) + ".yuv" + if os.path.exists(out_file_name): + continue + + y_orig = y_pad[y_y_startoffset:y_y_endoffset, y_x_startoffset:y_x_endoffset].flatten() + + #Chroma Offsets + uv_y_startoffset = int(y/2) - pad_sz + uv_y_endoffset = int(y/2) + ctu_sz + pad_sz + uv_x_startoffset = int(x/2) - pad_sz + uv_x_endoffset = int(x/2) + ctu_sz + pad_sz + u_orig = u_pad[uv_y_startoffset:uv_y_endoffset, uv_x_startoffset:uv_x_endoffset].flatten() + v_orig = v_pad[uv_y_startoffset:uv_y_endoffset, uv_x_startoffset:uv_x_endoffset].flatten() + + yuv_orig = np.concatenate((y_orig, u_orig, v_orig)) + + out_file_name = "_".join([inter_yuv_basename, + str(frm_cnt), + str(y_x_startoffset)+ "-" + str(y_y_startoffset)]) + ".yuv" + binary_file = open(os.path.join(args['out_dir'], out_file_name), "wb") + + binary_file.write(yuv_orig.tobytes()) + binary_file.close() + + frm_cnt += 1 + + return + +if __name__=="__main__": + args = parse_arguments() + + yuv_file = None + delete_inter_yuv = False + if os.path.splitext(args['stream'])[1] != ".yuv": + yuv_file = os.path.join(args['out_dir'], os.path.splitext(os.path.basename(args['stream']))[0] + ".yuv") + decompr_cmd = " ".join(["ffmpeg -v error", "-i", args['stream'], "-pix_fmt", "yuv420p10le", "-y", + yuv_file]) + status = os.system(decompr_cmd) + if status: + print("Failed for cmd "+ decompr_cmd, file=stderr) + exit(-1) + delete_inter_yuv = args['del_recon_bs'] + else: + yuv_file = args['stream'] + + if args['dump_ctus']: + pad_n_dump_ctus(args, yuv_file) + + + if delete_inter_yuv: + os.remove(yuv_file) + print("Done") \ No newline at end of file diff --git a/training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/gt_bvidvc_parallel_runs.py b/training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/gt_bvidvc_parallel_runs.py new file mode 100644 index 0000000000..d59b62470b --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/1_generate_raw_data/gt_bvidvc_parallel_runs.py @@ -0,0 +1,44 @@ +import os +import argparse +from multiprocessing import Pool +from sys import stderr + +def parse_arguments(): + parser = argparse.ArgumentParser() + parser.add_argument('--stream_dir', type=str, + help="Path to the input streams/yuvs directory", required=True) + parser.add_argument('--out_dir', type=str, + help="Path to the directory where outputs should be stored", + required=True) + parser.add_argument('--num_cores', type=int, + help="Number of gt_data_gen.py runs to be triggered, default=1", + default=1, required=False) + return vars(parser.parse_args()) + +def run_cmd(inp_cmd): + status = os.system(inp_cmd) + # print(inp_cmd) + if status: + print("Failed for cmd "+ inp_cmd, file=stderr) + return + +def run_multiple_cmds(list_inp_cmd, num_cores): + pool = Pool(processes=num_cores) + pool.map_async(run_cmd, list_inp_cmd) + pool.close() + pool.join() + return + +if __name__=="__main__": + + args = parse_arguments() + + stream_dir = args['stream_dir'] + cmd_list = [] + for stream_file in os.listdir(stream_dir): + cmd = " ".join(["python gt_bvidvc_datagen.py", + "--stream", os.path.join(stream_dir, stream_file), + "--out_dir", args['out_dir']]) + + cmd_list.append(cmd) + run_multiple_cmds(cmd_list, args['num_cores']) \ No newline at end of file diff --git a/training/training_scripts/Nn_Filtering_Set_LC/2_generate_compression_data/encode_streams.py b/training/training_scripts/Nn_Filtering_Set_LC/2_generate_compression_data/encode_streams.py new file mode 100644 index 0000000000..83d161b633 --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/2_generate_compression_data/encode_streams.py @@ -0,0 +1,91 @@ +import os +import argparse +import re + +def parse_arguments(): + parser = argparse.ArgumentParser() + parser.add_argument('--input_dir', type=str, + help="Path to the folder where yuv files are present", + required=True) + parser.add_argument('--output_dir', type=str, + help="Path to the directory where outputs to be stored", + required=True) + parser.add_argument('--enc_exe', type=str, + help="Path to the encoder executable", + required=False, default="./EncoderApp") + parser.add_argument('--enc_type', type=str, + help="RA: Random Access, AI: All Intra", + required=True) + parser.add_argument('--model_paths', type=str, + help="Model files paths,separated by comma needed if it is RA. Eg: model0,model1,model2,model4", + required=False, default=None) + return vars(parser.parse_args()) + +if __name__=="__main__": + + qp_list = [20,25,30,35,40,45] + + args = parse_arguments() + + in_dir = args['input_dir'] + out_dir = args['output_dir'] + enc_exe = args['enc_exe'] + enc_type = args['enc_type'] + + cfg_file = None + if enc_type == "AI": + cfg_file = "encoder_intra_vtm.cfg" + num_frames = 1 + inp_file_parser = re.compile(r'(\w+)_(\d+x\d+)_(\d+)bit_(\d+).yuv') + elif enc_type == "RA": + cfg_file = "encoder_randomaccess_vtm.cfg" + num_frames = 64 + inp_file_parser = re.compile(r'(\w+)_(\d+x\d+)_(\d+)fps_(\d+)bit_(\d+).yuv') + if args['model_paths'] == None: + print("Error model files needs to be present for BVIDVC encoding for RA") + exit(-1) + else: + print("Error: Unknown encoding type") + exit(-1) + + for inp in os.listdir(in_dir): + input_path = os.path.join(in_dir, inp) + match_obj = inp_file_parser.match(inp) + if not match_obj: + # print("Skipping input "+inp+" due to unsupported naming convention") + continue + inp_basename = os.path.splitext(inp)[0] + inp_bitdepth = int(inp_basename.split("_")[-2][:-3]) + chroma_fmt = int(inp_basename.split("_")[-1]) + if enc_type == "RA": + fps = int(inp_basename.split("_")[2][:-3]) #ignoring last 3 elements i.e. fps + else: + fps = 30 + frame_skip = 0 + width, height = inp_basename.split("_")[1].split("x") + + enc_cmd_part1 = " ".join([enc_exe, + "-c", cfg_file, + "-i", input_path, + "--InputBitDepth="+str(inp_bitdepth), + "--InputChromaFormat="+str(chroma_fmt), + "--FrameRate="+str(fps), + "--FrameSkip="+str(frame_skip), + "--SourceWidth="+str(width), + "--SourceHeight="+str(height), + "--ConformanceWindowMode=1", #To handle non-multiple of 8 + "--FramesToBeEncoded="+str(num_frames)]) + + if enc_type == "RA": + intra_period = 64 + enc_cmd_part1 = " ".join([enc_cmd_part1, "-ip", str(intra_period), "--NnlfOption=3", + "--LCModelPath="+args['model_paths']]) + + for qp in qp_list: + enc_out_file = "_".join([enc_type, str(qp)+"qp", inp_basename]) + ".266" + enc_out_path = os.path.join(out_dir, enc_out_file) + + enc_cmd = " ".join([enc_cmd_part1, + "-q", str(qp), + "-b", enc_out_path]) + print(enc_cmd) \ No newline at end of file diff --git a/training/training_scripts/Nn_Filtering_Set_LC/2_generate_compression_data/training_data_gen.py b/training/training_scripts/Nn_Filtering_Set_LC/2_generate_compression_data/training_data_gen.py new file mode 100644 index 0000000000..2f40d89545 --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/2_generate_compression_data/training_data_gen.py @@ -0,0 +1,271 @@ +import os +import numpy as np +import math +import argparse +from sys import stderr +import random +import itertools + +def str2bool(v): + if isinstance(v, bool): + return v + if v.lower() in ('yes', 'y', 'true', 't', '1'): + return True + elif v.lower() in ('no', 'n', 'false', 'f', '0'): + return False + else: + raise argparse.ArgumentTypeError('Boolean value expected.') + +def parse_arguments(): + parser = argparse.ArgumentParser() + parser.add_argument('--dec_exe', type=str, + help="Path to the decoder executable", + default=None, required=False) + parser.add_argument('--dump_ctus', type=str2bool, + help="Whether to dump ctus, default=True", + required=True) + parser.add_argument('--stream', type=str, + help="Path to the input stream", required=False) + parser.add_argument('--DumpBasename', type=str, + help="Path to the reconstructed yuv file before deblocking", + required=False) + parser.add_argument('--out_dir', type=str, + help="Path to the directory where outputs should be stored", + required=False) + parser.add_argument('--decoder_log', type=str, + help="Path to the decoder output log file", + default=None, required=False) + parser.add_argument('--del_recon_bs', type=str2bool, + help="DO you want to delete the decoded and bs info yuvs", + default=True, required=False) + parser.add_argument('--bit_depth', type=int, + help="Bit depth of the recon and bs yuv files, default=10", + default=10, required=False) + parser.add_argument('--ctu_size', type=int, + help="CTU size that is used for training NNLF, default=128", + default=128, required=False) + parser.add_argument('--pad_size', type=int, + help="Padding size across one side of the CTU, default=8", + default=8, required=False) + parser.add_argument('--ModelPath', type=str, + help="NNLF Models to be used by decoder when the encoder_cfg is RA", + default="", required=False) + parser.add_argument('--encode_cfg', type=str, + help="RA or AI, default=AI", default="AI", required=False) + parser.add_argument('--subsample_factor', type=int, + help="Subsample factor in a frame, default=1, "+ \ + "totalCTUs/subsample_factor number of CTUs will be picked randomly from frame", default=1, + required=False) + args = parser.parse_args() + if (not args.ModelPath) and (args.encode_cfg == 'RA'): + parser.error('--ModelPath should be provided when --encode_cfg=RA') + + return vars(args) + +class VideoCaptureYUV: + def __init__(self, filename, size, bitdepth): + self.height, self.width = size + self.bitdepth = bitdepth + self.f = open(filename, 'rb') + self.frame_len = int(self.width * self.height * 3 / 2) + self.data_type = np.uint8 + if self.bitdepth > 8: + self.frame_len *= 2 + self.data_type = np.uint16 + self.luma_shape = (self.height, self.width) + self.chroma_shape = (int(self.height/2), int(self.width/2)) + self.cb_offset = self.height * self.width + self.cr_offset = self.height * self.width + int(self.height * self.width / 4) + def read_raw(self): + try: + raw = self.f.read(self.frame_len) + yuv = np.frombuffer(raw, dtype=self.data_type) + + + y = yuv[0 : self.cb_offset].reshape(self.luma_shape) + u = yuv[self.cb_offset : self.cr_offset].reshape(self.chroma_shape) + v = yuv[self.cr_offset : ].reshape(self.chroma_shape) + except Exception as e: + print(str(e)) + return False, None, None, None + return True,y,u,v + +#Padding size and block size of luma is 2 times of chroma +def pad_n_ext2mul_blksz(y,u,v,ctu_sz,pad_sz): + pad_lh = math.ceil(y.shape[0]/(2*ctu_sz)) * 2 * ctu_sz + 4 * pad_sz + pad_lw = math.ceil(y.shape[1]/(2*ctu_sz)) * 2 * ctu_sz + 4 * pad_sz + pad_ch = int(pad_lh/2) + pad_cw = int(pad_lw/2) + y_pad = np.pad(y, pad_width=pad_sz*2, mode='edge') + u_pad = np.pad(u, pad_width=pad_sz, mode='edge') + v_pad = np.pad(v, pad_width=pad_sz, mode='edge') + + for i in range(y_pad.shape[0], pad_lh): + y_pad = np.append(y_pad, y_pad[-1, :].reshape(1, y_pad.shape[1]), 0) + for i in range(y_pad.shape[1], pad_lw): + y_pad = np.append(y_pad, y_pad[:, -1].reshape(y_pad.shape[0],1), 1) + + for i in range(u_pad.shape[0], pad_ch): + u_pad = np.append(u_pad, u_pad[-1, :].reshape(1, u_pad.shape[1]), 0) + v_pad = np.append(v_pad, v_pad[-1, :].reshape(1, v_pad.shape[1]), 0) + for i in range(u_pad.shape[1], pad_cw): + u_pad = np.append(u_pad, u_pad[:, -1].reshape(u_pad.shape[0],1), 1) + v_pad = np.append(v_pad, v_pad[:, -1].reshape(v_pad.shape[0],1), 1) + + return y_pad, u_pad, v_pad + +def get_offsets(wd,ht,blkSize,subsamplingfactor): + blocksX = math.ceil(wd/blkSize) + blocksY = math.ceil(ht/blkSize) + reqd_blocks = int(blocksX * blocksY / subsamplingfactor) + lst_x = list(range(0,wd, blkSize)) + lst_y = list(range(0,ht, blkSize)) + + offset_list = [element for element in itertools.product(lst_y, lst_x)] + subs_off_list = random.sample(offset_list, reqd_blocks) + + return subs_off_list + +def parse_dec_logs(dec_log_file): + f = open(dec_log_file, 'r') + Lines = f.readlines() + + frame_info_dict = {} + + for line in Lines: + if line.startswith('POC'): + words = line.split() + index_poc = words.index('POC') + index_qp = words.index('QP') + + slicetype = None + if "I-SLICE" in line: + slicetype = "I" + elif "B-SLICE" in line: + slicetype = "B" + else: + print("Error: Unrecognized frame/slice type") + exit(-1) + + poc_num = int(words[index_poc+1]) + frame_info_dict[poc_num] = { 'poc': poc_num, + 'qp': int(words[index_qp+1]), + 'slicetype': slicetype} + + return frame_info_dict + + +def pad_n_dump_ctus(args): + + recon_basename = os.path.splitext(os.path.basename(args['DumpBasename']+"_rec_before_dbf.yuv"))[0] + recon_basename = "_".join(recon_basename.split("_")[:-3]) + #CTU size and padding size taken here are for Chroma + ctu_sz = int(args['ctu_size']/2) + pad_sz = int(args['pad_size']/2) + bitdepth = args['bit_depth'] + + output_dir = args['out_dir'] + if not os.path.exists(output_dir): + os.makedirs(output_dir) + + [width, height] = list(map(int, recon_basename.split("_")[1].split("x"))) + img_size = (height, width) + sub_factor = args['subsample_factor'] + print("Subfactor", sub_factor) + cap_recon = VideoCaptureYUV(args['DumpBasename']+"_rec_before_dbf.yuv", img_size, bitdepth) + cap_bs = VideoCaptureYUV(args['DumpBasename']+"_bs.yuv", img_size, bitdepth) + dec_log = args['decoder_log'] + frame_info_dict = parse_dec_logs(dec_log) + + BS_Lut = {0:0,512:1,1023:2} + + + + frm_cnt = 0 + while 1: + ret, y, u, v = cap_recon.read_raw() + + if not ret: + print("Reached end of sequence, num of frames =", frm_cnt) + break + ret, bsy, bsu, bsv = cap_bs.read_raw() + if not ret: + print("Reached end of sequence, num of frames =", frm_cnt) + break + + y_pad, u_pad, v_pad = pad_n_ext2mul_blksz(y, u, v, ctu_sz, pad_sz) + bs_yp, bs_up, bs_vp = pad_n_ext2mul_blksz(bsy, bsu, bsv, ctu_sz, pad_sz) + offset_list = get_offsets(width, height, 2 * ctu_sz, sub_factor) + + # JVET-CTC for AI will only encode frames with interval 8 + if args['encode_cfg'] == 'AI': + poc = frame_info_dict[frm_cnt]['poc'] * 8 + else: + poc = frame_info_dict[frm_cnt]['poc'] + #Skip I frame for RA + if frame_info_dict[frm_cnt]['slicetype'] == "I": + frm_cnt += 1 + continue + + + for offset in offset_list: + y_y_startoffset = offset[0] + y_y_endoffset = offset[0] + 2 * (ctu_sz + 2 * pad_sz) + y_x_startoffset = offset[1] + y_x_endoffset = offset[1] + 2 * (ctu_sz + 2 * pad_sz) + uv_y_startoffset = int(offset[0]/2) + uv_y_endoffset = int(offset[0]/2) + ctu_sz + 2 * pad_sz + uv_x_startoffset = int(offset[1]/2) + uv_x_endoffset = int(offset[1]/2) + ctu_sz + 2 * pad_sz + + map_cu = [np.uint16((y_pad[y_y_startoffset:y_y_endoffset:2, y_x_startoffset:y_x_endoffset:2] << 6)+np.vectorize(BS_Lut.get)(bs_yp[y_y_startoffset:y_y_endoffset:2, y_x_startoffset:y_x_endoffset:2])), + y_pad[y_y_startoffset:y_y_endoffset:2, y_x_startoffset+1:y_x_endoffset:2], + y_pad[y_y_startoffset+1:y_y_endoffset:2, y_x_startoffset:y_x_endoffset:2], + y_pad[y_y_startoffset+1:y_y_endoffset:2, y_x_startoffset+1:y_x_endoffset:2]] + map_cu.extend([np.uint16((u_pad[uv_y_startoffset:uv_y_endoffset, uv_x_startoffset:uv_x_endoffset] << 6)+ np.vectorize(BS_Lut.get)(bs_up[uv_y_startoffset:uv_y_endoffset, uv_x_startoffset:uv_x_endoffset])),np.uint16((v_pad[uv_y_startoffset:uv_y_endoffset, uv_x_startoffset:uv_x_endoffset] << 6)+ np.vectorize(BS_Lut.get)(bs_vp[uv_y_startoffset:uv_y_endoffset, uv_x_startoffset:uv_x_endoffset]))]) + + out_file_name = "_".join([recon_basename, + str(frame_info_dict[frm_cnt]['qp']), + str(poc), + str(y_x_startoffset)+ "-" + str(y_y_startoffset)]) + ".npy" + + np.save(os.path.join(output_dir, out_file_name), map_cu) + frm_cnt += 1 + return + +def generate_dec_cmd(decexe, input, extraoptions=[], output=None, console_log=""): + dec_cmd_list = [decexe, "-b", input] + for extraoption in extraoptions: + dec_cmd_list.append(extraoption) + if output: + dec_cmd_list.extend(["-o", output]) + if console_log: + dec_cmd_list.extend([">", console_log, "2>&1"]) + return " ".join(dec_cmd_list) + +if __name__=="__main__": + args = parse_arguments() + if args['dec_exe']: + if args['encode_cfg'] == 'RA': + dec_cmd = generate_dec_cmd(args['dec_exe'], args['stream'], + output = "/dev/null", + extraoptions=["--DumpBasename="+args['DumpBasename'], "--LCModelPath="+args['ModelPath']], + console_log=args['decoder_log']) + else: + dec_cmd = generate_dec_cmd(args['dec_exe'], args['stream'], + output = "/dev/null", + extraoptions=["--DumpBasename="+args['DumpBasename']], + console_log=args['decoder_log']) + status = os.system(dec_cmd) + if status: + print("Failed for cmd "+ dec_cmd, file=stderr) + exit(-1) + + if args['dump_ctus']: + pad_n_dump_ctus(args) + + + if args['del_recon_bs']: + os.system("rm " + args['DumpBasename']+"_*") + # os.remove(args['DumpBasename']+"_rec_before_dbf.yuv") + print("Done") diff --git a/training/training_scripts/Nn_Filtering_Set_LC/2_generate_compression_data/training_data_gen_parallel.py b/training/training_scripts/Nn_Filtering_Set_LC/2_generate_compression_data/training_data_gen_parallel.py new file mode 100644 index 0000000000..9d88582da7 --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/2_generate_compression_data/training_data_gen_parallel.py @@ -0,0 +1,89 @@ +import os +import argparse +from multiprocessing import Pool +from sys import stderr +import sys + +def parse_arguments(): + parser = argparse.ArgumentParser() + parser.add_argument('--dec_exe', type=str, + help="Path to the decoder executable", required=False, + default="./DecoderApp") + parser.add_argument('--stream_dir', type=str, + help="Path to the input streams directory", required=True) + parser.add_argument('--consol_dir', type=str, + help="Path to dump the console logs (Can't be same as out_dir)", + required=True) + parser.add_argument('--out_dir', type=str, + help="Path to the output directory", + required=True) + parser.add_argument('--num_cores', type=int, + help="Number of training_data_gen.py runs to be triggered, default=1", + default=1, required=False) + parser.add_argument('--subsample_factor', type=int, + help="Subsample factor in a frame, default=1, "+ \ + "totalCTUs/subsample_factor number of CTUs will be picked randomly from frame", default=1, + required=False) + parser.add_argument('--encode_cfg',type=str,help="Type of streams in --streams_dir AI or RA, default is AI",default='AI',required=False) + parser.add_argument('--ModelPath',type=str, + help="NNLF Models used by decoder when --encoder_cfg is RA",default=None,required=False) + + args = parser.parse_args(); + + if (not args.ModelPath) and (args.encode_cfg == 'RA'): + parser.error('--ModelPath should be provided when --encode_cfg=RA') + + return vars(args) + +def run_cmd(inp_cmd): + status = os.system(inp_cmd) + if status: + print("Failed for cmd "+ inp_cmd, file=stderr) + return + +def run_multiple_cmds(list_inp_cmd, num_cores): + pool = Pool(processes=num_cores) + pool.map_async(run_cmd, list_inp_cmd) + pool.close() + pool.join() + return + +if __name__=="__main__": + + args = parse_arguments() + + stream_dir = args['stream_dir'] + cmd_list = [] + for stream_file in os.listdir(stream_dir): + if os.path.splitext(stream_file)[1] not in [".266", ".bit"]: + continue + stream_file_split = stream_file.split("_") + qp = stream_file_split[1][:-2] + inter_file_prefix = "_".join([stream_file_split[2], stream_file_split[3], str(qp)]) + interm_dir = args['consol_dir'] + if not os.path.exists(interm_dir): + os.makedirs(interm_dir) + + if not os.path.exists(args['out_dir']): + os.makedirs(args['out_dir']) + + dump_basename = os.path.join(interm_dir, inter_file_prefix) + console_file = os.path.join(interm_dir, inter_file_prefix + "_console.txt") + dec_log_file = os.path.join(interm_dir, inter_file_prefix + "_dec_log.txt") + dump_ctus = 1 + + enc_cmd_list = ["python", os.path.join(os.path.dirname(sys.argv[0]), "training_data_gen.py"), + "--dec_exe", args['dec_exe'], + "--dump_ctus", str(dump_ctus), + "--stream", os.path.join(stream_dir, stream_file), + "--DumpBasename", dump_basename, + "--out_dir", args['out_dir'], + "--subsample_factor", str(args['subsample_factor']), + "--decoder_log",dec_log_file, + "--encode_cfg",args['encode_cfg']] + if args['ModelPath']: + enc_cmd_list.extend(["--ModelPath",args['ModelPath']]) + cmd = " ".join(enc_cmd_list) + + cmd_list.append(cmd + " > " + console_file) + run_multiple_cmds(cmd_list, args['num_cores']) diff --git a/training/training_scripts/Nn_Filtering_Set_LC/3_training/AI_LC-CPDF_model0_cfg.json b/training/training_scripts/Nn_Filtering_Set_LC/3_training/AI_LC-CPDF_model0_cfg.json new file mode 100644 index 0000000000..985d093e8f --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/3_training/AI_LC-CPDF_model0_cfg.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a04d81203a9bb034e6bd5160c31dccd431f6a401b81f58d3307a921818dd43e7 +size 1172 diff --git a/training/training_scripts/Nn_Filtering_Set_LC/3_training/AI_LC-CPDF_model1_cfg.json b/training/training_scripts/Nn_Filtering_Set_LC/3_training/AI_LC-CPDF_model1_cfg.json new file mode 100644 index 0000000000..f3c69fcd0c --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/3_training/AI_LC-CPDF_model1_cfg.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8f3394e1fb2a7bfd3414e44091e1f950d125bef588c0a096cf5916c454f261df +size 1172 diff --git a/training/training_scripts/Nn_Filtering_Set_LC/3_training/AI_LC-baseline_cfg.json b/training/training_scripts/Nn_Filtering_Set_LC/3_training/AI_LC-baseline_cfg.json new file mode 100644 index 0000000000..6d4f2b40e3 --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/3_training/AI_LC-baseline_cfg.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af1f2f8239c978a455c1b440498055e151dc1279e686a571de04562e1b36ab74 +size 863 diff --git a/training/training_scripts/Nn_Filtering_Set_LC/3_training/NNLF_data_loader.py b/training/training_scripts/Nn_Filtering_Set_LC/3_training/NNLF_data_loader.py new file mode 100644 index 0000000000..3204f89605 --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/3_training/NNLF_data_loader.py @@ -0,0 +1,129 @@ +from tensorflow.keras.utils import Sequence +import math +import numpy as np +import os + +class X0140_data_loader(Sequence): + + def __init__(self, x_set, y_set, batch_size=16, luma_patch_size=128, is_train_data=True, augment_data=False): + self.x, self.y = x_set, y_set + self.batch_size = batch_size + self.patch_size = luma_patch_size + self.is_train_data = is_train_data + self.augment_data = augment_data + # Boundary Strength mapping + self.BS_Lut = np.array([0,512,1023]) + + def __len__(self): + return math.ceil(len(self.x) / self.batch_size) + + def parse_recon_file(self, filename, flip=-1, rot=0): + input = np.load(filename) + + # Extract BS Info and Recon values + input = np.append(input, [(input[0,:,:] & 0x3F),(input[4,:,:] & 0x3F),(input[5,:,:] & 0x3F)], axis=0) + input[0,:,:] = (input[0,:,:] >> 6) + input[4,:,:] = (input[4,:,:] >> 6) + input[5,:,:] = (input[5,:,:] >> 6) + + # Map BS to actual values used in VTM-NNVC. + input[6,:,:] = self.BS_Lut[input[6,:,:]] + input[7,:,:] = self.BS_Lut[input[7,:,:]] + input[8,:,:] = self.BS_Lut[input[8,:,:]] + + input = np.float32(input) + input = input / 1023.0 + test_basename = os.path.splitext(os.path.basename(filename))[0] + qp = np.float32(test_basename.split("_")[3]) + qpin = np.power(2, (qp-42.)/6.) + qpPlane = np.full_like(input[0,:,:], qpin) + input = np.insert(input, 6, qpPlane, axis=0) + + input = np.moveaxis(input, 0, 2) + + if flip==-1 and rot==0: + return input + + # Compose channel Y to single plane and rotate, flip + Yrec = np.empty((2*input.shape[0], 2*input.shape[1])) + Yrec[::2,::2] = input[:,:,0] + Yrec[::2, 1::2] = input[:,:,1] + Yrec[1::2, ::2] = input[:,:,2] + Yrec[1::2, 1::2] = input[:,:,3] + + if(-1 != flip): + Yrec = np.flip(Yrec, axis = flip) + input[:,:,4:] = np.flip(input[:,:,4:], axis = flip) + + input[:,:,4:] = np.rot90(input[:,:,4:], k = rot) + Yrec = np.rot90(Yrec, k = rot) + + input[:,:,0] = Yrec[::2,::2] + input[:,:,1] = Yrec[::2, 1::2] + input[:,:,2] = Yrec[1::2, ::2] + input[:,:,3] = Yrec[1::2, 1::2] + + + return input + + def parse_orig_yuv(self,yuv_file, flip=-1, rot=0): + + datatype = np.uint16 + shift = 0 + offset_multiplier = 2 + + if "DIV2K" in yuv_file: + datatype = np.uint8 + shift = 2 + offset_multiplier = 1 + + num_luma_pixels_in_patch = self.patch_size*self.patch_size + Yarr = np.fromfile(yuv_file, dtype=datatype, count=num_luma_pixels_in_patch) + Yarr = Yarr.reshape(self.patch_size, self.patch_size) + Uarr = np.fromfile(yuv_file, dtype=datatype, count=(num_luma_pixels_in_patch)>>2, offset=offset_multiplier*num_luma_pixels_in_patch) + Uarr = Uarr.reshape(self.patch_size>>1, self.patch_size>>1) + Varr = np.fromfile(yuv_file, dtype=datatype, count=(num_luma_pixels_in_patch)>>2, offset=(offset_multiplier*((num_luma_pixels_in_patch*5)>>2))) + Varr = Varr.reshape(self.patch_size>>1, self.patch_size>>1) + + # Flip and rotate + if(-1 != flip): + Yarr = np.flip(Yarr, axis = flip) + Uarr = np.flip(Uarr, axis = flip) + Varr = np.flip(Varr, axis = flip) + + Yarr = np.rot90(Yarr, k = rot) + Uarr = np.rot90(Uarr, k = rot) + Varr = np.rot90(Varr, k = rot) + + Yarr_1 = Yarr[::2,::2] + Yarr_2 = Yarr[::2, 1::2] + Yarr_3 = Yarr[1::2, ::2] + Yarr_4 = Yarr[1::2, 1::2] + + orig_yuv = np.stack((Yarr_1,Yarr_2,Yarr_3,Yarr_4,Uarr,Varr)) + orig_yuv = np.float32(np.uint16(orig_yuv)<<shift)/1023. + + orig_yuv = np.moveaxis(orig_yuv, 0, 2) + + return orig_yuv + + def __getitem__(self, idx): + batch_x = self.x[idx * self.batch_size:(idx + 1) * self.batch_size] + batch_y = self.y[idx * self.batch_size:(idx + 1) * self.batch_size] + + # data augmentation + if self.is_train_data and self.augment_data: + # 0 = 0 deg, 1 = 90 deg, 2 = 180 deg, 3 = 270 deg + rot_choice = np.random.choice([0,1,2,3], self.batch_size) + #1 = Horizontal Flip -1 = no flip, #vertical flip is redundant with rotation combinations + flip_choice = np.random.choice([1,-1], self.batch_size) + data = np.array([self.parse_recon_file(file_name, f, r) for file_name,f,r in zip(batch_x,flip_choice,rot_choice)]), np.array([self.parse_orig_yuv(file_name_gt, f, r) for file_name_gt,f,r in zip(batch_y,flip_choice,rot_choice)]) + else: + data = np.array([self.parse_recon_file(file_name) for file_name in batch_x]), np.array([self.parse_orig_yuv(file_name_gt) for file_name_gt in batch_y]) + + return data + + def __on_epoch_end__(self): + c = list(zip(self.x, self.y)) + random.shuffle(c) + self.x, self.y = zip(*c) diff --git a/training/training_scripts/Nn_Filtering_Set_LC/3_training/NNLF_models.py b/training/training_scripts/Nn_Filtering_Set_LC/3_training/NNLF_models.py new file mode 100644 index 0000000000..5ab23dd151 --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/3_training/NNLF_models.py @@ -0,0 +1,1024 @@ +import tensorflow as tf +from tensorflow import keras +from tensorflow.keras.models import Model +from tensorflow.keras.layers import Conv2D +from tensorflow.keras.layers import DepthwiseConv2D +from tensorflow.keras.layers import Add +from tensorflow.keras.layers import Conv1D +from tensorflow.keras.layers import LeakyReLU +from tensorflow.keras.layers import Input +from tensorly.decomposition import parafac +import numpy as np + + +class modelParamsError(Exception): + """Error in model params""" + pass + +def weighted_MSE_1211(y_true, y_pred): + mse = tf.keras.losses.MeanSquaredError() + loss_y = mse(y_pred[:,:,:,0:4], y_true[:,:,:,0:4]) + loss_cb = mse(y_pred[:,:,:,4], y_true[:,:,:,4]) + loss_cr = mse(y_pred[:,:,:,5], y_true[:,:,:,5]) + + loss = 12 * loss_y + loss_cb + loss_cr + return loss + +def weighted_MSE_411(y_true, y_pred): + mse = tf.keras.losses.MeanSquaredError() + loss_y = mse(y_pred[:,:,:,0:4], y_true[:,:,:,0:4]) + loss_cb = mse(y_pred[:,:,:,4], y_true[:,:,:,4]) + loss_cr = mse(y_pred[:,:,:,5], y_true[:,:,:,5]) + + loss = 4 * loss_y + loss_cb + loss_cr + return loss + +def weighted_MSE_loss_default(y_true, y_pred): + mse = tf.keras.losses.MeanSquaredError() + loss_y = mse(y_pred[:,:,:,0:4], y_true[:,:,:,0:4]) + loss_cb = mse(y_pred[:,:,:,4], y_true[:,:,:,4]) + loss_cr = mse(y_pred[:,:,:,5], y_true[:,:,:,5]) + + loss = 12 * loss_y + loss_cb + loss_cr + return loss + +class InpVal_model(): + def __init__(self): + self.input_shape = (72,72,10) + + def create_model(self): + input = Input(shape=self.input_shape, name="input") + input_sliced = input[:,4:68, 4:68, 0:6] + model = Model(inputs=input, outputs=input_sliced) + opt = keras.optimizers.Adam() + model.compile(optimizer=opt,loss=weighted_MSE_loss_default,experimental_run_tf_function=False) + return model + +class X0140_model(): + def __init__(self, model_params, training=False, training_params=None): + + # compile the model if training is True + self.training = training + # Number of large activation channels + self.M = model_params['M'] + # Number of channels out of hidden layer + self.K = model_params['K'] + # Number of hidden layers + self.N = model_params['N'] + + #input shape to network + self.input_shape = (72,72,10) + + # Ouput number of channles, will differ based on whether + # Luma only Model + # Chroma only Model + # Combined Model + if (model_params['luma_output'] == True) and (model_params['chroma_output'] == True): + self.num_output_channels = 6 + elif (model_params['luma_output'] == True) and (model_params['chroma_output'] == False): + self.num_output_channels = 4 + elif (model_params['luma_output'] == False) and (model_params['chroma_output'] == True): + self.num_output_channels = 2 + else: + raise modelParamsError("Luma and Chroma Output atleast one should be True") + + if training == True: + if 'learning_rate' in training_params: + self.lR = training_params['learning_rate'] + else: + self.lR = None + + # Model file from which weights need to be initialized + # Valid only for CP and baseline network + if 'ref_model' in model_params: + self.ref_model_file = model_params['ref_model'] + if 'ref_model_type' in model_params: + self.ref_model_type = model_params['ref_model_type'] + else: + raise modelParamsError("ref_model_type is required when ref_model is given") + + else: + self.ref_model_file = "" + + # Paramete for Relu activation layers in the network + self.alpha = 0.2 + self.CP_layer_cfg = {} + self.DSC_layer_cfg = {} + + if 'CP_layers' in model_params: + self.CP_layer_cfg = model_params['CP_layers'] + self.CP_layer_cfg = {int(k):int(v) for k,v in self.CP_layer_cfg.items()} + + if 'DSC_layers' in model_params: + self.DSC_layer_cfg = model_params['DSC_layers'] + + + if len(list(set(self.CP_layer_cfg.keys()) & set(self.DSC_layer_cfg))): + raise modelParamsError("Hidden layers cannot be both CP and DSC") + # print("Hidden layers cannot be both CP and DSC") + # return None + + def hidden_layer(self,idx, input, weights=None): + + layer1 = Conv2D(filters=self.M, kernel_size=1, name="hidden_"+str(idx)+ "_1") + x = layer1(input) + if weights is not None: + layer1.set_weights(weights[0]) + + x = LeakyReLU(alpha=self.alpha, name="hidden_activation_"+str(idx))(x) + layer2 = Conv2D(filters=self.K, kernel_size=1,name="hidden_"+str(idx)+ "_2") + x = layer2(x) + + if weights is not None: + layer2.set_weights(weights[1]) + + layer3 = Conv2D(filters=self.K, kernel_size=3, padding='same', name="hidden_"+str(idx)+ "_3") + out = layer3(x) + + if weights is not None: + layer3.set_weights(weights[2]) + + return out + + def hidden_layer_CP(self, idx, input, last_layer, wts=None, wts_prev=None): + + wts_for_next = None + + R = self.CP_layer_cfg[idx] + + layer1 = Conv2D(filters=self.M, kernel_size=1,name="hidden_"+str(idx)+ "_1") + x = layer1(input) + + if wts is not None: + if wts_prev is None: + layer1.set_weights(wts[0]) + else: + b_prev = wts_prev[1].reshape(-1,1) + prev_wt_matrix = np.moveaxis(wts_prev[0][0,0,:,:],0,1) + cur_wt_matrix = np.moveaxis(wts[0][0][0,0,:,:],0,1) + b_cur = wts[0][1].reshape(-1,1) + + wts_prod = np.matmul(cur_wt_matrix, prev_wt_matrix) + bias_fuse = np.add(np.matmul(cur_wt_matrix, b_prev), b_cur) + + wt_matrix = np.moveaxis(wts_prod, 0, 1) + layer1.set_weights([wt_matrix[np.newaxis, np.newaxis, :, :], bias_fuse.flatten()]) + + x = LeakyReLU(alpha=self.alpha, name="hidden_activation_"+str(idx))(x) + + layer2 = Conv2D(filters=R, kernel_size=1, name="hidden_"+str(idx)+ "_2") + x = layer2(x) + + if wts is not None: + cp_factors = parafac(np.moveaxis(wts[2][0],2,0), R,n_iter_max=10000, linesearch=True) + + cur_wt_matrix = np.moveaxis(cp_factors[1][0], 0, 1) + prev_wt_matrix = np.moveaxis(wts[1][0][0,0,:,:], 0, 1) + + b_prev = wts[1][1].reshape(-1, 1) + b_cur = np.matmul(cur_wt_matrix, b_prev) + + wts_prod = np.matmul(cur_wt_matrix, prev_wt_matrix) + wt_matrix = np.moveaxis(wts_prod,0,1) + layer2.set_weights([wt_matrix[np.newaxis, np.newaxis, :,:],b_cur.flatten()]) + + # Grouped convolution is not cupported currenlty on tesnorflow CPU + # Separable convolutions are realized using Grouped convlution to make training faster, + # as Separable convolution layers are sub optimally implemented in CUDA gpu + # + if tf.test.is_gpu_available(): + layer3b = Conv2D(filters=R, kernel_size=(3,1), use_bias=False, groups=R, padding='same', name="hidden_"+str(idx)+ "_3b") + x = layer3b(x) + layer3c = Conv2D(filters=R, kernel_size=(1,3), use_bias=False,groups=R,padding='same', name="hidden_"+str(idx)+ "_3c") + x = layer3c(x) + + if wts is not None: + layer3b.set_weights([cp_factors[1][1][:,np.newaxis,np.newaxis,:]]) + layer3c.set_weights([cp_factors[1][2][np.newaxis,:,np.newaxis,:]]) + else: + layer3b = DepthwiseConv2D(kernel_size=(3,1), use_bias=False, padding='same', name="hidden_"+str(idx)+ "_3b") + x = layer3b(x) + layer3c = DepthwiseConv2D(kernel_size=(1,3), use_bias=False,padding='same', name="hidden_"+str(idx)+ "_3c") + x = layer3c(x) + + if wts is not None: + layer3b.set_weights([cp_factors[1][1][:,np.newaxis,:,np.newaxis]]) + layer3c.set_weights([cp_factors[1][2][np.newaxis,:,:,np.newaxis]]) + + if last_layer: + layer3d = Conv2D(filters=self.K, kernel_size=1, name="hidden_"+str(idx)+ "_3d") + x = layer3d(x) + + if wts is not None: + layer3d.set_weights([np.moveaxis(cp_factors[1][3],0,1)[np.newaxis, np.newaxis, :,:], wts[2][1]]) + + else: + if wts is not None: + wts_for_next = [np.moveaxis(cp_factors[1][3],0,1)[np.newaxis, np.newaxis, :,:], wts[2][1]] + return x,wts_for_next + + def hidden_layer_DSC(self,idx, input, weights=None): + + layer1 = Conv2D(filters=self.M, kernel_size=1, name="hidden_"+str(idx)+ "_1") + x = layer1(input) + if weights is not None: + layer1.set_weights(weights[0]) + + x = LeakyReLU(alpha=self.alpha, name="hidden_activation_"+str(idx))(x) + layer2 = Conv2D(filters=self.K, kernel_size=1,name="hidden_"+str(idx)+ "_2") + x = layer2(x) + + if weights is not None: + layer2.set_weights(weights[1]) + + if tf.test.is_gpu_available(): + layer3_a = Conv2D(filters=self.K, kernel_size=(3,3), groups=self.K, padding='same', name="hidden_"+str(idx)+ "_3a") + else: + layer3_a = DepthwiseConv2D(kernel_size=(3,3), padding='same', name="hidden_"+str(idx)+ "_3a") + + x = layer3_a(x) + + layer3_b = Conv2D(filters=self.K,kernel_size=1, padding='same', name="hidden_"+str(idx)+ "_3b") + out = layer3_b(x) + + return out + + def create_x140_model_from_ref(self): + + ref_model = keras.models.load_model(self.ref_model_file, compile=False) + ref_layer_name = [] + ref_model_wts = [] + for layer in ref_model.layers: + if len(layer.get_weights()) > 0: + ref_layer_name.append(layer.name) + ref_model_wts.append(layer.get_weights()) + + last_wt_idx = 0 + input = Input(shape=self.input_shape, name="input_1") + + conv1 = Conv2D(filters=self.M, kernel_size=3,padding='same',name="conv1") + x = conv1(input) + + conv1.set_weights(ref_model.get_layer(ref_layer_name[last_wt_idx]).get_weights()) + last_wt_idx = last_wt_idx+1 + + x = LeakyReLU(alpha=self.alpha, name="hidden_activation_pre")(x) + + #first hidden layer has slightly different strcture + hidden_0_1 = Conv2D(filters=self.M, kernel_size=1,name="hidden_0_1") + x = hidden_0_1(x) + hidden_0_1.set_weights(ref_model.get_layer(ref_layer_name[last_wt_idx]).get_weights()) + last_wt_idx = last_wt_idx+1 + + x = LeakyReLU(alpha=self.alpha, name="hidden_activation_0")(x) + if 0 in self.CP_layer_cfg: + R = self.CP_layer_cfg[0] + hidden_0_2 = Conv2D(filters=R, kernel_size=1,name="hidden_0_2") + x = hidden_0_2(x) + wts_prev = np.moveaxis(ref_model_wts[last_wt_idx][0][0,0,:,:], 0, 1) + bias_prev = ref_model_wts[last_wt_idx][1] + last_wt_idx = last_wt_idx + 1 + + cp_factors = parafac(np.moveaxis(ref_model_wts[last_wt_idx][0],2,0), rank=R, n_iter_max=10000, linesearch=True) + + cur_wt_matrix = np.moveaxis(cp_factors[1][0], 0, 1) + + wts_cur = np.moveaxis(np.matmul(cur_wt_matrix, wts_prev), 0, 1)[np.newaxis,np.newaxis,:,:] + bias_cur = np.matmul(cur_wt_matrix, bias_prev.reshape(-1,1)).flatten() + + hidden_0_2.set_weights([wts_cur,bias_cur]) + else: + hidden_0_2 = Conv2D(filters=self.K, kernel_size=1,name="hidden_0_2") + x = hidden_0_2(x) + + hidden_0_2.set_weights(ref_model.get_layer(ref_layer_name[last_wt_idx]).get_weights()) + last_wt_idx = last_wt_idx+1 + + if 0 in self.CP_layer_cfg: + R = self.CP_layer_cfg[0] + cp_factors = parafac(np.moveaxis(ref_model.get_layer(ref_layer_name[last_wt_idx]).get_weights()[0],2,0), rank=R, n_iter_max=10000, linesearch=True) + + if tf.test.is_gpu_available(): + layer3b = Conv2D(filters=R,kernel_size=(3,1), use_bias=False, groups=R, padding='same', name="hidden_0"+"_3b") + x = layer3b(x) + layer3b.set_weights([cp_factors[1][1][:,np.newaxis,np.newaxis,:]]) + + layer3c = Conv2D(filters=R,kernel_size=(1,3), use_bias=False, groups=R, padding='same', name="hidden_0"+"_3c") + x = layer3c(x) + layer3c.set_weights([cp_factors[1][2][np.newaxis,:,np.newaxis,:]]) + + else: + layer3b = DepthwiseConv2D(kernel_size=(3,1), use_bias=False, padding='same', name="hidden_0"+"_3b") + x = layer3b(x) + layer3b.set_weights([cp_factors[1][1][:,np.newaxis,:,np.newaxis]]) + + layer3c = DepthwiseConv2D(kernel_size=(1,3), use_bias=False,padding='same', name="hidden_0"+"_3c") + x = layer3c(x) + layer3c.set_weights([cp_factors[1][2][np.newaxis,:,:,np.newaxis]]) + + w_prev = [np.moveaxis(cp_factors[1][3],0,1)[np.newaxis, np.newaxis, :,:], ref_model.get_layer(ref_layer_name[last_wt_idx]).get_weights()[1]] + elif 0 in self.DSC_layer_cfg: + + if tf.test.is_gpu_available(): + layer3a = Conv2D(filters=self.K, kernel_size=(3,3), groups=self.K, padding='same', name="hidden_0"+ "_3a") + else: + layer3a = DepthwiseConv2D(kernel_size=(3,3), padding='same', name="hidden_0"+ "_3a") + + x = layer3a(x) + + layer3b = Conv2D(filters=self.K,kernel_size=1, padding='same', name="hidden_0"+ "_3b") + x = layer3b(x) + + else: + + hidden_0_3 = Conv2D(filters=self.K, kernel_size=3, padding='same', name="hidden_0_3") + x = hidden_0_3(x) + hidden_0_3.set_weights(ref_model.get_layer(ref_layer_name[last_wt_idx]).get_weights()) + + last_wt_idx = last_wt_idx+1 + + for idx in range(1, self.N): + wts = [] + wts.append(ref_model.get_layer(ref_layer_name[last_wt_idx]).get_weights()) + wts.append(ref_model.get_layer(ref_layer_name[last_wt_idx + 1]).get_weights()) + wts.append(ref_model.get_layer(ref_layer_name[last_wt_idx + 2]).get_weights()) + + if idx in self.CP_layer_cfg: + last_layer = False + if idx == (self.N - 1): + last_layer = True + x, w_prev = self.hidden_layer_CP(idx, x, last_layer, wts=wts, wts_prev=w_prev) + elif idx in self.DSC_layer_cfg: + x = self.hidden_layer_DSC(idx, x, wts) + else: + x = self.hidden_layer(idx, x, wts) + + last_wt_idx = last_wt_idx+3 + + output_layer = Conv2D(filters=self.num_output_channels, kernel_size=3, padding='same', name="output_layer") + x = output_layer(x) + output_layer.set_weights(ref_model.get_layer(ref_layer_name[last_wt_idx]).get_weights()) + last_wt_idx = last_wt_idx + 1 + + res_sliced = x[:,4:68, 4:68, 0:6] + input_sliced = input[:,4:68, 4:68, 0:6] + + output = Add(name='add')([res_sliced,input_sliced]) + + model = Model(inputs=input, outputs=output) + + if self.training: + if self.lR: + opt = keras.optimizers.Adam(learning_rate=self.lR) + else: + opt = keras.optimizers.Adam() + + model.compile(optimizer=opt,loss=weighted_MSE_loss_default,experimental_run_tf_function=False) + + return model + + def create_x140_model(self): + + input = Input(shape=self.input_shape, name="input_1") + conv1 = Conv2D(filters=self.M, kernel_size=3,padding='same',name="conv1") + x = conv1(input) + x = LeakyReLU(alpha=self.alpha, name="hidden_activation_pre")(x) + + #first hidden layer has slightly different strcture + hidden_0_1 = Conv2D(filters=self.M, kernel_size=1,name="hidden_0_1") + x = hidden_0_1(x) + x = LeakyReLU(alpha=self.alpha, name="hidden_activation_0")(x) + + if 0 in self.CP_layer_cfg: + R = self.CP_layer_cfg[0] + hidden_0_2 = Conv2D(filters=R, kernel_size=1,name="hidden_0_2") + else: + hidden_0_2 = Conv2D(filters=self.K, kernel_size=1,name="hidden_0_2") + x = hidden_0_2(x) + + if 0 in self.CP_layer_cfg: + + if tf.test.is_gpu_available(): + layer3b = Conv2D(filters=R, kernel_size=(3,1), groups=R, use_bias=False, padding='same', name="hidden_0"+"_3b") + layer3c = Conv2D(filters=R, kernel_size=(1,3), groups=R, use_bias=False,padding='same', name="hidden_0"+"_3c") + else: + layer3b = DepthwiseConv2D(kernel_size=(3,1), use_bias=False, padding='same', name="hidden_0"+"_3b") + layer3c = DepthwiseConv2D(kernel_size=(1,3), use_bias=False,padding='same', name="hidden_0"+"_3c") + + x = layer3b(x) + x = layer3c(x) + + elif 0 in self.DSC_layer_cfg: + # Grouped convolution is not cupported currenlty on tesnorflow CPU + # Separable convolutions are realized using Grouped convlution to make training faster, + # as Separable convolution layers are sub optimally implemented in CUDA gpu + if tf.test.is_gpu_available(): + layer3a = Conv2D(filters=self.K, kernel_size=(3,3), groups=self.K, padding='same', name="hidden_0"+ "_3a") + else: + layer3a = DepthwiseConv2D(kernel_size=(3,3), padding='same', name="hidden_0" + "_3a") + + x = layer3a(x) + + layer3b = Conv2D(filters=self.K,kernel_size=1, padding='same', name="hidden_0"+ "_3b") + x = layer3b(x) + + else: + hidden_0_3 = Conv2D(filters=self.K, kernel_size=3, padding='same', name="hidden_0_3") + x = hidden_0_3(x) + + w_prev = None + for idx in range(1, self.N): + if idx in self.CP_layer_cfg: + last_layer = False + if idx == (self.N - 1): + last_layer = True + x, w_prev = self.hidden_layer_CP(idx, x, last_layer, wts=None, wts_prev=w_prev) + elif idx in self.DSC_layer_cfg: + x = self.hidden_layer_DSC(idx,x) + else: + x = self.hidden_layer(idx, x) + + output_layer = Conv2D(filters=self.num_output_channels, kernel_size=3, padding='same', name="output_layer") + x = output_layer(x) + + res_sliced = x[:,4:68, 4:68, 0:6] + input_sliced = input[:,4:68, 4:68, 0:6] + + output = Add(name='add')([res_sliced,input_sliced]) + + model = Model(inputs=input, outputs=output) + + if self.training: + if self.lR: + opt = keras.optimizers.Adam(learning_rate=self.lR) + else: + opt = keras.optimizers.Adam() + model.compile(optimizer=opt,loss=weighted_MSE_loss_default,experimental_run_tf_function=False) + + return model + + + def get_model(self): + if self.ref_model_file: + if self.ref_model_type == 'baseline': + target_model = self.create_x140_model_from_ref() + return(target_model) + else: + target_model = keras.models.load_model(self.ref_model_file, compile=False) + if self.training: + if self.lR: + opt = keras.optimizers.Adam(learning_rate=self.lR) + else: + opt = keras.optimizers.Adam() + target_model.compile(optimizer=opt,loss=weighted_MSE_loss_default,experimental_run_tf_function=False) + return target_model + else: + return(self.create_x140_model()) + +class LC_NNLF_model(): + def __init__(self, model_params, training=False, training_params=None): + + # compile the model if training is True + self.training = training + + self.K = model_params['K'] + self.M_Y = model_params['M_Y'] + self.K_Y = model_params['K_Y'] + self.M_C = model_params['M_C'] + + #if self.K <= self.K_Y: + # raise modelParamsError("K should be greater than Ky") + #self.K_C = self.K - self.K_Y + + self.K_C = model_params['K_C'] + + self.N_Y = model_params['N_Y'] + self.N_C = model_params['N_C'] + + self.model_type = model_params['model_type'] + self.M = model_params['M'] + + if self.model_type == 'cp': + self.R = model_params['R'] + + #input shape to network + self.input_shape = (72,72,10) + self.output_shape = (64,64,6) + + if training == True: + if 'learning_rate' in training_params: + self.lR = training_params['learning_rate'] + else: + self.lR = None + # Paramete for Relu activation layers in the network + self.alpha = 0.2 + + # Model file from which weights need to be initialized + # Valid only for CP and baseline network + if 'ref_model' in model_params: + self.ref_model_file = model_params['ref_model'] + + if 'ref_model_type' in model_params: + self.ref_model_type = model_params['ref_model_type'] + else: + raise modelParamsError("Ref model type must be mentioned, when ref model file is given") + else: + self.ref_model_file = "" + self.ref_model_type = "" + + + + self.CP_layer_cfg_y = {} + self.CP_layer_cfg_c = {} + + self.DSC_layer_cfg_y = {} + self.DSC_layer_cfg_c = {} + + if self.model_type == 'cp': + if 'CP_layers_y' in model_params: + self.CP_layer_cfg_y = model_params['CP_layers_y'] + self.CP_layer_cfg_y = {int(k):int(v) for k,v in self.CP_layer_cfg_y.items()} + + if 'CP_layers_c' in model_params: + self.CP_layer_cfg_c = model_params['CP_layers_c'] + self.CP_layer_cfg_c = {int(k):int(v) for k,v in self.CP_layer_cfg_c.items()} + + if self.model_type == 'dsc': + if 'DSC_layers_y' in model_params: + self.DSC_layer_cfg_y = model_params['DSC_layers_y'] + + if 'DSC_layers_c' in model_params: + self.DSC_layer_cfg_c = model_params['DSC_layers_c'] + + + def hidden_layer(self,idx, input, is_luma, weights=None): + + if is_luma == True: + M = self.M_Y + K = self.K_Y + layer_suffix = str(idx) + "_y" + else: + M = self.M_C + K = self.K_C + layer_suffix = str(idx) + "_c" + + layer1 = Conv2D(filters=M, kernel_size=1, name="hidden_"+layer_suffix+ "_1") + x = layer1(input) + + if weights is not None: + layer1.set_weights(weights[0]) + + x = LeakyReLU(alpha=self.alpha, name="hidden_activation_"+layer_suffix)(x) + layer2 = Conv2D(filters=K, kernel_size=1,name="hidden_"+layer_suffix+ "_2") + x = layer2(x) + + if weights is not None: + layer2.set_weights(weights[1]) + + layer3 = Conv2D(filters=K, kernel_size=3, padding='same', name="hidden_"+layer_suffix+ "_3") + out = layer3(x) + + if weights is not None: + layer3.set_weights(weights[2]) + + return out + + + def hidden_layer_CP(self, idx, input, is_luma, last_layer, wts=None, wts_prev=None): + + wts_for_next = None + + if is_luma == True: + M = self.M_Y + K = self.K_Y + layer_suffix = str(idx) + "_y" + R = self.CP_layer_cfg_y[idx] + else: + M = self.M_C + K = self.K_C + layer_suffix = str(idx) + "_c" + R = self.CP_layer_cfg_c[idx] + + layer1 = Conv2D(filters=M, kernel_size=1,name="hidden_"+layer_suffix+ "_1") + x = layer1(input) + + if wts is not None: + if wts_prev is None: + layer1.set_weights(wts[0]) + else: + b_prev = wts_prev[1].reshape(-1,1) + prev_wt_matrix = np.moveaxis(wts_prev[0][0,0,:,:],0,1) + cur_wt_matrix = np.moveaxis(wts[0][0][0,0,:,:],0,1) + b_cur = wts[0][1].reshape(-1, 1) + + wts_prod = np.matmul(cur_wt_matrix,prev_wt_matrix) + bias_fused = np.add(np.matmul(cur_wt_matrix,b_prev),b_cur) + + wt_matrix = np.moveaxis(wts_prod,0,1) + layer1.set_weights([wt_matrix[np.newaxis, np.newaxis, :,:],bias_fused.flatten()]) + + x = LeakyReLU(alpha=self.alpha, name="hidden_activation_"+layer_suffix)(x) + layer2 = Conv2D(filters=R, kernel_size=1, name="hidden_"+layer_suffix+ "_2") + x = layer2(x) + + if wts is not None: + cp_factors = parafac(np.moveaxis(wts[2][0],2,0), R,n_iter_max=10000, linesearch=True) + + cur_wt_matrix = np.moveaxis(cp_factors[1][0], 0, 1) + prev_wt_matrix = np.moveaxis(wts[1][0][0,0,:,:], 0, 1) + + b_prev = wts[1][1].reshape(-1, 1) + b_cur = np.matmul(cur_wt_matrix, b_prev) + + wts_prod = np.matmul(cur_wt_matrix, prev_wt_matrix) + wt_matrix = np.moveaxis(wts_prod,0,1) + layer2.set_weights([wt_matrix[np.newaxis, np.newaxis, :,:],b_cur.flatten()]) + + # Grouped convolution is not cupported currenlty on tesnorflow CPU + # Separable convolutions are realized using Grouped convlution to make training faster, + # as Separable convolution layers are sub optimally implemented in CUDA gpu + # + if tf.test.is_gpu_available(): + layer3b = Conv2D(filters=R, kernel_size=(3,1), use_bias=False, groups=R, padding='same', name="hidden_"+layer_suffix+ "_3b") + x = layer3b(x) + + layer3c = Conv2D(filters=R, kernel_size=(1,3), use_bias=False,groups=R,padding='same', name="hidden_"+layer_suffix+ "_3c") + x = layer3c(x) + + if wts is not None: + layer3b.set_weights([cp_factors[1][1][:,np.newaxis,np.newaxis,:]]) + layer3c.set_weights([cp_factors[1][2][np.newaxis,:,np.newaxis,:]]) + + else: + layer3b = DepthwiseConv2D(kernel_size=(3,1), use_bias=False, padding='same', name="hidden_"+layer_suffix+ "_3b") + x = layer3b(x) + + layer3c = DepthwiseConv2D(kernel_size=(1,3), use_bias=False,padding='same', name="hidden_"+layer_suffix+ "_3c") + x = layer3c(x) + + if wts is not None: + layer3b.set_weights([cp_factors[1][1][:,np.newaxis,:,np.newaxis]]) + layer3c.set_weights([cp_factors[1][2][np.newaxis,:,:,np.newaxis]]) + + + if last_layer: + layer3d = Conv2D(filters=K, kernel_size=1,name="hidden_"+layer_suffix+ "_3d") + x = layer3d(x) + + if wts is not None: + layer3d.set_weights([np.moveaxis(cp_factors[1][3],0,1)[np.newaxis, np.newaxis, :,:], wts[2][1]]) + + else: + if wts is not None: + wts_for_next = [np.moveaxis(cp_factors[1][3],0,1)[np.newaxis, np.newaxis, :,:], wts[2][1]] + + + + return x,wts_for_next + + + def hidden_layer_DSC(self,idx, input, weights=None): + + layer1 = Conv2D(filters=self.M, kernel_size=1, name="hidden_"+str(idx)+ "_1") + x = layer1(input) + if weights is not None: + layer1.set_weights(weights[0]) + + x = LeakyReLU(alpha=self.alpha, name="hidden_activation_"+str(idx))(x) + layer2 = Conv2D(filters=self.K, kernel_size=1,name="hidden_"+str(idx)+ "_2") + x = layer2(x) + + if weights is not None: + layer2.set_weights(weights[1]) + + if tf.test.is_gpu_available(): + layer3_a = Conv2D(filters=self.K, kernel_size=(3,3), groups=self.K, padding='same', name="hidden_"+str(idx)+ "_3a") + else: + layer3_a = DepthwiseConv2D(kernel_size=(3,3), padding='same', name="hidden_"+str(idx)+ "_3a") + + x = layer3_a(x) + + layer3_b = Conv2D(filters=self.K,kernel_size=1, padding='same', name="hidden_"+str(idx)+ "_3b") + out = layer3_b(x) + + return out + + def create_LC_NNLF_model(self): + + input = Input(shape=self.input_shape, name="input_1") + + conv1 = Conv2D(filters=self.M, kernel_size=3,padding='same',name="conv1") + x = conv1(input) + x = LeakyReLU(alpha=self.alpha, name="hidden_activation_pre")(x) + + #first hidden layer has slightly different strcture + hidden_0_1 = Conv2D(filters=self.M, kernel_size=1,name="hidden_0_1") + x = hidden_0_1(x) + + x = LeakyReLU(alpha=self.alpha, name="hidden_activation_0")(x) + + if self.model_type == 'cp': + hidden_0_2 = Conv2D(filters=self.R, kernel_size=1,name="hidden_0_2") + elif self.model_type == 'baseline': + hidden_0_2 = Conv2D(filters=self.K, kernel_size=1,name="hidden_0_2") + + x = hidden_0_2(x) + + if self.model_type == 'cp': + if tf.test.is_gpu_available(): + layer3b = Conv2D(filters=self.R, kernel_size=(3,1), groups=self.R, use_bias=False, padding='same', name="hidden_0"+"_3b") + x = layer3b(x) + + layer3c = Conv2D(filters=self.R, kernel_size=(1,3), groups=self.R, use_bias=False,padding='same', name="hidden_0"+"_3c") + x = layer3c(x) + + else: + layer3b = DepthwiseConv2D(kernel_size=(3,1), use_bias=False, padding='same', name="hidden_0"+"_3b") + x = layer3b(x) + + layer3c = DepthwiseConv2D(kernel_size=(1,3), use_bias=False,padding='same', name="hidden_0"+"_3c") + x = layer3c(x) + + + layer3d = Conv2D(filters=self.K, kernel_size=1, name="hidden_0"+"_3d") + x = layer3d(x) + + elif self.model_type == 'dsc': + # Grouped convolution is not cupported currenlty on tesnorflow CPU + # Separable convolutions are realized using Grouped convlution to make training faster, + # as Separable convolution layers are sub optimally implemented in CUDA gpu + if tf.test.is_gpu_available(): + layer3a = Conv2D(filters=self.K, kernel_size=(3,3), groups=self.K, padding='same', name="hidden_0"+ "_3a") + else: + layer3a = DepthwiseConv2D(kernel_size=(3,3), padding='same', name="hidden_0" + "_3a") + + x = layer3a(x) + + layer3b = Conv2D(filters=self.K,kernel_size=1, padding='same', name="hidden_0"+ "_3b") + x = layer3b(x) + + else: + hidden_0_3 = Conv2D(filters=self.K, kernel_size=3, padding='same', name="hidden_0_3") + x = hidden_0_3(x) + + x_Y = x[:,:,:,:16] + x_C = x[:,:,:,16:] + + w_Y = None + for idx in range(1, self.N_Y): + if idx in self.CP_layer_cfg_y: + last_layer = False + if idx == (self.N_Y-1): + last_layer = True + x_Y,w_Y = self.hidden_layer_CP(idx, x_Y,True, last_layer, wts=None, wts_prev=w_Y) + elif idx in self.DSC_layer_cfg_y: + x_Y = self.hidden_layer_DSC(idx, x_Y,True) + else: + x_Y = self.hidden_layer(idx, x_Y,True) + + w_C = None + for idx in range(1, self.N_C): + if idx in self.CP_layer_cfg_c: + last_layer = False + if idx == (self.N_C-1): + last_layer = True + x_C,w_C = self.hidden_layer_CP(idx, x_C,False,last_layer, wts=None, wts_prev=w_C) + elif idx in self.DSC_layer_cfg_y: + x_C = self.hidden_layer_DSC(idx, x_C,False) + else: + x_C = self.hidden_layer(idx, x_C,False) + + + output_layer_Y = Conv2D(filters=4, kernel_size=3, padding='same', name="output_layer_Y") + x_Y = output_layer_Y(x_Y) + + output_layer_C = Conv2D(filters=2, kernel_size=3, padding='same', name="output_layer_C") + x_C = output_layer_C(x_C) + + + res_sliced_y = x_Y[:,4:68, 4:68, 0:4] + input_sliced_y = input[:,4:68, 4:68, 0:4] + + res_sliced_c = x_C[:,4:68, 4:68, 0:2] + input_sliced_c = input[:,4:68, 4:68, 4:6] + + output_Y = Add(name='add_1')([res_sliced_y,input_sliced_y]) + output_C = Add(name='add_2')([res_sliced_c,input_sliced_c]) + + output = tf.keras.layers.Concatenate()([output_Y, output_C]) + + model = Model(inputs=input, outputs=output) + + if self.training: + if self.lR: + opt = keras.optimizers.Adam(learning_rate=self.lR) + else: + opt = keras.optimizers.Adam() + model.compile(optimizer=opt,loss=weighted_MSE_loss_default,experimental_run_tf_function=False) + + return model + + + def create_LC_NNLF_model_from_ref(self): + + ref_model = keras.models.load_model(self.ref_model_file, compile=False) + ref_model_wts_common = [] + ref_model_wts_luma = [] + ref_model_wts_chroma = [] + + for layer in ref_model.layers: + if len(layer.get_weights()) > 0: + if ('_y_' in layer.name) or layer.name.endswith('_Y'): + ref_model_wts_luma.append(layer.get_weights()) + elif ('_c_' in layer.name) or layer.name.endswith('_C'): + ref_model_wts_chroma.append(layer.get_weights()) + else: + ref_model_wts_common.append(layer.get_weights()) + + last_wt_idx = 0 + last_wt_idx_y = 0 + last_wt_idx_c = 0 + + input = Input(shape=self.input_shape, name="input_1") + + conv1 = Conv2D(filters=self.M, kernel_size=3,padding='same',name="conv1") + x = conv1(input) + + conv1.set_weights(ref_model_wts_common[last_wt_idx]) + last_wt_idx = last_wt_idx+1 + + x = LeakyReLU(alpha=self.alpha, name="hidden_activation_pre")(x) + + #first hidden layer has slightly different strcture + hidden_0_1 = Conv2D(filters=self.M, kernel_size=1,name="hidden_0_1") + x = hidden_0_1(x) + + hidden_0_1.set_weights(ref_model_wts_common[last_wt_idx]) + last_wt_idx = last_wt_idx+1 + + x = LeakyReLU(alpha=self.alpha, name="hidden_activation_0")(x) + + if self.model_type == 'baseline': + hidden_0_2 = Conv2D(filters=self.K, kernel_size=1,name="hidden_0_2") + x = hidden_0_2(x) + + hidden_0_2.set_weights(ref_model_wts_common[last_wt_idx]) + last_wt_idx = last_wt_idx+1 + + elif self.model_type == 'cp': + hidden_0_2 = Conv2D(filters=self.R, kernel_size=1,name="hidden_0_2") + x = hidden_0_2(x) + + wts_prev = np.moveaxis(ref_model_wts_common[last_wt_idx][0][0,0,:,:], 0, 1) + bias_prev = ref_model_wts_common[last_wt_idx][1] + last_wt_idx = last_wt_idx + 1 + + cp_factors = parafac(np.moveaxis(ref_model_wts_common[last_wt_idx][0],2,0), rank=self.R, n_iter_max=10000, linesearch=True) + + cur_wt_matrix = np.moveaxis(cp_factors[1][0], 0, 1) + + wts_cur = np.moveaxis(np.matmul(cur_wt_matrix, wts_prev), 0, 1)[np.newaxis,np.newaxis,:,:] + bias_cur = np.matmul(cur_wt_matrix, bias_prev.reshape(-1,1)).flatten() + + hidden_0_2.set_weights([wts_cur,bias_cur]) + + # + if tf.test.is_gpu_available(): + layer3b = Conv2D(filters=self.R,kernel_size=(3,1), use_bias=False, groups=self.R, padding='same', name="hidden_0"+"_3b") + x = layer3b(x) + layer3b.set_weights([cp_factors[1][1][:,np.newaxis,np.newaxis,:]]) + + layer3c = Conv2D(filters=self.R,kernel_size=(1,3), use_bias=False, groups=self.R, padding='same', name="hidden_0"+"_3c") + x = layer3c(x) + layer3c.set_weights([cp_factors[1][2][np.newaxis,:,np.newaxis,:]]) + + else: + layer3b = DepthwiseConv2D(kernel_size=(3,1), use_bias=False, padding='same', name="hidden_0"+"_3b") + x = layer3b(x) + layer3b.set_weights([cp_factors[1][1][:,np.newaxis,:,np.newaxis]]) + + layer3c = DepthwiseConv2D(kernel_size=(1,3), use_bias=False,padding='same', name="hidden_0"+"_3c") + x = layer3c(x) + layer3c.set_weights([cp_factors[1][2][np.newaxis,:,:,np.newaxis]]) + + + layer3d = Conv2D(filters=self.K, kernel_size=1, name="hidden_0"+"_3d") + x = layer3d(x) + layer3d.set_weights([np.moveaxis(cp_factors[1][3],0,1)[np.newaxis, np.newaxis, :,:], ref_model_wts_common[last_wt_idx][1]]) + + elif 0 in self.DSC_layer_cfg: + + if tf.test.is_gpu_available(): + layer3a = Conv2D(filters=self.K, kernel_size=(3,3), groups=self.K, padding='same', name="hidden_0"+ "_3a") + else: + layer3a = DepthwiseConv2D(kernel_size=(3,3), padding='same', name="hidden_0"+ "_3a") + + x = layer3a(x) + + layer3b = Conv2D(filters=self.K,kernel_size=1, padding='same', name="hidden_0"+ "_3b") + x = layer3b(x) + + else: + + hidden_0_3 = Conv2D(filters=self.K, kernel_size=3, padding='same', name="hidden_0_3") + x = hidden_0_3(x) + hidden_0_3.set_weights(ref_model.get_layer(ref_model_wts_common[last_wt_idx]).get_weights()) + + last_wt_idx = last_wt_idx+1 + + x_Y = x[:,:,:,:16] + x_C = x[:,:,:,16:] + + w_Y = None + w_C = None + for idx in range(1, self.N_Y): + if idx in self.CP_layer_cfg_y: + last_layer = False + if idx == (self.N_Y-1): + last_layer = True + + wts = [] + wts.append(ref_model_wts_luma[last_wt_idx_y]) + wts.append(ref_model_wts_luma[last_wt_idx_y + 1]) + wts.append(ref_model_wts_luma[last_wt_idx_y + 2]) + + x_Y, w_Y = self.hidden_layer_CP(idx, x_Y,True, last_layer, wts=wts, wts_prev=w_Y) + + elif idx in self.DSC_layer_cfg_y: + x_Y = self.hidden_layer_DSC(idx, x_Y,True) + else: + x_Y = self.hidden_layer(idx, x_Y,True) + + last_wt_idx_y = last_wt_idx_y+3 + + for idx in range(1, self.N_C): + if idx in self.CP_layer_cfg_c: + last_layer = False + if idx == (self.N_C-1): + last_layer = True + + wts = [] + wts.append(ref_model_wts_chroma[last_wt_idx_c]) + wts.append(ref_model_wts_chroma[last_wt_idx_c + 1]) + wts.append(ref_model_wts_chroma[last_wt_idx_c + 2]) + + x_C, w_C = self.hidden_layer_CP(idx, x_C,False,last_layer, wts=wts, wts_prev=w_C) + + elif idx in self.DSC_layer_cfg_y: + x_C = self.hidden_layer_DSC(idx, x_C,False) + else: + x_C = self.hidden_layer(idx, x_C,False) + + last_wt_idx_c = last_wt_idx_c + 3 + + output_layer_Y = Conv2D(filters=4, kernel_size=3, padding='same', name="output_layer_Y") + x_Y = output_layer_Y(x_Y) + output_layer_Y.set_weights(ref_model_wts_luma[last_wt_idx_y]) + last_wt_idx_y = last_wt_idx_y + 1 + + output_layer_C = Conv2D(filters=2, kernel_size=3, padding='same', name="output_layer_C") + x_C = output_layer_C(x_C) + output_layer_C.set_weights(ref_model_wts_chroma[last_wt_idx_c]) + last_wt_idx_c = last_wt_idx_c + 1 + + res_sliced_y = x_Y[:,4:68, 4:68, 0:4] + input_sliced_y = input[:,4:68, 4:68, 0:4] + + res_sliced_c = x_C[:,4:68, 4:68, 0:2] + input_sliced_c = input[:,4:68, 4:68, 4:6] + + output_Y = Add(name='add_1')([res_sliced_y,input_sliced_y]) + output_C = Add(name='add_2')([res_sliced_c,input_sliced_c]) + + output = tf.keras.layers.Concatenate()([output_Y, output_C]) + + model = Model(inputs=input, outputs=output) + + if self.training: + if self.lR: + opt = keras.optimizers.Adam(learning_rate=self.lR) + else: + opt = keras.optimizers.Adam() + + model.compile(optimizer=opt,loss=weighted_MSE_loss_default,experimental_run_tf_function=False) + + return model + + + def get_model(self): + + if self.ref_model_file: + if self.model_type == self.ref_model_type: + target_model = keras.models.load_model(self.ref_model_file, compile=False) + if self.training: + if self.lR: + opt = keras.optimizers.Adam(learning_rate=self.lR) + else: + opt = keras.optimizers.Adam() + target_model.compile(optimizer=opt,loss=weighted_MSE_loss_default,experimental_run_tf_function=False) + return target_model + else: + return(self.create_LC_NNLF_model_from_ref()) + else: + return(self.create_LC_NNLF_model()) + diff --git a/training/training_scripts/Nn_Filtering_Set_LC/3_training/RA_LC-CPDF_model2_cfg_stage1.json b/training/training_scripts/Nn_Filtering_Set_LC/3_training/RA_LC-CPDF_model2_cfg_stage1.json new file mode 100644 index 0000000000..65c95c1bd1 --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/3_training/RA_LC-CPDF_model2_cfg_stage1.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ba31a633735f9453af0972cbc5d4a9e7ba925c1e6c35777c86abf8245cfe960 +size 1931 diff --git a/training/training_scripts/Nn_Filtering_Set_LC/3_training/RA_LC-CPDF_model2_cfg_stage2.json b/training/training_scripts/Nn_Filtering_Set_LC/3_training/RA_LC-CPDF_model2_cfg_stage2.json new file mode 100644 index 0000000000..fced6f4d65 --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/3_training/RA_LC-CPDF_model2_cfg_stage2.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:861d1e34fcc181e3f30ebafb12fb06d04e1a7d78309172033725111928646636 +size 1938 diff --git a/training/training_scripts/Nn_Filtering_Set_LC/3_training/RA_LC-CPDF_model3_cfg_stage1.json b/training/training_scripts/Nn_Filtering_Set_LC/3_training/RA_LC-CPDF_model3_cfg_stage1.json new file mode 100644 index 0000000000..4ba9f9fc39 --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/3_training/RA_LC-CPDF_model3_cfg_stage1.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:60011bf124c123a3e5fd3cf79002fed19cd58c18a8939ad2ec033a0c1c58a68e +size 1931 diff --git a/training/training_scripts/Nn_Filtering_Set_LC/3_training/RA_LC-CPDF_model3_cfg_stage2.json b/training/training_scripts/Nn_Filtering_Set_LC/3_training/RA_LC-CPDF_model3_cfg_stage2.json new file mode 100644 index 0000000000..4e82864055 --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/3_training/RA_LC-CPDF_model3_cfg_stage2.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cd6f624d646717e089b42d9e515698a36a9b574e2a93dded245166e6c20b53c5 +size 1938 diff --git a/training/training_scripts/Nn_Filtering_Set_LC/3_training/train_NNLF.py b/training/training_scripts/Nn_Filtering_Set_LC/3_training/train_NNLF.py new file mode 100644 index 0000000000..1622fdd9a1 --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/3_training/train_NNLF.py @@ -0,0 +1,223 @@ +import numpy as np +import sys +import glob +import os +import json +import shutil +import tensorflow as tf +from tensorflow import keras +from tensorflow.python.platform import gfile +from tensorflow.python.framework.ops import disable_eager_execution +import tensorflow.python.util.deprecation as deprecation +from NNLF_models import X0140_model,LC_NNLF_model,InpVal_model +from NNLF_data_loader import X0140_data_loader +import keras.backend as K +from NNLF_models import weighted_MSE_1211, weighted_MSE_411 + +def ra_lr_scheduler_st1(epoch, lr): + if epoch == 20: + return lr * 0.5 + elif epoch == 30: + return lr * 0.2 + elif epoch == 35: + return lr * 0.1 + else: + return lr + +def ra_lr_scheduler_st2(epoch, lr): + if epoch == 10: + return lr * 0.5 + elif epoch == 20: + return lr * 0.2 + elif epoch == 25: + return lr * 0.1 + else: + return lr + +def ai_lr_scheduler(epoch, lr): + if (epoch >= 300) and (epoch % 50 == 0): + return lr * 0.1 + else: + return lr + +def train_NNLF(): + os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + deprecation._PRINT_DEPRECATION_WARNINGS = False + + cfg = json.load(open(sys.argv[1])) + + model_params = cfg['model_parameters'] + training_data_info = cfg['training_data'] + training_params = cfg['training_parameters'] + out_artifacts = cfg['out_artifacts'] + + if not os.path.exists(out_artifacts['out_dir']): + os.makedirs(out_artifacts['out_dir']) + + train_dir_list = training_data_info['train_dir_list'] + valid_dir_list = training_data_info['valid_dir_list'] + gt_train_dir_list = training_data_info['gt_train_dir_list'] + gt_valid_dir_list = training_data_info['gt_valid_dir_list'] + + chkpt_dir = os.path.join(out_artifacts['out_dir'], "checkpoints") + tensorboard_logs = os.path.join(out_artifacts['out_dir'], "tensorboardlogs") + best_checkpoint_filepath = os.path.join(out_artifacts['out_dir'], "model") + + train_data_file_list = [] + train_data_gt_file_list = [] + + qp_list = model_params['qp_list'] + + gt_prefix = "" + if training_params['encode_cfg'] == "AI": + gt_prefix = "DIV2K" + + for train_dir in train_dir_list: + for train_file in os.listdir(train_dir): + train_basename = os.path.splitext(train_file)[0] + train_base_split = train_basename.split("_") + + if int(train_base_split[2]) not in qp_list: + continue + + gt_file = gt_prefix + "_".join(train_base_split[:2] + train_base_split[4:]) + ".yuv" + for gt_train_dir in gt_train_dir_list: + gt_file_full_path = os.path.join(gt_train_dir, gt_file) + if os.path.exists(gt_file_full_path): + train_data_gt_file_list.append(gt_file_full_path) + break + train_data_file_list.append(os.path.join(train_dir, train_file)) + + if(len(train_data_file_list) != len(train_data_gt_file_list)): + print("Train data and ground truth data are not of same size") + return + + test_data_file_list = [] + test_data_gt_file_list = [] + + for valid_dir in valid_dir_list: + for valid_file in os.listdir(valid_dir): + valid_basename = os.path.splitext(valid_file)[0] + valid_base_split = valid_basename.split("_") + if int(valid_base_split[2]) not in qp_list: + continue + gt_file = gt_prefix + "_".join(valid_base_split[:2] + valid_base_split[4:]) + ".yuv" + + for gt_valid_dir in gt_valid_dir_list: + gt_file_full_path = os.path.join(gt_valid_dir, gt_file) + if os.path.exists(gt_file_full_path): + test_data_gt_file_list.append(gt_file_full_path) + break + test_data_file_list.append(os.path.join(valid_dir, valid_file)) + + if(len(test_data_file_list) != len(test_data_gt_file_list)): + print("Test data and ground truth data are not of same size") + return + + graph = tf.Graph() + with graph.as_default(): + # Prepare dataset + training_generator = X0140_data_loader(train_data_file_list, train_data_gt_file_list, batch_size=training_params['batch_size'], is_train_data = True, augment_data=training_params['augment_data']) + validation_generator = X0140_data_loader(test_data_file_list, test_data_gt_file_list, batch_size=training_params['batch_size'], is_train_data = False, augment_data=training_params['augment_data']) + + checkpoint_filepattern = "weights-{epoch:03d}-{val_loss:.8f}.ckpt" + checkpoint_filepath = os.path.sep.join([chkpt_dir,checkpoint_filepattern]) + + model_checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_filepath,monitor='val_loss',mode='min',save_weights_only=True) + tb_callback = tf.keras.callbacks.TensorBoard(log_dir=tensorboard_logs, write_graph=False, profile_batch=1) + + start_epoch = 0 + init_val_loss = None + if model_params['model_arch'] == 'X0140': + model_obj = X0140_model(model_params, True, training_params) + model = model_obj.get_model() + elif model_params['model_arch'] == 'LC_NNLF': + model_obj = LC_NNLF_model(model_params, True, training_params) + model = model_obj.get_model() + else: + model = None + + if model == None: + print('Unable to create ' + str(model_params['model_arch']) + ' model') + return + else: + print(model.summary()) + + if training_params['resume_chkpt']: + checkpoint_path = training_params['resume_chkpt'] + model.load_weights(checkpoint_path).expect_partial() + #resume from checkpoint sets LR from checkpoint + if training_params['learning_rate']: + K.set_value(model.optimizer.learning_rate, training_params['learning_rate']) + checkpoint_file_name = os.path.splitext(os.path.basename(checkpoint_path))[0] + start_epoch = int(checkpoint_file_name.split('-')[1]) + init_val_loss = float(checkpoint_file_name.split('-')[2]) + + best_model_checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(filepath=best_checkpoint_filepath,monitor='val_loss',mode='min',save_best_only=True, initial_value_threshold=init_val_loss) + + lr_callback = tf.keras.callbacks.LearningRateScheduler(ai_lr_scheduler) + if training_params['encode_cfg'] == "RA_stage1": + lr_callback = tf.keras.callbacks.LearningRateScheduler(ra_lr_scheduler_st1) + elif training_params['encode_cfg'] == "RA_stage2": + lr_callback = tf.keras.callbacks.LearningRateScheduler(ra_lr_scheduler_st2) + + # Copy the config file to the output directory for reference + shutil.copy(sys.argv[1], os.path.join(out_artifacts['out_dir'], "config_file_start_epoch_"+str(start_epoch)+".json")) + + ###Printing initial validation loss, just between input to NNLF and ground truth + empty_model_obj = InpVal_model() + empty_model = empty_model_obj.create_model() + init_hist = empty_model.evaluate(validation_generator, + workers=training_params['num_workers'], + use_multiprocessing=True, + verbose=1) + no_nnlf_loss_file = open(os.path.join(out_artifacts['out_dir'], "valLoss_without_NNLF.txt"), "w") + print("Validation directory list: ", valid_dir_list, file=no_nnlf_loss_file) + print("The loss without NNLF is :", init_hist, file=no_nnlf_loss_file) + no_nnlf_loss_file.close() + + if "train_iter" in cfg.keys(): + for i,train_iter in enumerate(cfg['train_iter']): + if start_epoch > train_iter['end_epoch']: + continue + opt = keras.optimizers.Adam(learning_rate=train_iter['learning_rate']) + loss_function = None + if train_iter['loss'] == "weighted_MSE_411": + loss_function = weighted_MSE_411 + elif train_iter['loss'] == "weighted_MSE_1211": + loss_function = weighted_MSE_1211 + else: + print("ERROR: The loss function passed in config file is invalid") + exit(-1) + model.compile(optimizer=opt, loss=loss_function, experimental_run_tf_function=False) + + if (i > 0) and ((cfg['train_iter'][i-1]['loss'] == "weighted_MSE_411") and (train_iter['loss'] == "weighted_MSE_1211")): + init_val_loss = None + best_model_checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(filepath=best_checkpoint_filepath, monitor='val_loss', mode='min', save_best_only=True, initial_value_threshold=init_val_loss) + + history = model.fit(training_generator, validation_data=validation_generator, use_multiprocessing=True, + workers=training_params['num_workers'], initial_epoch=start_epoch,epochs=train_iter['end_epoch'], + callbacks=[model_checkpoint_callback, best_model_checkpoint_callback, tb_callback, lr_callback]) + start_epoch = train_iter['end_epoch'] + else: + history = model.fit(training_generator, validation_data=validation_generator, use_multiprocessing=True, + workers=training_params['num_workers'], initial_epoch=start_epoch,epochs=training_params['num_epochs'], + callbacks=[model_checkpoint_callback, best_model_checkpoint_callback, tb_callback, lr_callback]) + +def gpu_mem_growth(): + gpus = tf.config.list_physical_devices('GPU') + if gpus: + try: + # Currently, memory growth needs to be the same across GPUs + for gpu in gpus: + tf.config.experimental.set_memory_growth(gpu, True) + logical_gpus = tf.config.list_logical_devices('GPU') + print(len(gpus), "Physical GPUs,", len(logical_gpus), "Logical GPUs") + except RuntimeError as e: + # Memory growth must be set before GPUs have been initialized + print(e) + return + +if __name__ == "__main__": + gpu_mem_growth() + train_NNLF() diff --git a/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/norm_auto.py b/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/norm_auto.py new file mode 100644 index 0000000000..f079dda9be --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/norm_auto.py @@ -0,0 +1,529 @@ +from sys import argv +from keras import models +import os + +def print_model(model): + for idx, layer in enumerate(model.layers[1:]): + wts = layer.get_weights() + if len(wts) == 1 and wts != []: + print(abs(wts[0]).max()) + elif len(wts) == 2 and wts != []: + print(abs(wts[0]).max()) + print(abs(wts[1]).max()) + + return + +def max_weight(model): + max_value = 0.0 + layer_name = '' + for idx, layer in enumerate(model.layers[1:]): + wts = layer.get_weights() + if len(wts) == 2 and wts != [] and layer.name != 'output_layer': + if max_value < abs(wts[0]).max(): + max_value = abs(wts[0]).max() + layer_name = layer.name + + elif len(wts) == 1 and wts != []: + if max_value < abs(wts[0]).max(): + max_value = abs(wts[0]).max() + layer_name = layer.name + + return layer_name, max_value + +def non_split_max_weight(model): + max_value = 0.0 + layer_name = '' + for idx, layer in enumerate(model.layers[1:]): + wts = layer.get_weights() + if len(wts) == 2 and wts != []: + if max_value < abs(wts[0]).max(): + max_value = abs(wts[0]).max() + layer_name = layer.name + + elif len(wts) == 1 and wts != []: + if max_value < abs(wts[0]).max(): + max_value = abs(wts[0]).max() + layer_name = layer.name + + if layer.name == 'hidden_1_y_1' or layer.name == 'hidden_1_c_1': + break + + return layer_name, max_value + +def max_split_weight(model, y_flag, c_flag): + max_value = 0.0 + layer_name = '' + if y_flag is True and c_flag is True: + return "Completed", 0.0 + elif y_flag is True and c_flag is False: + for idx, layer in enumerate(model.layers[1:]): + if '_c_' not in layer.name: + continue + wts = layer.get_weights() + if len(wts) == 2 and wts != []: + if max_value < abs(wts[0]).max(): + max_value = abs(wts[0]).max() + layer_name = layer.name + + elif len(wts) == 1 and wts != []: + if max_value < abs(wts[0]).max(): + max_value = abs(wts[0]).max() + layer_name = layer.name + elif y_flag is False and c_flag is True: + for idx, layer in enumerate(model.layers[1:]): + if '_y_' not in layer.name: + continue + wts = layer.get_weights() + if len(wts) == 2 and wts != []: + if max_value < abs(wts[0]).max(): + max_value = abs(wts[0]).max() + layer_name = layer.name + + elif len(wts) == 1 and wts != []: + if max_value < abs(wts[0]).max(): + max_value = abs(wts[0]).max() + layer_name = layer.name + else: + for idx, layer in enumerate(model.layers[1:]): + if '_y_' not in layer.name and '_c_' not in layer.name: + continue + wts = layer.get_weights() + if len(wts) == 2 and wts != []: + if max_value < abs(wts[0]).max(): + max_value = abs(wts[0]).max() + layer_name = layer.name + + elif len(wts) == 1 and wts != []: + if max_value < abs(wts[0]).max(): + max_value = abs(wts[0]).max() + layer_name = layer.name + + return layer_name, max_value + +def max_bias(model): + max_value = 0.0 + layer_name = '' + for idx, layer in enumerate(model.layers[1:]): + wts = layer.get_weights() + if len(wts) == 2 and wts != [] and layer.name != 'conv1' and layer.name != 'output_layer': + if max_value < abs(wts[1]).max(): + max_value = abs(wts[1]).max() + layer_name = layer.name + + return layer_name, max_value + +def mul_backward(model, prev_model, start_layer, end_layer, mul_factor, geo_mean): + start = False + layer_type = 'x0140' + if ('_y_' in start_layer): + layer_type = '_y_' + elif ('_c_' in start_layer): + layer_type = '_c_' + + for idx, layer in enumerate(model.layers[::-1]): + if layer.name != start_layer and start == False: + continue + elif layer.name == end_layer: + break + else: + start = True + wts = layer.get_weights() + if layer.name == start_layer: + wts[0] /= mul_factor + elif layer.name != start_layer and (layer_type == 'x0140' or layer_type in layer.name): + if len(wts) == 2: + f_w = float(geo_mean/abs(wts[0]).max()) + if f_w >= mul_factor: + wts[0] *= mul_factor + wts[1] *= mul_factor + mul_factor = 1 + layer.set_weights(wts) + break + elif f_w < 1: + wts[1] *= mul_factor + else: + wts[0] *= f_w + wts[1] *= mul_factor + mul_factor /= f_w + elif len(wts) == 1: + f = float(geo_mean/abs(wts[0]).max()) + if f >= mul_factor: + wts[0] *= mul_factor + mul_factor = 1 + layer.set_weights(wts) + break + elif f < 1: + continue + else: + wts[0] *= f + mul_factor /= f + + layer.set_weights(wts) + + if mul_factor == 1: + break + if mul_factor != 1 and layer.name == end_layer: + model.set_weights(prev_model.get_weights()) + return mul_factor, False + + return 1, True + +def mul_forward(model, prev_model, start_layer, end_layer, div_factor, geo_mean): + start = False + layer_type = 'x0140' + if ('_y_' in start_layer): + layer_type = '_y_' + elif ('_c_' in start_layer): + layer_type = '_c_' + + for idx, layer in enumerate(model.layers[1:]): + if layer.name != start_layer and start == False: + continue + elif layer.name == end_layer: + break + else: + start = True + wts = layer.get_weights() + if layer.name == start_layer: + wts[0] /= div_factor + if len(wts) == 2: + wts[1] /= div_factor + elif layer.name != start_layer and (layer_type == 'x0140' or layer_type in layer.name): + if len(wts) == 2: + f_w = float(geo_mean/abs(wts[0]).max()) + if f_w >= div_factor: + wts[0] *= div_factor + div_factor = 1 + layer.set_weights(wts) + break + elif f_w < 1: + wts[1] /= div_factor + else: + wts[0] *= f_w + div_factor /= f_w + wts[1] /= div_factor + elif len(wts) == 1: + f = float(geo_mean/abs(wts[0]).max()) + if f >= div_factor: + wts[0] *= div_factor + div_factor = 1 + layer.set_weights(wts) + break + elif f < 1: + continue + else: + wts[0] *= f + div_factor /= f + + layer.set_weights(wts) + + if div_factor == 1: + break + if div_factor != 1 and layer.name == end_layer: + model.set_weights(prev_model.get_weights()) + return div_factor, False + + return 1, True + +if __name__ == '__main__': + + os.environ["CUDA_VISIBLE_DEVICES"]="-1" + model = models.load_model(argv[1], compile=False) + prev_model = models.load_model(argv[1], compile=False) + out_model_path = argv[2] + + model_type = '' + + for idx, layer in enumerate(model.layers[1:]): + if ('_y_' in layer.name) or layer.name.endswith('_Y') or layer.name.endswith('_y') or ('_c_' in layer.name) or layer.name.endswith('_C') or layer.name.endswith('_c'): + model_type = 'LC_split' + break + else: + model_type = 'x0140' + + if model_type == 'LC_split': + layer_prod = 1.0 + layer_prod_y = 1.0 + layer_prod_c = 1.0 + layer_count = 0 + layer_count_y = 0 + layer_count_c = 0 + bias_limit = 2**(15-11) + y_flag = False + c_flag = False + limit_1x1 = 1.0 + limit_3x1_1x3 = 1.0 + div_factor = 1.0 + + # print_model(model) + + # first loop through model + for idx, layer in enumerate(model.layers[1:]): + wts = layer.get_weights() + layer_y = 0 + layer_c = 0 + + if ('_y_' in layer.name): + layer_y = 1 + elif ('_c_' in layer.name): + layer_c = 1 + + if wts != [] and layer_y == 1: + layer_prod_y *= abs(wts[0]).max() + layer_count_y += 1 + elif wts != [] and layer_c == 1: + layer_prod_c *= abs(wts[0]).max() + layer_count_c += 1 + elif wts != [] and layer_y == 0 and layer_c == 0 and not (layer.name.endswith("Y") or layer.name.endswith("C")): + layer_prod *= abs(wts[0]).max() + layer_count += 1 + + geo_mean_y = layer_prod_y**(1/layer_count_y) + geo_mean_c = layer_prod_c**(1/layer_count_c) + geo_mean = layer_prod**(1/layer_count) + + print("Non-split Geo mean :", geo_mean) + print("Luma Geo Mean : ", geo_mean_y) + print("Chroma Geo Mean : ", geo_mean_c) + + start_layer, max_value = non_split_max_weight(model) + prev_max_value = 0 + + while True: + prev_model.set_weights(model.get_weights()) + factor = float(max_value/geo_mean) + + if factor <= 1: + break + + first_layer = '' + last_layer = 'hidden_0_3d' + + mul_fac, result = mul_forward(model, prev_model, start_layer, last_layer, factor, geo_mean) + if result == False: + div_fac, result = mul_backward(model, prev_model, start_layer, first_layer, factor, geo_mean) + if result == False: + if mul_fac > div_fac: + factor /= mul_fac + _, result = mul_backward(model, prev_model, start_layer, first_layer, factor, geo_mean) + elif mul_fac < div_fac: + factor /= div_fac + _, result = mul_forward(model, prev_model, start_layer, last_layer, factor, geo_mean) + + if result == False: + print_model(model) + print("Error cannot reduce", start_layer, max_value) + break + + start_layer, max_value = non_split_max_weight(model) + + if prev_max_value == max_value: + print("Non-split Normalization Complete:") + break + + prev_max_value = max_value + + # weight normalization + start_layer, max_value = max_split_weight(model, y_flag, c_flag) + prev_max_value = 0 + + while True: + if ('_y_' in start_layer): + + prev_model.set_weights(model.get_weights()) + factor = float(max_value/geo_mean_y) + + if factor <= 1: + break + + first_layer = 'hidden_0_3d' + last_layer = 'hidden_10_c_3d' + + mul_fac, result = mul_forward(model, prev_model, start_layer, last_layer, factor, geo_mean_y) + if result == False: + div_fac, result = mul_backward(model, prev_model, start_layer, first_layer, factor, geo_mean_y) + if result == False: + if mul_fac > div_fac: + factor /= mul_fac + _, result = mul_backward(model, prev_model, start_layer, first_layer, factor, geo_mean_y) + elif mul_fac < div_fac: + factor /= div_fac + _, result = mul_forward(model, prev_model, start_layer, last_layer, factor, geo_mean_y) + + if result == False: + print_model(model) + print("Error cannot reduce", start_layer, max_value) + break + + start_layer, max_value = max_split_weight(model, y_flag, c_flag) + + if prev_max_value == max_value: + print("Y Normalization Complete:") + y_flag = True + + prev_max_value = max_value + + elif ('_c_' in start_layer): + + prev_model.set_weights(model.get_weights()) + factor = float(max_value/geo_mean_c) + + if factor <= 1: + break + + first_layer = 'hidden_1_y_1' + last_layer = 'output_layer_Y' + + mul_fac, result = mul_forward(model, prev_model, start_layer, last_layer, factor, geo_mean_c) + if result == False: + div_fac, result = mul_backward(model, prev_model, start_layer, first_layer, factor, geo_mean_c) + if result == False: + if mul_fac > div_fac: + factor /= mul_fac + _, result = mul_backward(model, prev_model, start_layer, first_layer, factor, geo_mean_c) + elif mul_fac < div_fac: + factor /= div_fac + _, result = mul_forward(model, prev_model, start_layer, last_layer, factor, geo_mean_c) + + if result == False: + print_model(model) + print("Error cannot reduce", start_layer, max_value) + break + + start_layer, max_value = max_split_weight(model, y_flag, c_flag) + + if prev_max_value == max_value: + print("C Normalization Complete") + c_flag = True + + prev_max_value = max_value + + elif (start_layer == 'Completed'): + # print_model(model) + break + + # bias normalization + start_layer, max_value = max_bias(model) + prev_max_value = 0 + + while max_value >= bias_limit: + + prev_model.set_weights(model.get_weights()) + + factor = float(max_value/bias_limit) + if factor <= 1: + print("\nBias Normalization Complete:") + break + first_layer = 'conv1' + last_layer = 'output_layer' + + result = mul_forward(model, prev_model, start_layer, last_layer, factor, bias_limit) + if result == False: + result = mul_backward(model, prev_model, start_layer, first_layer, factor, bias_limit) + if result == False: + print_model(model) + print("Error cannot reduce", start_layer, max_value) + break + + start_layer, max_value = max_bias(model) + + if max_value == bias_limit: + print("\nBias Normalization Complete:") + # print_model(model) + break + + prev_max_value = max_value + + elif model_type == 'x0140': + layer_prod = 1.0 + layer_count = 0 + bias_limit = 2**(15-11) + + # print_model(model) + + # first loop through model + for idx, layer in enumerate(model.layers[1:]): + wts = layer.get_weights() + if layer.name == 'output_layer': + continue + + if wts != []: + layer_prod *= abs(wts[0]).max() + layer_count += 1 + + geo_mean = layer_prod**(1/layer_count) + + print("Geo Mean : ", geo_mean) + + # weight normalization + start_layer, max_value = max_weight(model) + prev_max_value = 0 + + while max_value >= geo_mean: + + prev_model.set_weights(model.get_weights()) + factor = float(max_value/geo_mean) + if factor <= 1: + break + first_layer = '' + last_layer = 'output_layer' + + mul_fac, result = mul_forward(model, prev_model, start_layer, last_layer, factor, geo_mean) + if result == False: + div_fac, result = mul_backward(model, prev_model, start_layer, first_layer, factor, geo_mean) + if result == False: + if mul_fac > div_fac: + factor /= mul_fac + _, result = mul_backward(model, prev_model, start_layer, first_layer, factor, geo_mean) + elif mul_fac < div_fac: + factor /= div_fac + _, result = mul_forward(model, prev_model, start_layer, last_layer, factor, geo_mean) + + if result == False: + print_model(model) + print("Error cannot reduce", start_layer, max_value) + break + + start_layer, max_value = max_weight(model) + + if prev_max_value == max_value: + print("Weight Normalization Complete:") + # print_model(model) + break + + prev_max_value = max_value + + # bias normalization + start_layer, max_value = max_bias(model) + prev_max_value = 0 + + while max_value >= bias_limit: + + prev_model.set_weights(model.get_weights()) + + factor = float(max_value/bias_limit) + if factor <= 1: + print("Bias Normalization Complete:") + break + first_layer = 'conv1' + last_layer = 'output_layer' + + result = mul_forward(model, prev_model, start_layer, last_layer, factor, bias_limit) + if result == False: + result = mul_backward(model, prev_model, start_layer, first_layer, factor, bias_limit) + if result == False: + print_model(model) + print("Error cannot reduce", start_layer, max_value) + break + + start_layer, max_value = max_bias(model) + + if max_value == bias_limit: + print("Bias Normalization Complete:") + # print_model(model) + break + + prev_max_value = max_value + + # model saved + model.save(out_model_path) \ No newline at end of file diff --git a/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/q_factor_gen.py b/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/q_factor_gen.py new file mode 100644 index 0000000000..4a39007427 --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/q_factor_gen.py @@ -0,0 +1,106 @@ +from sys import argv + +# Assuming int16 is the required sadl format + +word = "[INFO] id: " +stop_word = "[INFO] == end model loading ==" +input_1 = open(argv[1], "r") +input_2 = open(argv[1], "r") +output = open(argv[2], "w") + +model_num = str(argv[1].split('/')[-1]).split('_')[-1] + +q = 11 #input +q_bias = 11 +groups = 1 + +string = "" +i = 0 +model_type = '' + +temp = input_1.readlines() +for idx, line in enumerate(temp): + if ('_y_' in line) or line.endswith('_Y') or ('_c_' in line) or line.endswith('_C'): + model_type = 'LC_split' + break + else: + model_type = 'x0140' + +content = input_2.readlines() +for idx, line in enumerate(content): + i += 1 + if line.find(word) != -1: + line_split = line.split(" ") + id = line_split[2] + name = line_split[5] + if name == "Placeholder\n": + string = string + id + " " + str(q) + " " + elif name == "Conv2D\n": + q_dynamic = 0 + if groups >= int(1): + data_line = content[idx - 2] + tensor_line = content[idx - 3] + data = float(data_line.split(" ")[-1]) + for q_dynamic in range(0, 15): + if(2**q_dynamic > abs(data)): + break + q_upd = 15 - q_dynamic + groups = 0 + if(int(q) > 9): + string = string[0:-3] + str(q_upd) + " " + id + " 0 " + else: + string = string[0:-2] + str(q_upd) + " " + id + " 0 " + else: + string = string + id + " 0 " + elif name == "LeakyRelu\n" or name == "Concat\n" or name == "Add\n": + id = id + elif name == "BiasAdd\n": + # Split model + if (model_type == 'LC_split') and content[idx + 1].find('hidden_1_c_1') != -1: + q_bias = 11 + elif (model_type == 'LC_split') and content[idx + 1].find('hidden_2_c_1') != -1: + q_bias = 14 + elif (model_type == 'LC_split') and content[idx + 1].find('hidden_10_c_1') != -1: + q_bias = 12 + elif (model_type == 'LC_split') and content[idx + 1].find('output_layer_C') != -1: + q_bias = 13 + elif (model_type == 'LC_split') and content[idx + 1].find('hidden_1_y_1') != -1: + q_bias = 11 + elif (model_type == 'LC_split') and content[idx + 1].find('hidden_3_y_1') != -1: + q_bias = 12 + elif (model_type == 'LC_split') and content[idx + 1].find('output_layer_Y') != -1: + q_bias = 13 + + # X0140 model + elif (model_type == 'x0140') and content[idx + 1].find('hidden_2_1') != -1: + q_bias = 12 + elif (model_type == 'x0140') and content[idx + 1].find('hidden_10_1') != -1: + q_bias = 13 + + if True: + q_upd = q_bias + + if(int(q) > 9): + string = string[0:-3] + str(q_upd) + " " + else: + string = string[0:-2] + str(q_upd) + " " + + else: + if (name == "Const\n") and content[idx + 6].find("Concat") != -1: + q_upd = 0 + else: + q_upd = q + string = string + id + " " + str(q_upd) + " " + if (name != "Const\n"): + print("Entered here for wrong name", name) + elif(line.find(stop_word)!=-1): + break + else: + grp_line = content[idx + 6] + if(grp_line.find("groups:")!= -1): + groups = int(grp_line.split(" ")[-1][:-1]) + +output.write(string) +input_1.close() +input_2.close() +output.close() diff --git a/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/sadl_mod_src_files/layer_const.h b/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/sadl_mod_src_files/layer_const.h new file mode 100644 index 0000000000..586308c182 --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/sadl_mod_src_files/layer_const.h @@ -0,0 +1,154 @@ +/* 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-2023, 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. + */ +#pragma once +#include <cmath> + +#include "layer.h" + +namespace sadl +{ +namespace layers +{ +template<typename T> class Const : public Layer<T> +{ +public: + using Layer<T>::Layer; + using Layer<T>::m_out; // to avoid this-> + using Layer<T>::m_initDone; + + virtual bool apply(std::vector<Tensor<T> *> &in) override; + virtual bool init(const std::vector<Tensor<T> *> &in) override; + +protected: + virtual bool loadInternal(std::istream &file, Version v) override; + template<typename U> void readTensor(std::istream &file, Tensor<T> &out); + DUMP_MODEL_EXT; +}; + +template<typename T> bool Const<T>::apply(std::vector<Tensor<T> *> &in) +{ + assert(in.size() == 0); + (void) in; + // assert(ptr==ptr) + return true; +} + +template<typename T> bool Const<T>::init(const std::vector<Tensor<T> *> &in) +{ + if (in.size() != 0) + return false; + m_initDone = true; + return true; +} + +template<typename T> template<typename U> void Const<T>::readTensor(std::istream &file, Tensor<T> &out) +{ + if (std::is_same<T, U>::value) + file.read((char *) out.data(), sizeof(T) * out.size()); + else + { + std::vector<U> data(out.size()); + file.read((char *) data.data(), sizeof(U) * data.size()); + for (int k = 0; k < (int) data.size(); ++k) + out[k] = static_cast<T>(data[k]); + } +} + +template<typename T> bool Const<T>::loadInternal(std::istream &file, Version v) +{ + // load values + int32_t x = 0; + file.read((char *) &x, sizeof(x)); + if (x <= 0 || x > Dimensions::MaxDim) + { + std::cerr << "[ERROR] invalid nb of dimensions: " << x << std::endl; + return false; + } + Dimensions d; + d.resize(x); + for (int k = 0; k < d.size(); ++k) + { + file.read((char *) &x, sizeof(x)); + d[k] = x; + } + + if (d.nbElements() >= Tensor<T>::kMaxSize) + { + std::cerr << "[ERROR] tensor too large? " << d.nbElements() << std::endl; + return false; + } + m_out.resize(d); + SADL_DBG(std::cout << " - tensor: " << m_out.dims() << std::endl); + + file.read((char *) &x, sizeof(x)); + + // cannot check internal type because tensor also used by reshape etc. + switch (x) + { + case TensorInternalType::Int32: + // assert((std::is_same<T,int32_t>::value)); + file.read((char *) &m_out.quantizer, sizeof(m_out.quantizer)); + readTensor<int32_t>(file, m_out); + break; + case TensorInternalType::Float: + // assert((std::is_same<T, float>::value)); + readTensor<float>(file, m_out); + break; + case TensorInternalType::Int16: + // assert((std::is_same<T, int16_t>::value)); + file.read((char *) &m_out.quantizer, sizeof(m_out.quantizer)); + readTensor<int16_t>(file, m_out); + break; + default: + std::cerr << "[ERROR] unknown internal type " << x << std::endl; + return false; + } + + float max_tensor = 0.0; + int k = 0; + max_tensor = m_out[0]; + for (k = 0; k < m_out.size(); ++k) + if(fabs(max_tensor) < fabs(m_out[k])) + { + max_tensor = m_out[k]; + } + SADL_DBG( std::cout << " - data: "; + std::cout << max_tensor << std::endl; + ); + SADL_DBG(std::cout << " - quantizer: " << m_out.quantizer << std::endl); + // SADL_DBG(std::cout<<m_out<<std::endl;) + return true; +} + +} // namespace layers +} // namespace sadl diff --git a/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/sadl_mod_src_files/layer_conv2d_1x1.h b/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/sadl_mod_src_files/layer_conv2d_1x1.h new file mode 100644 index 0000000000..db3de6f1ed --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/sadl_mod_src_files/layer_conv2d_1x1.h @@ -0,0 +1,444 @@ +/* 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-2023, 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. + */ +#pragma once +#include <cmath> +#include "layer.h" +#if __AVX2__ +#include <immintrin.h> +#endif + +namespace sadl +{ +namespace layers +{ +// //////////////////////////////////////////////////////////////////////////////////////////////////////// +// 1x1 +// //////////////////////////////////////////////////////////////////////////////////////////////////////// +template<typename T> template<int s_h, int s_w> void Conv2D<T>::conv2d_1x1_s_dispatch(const Tensor<T> &A, const Tensor<T> &kernel) +{ +#if __AVX2__ +#define CONV_MOD8 simd8_conv2d_1x1_s_d +#define CONV_MOD16 simd16_conv2d_1x1_s_d +#define CONV_MOD32 simd32_conv2d_1x1_s_d +#else +#define CONV_MOD8 conv2d_1x1_s_d +#define CONV_MOD16 conv2d_1x1_s_d +#define CONV_MOD32 conv2d_1x1_s_d +#endif + const int in_D{ A.dims()[3] }; + switch (in_D) + { + case 1: + conv2d_1x1_s_d<1, s_h, s_w>(A, kernel); + break; + case 2: + conv2d_1x1_s_d<2, s_h, s_w>(A, kernel); + break; + case 4: + conv2d_1x1_s_d<4, s_h, s_w>(A, kernel); + break; + case 8: + CONV_MOD8<8, s_h, s_w>(A, kernel); + break; + case 16: + CONV_MOD16<16, s_h, s_w>(A, kernel); + break; + case 24: + CONV_MOD8<24, s_h, s_w>(A, kernel); + break; + case 32: + CONV_MOD32<32, s_h, s_w>(A, kernel); + break; + case 48: + CONV_MOD16<48, s_h, s_w>(A, kernel); + break; + case 64: + CONV_MOD32<64, s_h, s_w>(A, kernel); + break; + case 72: + // better do 64 and than 8 + CONV_MOD8<72, s_h, s_w>(A, kernel); + break; + case 96: + CONV_MOD32<96, s_h, s_w>(A, kernel); + break; + case 128: + CONV_MOD32<128, s_h, s_w>(A, kernel); + break; + case 160: + CONV_MOD32<160, s_h, s_w>(A, kernel); + break; + case 384: + CONV_MOD32<384, s_h, s_w>(A, kernel); + break; + case 480: + CONV_MOD32<480, s_h, s_w>(A, kernel); + break; + default: + conv2d_1x1_s<s_h, s_w>(A, kernel); + break; + } +#undef CONV_MOD8 +#undef CONV_MOD16 +#undef CONV_MOD32 +} + +template<typename T> template<int s_h, int s_w> void Conv2D<T>::conv2d_1x1_s(const Tensor<T> &A, const Tensor<T> &kernel) +{ + const int in_H{ A.dims()[1] }; + const int in_W{ A.dims()[2] }; + const int in_D{ A.dims()[3] }; + const int nb_filters{ kernel.dims()[2] }; + constexpr int half_size_h{ 0 }; + constexpr int half_size_w{ 0 }; + const int top{ m_pads[0] }; + const int left{ m_pads[1] }; + const int start_h{ half_size_h - top }; + const int start_w{ half_size_w - left }; + +#if DEBUG_SIMD && __AVX2__ + std::cout << "\n[WARN] generic version conv1x1 inD=" << in_D << " outD=" << nb_filters << " s=[" << s_w << ' ' << s_h << "] " << in_H << 'x' << in_W << " " + << in_D * kernel.dims()[0] * kernel.dims()[1] * nb_filters * (in_H / s_h) * (in_W / s_w) / 1000 << " kMAC" << std::endl; +#endif + constexpr int im_nb = 0; + const int shift = kernel.quantizer + m_q; + for (int im_i = start_h; im_i < in_H; im_i += s_h) + { + for (int im_j = start_w; im_j < in_W; im_j += s_w) + { + for (int filter_nb = 0; filter_nb < nb_filters; ++filter_nb) + { + typename ComputationType<T>::type x = 0; + for (int filter_d = 0; filter_d < in_D; ++filter_d) + { + { + x += (typename ComputationType<T>::type) A(im_nb, im_i, im_j, filter_d) * kernel(0, 0, filter_nb, filter_d); + COUNTERS_MAC(kernel(0, 0, filter_nb, filter_d)); + } + } + ComputationType<T>::quantize(x, shift); + COUNTERS(x); + SATURATE(x); + m_out(im_nb, im_i / s_w, im_j / s_h, filter_nb) = static_cast<T>(x); + } + } + } +} + +template<typename T> template<int in_D, int s_h, int s_w> void Conv2D<T>::conv2d_1x1_s_d(const Tensor<T> &A, const Tensor<T> &kernel) +{ + const int in_H{ A.dims()[1] }; + const int in_W{ A.dims()[2] }; + const int nb_filters{ kernel.dims()[2] }; + constexpr int half_size_h{ 0 }; + constexpr int half_size_w{ 0 }; + const int top{ m_pads[0] }; + const int left{ m_pads[1] }; + const int start_h{ half_size_h - top }; + const int start_w{ half_size_w - left }; + +#if DEBUG_SIMD && __AVX2__ + std::cout << "\n[WARN] generic version conv 1x1 inD=" << in_D << " outD=" << nb_filters << " s=[" << s_w << ' ' << s_h << "] " << in_H << 'x' << in_W << " " + << in_D * kernel.dims()[0] * kernel.dims()[1] * nb_filters * (in_H / s_h) * (in_W / s_w) / 1000 << " kMAC" << std::endl; +#endif + + constexpr int im_nb = 0; + const int shift = kernel.quantizer + m_q; + for (int im_i = start_h; im_i < in_H; im_i += s_h) + { + for (int im_j = start_w; im_j < in_W; im_j += s_w) + { + for (int filter_nb = 0; filter_nb < nb_filters; ++filter_nb) + { + typename ComputationType<T>::type x = 0; + for (int filter_d = 0; filter_d < in_D; ++filter_d) + { + x += (typename ComputationType<T>::type) A(im_nb, im_i, im_j, filter_d) * kernel(0, 0, filter_nb, filter_d); + COUNTERS_MAC(kernel(0, 0, filter_nb, filter_d)); + } + ComputationType<T>::quantize(x, shift); + COUNTERS(x); + SATURATE(x); + m_out(im_nb, im_i / s_h, im_j / s_w, filter_nb) = static_cast<T>(x); + } + } + } +} + +#if __AVX2__ +// //////////////////////////////////////////////////////////////////////////////////////////////////////// +// 1x1 +// //////////////////////////////////////////////////////////////////////////////////////////////////////// +template<> template<int in_D, int s_h, int s_w> inline void Conv2D<float>::simd8_conv2d_1x1_s_d(const Tensor<float> &A, const Tensor<float> &kernel) +{ + static_assert(in_D % 8 == 0, "Should be used with mod8 filters."); + const int in_H{ A.dims()[1] }; + const int in_W{ A.dims()[2] }; + const int nb_filters{ kernel.dims()[2] }; + constexpr int half_size_h{ 0 }; + constexpr int half_size_w{ 0 }; + const int top{ m_pads[0] }; + const int left{ m_pads[1] }; + const int start_h{ half_size_h - top }; + const int start_w{ half_size_w - left }; +#if DEBUG_SIMD && __AVX512F__ + if (in_D >= 16) + { + std::cout << "\n[WARN] suboptimal SIMD8 version conv 1x1 inD=" << in_D << " outD=" << nb_filters << " s=[" << s_w << ' ' << s_h << "] " << in_H << 'x' + << in_W << " " << in_D * kernel.dims()[0] * kernel.dims()[1] * nb_filters * (in_H / s_h) * (in_W / s_w) / 1000 << " kMAC" << std::endl; + } +#endif + constexpr int im_nb = 0; + for (int im_i = start_h; im_i < in_H; im_i += s_h) + { + for (int im_j = start_w; im_j < in_W; im_j += s_w) + { + for (int filter = 0; filter < nb_filters; ++filter) + { + __m256 s = _mm256_setzero_ps(); + for (int filter_d = 0; filter_d < in_D; filter_d += 8) + { + const float *kptr = kernel.addr(0, 0, filter, filter_d); + const float *aptr = A.addr(im_nb, im_i, im_j, filter_d); + const __m256 k0 = _mm256_load_ps(kptr); +#if __FMA__ + s = _mm256_fmadd_ps(k0, _mm256_load_ps(aptr), s); +#else + const __m256 m0 = _mm256_mul_ps(k0, _mm256_load_ps(aptr)); + s = _mm256_add_ps(s, m0); + // s + m0; // s = _mm256_hadd_ps(s, m0); +#endif + } + m_out(im_nb, im_i / s_h, im_j / s_w, filter) = sum8_float(s); + } + } + } +} + +#if __AVX512F__ +template<> template<int in_D, int s_h, int s_w> inline void Conv2D<float>::simd16_conv2d_1x1_s_d(const Tensor<float> &A, const Tensor<float> &kernel) +{ + static_assert(in_D % 16 == 0, "Should be used with mod16 filters."); + constexpr int im_nb = 0; + const int in_H{ A.dims()[1] }; + const int in_W{ A.dims()[2] }; + const int nb_filters{ kernel.dims()[2] }; + constexpr int half_size_h{ 0 }; + constexpr int half_size_w{ 0 }; + const int top{ m_pads[0] }; + const int left{ m_pads[1] }; + const int start_h{ half_size_h - top }; + const int start_w{ half_size_w - left }; + for (int im_i = start_h; im_i < in_H; im_i += s_h) + { + for (int im_j = start_w; im_j < in_W; im_j += s_w) + { + for (int filter = 0; filter < nb_filters; ++filter) + { + __m512 s = _mm512_setzero_ps(); + for (int filter_d = 0; filter_d < in_D; filter_d += 16) + { + const float *kptr = kernel.addr(0, 0, filter, filter_d); + const float *aptr = A.addr(im_nb, im_i, im_j, filter_d); + const __m512 k0 = _mm512_load_ps(kptr); +#if __FMA__ + s = _mm512_fmadd_ps(k0, _mm512_load_ps(aptr), s); +#else + const __m512 m0 = _mm512_mul_ps(k0, _mm512_load_ps(aptr)); + s = _mm512_add_ps(s, m0); +#endif + } + m_out(im_nb, im_i / s_h, im_j / s_w, filter) = sum16_float(s); + } + } + } +} +#endif + +// int16 +template<> template<int in_D, int s_h, int s_w> void Conv2D<int16_t>::simd8_conv2d_1x1_s_d(const Tensor<int16_t> &A, const Tensor<int16_t> &kernel) +{ // should be sse42 +#if DEBUG_COUNTERS || SATURATE_RESULT + using T = int16_t; +#endif + const int in_H{ A.dims()[1] }; + const int in_W{ A.dims()[2] }; + const int nb_filters{ kernel.dims()[2] }; + constexpr int half_size_h{ 0 }; + constexpr int half_size_w{ 0 }; + const int top{ m_pads[0] }; + const int left{ m_pads[1] }; + const int start_h{ half_size_h - top }; + const int start_w{ half_size_w - left }; + static_assert(in_D % 8 == 0, "Should be used with mod16 filters."); +#if DEBUG_SIMD && __AVX2__ + if (in_D >= 8) + { + std::cout << "\n[WARN] suboptimal SIMD8 version conv 3x3 inD=" << in_D << " outD=" << nb_filters << " s=[" << s_w << ' ' << s_h << "] " << in_H << 'x' + << in_W << " " << in_D * kernel.dims()[0] * kernel.dims()[1] * nb_filters * (in_H / s_h) * (in_W / s_w) / 1000 << " kMAC" << std::endl; + } +#endif + constexpr int im_nb = 0; + const int shift = kernel.quantizer + m_q; + for (int im_i = start_h; im_i < in_H; im_i += s_h) + { + for (int im_j = start_w; im_j < in_W; im_j += s_w) + { + for (int filter = 0; filter < nb_filters; ++filter) + { + __m128i s = _mm_setzero_si128(); + for (int filter_d = 0; filter_d < in_D; filter_d += 8) + { + const __m128i *kptr = (const __m128i *) kernel.addr(0, 0, filter, filter_d); + const __m128i k0 = _mm_load_si128(kptr); // or loadu ? + const __m128i *aptr = (const __m128i *) A.addr(im_nb, im_i, im_j, filter_d); + const __m128i v0 = _mm_load_si128(aptr); + + const __m128i mad0 = _mm_madd_epi16(k0, v0); // res in si32 + s = _mm_add_epi32(s, mad0); + } +#if ENABLE_HALF_ROUND + typename ComputationType<T>::type z = shift ? ((sum32_int16(s) + (1 << (shift - 1))) >> shift) : sum32_int16(s); +#else + typename ComputationType<T>::type z = (sum32_int16(s) >> shift); +#endif + SATURATE(z); + m_out(im_nb, im_i / s_h, im_j / s_w, filter) = static_cast<int16_t>(z); + } + } + } +} + +template<> template<int in_D, int s_h, int s_w> void Conv2D<int16_t>::simd16_conv2d_1x1_s_d(const Tensor<int16_t> &A, const Tensor<int16_t> &kernel) +{ +#if DEBUG_COUNTERS || SATURATE_RESULT + using T = int16_t; +#endif + const int in_H{ A.dims()[1] }; + const int in_W{ A.dims()[2] }; + const int nb_filters{ kernel.dims()[2] }; + constexpr int half_size_h{ 0 }; + constexpr int half_size_w{ 0 }; + const int top{ m_pads[0] }; + const int left{ m_pads[1] }; + const int start_h{ half_size_h - top }; + const int start_w{ half_size_w - left }; + static_assert(in_D % 16 == 0, "Should be used with mod16 filters."); +#if DEBUG_SIMD && __AVX512BW__ + if (in_D >= 32) + { + std::cout << "\n[WARN] suboptimal SIMD16 version conv 3x3 inD=" << in_D << " outD=" << nb_filters << " s=[" << s_w << ' ' << s_h << "] " << in_H << 'x' + << in_W << " " << in_D * kernel.dims()[0] * kernel.dims()[1] * nb_filters * (in_H / s_h) * (in_W / s_w) / 1000 << " kMAC" << std::endl; + } +#endif + constexpr int im_nb = 0; + const int shift = kernel.quantizer + m_q; + for (int im_i = start_h; im_i < in_H; im_i += s_h) + { + for (int im_j = start_w; im_j < in_W; im_j += s_w) + { + for (int filter = 0; filter < nb_filters; ++filter) + { + __m256i s = _mm256_setzero_si256(); + for (int filter_d = 0; filter_d < in_D; filter_d += 16) + { + const __m256i *kptr = (const __m256i *) kernel.addr(0, 0, filter, filter_d); + const __m256i k0 = _mm256_load_si256(kptr); // or loadu ? + const __m256i *aptr = (const __m256i *) A.addr(im_nb, im_i, im_j, filter_d); + const __m256i v0 = _mm256_load_si256(aptr); + + const __m256i mad0 = _mm256_madd_epi16(k0, v0); // res in si32 + s = _mm256_add_epi32(s, mad0); + } +#if ENABLE_HALF_ROUND + typename ComputationType<T>::type z = shift ? ((sum32_int16(s) + (1 << (shift - 1))) >> shift) : sum32_int16(s); +#else + typename ComputationType<T>::type z = (sum32_int16(s) >> shift); +#endif + SATURATE(z); + m_out(im_nb, im_i / s_h, im_j / s_w, filter) = static_cast<int16_t>(z); + } + } + } +} + +#if __AVX512BW__ +template<> template<int in_D, int s_h, int s_w> void Conv2D<int16_t>::simd32_conv2d_1x1_s_d(const Tensor<int16_t> &A, const Tensor<int16_t> &kernel) +{ + static_assert(in_D % 32 == 0, "Should be used with mod32 filters."); + using T = int16_t; + const int in_H{ A.dims()[1] }; + const int in_W{ A.dims()[2] }; + const int nb_filters{ kernel.dims()[2] }; + constexpr int half_size_h{ 0 }; + constexpr int half_size_w{ 0 }; + const int top{ m_pads[0] }; + const int left{ m_pads[1] }; + const int start_h{ half_size_h - top }; + const int start_w{ half_size_w - left }; + constexpr int im_nb = 0; + const int shift = kernel.quantizer + m_q; + for (int im_i = start_h; im_i < in_H; im_i += s_h) + { + for (int im_j = start_w; im_j < in_W; im_j += s_w) + { + for (int filter = 0; filter < nb_filters; ++filter) + { + __m512i s = _mm512_setzero_si512(); + for (int filter_d = 0; filter_d < in_D; filter_d += 32) + { + const __m512i *kptr = (const __m512i *) kernel.addr(0, 0, filter, filter_d); + const __m512i k0 = _mm512_load_si512(kptr); + const __m512i *aptr = (const __m512i *) A.addr(im_nb, im_i, im_j, filter_d); + const __m512i v0 = _mm512_load_si512(aptr); + + const __m512i mad0 = _mm512_madd_epi16(k0, v0); // res in si32 + s = _mm512_add_epi32(s, mad0); + } +#if ENABLE_HALF_ROUND + typename ComputationType<int32_t>::type z = shift ? ((_mm512_reduce_add_epi32(s) + (1 << (shift - 1))) >> shift) : _mm512_reduce_add_epi32(s); +#else + typename ComputationType<int32_t>::type z = (_mm512_reduce_add_epi32(s) >> shift); +#endif + COUNTERS(z); + SATURATE(z); + m_out(im_nb, im_i / s_h, im_j / s_w, filter) = z; + } + } + } +} +#endif +#endif + +} // namespace layers +} // namespace sadl diff --git a/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/sadl_mod_src_files/layer_conv2d_3x3.h b/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/sadl_mod_src_files/layer_conv2d_3x3.h new file mode 100644 index 0000000000..36d5c040af --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/sadl_mod_src_files/layer_conv2d_3x3.h @@ -0,0 +1,787 @@ +/* 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-2023, 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. + */ +#pragma once +#include <cmath> +#include "layer.h" +#if __AVX2__ +#include <immintrin.h> +#endif + +namespace sadl +{ +namespace layers +{ +// //////////////////////////////////////////////////////////////////////////////////////////////////////// +// 3x3 +// //////////////////////////////////////////////////////////////////////////////////////////////////////// +template<typename T> template<int s_h, int s_w> void Conv2D<T>::conv2d_3x3_s_peel(const Tensor<T> &A, const Tensor<T> &kernel) +{ + constexpr int im_nb = 0; + const int shift = kernel.quantizer + m_q; + constexpr int ihalf_size = 1; + const int in_H{ A.dims()[1] }; + const int in_W{ A.dims()[2] }; + const int nb_filters{ kernel.dims()[2] }; + constexpr int half_size_h{ ihalf_size }; + constexpr int half_size_w{ ihalf_size }; + const int top{ m_pads[0] }; + const int left{ m_pads[1] }; + const int start_h{ half_size_h - top }; + const int start_w{ half_size_w - left }; + const int in_D{ A.dims()[3] }; + for (int filter_nb = 0; filter_nb < nb_filters; ++filter_nb) + { + // corners + { + int im_i; + int im_j; + auto loop_with_cond = [&, filter_nb, shift](int i0, int i1, int j0, int j1) + { + typename ComputationType<T>::type x = 0; + for (int filter_i = i0; filter_i <= i1; ++filter_i) + { + for (int filter_j = j0; filter_j <= j1; ++filter_j) + { + for (int filter_d = 0; filter_d < in_D; ++filter_d) + { + int ii = im_i + filter_i; + int jj = im_j + filter_j; + int ki = ihalf_size + filter_i; + int kj = ihalf_size + filter_j; + x += (typename ComputationType<T>::type) A(im_nb, ii, jj, filter_d) * kernel(ki, kj, filter_nb, filter_d); + COUNTERS_MAC(kernel(ki, kj, filter_nb, filter_d)); + } + } + } + ComputationType<T>::quantize(x, shift); + COUNTERS(x); + SATURATE(x); + m_out(im_nb, im_i / s_h, im_j / s_w, filter_nb) = static_cast<T>(x); + }; + + im_j = start_w; + if (im_j < in_W) + { // left side + im_i = start_h; + if (im_i < in_H) + { // top left corner + loop_with_cond(-start_h, ihalf_size, -start_w, ihalf_size); + } + im_i = ((in_H - ihalf_size - start_h) / s_h) * s_h + start_h; + if (im_i > 0 && im_i < in_H && im_i != start_h) + { // bottom left corner + const int end_i = (im_i + 1 < in_H) ? 1 : 0; + loop_with_cond(-ihalf_size, end_i, -start_w, ihalf_size); + } + } + + im_j = ((in_W - ihalf_size - start_w) / s_w) * s_w + start_w; + const int end_j = (im_j + 1 < in_W) ? 1 : 0; + if (im_j > 0 && im_j < in_W && im_j != start_w) + { // rihgt side + im_i = start_h; + if (im_i < in_H) + { // top right corner + loop_with_cond(-start_h, ihalf_size, -ihalf_size, end_j); + } + + im_i = ((in_H - ihalf_size - start_h) / s_h) * s_h + start_h; + if (im_i > 0 && im_i < in_H && im_i != start_h) + { // bottom right corner + const int end_i = (im_i + 1 < in_H) ? 1 : 0; + loop_with_cond(-ihalf_size, end_i, -ihalf_size, end_j); + } + } + } + + // vertical borders + { + for (int im_i = start_h + s_h; im_i < in_H - ihalf_size; im_i += s_h) + { + int im_j = start_w; // can be only 0 or 1 + if (im_j < in_W) + { // left side + typename ComputationType<T>::type x = 0; + for (int filter_i = -ihalf_size; filter_i <= ihalf_size; ++filter_i) + { + for (int filter_j = -start_w; filter_j <= ihalf_size; ++filter_j) + { + for (int filter_d = 0; filter_d < in_D; ++filter_d) + { + int ii = im_i + filter_i; + int jj = im_j + filter_j; + int ki = ihalf_size + filter_i; + int kj = ihalf_size + filter_j; + x += (typename ComputationType<T>::type) A(im_nb, ii, jj, filter_d) * kernel(ki, kj, filter_nb, filter_d); + COUNTERS_MAC(kernel(ki, kj, filter_nb, filter_d)); + } + } + } + ComputationType<T>::quantize(x, shift); + COUNTERS(x); + SATURATE(x); + m_out(im_nb, im_i / s_h, im_j / s_w, filter_nb) = static_cast<T>(x); + } + + im_j = ((in_W - ihalf_size - start_w) / s_w) * s_w + start_w; + if (im_j > 0 && im_j < in_W && im_j != start_w) + { // rihgt side + typename ComputationType<T>::type x = 0; + const int end_filter = (im_j + 1) < in_W ? 1 : 0; + for (int filter_i = -ihalf_size; filter_i <= ihalf_size; ++filter_i) + { + for (int filter_j = -ihalf_size; filter_j <= end_filter; ++filter_j) + { + for (int filter_d = 0; filter_d < in_D; ++filter_d) + { + int ii = im_i + filter_i; + int jj = im_j + filter_j; + int ki = ihalf_size + filter_i; + int kj = ihalf_size + filter_j; + x += (typename ComputationType<T>::type) A(im_nb, ii, jj, filter_d) * kernel(ki, kj, filter_nb, filter_d); + COUNTERS_MAC(kernel(ki, kj, filter_nb, filter_d)); + } + } + } + ComputationType<T>::quantize(x, shift); + COUNTERS(x); + SATURATE(x); + m_out(im_nb, im_i / s_h, im_j / s_w, filter_nb) = static_cast<T>(x); + } + } + } + { + // horizontal borders + for (int im_j = s_w + start_w; im_j < in_W - ihalf_size; im_j += s_w) + { + int im_i = start_h; // 0 or 1 -> adapt filter start + if (im_i < in_H) + { // top line + typename ComputationType<T>::type x = 0; + for (int filter_i = -start_h; filter_i <= ihalf_size; ++filter_i) + { + for (int filter_j = -ihalf_size; filter_j <= ihalf_size; ++filter_j) + { + for (int filter_d = 0; filter_d < in_D; ++filter_d) + { + int ii = im_i + filter_i; + int jj = im_j + filter_j; + int ki = ihalf_size + filter_i; + int kj = ihalf_size + filter_j; + x += (typename ComputationType<T>::type) A(im_nb, ii, jj, filter_d) * kernel(ki, kj, filter_nb, filter_d); + COUNTERS_MAC(kernel(ki, kj, filter_nb, filter_d)); + } + } + } + + ComputationType<T>::quantize(x, shift); + COUNTERS(x); + SATURATE(x); + m_out(im_nb, im_i / s_h, im_j / s_w, filter_nb) = static_cast<T>(x); + } + im_i = ((in_H - ihalf_size - start_h) / s_h) * s_h + start_h; + if (im_i > 0 && im_i < in_H && im_i != start_h) + { // bottom line + typename ComputationType<T>::type x = 0; + const int end_filter = (im_i + 1) < in_H ? 1 : 0; + for (int filter_i = -ihalf_size; filter_i <= end_filter; ++filter_i) + { + for (int filter_j = -ihalf_size; filter_j <= ihalf_size; ++filter_j) + { + for (int filter_d = 0; filter_d < in_D; ++filter_d) + { + int ii = im_i + filter_i; + int jj = im_j + filter_j; + int ki = ihalf_size + filter_i; + int kj = ihalf_size + filter_j; + x += (typename ComputationType<T>::type) A(im_nb, ii, jj, filter_d) * kernel(ki, kj, filter_nb, filter_d); + COUNTERS_MAC(kernel(ki, kj, filter_nb, filter_d)); + } + } + } + ComputationType<T>::quantize(x, shift); + COUNTERS(x); + SATURATE(x); + m_out(im_nb, im_i / s_h, im_j / s_w, filter_nb) = static_cast<T>(x); + } + } + } + } // filter_nb +} + +template<typename T> template<int s_h, int s_w> void Conv2D<T>::conv2d_3x3_s_core(const Tensor<T> &A, const Tensor<T> &kernel) +{ + const int nb_filters{ kernel.dims()[2] }; + const int in_H{ A.dims()[1] }; + const int in_W{ A.dims()[2] }; + const int in_D{ A.dims()[3] }; + constexpr int ihalf_size = 1; + constexpr int half_size_h{ ihalf_size }; + constexpr int half_size_w{ ihalf_size }; + const int top{ m_pads[0] }; + const int left{ m_pads[1] }; + const int start_h{ half_size_h - top }; + const int start_w{ half_size_w - left }; + constexpr int im_nb = 0; + constexpr int half_size = 1; + const int shift = kernel.quantizer + m_q; +#if DEBUG_SIMD && __AVX2__ + std::cout << "\n[WARN] generic version conv 3x3 inD=" << in_D << " outD=" << nb_filters << " s=[" << s_w << ' ' << s_h << "] " << in_H << 'x' << in_W << " " + << in_D * kernel.dims()[0] * kernel.dims()[1] * nb_filters * (in_H / s_h) * (in_W / s_w) / 1000 << " kMAC" << std::endl; +#endif + assert(start_h + s_h - half_size_h >= 0); + assert(start_w + s_w - half_size_w >= 0); + for (int im_i = start_h + s_h; im_i < in_H - half_size_h; im_i += s_h) + { + for (int im_j = start_w + s_w; im_j < in_W - half_size_w; im_j += s_w) + { + for (int filter = 0; filter < nb_filters; ++filter) + { + typename ComputationType<T>::type x = 0; + for (int filter_i = -half_size; filter_i <= half_size; ++filter_i) + { // fixed + for (int filter_j = -half_size; filter_j <= half_size; ++filter_j) + { // fixed + for (int filter_d = 0; filter_d < in_D; ++filter_d) + { + int ii = im_i + filter_i; + int jj = im_j + filter_j; + int ki = half_size + filter_i; + int kj = half_size + filter_j; + x += (typename ComputationType<T>::type) A(im_nb, ii, jj, filter_d) * kernel(ki, kj, filter, filter_d); + COUNTERS_MAC(kernel(ki, kj, filter, filter_d)); + } + } + } + ComputationType<T>::quantize(x, shift); + COUNTERS(x); + SATURATE(x); + m_out(im_nb, im_i / s_h, im_j / s_w, filter) = static_cast<T>(x); + } + } + } +} + +template<typename T> template<int s_h, int s_w> void Conv2D<T>::conv2d_3x3_s_core_dispatch(const Tensor<T> &A, const Tensor<T> &kernel) +{ +#if __AVX2__ +#define CONV_MOD8 simd8_conv2d_3x3_s_d +#define CONV_MOD16 simd16_conv2d_3x3_s_d +#define CONV_MOD32 simd32_conv2d_3x3_s_d +#else +#define CONV_MOD8 conv2d_3x3_s_d_core +#define CONV_MOD16 conv2d_3x3_s_d_core +#define CONV_MOD32 conv2d_3x3_s_d_core +#endif + const int in_D{ A.dims()[3] }; + switch (in_D) + { + case 1: + conv2d_3x3_s_d_core<1, s_h, s_w>(A, kernel); + break; + case 2: + conv2d_3x3_s_d_core<2, s_h, s_w>(A, kernel); + break; + case 4: + conv2d_3x3_s_d_core<4, s_h, s_w>(A, kernel); + break; + case 8: + CONV_MOD8<8, s_h, s_w>(A, kernel); + break; + case 16: + CONV_MOD16<16, s_h, s_w>(A, kernel); + break; + case 24: + CONV_MOD8<24, s_h, s_w>(A, kernel); + break; + case 32: + CONV_MOD32<32, s_h, s_w>(A, kernel); + break; + case 48: + CONV_MOD16<48, s_h, s_w>(A, kernel); + break; + case 64: + CONV_MOD32<64, s_h, s_w>(A, kernel); + break; + case 72: + CONV_MOD8<72, s_h, s_w>(A, kernel); // better do 64 and than 8 + break; + case 96: + CONV_MOD32<96, s_h, s_w>(A, kernel); + break; + case 128: + CONV_MOD32<128, s_h, s_w>(A, kernel); + break; + default: + conv2d_3x3_s_core<s_h, s_w>(A, kernel); + break; + } +#undef CONV_MOD8 +#undef CONV_MOD16 +#undef CONV_MOD32 +} + +template<typename T> template<int in_D, int s_h, int s_w> void Conv2D<T>::conv2d_3x3_s_d_core(const Tensor<T> &A, const Tensor<T> &kernel) +{ + constexpr int im_nb = 0; + constexpr int half_size = 1; + const int shift = kernel.quantizer + m_q; + const int nb_filters{ kernel.dims()[2] }; + const int in_H{ A.dims()[1] }; + const int in_W{ A.dims()[2] }; + constexpr int ihalf_size = 1; + constexpr int half_size_h{ ihalf_size }; + constexpr int half_size_w{ ihalf_size }; + const int top{ m_pads[0] }; + const int left{ m_pads[1] }; + const int start_h{ half_size_h - top }; + const int start_w{ half_size_w - left }; +#if DEBUG_SIMD && __AVX2__ + std::cout << "\n[WARN] generic version conv 3x3 inD=" << in_D << " outD=" << nb_filters << " s=[" << s_w << ' ' << s_h << "] " << in_H << 'x' << in_W << " " + << in_D * kernel.dims()[0] * kernel.dims()[1] * nb_filters * (in_H / s_h) * (in_W / s_w) / 1000 << " kMAC" << std::endl; +#endif + // const int top{ m_pads[0] }; + // const int left{ m_pads[1] }; + for (int im_i = start_h + s_h; im_i < in_H - half_size_h; im_i += s_h) + { + for (int im_j = start_w + s_w; im_j < in_W - half_size_w; im_j += s_w) + { + for (int filter = 0; filter < nb_filters; ++filter) + { + typename ComputationType<T>::type x = 0; + for (int filter_d = 0; filter_d < in_D; ++filter_d) + { + for (int filter_i = -half_size; filter_i <= half_size; ++filter_i) + { // fixed + for (int filter_j = -half_size; filter_j <= half_size; ++filter_j) + { // fixed + int ii = im_i + filter_i; + int jj = im_j + filter_j; + int ki = half_size + filter_i; + int kj = half_size + filter_j; + x += (typename ComputationType<T>::type) A(im_nb, ii, jj, filter_d) * kernel(ki, kj, filter, filter_d); + COUNTERS_MAC(kernel(ki, kj, filter, filter_d)); + } + } + } + ComputationType<T>::quantize(x, shift); + COUNTERS(x); + SATURATE(x); + m_out(im_nb, im_i / s_h, im_j / s_w, filter) = static_cast<T>(x); + } + } + } +} + +// //////////////////////////////////////////////////////////////////////////////////////////////////////// +// 3x3 +// //////////////////////////////////////////////////////////////////////////////////////////////////////// +/// +#if __AVX2__ +template<> template<int in_D, int s_h, int s_w> void Conv2D<float>::simd8_conv2d_3x3_s_d(const Tensor<float> &A, const Tensor<float> &kernel) +{ + static_assert(in_D % 8 == 0, "Should be used with mod8 filters."); + constexpr int im_nb = 0; + constexpr int half_size = 1; + const int nb_filters{ kernel.dims()[2] }; + const int in_H{ A.dims()[1] }; + const int in_W{ A.dims()[2] }; + constexpr int ihalf_size = 1; + constexpr int half_size_h{ ihalf_size }; + constexpr int half_size_w{ ihalf_size }; + const int top{ m_pads[0] }; + const int left{ m_pads[1] }; + const int start_h{ half_size_h - top }; + const int start_w{ half_size_w - left }; +#if DEBUG_SIMD && __AVX512F__ + if (in_D >= 16) + { + std::cout << "\n[WARN] suboptimal SIMD8 version conv 3x3 inD=" << in_D << " outD=" << nb_filters << " s=[" << s_w << ' ' << s_h << "] " << in_H << 'x' + << in_W << " " << in_D * kernel.dims()[0] * kernel.dims()[1] * nb_filters * (in_H / s_h) * (in_W / s_w) / 1000 << " kMAC" << std::endl; + } +#endif + for (int im_i = start_h + s_h; im_i < in_H - half_size_h; im_i += s_h) + { + for (int im_j = start_w + s_w; im_j < in_W - half_size_w; im_j += s_w) + { + for (int filter = 0; filter < nb_filters; ++filter) + { + __m256 s = _mm256_setzero_ps(); + for (int filter_i = -half_size; filter_i <= half_size; ++filter_i) + { // fixed + for (int filter_j = -half_size; filter_j <= half_size; ++filter_j) + { // fixed + const int ii = im_i + filter_i; + const int jj = im_j + filter_j; + const int ki = half_size + filter_i; + const int kj = half_size + filter_j; + + for (int filter_d = 0; filter_d < in_D; filter_d += 8) + { + const float *kptr = kernel.addr(ki, kj, filter, filter_d); + const __m256 k0 = _mm256_load_ps(kptr); + const float *aptr = A.addr(im_nb, ii, jj, filter_d); +#if __FMA__ + s = _mm256_fmadd_ps(k0, _mm256_load_ps(aptr), s); +#else + const __m256 m0 = _mm256_mul_ps(k0, _mm256_load_ps(aptr)); + s = _mm256_add_ps(s, m0); + ; // s + m0; // s = _mm256_hadd_ps(s, m0); +#endif + } + } + } + m_out(im_nb, im_i / s_h, im_j / s_w, filter) = sum8_float(s); + } + } + } +} + +#if __AVX512F__ +template<> template<int in_D, int s_h, int s_w> inline void Conv2D<float>::simd16_conv2d_3x3_s_d(const Tensor<float> &A, const Tensor<float> &kernel) +{ + static_assert(in_D % 16 == 0, "Should be used with mod16 filters."); + constexpr int im_nb = 0; + constexpr int half_size = 1; + const int nb_filters{ kernel.dims()[2] }; + const int in_H{ A.dims()[1] }; + const int in_W{ A.dims()[2] }; + constexpr int ihalf_size = 1; + constexpr int half_size_h{ ihalf_size }; + constexpr int half_size_w{ ihalf_size }; + const int top{ m_pads[0] }; + const int left{ m_pads[1] }; + const int start_h{ half_size_h - top }; + const int start_w{ half_size_w - left }; + for (int im_i = start_h + s_h; im_i < in_H - half_size_h; im_i += s_h) + { + for (int im_j = start_w + s_w; im_j < in_W - half_size_w; im_j += s_w) + { + for (int filter = 0; filter < nb_filters; ++filter) + { + __m512 s = _mm512_setzero_ps(); + for (int filter_i = -half_size; filter_i <= half_size; ++filter_i) + { // fixed + for (int filter_j = -half_size; filter_j <= half_size; ++filter_j) + { // fixed + const int ii = im_i + filter_i; + const int jj = im_j + filter_j; + const int ki = half_size + filter_i; + const int kj = half_size + filter_j; + + for (int filter_d = 0; filter_d < in_D; filter_d += 16) + { + const float *kptr = kernel.addr(ki, kj, filter, filter_d); + const __m512 k0 = _mm512_load_ps(kptr); + const float *aptr = A.addr(im_nb, ii, jj, filter_d); +#if __FMA__ + s = _mm512_fmadd_ps(k0, _mm512_load_ps(aptr), s); +#else + const __m512 m0 = _mm512_mul_ps(k0, _mm512_load_ps(aptr)); + s = _mm512_add_ps(s, m0); +#endif + } + } + } + m_out(im_nb, im_i / s_h, im_j / s_w, filter) = sum16_float(s); + } + } + } +} +#endif + +template<> template<int in_D, int s_h, int s_w> void Conv2D<int32_t>::simd8_conv2d_3x3_s_d(const Tensor<int32_t> &A, const Tensor<int32_t> &kernel) +{ +#if DEBUG_COUNTERS || SATURATE_RESULT + using T = int32_t; +#endif + static_assert(in_D % 8 == 0, "Should be used with mod8 filters."); + constexpr int im_nb = 0; + constexpr int half_size = 1; + const int shift = kernel.quantizer + m_q; + const int nb_filters{ kernel.dims()[2] }; + const int in_H{ A.dims()[1] }; + const int in_W{ A.dims()[2] }; + constexpr int ihalf_size = 1; + constexpr int half_size_h{ ihalf_size }; + constexpr int half_size_w{ ihalf_size }; + const int top{ m_pads[0] }; + const int left{ m_pads[1] }; + const int start_h{ half_size_h - top }; + const int start_w{ half_size_w - left }; +#if DEBUG_SIMD && __AVX512F__ + if (in_D >= 16) + { + std::cout << "\n[WARN] suboptimal SIMD8 version conv 3x3 inD=" << in_D << " outD=" << nb_filters << " s=[" << s_w << ' ' << s_h << "] " << in_H << 'x' + << in_W << " " << in_D * kernel.dims()[0] * kernel.dims()[1] * nb_filters * (in_H / s_h) * (in_W / s_w) / 1000 << " kMAC" << std::endl; + } +#endif + + for (int im_i = start_h + s_h; im_i < in_H - half_size_h; im_i += s_h) + { + for (int im_j = start_w + s_w; im_j < in_W - half_size_w; im_j += s_w) + { + for (int filter = 0; filter < nb_filters; ++filter) + { + __m256i s = _mm256_setzero_si256(); + for (int filter_i = -half_size; filter_i <= half_size; ++filter_i) + { // fixed + for (int filter_j = -half_size; filter_j <= half_size; ++filter_j) + { // fixed + for (int filter_d = 0; filter_d < in_D; filter_d += 8) + { + const int ii = im_i + filter_i; + const int jj = im_j + filter_j; + const int ki = half_size + filter_i; + const int kj = half_size + filter_j; + const __m256i *kptr = (const __m256i *) kernel.addr(ki, kj, filter, filter_d); + const __m256i k0 = _mm256_load_si256(kptr); + const __m256i *aptr = (const __m256i *) A.addr(im_nb, ii, jj, filter_d); + const __m256i v0 = _mm256_load_si256(aptr); + const __m256i m0 = _mm256_mul_epi32(k0, v0); + + const __m256i k1 = _mm256_shuffle_epi32(k0, 0b11110101); + const __m256i v1 = _mm256_shuffle_epi32(v0, 0b11110101); + + s = _mm256_add_epi64(s, m0); + + const __m256i m1 = _mm256_mul_epi32(k1, v1); + s = _mm256_add_epi64(s, m1); + } + } + } +#if ENABLE_HALF_ROUND + typename ComputationType<T>::type z = shift ? ((sum64_int32(s) + (1 << (shift - 1))) >> shift) : sum64_int32(s); +#else + typename ComputationType<T>::type z = (sum64_int32(s) >> shift); +#endif + SATURATE(z); + m_out(im_nb, im_i / s_h, im_j / s_w, filter) = static_cast<int32_t>(z); + } + } + } +} + +// actually SSE42 +template<> template<int in_D, int s_h, int s_w> void Conv2D<int16_t>::simd8_conv2d_3x3_s_d(const Tensor<int16_t> &A, const Tensor<int16_t> &kernel) +{ +#if DEBUG_COUNTERS || SATURATE_RESULT + using T = int16_t; +#endif + static_assert(in_D % 8 == 0, "Should be used with mod8 filters."); + constexpr int im_nb = 0; + constexpr int half_size = 1; + const int shift = kernel.quantizer + m_q; + const int nb_filters{ kernel.dims()[2] }; + const int in_H{ A.dims()[1] }; + const int in_W{ A.dims()[2] }; + constexpr int ihalf_size = 1; + constexpr int half_size_h{ ihalf_size }; + constexpr int half_size_w{ ihalf_size }; + const int top{ m_pads[0] }; + const int left{ m_pads[1] }; + const int start_h{ half_size_h - top }; + const int start_w{ half_size_w - left }; +#if DEBUG_SIMD + if (in_D >= 8) + { + std::cout << "\n[WARN] suboptimal SIMD8 version conv 3x3 inD=" << in_D << " outD=" << nb_filters << " s=[" << s_w << ' ' << s_h << "] " << in_H << 'x' + << in_W << " " << in_D * kernel.dims()[0] * kernel.dims()[1] * nb_filters * (in_H / s_h) * (in_W / s_w) / 1000 << " kMAC" << std::endl; + } +#endif + for (int im_i = start_h + s_h; im_i < in_H - half_size_h; im_i += s_h) + { + for (int im_j = start_w + s_w; im_j < in_W - half_size_w; im_j += s_w) + { + for (int filter = 0; filter < nb_filters; ++filter) + { + __m128i s = _mm_setzero_si128(); + for (int filter_i = -half_size; filter_i <= half_size; ++filter_i) + { // fixed + for (int filter_j = -half_size; filter_j <= half_size; ++filter_j) + { // fixed + for (int filter_d = 0; filter_d < in_D; filter_d += 8) + { + const int ii = im_i + filter_i; + const int jj = im_j + filter_j; + const int ki = half_size + filter_i; + const int kj = half_size + filter_j; + const __m128i *kptr = (const __m128i *) kernel.addr(ki, kj, filter, filter_d); + const __m128i k0 = _mm_load_si128(kptr); // or loadu ? + const __m128i *aptr = (const __m128i *) A.addr(im_nb, ii, jj, filter_d); + const __m128i v0 = _mm_load_si128(aptr); + + const __m128i mad0 = _mm_madd_epi16(k0, v0); // res in si32 + s = _mm_add_epi32(s, mad0); + } + } + } +#if ENABLE_HALF_ROUND + typename ComputationType<T>::type z = shift ? ((sum32_int16(s) + (1 << (shift - 1))) >> shift) : sum32_int16(s); +#else + typename ComputationType<T>::type z = (sum32_int16(s) >> shift); +#endif + SATURATE(z); + m_out(im_nb, im_i / s_h, im_j / s_w, filter) = static_cast<int16_t>(z); + } + } + } +} + +template<> template<int in_D, int s_h, int s_w> void Conv2D<int16_t>::simd16_conv2d_3x3_s_d(const Tensor<int16_t> &A, const Tensor<int16_t> &kernel) +{ +#if DEBUG_COUNTERS || SATURATE_RESULT + using T = int16_t; +#endif + static_assert(in_D % 16 == 0, "Should be used with mod16 filters."); + constexpr int im_nb = 0; + constexpr int half_size = 1; + const int shift = kernel.quantizer + m_q; + const int nb_filters{ kernel.dims()[2] }; + const int in_H{ A.dims()[1] }; + const int in_W{ A.dims()[2] }; + constexpr int ihalf_size = 1; + constexpr int half_size_h{ ihalf_size }; + constexpr int half_size_w{ ihalf_size }; + const int top{ m_pads[0] }; + const int left{ m_pads[1] }; + const int start_h{ half_size_h - top }; + const int start_w{ half_size_w - left }; +#if DEBUG_SIMD && __AVX512BW__ + if (in_D >= 32) + { + std::cout << "\n[WARN] suboptimal SIMD16 version conv 3x3 inD=" << in_D << " outD=" << nb_filters << " s=[" << s_w << ' ' << s_h << "] " << in_H << 'x' + << in_W << " " << in_D * kernel.dims()[0] * kernel.dims()[1] * nb_filters * (in_H / s_h) * (in_W / s_w) / 1000 << " kMAC" << std::endl; + } +#endif + for (int im_i = start_h + s_h; im_i < in_H - half_size_h; im_i += s_h) + { + for (int im_j = start_w + s_w; im_j < in_W - half_size_w; im_j += s_w) + { + for (int filter = 0; filter < nb_filters; ++filter) + { + __m256i s = _mm256_setzero_si256(); + for (int filter_i = -half_size; filter_i <= half_size; ++filter_i) + { // fixed + for (int filter_j = -half_size; filter_j <= half_size; ++filter_j) + { // fixed + for (int filter_d = 0; filter_d < in_D; filter_d += 16) + { + const int ii = im_i + filter_i; + const int jj = im_j + filter_j; + const int ki = half_size + filter_i; + const int kj = half_size + filter_j; + const __m256i *kptr = (const __m256i *) kernel.addr(ki, kj, filter, filter_d); + const __m256i k0 = _mm256_load_si256(kptr); // or loadu ? + const __m256i *aptr = (const __m256i *) A.addr(im_nb, ii, jj, filter_d); + const __m256i v0 = _mm256_load_si256(aptr); + + const __m256i mad0 = _mm256_madd_epi16(k0, v0); // res in si32 + s = _mm256_add_epi32(s, mad0); + } + } + } +#if ENABLE_HALF_ROUND + typename ComputationType<T>::type z = shift ? ((sum32_int16(s) + (1 << (shift - 1))) >> shift) : sum32_int16(s); +#else + typename ComputationType<T>::type z = (sum32_int16(s) >> shift); +#endif + SATURATE(z); + m_out(im_nb, im_i / s_h, im_j / s_w, filter) = static_cast<int16_t>(z); + } + } + } +} + +#if __AVX512BW__ +template<> template<int in_D, int s_h, int s_w> void Conv2D<int16_t>::simd32_conv2d_3x3_s_d(const Tensor<int16_t> &A, const Tensor<int16_t> &kernel) +{ + static_assert(in_D % 32 == 0, "Should be used with mod32 filters."); + using T = int16_t; + constexpr int im_nb = 0; + constexpr int half_size = 1; + const int shift = kernel.quantizer + m_q; + const int nb_filters{ kernel.dims()[2] }; + const int in_H{ A.dims()[1] }; + const int in_W{ A.dims()[2] }; + constexpr int ihalf_size = 1; + constexpr int half_size_h{ ihalf_size }; + constexpr int half_size_w{ ihalf_size }; + const int top{ m_pads[0] }; + const int left{ m_pads[1] }; + const int start_h{ half_size_h - top }; + const int start_w{ half_size_w - left }; + for (int im_i = start_h + s_h; im_i < in_H - half_size_h; im_i += s_h) + { + for (int im_j = start_w + s_w; im_j < in_W - half_size_w; im_j += s_w) + { + for (int filter = 0; filter < nb_filters; ++filter) + { + __m512i s = _mm512_setzero_si512(); + for (int filter_i = -half_size; filter_i <= half_size; ++filter_i) + { // fixed + for (int filter_j = -half_size; filter_j <= half_size; ++filter_j) + { // fixed + for (int filter_d = 0; filter_d < in_D; filter_d += 32) + { + const int ii = im_i + filter_i; + const int jj = im_j + filter_j; + const int ki = half_size + filter_i; + const int kj = half_size + filter_j; + const __m512i *kptr = (const __m512i *) kernel.addr(ki, kj, filter, filter_d); + const __m512i k0 = _mm512_load_si512(kptr); + const __m512i *aptr = (const __m512i *) A.addr(im_nb, ii, jj, filter_d); + const __m512i v0 = _mm512_load_si512(aptr); + + const __m512i mad0 = _mm512_madd_epi16(k0, v0); // res in si32 + s = _mm512_add_epi32(s, mad0); + } + } + } +#if ENABLE_HALF_ROUND + typename ComputationType<T>::type z = shift ? ((_mm512_reduce_add_epi32(s) + (1 << (shift - 1))) >> shift) : _mm512_reduce_add_epi32(s); +#else + typename ComputationType<T>::type z = (_mm512_reduce_add_epi32(s) >> shift); +#endif + COUNTERS(z); + SATURATE(z); + m_out(im_nb, im_i / s_h, im_j / s_w, filter) = z; + } + } + } +} +#endif +#endif // avx2 + +} // namespace layers +} // namespace sadl diff --git a/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/sadl_mod_src_files/options.h b/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/sadl_mod_src_files/options.h new file mode 100644 index 0000000000..bb09418285 --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/sadl_mod_src_files/options.h @@ -0,0 +1,99 @@ +/* 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-2023, 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. + */ +#pragma once + +// build options +// behavior +#ifndef SATURATE_RESULT +#define SATURATE_RESULT 1 // avoid overflow in int NN +#endif + +#ifndef ENABLE_HALF_ROUND +#define ENABLE_HALF_ROUND 1 // half rounding during right shifts for integer operations +#endif +#if SPARSE_SUPPORT +// Sparse matmul threshold +static constexpr float kSparsifyThreshold = 0.8f; +static constexpr float kSparsifySizeThreshold = 1000.0f; +#endif + +// optimization +// nothing/-msse42: no simd +// -mavx2: avx2 +// -mavx2 -mfma: avx2 + fuse multiply/add +// -mavx512bw -mavx512f: avx512 +// #define NDEBUG 1 // remove sanity tests + +// debug +// #define DEBUG_VALUES 1 // show values +// #define DEBUG_MODEL 1 // show pb with model +// #define DEBUG_COUNTERS 1 // print overflow, MAC etc. +// #define DEBUG_PRINT 1 // print model info +// #define DEBUG_SIMD 1 // tell about non simd version +// #define DEBUG_KEEP_OUTPUT 1 // keep a copy of the output tensor +#if SATURATE_RESULT +#define SATURATE(X) \ + if (!std::is_same<T, float>::value) \ + X = (X > ComputationType<T>::max) ? ComputationType<T>::max : (X < -ComputationType<T>::max ? -ComputationType<T>::max : X) +#else +#define SATURATE(X) +#endif + +#if DEBUG_COUNTERS +template<typename T> T my_abs(T x) { return x < T{} ? -x : x; } +#define COUNTERS(X) \ + ++this->cpt_op; \ + if (my_abs(X) > ComputationType<T>::max) \ + ++this->cpt_overflow +#define COUNTERS_MAC(X) \ + ++this->cpt_mac; \ + if (X != 0) \ + ++this->cpt_mac_nz +#else +#define COUNTERS(X) (void) X +#define COUNTERS_MAC(X) (void) X +#endif + +#ifndef DUMP_MODEL_EXT +#define DUMP_MODEL_EXT +#endif +namespace sadl +{ +enum class Version +{ + unknown = -1, + sadl01 = 1, + sadl02 = 2, + sadl03 = 3, +}; +} diff --git a/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/sadl_mod_src_files/tensor.h b/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/sadl_mod_src_files/tensor.h new file mode 100644 index 0000000000..7a3ceb698b --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/sadl_conversion_utilities/sadl_mod_src_files/tensor.h @@ -0,0 +1,649 @@ +/* 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-2023, 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. + */ +#pragma once +#include <algorithm> +#include <cstdlib> +#if _WIN32 || __USE_ISOC11 +#include <malloc.h> +#else +#include <malloc/malloc.h> +#endif +#include <numeric> +#include <vector> +#include <limits> +#include "options.h" + +#include "dimensions.h" + +namespace sadl +{ +// tensor between layers: depth height width (or width height?) +template<typename T, std::size_t Alignment> struct aligned_allocator +{ + using pointer = T *; + using const_pointer = const T *; + using reference = T &; + using const_reference = const T &; + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + pointer address(reference r) const { return &r; } + const_pointer address(const_reference s) const { return &s; } + size_type max_size() const { return (static_cast<std::size_t>(0) - static_cast<std::size_t>(1)) / sizeof(T); } + template<typename U> struct rebind + { + typedef aligned_allocator<U, Alignment> other; + }; + + bool operator!=(const aligned_allocator &other) const { return !(*this == other); } + void construct(pointer p, const_reference t) const + { + void *const pv = static_cast<void *>(p); + new (pv) T(t); + } + void destroy(T *const p) const { p->~T(); } + bool operator==(const aligned_allocator & /*other*/) const { return true; } + + aligned_allocator() = default; + aligned_allocator(const aligned_allocator &) = default; + ~aligned_allocator() = default; + aligned_allocator &operator=(const aligned_allocator &) = delete; + + template<typename U> aligned_allocator(const aligned_allocator<U, Alignment> &) {} + + pointer allocate(const std::size_t n) const + { + if (n == 0) + return nullptr; + size_t s = ((n * sizeof(T) + Alignment - 1) / Alignment) * Alignment; + +#if _WIN32 +#if __MINGW32__ + void *const pv = __mingw_aligned_malloc(s, Alignment); +#else + void *const pv = _aligned_malloc(s, Alignment); +#endif +#else +#if __USE_ISOC11 + void *const pv = aligned_alloc(Alignment, s); +#else + void *pv = nullptr; + if (posix_memalign(&pv, Alignment, s)) + { + throw std::bad_alloc(); + } +#endif +#endif + + if (!pv) + throw std::bad_alloc(); + return static_cast<T *>(pv); + } + +#ifdef _WIN32 + void deallocate(T *const p, const std::size_t n) const { _aligned_free(p); } +#else + void deallocate(T *const p, const std::size_t /*n*/) const { free(p); } +#endif + + template<typename U> pointer allocate(const std::size_t n, const U * /* const hint */) const { return allocate(n); } +}; + +template<typename T> struct ComputationType +{ +}; + +// predecl for friendness +template<typename T> class Tensor; +template<typename T> void swap(Tensor<T> &t0, Tensor<T> &t1); +template<typename T> void swapData(Tensor<T> &t0, Tensor<T> &t1); +#if SPARSE_SUPPORT +template<typename T> void sparsify(Tensor<T> &weights); +#endif + +template<typename T> class Tensor +{ +public: + using value_type = T; + using Data = std::vector<value_type, aligned_allocator<value_type, 64>>; + using iterator = typename Data::iterator; + using const_iterator = typename Data::const_iterator; +#if SPARSE_SUPPORT + using index = uint16_t; +#endif + + static bool skip_border; // to replace by inline global C++17 + + Tensor() = default; + explicit Tensor(Dimensions d); + + void resize(Dimensions d); + + // linear access + value_type &operator[](int i); + value_type operator[](int i) const; + + // tensor access + value_type &operator()(int i); + value_type operator()(int i) const; + + value_type &operator()(int i, int j); + value_type operator()(int i, int j) const; + + value_type &operator()(int i, int j, int k); + value_type operator()(int i, int j, int k) const; + + value_type & operator()(int i, int j, int k, int l); + value_type operator()(int i, int j, int k, int l) const; + const value_type *addr(int i, int j, int k, int l) const; + + bool in(int i) const; + bool in(int i, int j) const; + bool in(int i, int j, int k) const; + bool in(int i, int j, int k, int l) const; + void fill(value_type value); + + const Dimensions &dims() const; + int64_t size() const; + + const value_type *data() const { return m_data.data(); } + value_type * data() { return m_data.data(); } + + iterator begin() + { +#if SPARSE_SUPPORT + assert(!isSparse()); +#endif + return m_data.begin(); + } + const_iterator begin() const { return m_data.begin(); } + iterator end() + { +#if SPARSE_SUPPORT + assert(!isSparse()); +#endif + return m_data.end(); + } + const_iterator end() const { return m_data.end(); } + + int quantizer = 0; // for int + int border_skip = 0; + static constexpr int64_t kMaxSize = 32LL * 1024 * 1024 * 1024; + + Data &getData() { return m_data; } +#if SPARSE_SUPPORT + const std::vector<value_type> &getDataSparse() const { return m_data_sparse; } + const std::vector<index> & getIndices() const { return m_indices; } + const std::vector<uint16_t> & getNbNonzerosCol() const { return m_nb_nonzeros_col; } + bool isSparse() const { return !m_data_sparse.empty(); } +#endif +private: + Dimensions m_dims; + Data m_data; +#if SPARSE_SUPPORT + std::vector<value_type> m_data_sparse; + std::vector<index> m_indices; + std::vector<uint16_t> m_nb_nonzeros_col; + friend void sparsify<>(Tensor<T> &weights); +#endif + + friend void swap<>(Tensor<T> &t0, Tensor<T> &t1); + friend void swapData<>(Tensor<T> &t0, Tensor<T> &t1); +#if DEBUG_PRINT +public: + static bool m_verbose; +#endif +}; + +#if SPARSE_SUPPORT +// Sparse Matmul +template<typename T> bool isFullMatrixSparse(const Tensor<T> &weights, float sparsity_threshold, float sparsity_size_threshold) +{ + int N = weights.dims()[0], M = weights.dims()[1]; + + if (N * M < sparsity_size_threshold) + return false; + + float cnt_zeros = 0; + + for (int j = 0; j < M; ++j) + { + for (int i = 0; i < N; ++i) + { + if (weights[N * j + i] == 0) + cnt_zeros++; + } + } +#if DEBUG_PRINT + std::cout << weights << ' ' << cnt_zeros << ' ' << M << ' ' << N << std::endl; +#endif + auto sparsity_level = cnt_zeros / (float) (N * M); + return (sparsity_level >= sparsity_threshold); +} + +template<typename T> void sparsify(Tensor<T> &weights) +{ + weights.m_data_sparse.clear(); + weights.m_nb_nonzeros_col.clear(); + + uint16_t N = weights.dims()[0], M = weights.dims()[1]; + assert(N < (1 << 16) && M < (1 << 16)); + + for (uint16_t j = 0; j < M; ++j) + { + auto cnt_non_zeros = 0; + + for (uint16_t i = 0; i < N; ++i) + { + auto val = weights.m_data[N * j + i]; + if (val != 0) + { + weights.m_data_sparse.push_back(val); + weights.m_indices.push_back(i); + cnt_non_zeros++; + } + } + +#if (__SSE4_2__ || __AVX2__) +#if __AVX2__ + int pad = 16; +#else + int pad = 8; +#endif + if (std::is_same<T, int16_t>::value) + { + int tmp = cnt_non_zeros; + while (tmp % pad != 0) + { + weights.m_data_sparse.push_back(0); + weights.m_indices.push_back(0); + tmp++; + } + } +#endif + + weights.m_nb_nonzeros_col.push_back(cnt_non_zeros); + } +} +#endif + +// spe +template<> struct ComputationType<float> +{ + using type = float; + static constexpr type max = std::numeric_limits<float>::max(); + static void quantize(type, int) {} // nothing to do + static void shift_left(type, int) {} // nothing to do +}; + +template<> struct ComputationType<int32_t> +{ + using type = int64_t; + static constexpr type max = std::numeric_limits<int32_t>::max(); +#if ENABLE_HALF_ROUND + static void quantize(type &z, int q) { z = q ? ((z+ (1 << (q-1))) >> q) : z; } + static void quantize(int32_t &z, int q) { z = q ? (int32_t)(((int64_t)z + (1 << (q-1))) >> q) : z; } +#else + static void quantize(type &z, int q) { z >>= q; } + static void quantize(int32_t &z, int q) { z >>= q; } +#endif + static void shift_left(type &z, int q) { z <<= q; } + static void shift_left(int32_t &z, int q) { z <<= q; } +}; + +template<> struct ComputationType<int16_t> +{ + using type = int32_t; + static constexpr type max = std::numeric_limits<int16_t>::max(); +#if ENABLE_HALF_ROUND + static void quantize(type &z, int q) { z = q ? ((z+(1<<(q-1)))>>q) : z; } + static void quantize(int16_t &z, int q) { z = q ? (int16_t)(((int32_t)z + (1 << (q-1))) >> q) : z; } +#else + static void quantize(type &z, int q) { z >>= q; } + static void quantize(int16_t &z, int q) { z >>= q; } +#endif + static void shift_left(type &z, int q) { z <<= q; } + static void shift_left(int16_t &z, int q) { z <<= q; } +}; + +// impl +template<typename T> bool Tensor<T>::skip_border = false; + +template<typename T> void swap(Tensor<T> &t0, Tensor<T> &t1) +{ + std::swap(t0.m_dims, t1.m_dims); + std::swap(t0.m_data, t1.m_data); + std::swap(t0.quantizer, t1.quantizer); + std::swap(t0.border_skip, t1.border_skip); +#if SPARSE_SUPPORT + std::swap(t0.m_data_sparse, t1.m_data_sparse); +#endif +} + +template<typename T> void swapData(Tensor<T> &t0, Tensor<T> &t1) +{ + assert(t0.size() == t1.size()); + std::swap(t0.m_data, t1.m_data); + std::swap(t0.quantizer, t1.quantizer); + std::swap(t0.border_skip, t1.border_skip); +#if SPARSE_SUPPORT + std::swap(t0.m_data_sparse, t1.m_data_sparse); +#endif +} + +template<typename T> Tensor<T>::Tensor(Dimensions d) +{ +#if SPARSE_SUPPORT + assert(!isSparse()); +#endif + resize(d); +} + +template<typename T> const Dimensions &Tensor<T>::dims() const { return m_dims; } + +template<typename T> int64_t Tensor<T>::size() const { return m_data.size(); } + +template<typename T> void Tensor<T>::resize(Dimensions d) +{ +#if SPARSE_SUPPORT + m_data_sparse.clear(); +#endif + m_dims = d; + int64_t m = m_dims.nbElements(); + assert(m < kMaxSize); + m_data.resize(m); +} + +// TODO: variadic template to define all accesors +template<typename T> T &Tensor<T>::operator[](int i) +{ +#if SPARSE_SUPPORT + assert(!isSparse()); +#endif + return m_data[i]; +} + +template<typename T> T &Tensor<T>::operator()(int i) +{ +#if SPARSE_SUPPORT + assert(!isSparse()); +#endif + assert(m_dims.size() == 1); + assert(i < m_dims[0] && i >= 0); + + return m_data[i]; +} + +template<typename T> bool Tensor<T>::in(int i) const { return m_dims.size() == 1 && i < m_dims[0] && i >= 0; } + +template<typename T> T Tensor<T>::operator[](int i) const { return m_data[i]; } + +template<typename T> T Tensor<T>::operator()(int i) const +{ + assert(m_dims.size() == 1); + assert(i < m_dims[0] && i >= 0); + + return m_data[i]; +} + +template<typename T> T &Tensor<T>::operator()(int i, int j) +{ +#if SPARSE_SUPPORT + assert(!isSparse()); +#endif + assert(m_dims.size() == 2); + assert(i < m_dims[0] && i >= 0); + assert(j < m_dims[1] && j >= 0); + + return m_data[(int64_t) m_dims[1] * i + j]; +} + +template<typename T> T Tensor<T>::operator()(int i, int j) const +{ + assert(m_dims.size() == 2); + assert(i < m_dims[0] && i >= 0); + assert(j < m_dims[1] && j >= 0); + + return m_data[(int64_t) m_dims[1] * i + j]; +} + +template<typename T> bool Tensor<T>::in(int i, int j) const { return m_dims.size() == 2 && i < m_dims[0] && i >= 0 && j < m_dims[1] && j >= 0; } + +template<typename T> T &Tensor<T>::operator()(int i, int j, int k) +{ +#if SPARSE_SUPPORT + assert(!isSparse()); +#endif + assert(m_dims.size() == 3); + assert(i < m_dims[0] && i >= 0); + assert(j < m_dims[1] && j >= 0); + assert(k < m_dims[2] && k >= 0); + + return m_data[(int64_t) m_dims[2] * (m_dims[1] * i + j) + k]; +} + +template<typename T> T Tensor<T>::operator()(int i, int j, int k) const +{ + assert(m_dims.size() == 3); + assert(i < m_dims[0] && i >= 0); + assert(j < m_dims[1] && j >= 0); + assert(k < m_dims[2] && k >= 0); + + return m_data[(int64_t) m_dims[2] * (m_dims[1] * i + j) + k]; +} + +template<typename T> bool Tensor<T>::in(int i, int j, int k) const +{ + return m_dims.size() == 3 && i < m_dims[0] && i >= 0 && j < m_dims[1] && j >= 0 && k < m_dims[2] && k >= 0; +} + +template<typename T> T &Tensor<T>::operator()(int i, int j, int k, int l) +{ +#if SPARSE_SUPPORT + assert(!isSparse()); +#endif + assert(m_dims.size() == 4); + assert(i < m_dims[0] && i >= 0); + assert(j < m_dims[1] && j >= 0); + assert(k < m_dims[2] && k >= 0); + assert(l < m_dims[3] && l >= 0); + + return m_data[(int64_t) m_dims[3] * (m_dims[2] * (m_dims[1] * i + j) + k) + l]; +} + +template<typename T> bool Tensor<T>::in(int i, int j, int k, int l) const +{ + return m_dims.size() == 4 && i < m_dims[0] && i >= 0 && j < m_dims[1] && j >= 0 && k < m_dims[2] && k >= 0 && l < m_dims[3] && l >= 0; +} + +template<typename T> const T *Tensor<T>::addr(int i, int j, int k, int l) const +{ + assert(m_dims.size() == 4); + assert(i < m_dims[0] && i >= 0); + assert(j < m_dims[1] && j >= 0); + assert(k < m_dims[2] && k >= 0); + assert(l < m_dims[3] && l >= 0); + return &m_data[(int64_t) m_dims[3] * (m_dims[2] * (m_dims[1] * i + j) + k) + l]; +} + +template<typename T> T Tensor<T>::operator()(int i, int j, int k, int l) const +{ + assert(m_dims.size() == 4); + assert(i < m_dims[0] && i >= 0); + assert(j < m_dims[1] && j >= 0); + assert(k < m_dims[2] && k >= 0); + assert(l < m_dims[3] && l >= 0); + return m_data[(int64_t) m_dims[3] * (m_dims[2] * (m_dims[1] * i + j) + k) + l]; +} + +template<typename T> void Tensor<T>::fill(value_type value) +{ +#if SPARSE_SUPPORT + m_data_sparse.clear(); +#endif + std::fill(m_data.begin(), m_data.end(), value); +} + +} // namespace sadl + +#include <iostream> +#include <sstream> + +#if DEBUG_PRINT +template<typename T> bool sadl::Tensor<T>::m_verbose = true; + +#define SADL_DBG(X) \ + if (sadl::Tensor<T>::m_verbose) \ + { \ + X; \ + } +#else +#define SADL_DBG(X) +#endif + +namespace sadl +{ +template<typename T> std::ostream &operator<<(std::ostream &out, const Tensor<T> &t) +{ + // adhoc + if (t.dims().size() == 4u) + { + out << "["; + if (t.dims()[0] > 1) + out << '\n'; + for (int k = 0; k < t.dims()[0]; ++k) + { + out << " ["; + if (t.dims()[1] > 1) + out << '\n'; + for (int d = 0; d < t.dims()[1]; ++d) + { + out << " ["; + if (t.dims()[2] > 1) + out << '\n'; + for (int i = 0; i < t.dims()[2]; ++i) + { + out << " ["; + for (int j = 0; j < t.dims()[3]; ++j) + out << t(k, d, i, j) << ' '; + out << " ]"; + if (t.dims()[2] > 1) + out << '\n'; + } + out << " ]"; + if (t.dims()[1] > 1) + out << '\n'; + } + out << " ]"; + if (t.dims()[0] > 1) + out << '\n'; + } + out << "]"; + } + else if (t.dims().size() == 3u) + { + out << "["; + for (int d = 0; d < t.dims()[0]; ++d) + { + out << " ["; + if (t.dims()[0] > 1) + out << '\n'; + for (int i = 0; i < t.dims()[1]; ++i) + { + out << " ["; + if (t.dims()[1] > 1) + out << '\n'; + for (int j = 0; j < t.dims()[2]; ++j) + out << t(d, i, j) << '\t'; + out << " ]"; + if (t.dims()[1] > 1) + out << '\n'; + } + out << " ]"; + if (t.dims()[0] > 1) + out << '\n'; + } + out << "]"; + } + else if (t.dims().size() == 2u) + { + out << "["; + for (int i = 0; i < t.dims()[0]; ++i) + { + out << " ["; + if (t.dims()[0] > 1) + out << '\n'; + for (int j = 0; j < t.dims()[1]; ++j) + out << t(i, j) << ' '; + out << " ]"; + if (t.dims()[0] > 1) + out << '\n'; + } + out << "]\n"; + } + else if (t.dims().size() == 1u) + { + out << "["; + for (int j = 0; j < t.dims()[0]; ++j) + out << t(j) << ' '; + out << "]"; + } + else + { + out << "TODO\n"; + } +#if SPARSE_SUPPORT + if (t.isSparse()) + { + uint32_t offset_data = 0; + int i = 0; + out << "data_sparse = [\n"; + for (const auto &nb_nonzero: t.getNbNonzerosCol()) + { + for (auto k = 0; k < nb_nonzero; ++k, ++offset_data) + { + uint16_t j = t.getIndices()[offset_data]; + out << i << ',' << j << ": " << t.getDataSparse()[offset_data] << '\n'; + } + i++; + } + out << "]\n"; + } +#endif + out << " shape=" << t.dims() << " type="; + + return out; +} + +} // namespace sadl diff --git a/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/tf2_onnx.py b/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/tf2_onnx.py new file mode 100644 index 0000000000..b3de9cf769 --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/4_conversions/tf2_onnx.py @@ -0,0 +1,65 @@ +import numpy as np +import onnx +import tensorflow as tf +import tf2onnx +import sys +import os +from tensorflow.keras.models import Model +from tensorflow.keras.layers import Input +from tensorflow import keras + +input_shape = (1, 72, 72, 10) +s = (72, 72, 10) + +os.environ["CUDA_VISIBLE_DEVICES"] = "" + +tf.config.run_functions_eagerly(True) +model = keras.models.load_model(sys.argv[1], compile=False) +count = 0 +input_layer = Input(shape=model.layers[0].input_shape[0][1:], name=model.layers[0].name) +x = input_layer +output = None + +is_split_model = False +x_Y = None +x_C = None +for idx, layer in enumerate(model.layers[1:]): + layer_type='common' + if ('_y_' in layer.name) or layer.name.endswith('_Y') or layer.name.endswith('_y'): + layer_type = 'luma' + if x_Y is None: + x_Y = x[:,:,:,:16] + elif ('_c_' in layer.name) or layer.name.endswith('_C') or layer.name.endswith('_c'): + layer_type = 'chroma' + if x_C is None: + x_C = x[:,:,:,16:] + + elif 'tf_op_layer_strided_slice' == layer.name or 'tf_op_layer_strided_slice_1' == layer.name: + continue + + if layer_type == 'luma': + x_Y = layer(x_Y) + is_split_model = True + elif layer_type == 'chroma': + x_C = layer(x_C) + is_split_model = True + else: + x = layer(x) + + if 'output_layer' in layer.name: + count = count + 1 + if is_split_model==False: + output = x + break + if 2 == count: + output = tf.keras.layers.Concatenate()([x_Y, x_C]) + break + + +tf_model = Model(inputs=input_layer, outputs=output) + +model_onnx, _ = tf2onnx.convert.from_keras(tf_model, [tf.TensorSpec(input_shape, name="input_1")], opset=13) + +x = np.linspace(0, 1, np.prod(s)).reshape(input_shape).astype(np.float32) +results_tf = tf_model(x).numpy() +onnx.save(model_onnx, sys.argv[2]) \ No newline at end of file diff --git a/training/training_scripts/Nn_Filtering_Set_LC/README.pdf b/training/training_scripts/Nn_Filtering_Set_LC/README.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ea5faac84bde5be69e96a81abff14eace6822503 GIT binary patch literal 336384 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俊P0LzUeNLIF^nC$yrz9c_Q*G#XD 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�mMcu?Dh#R3G^{V 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Ν({ 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 literal 0 HcmV?d00001 diff --git a/training/training_scripts/Nn_Filtering_Set_LC/requirements.txt b/training/training_scripts/Nn_Filtering_Set_LC/requirements.txt new file mode 100644 index 0000000000..5a76dbd21b --- /dev/null +++ b/training/training_scripts/Nn_Filtering_Set_LC/requirements.txt @@ -0,0 +1,5 @@ +numpy>=1.18.2 +tensorflow==2.8.0 +tensorly==0.7.0 +pandas>=1.0.3 +onnx \ No newline at end of file -- GitLab