diff --git a/cfg/encoder_lowdelay_P_vtm.cfg b/cfg/encoder_lowdelay_P_vtm.cfg index d2001e4a5691fac2b250977f20b840615d27b24d..9ec4a5d13cf00e9d8e1da1bfc47971a6f37dc64c 100644 --- a/cfg/encoder_lowdelay_P_vtm.cfg +++ b/cfg/encoder_lowdelay_P_vtm.cfg @@ -56,6 +56,11 @@ TransformSkipFast : 1 # Fast Transform skipping (0: OFF, 1 TransformSkipLog2MaxSize : 5 SAOLcuBoundary : 0 # SAOLcuBoundary using non-deblocked pixels (0: OFF, 1: ON) +#=========== TemporalFilter ================= +TemporalFilter : 0 # Enable/disable GOP Based Temporal Filter +TemporalFilterFutureReference : 0 # Enable/disable reading future frames +TemporalFilterStrengthFrame4 : 0.4 # Enable filter at every 4th frame with strength + #============ Slices ================ SliceMode : 0 # 0: Disable all slice options. # 1: Enforce maximum number of LCU in an slice, diff --git a/cfg/encoder_lowdelay_vtm.cfg b/cfg/encoder_lowdelay_vtm.cfg index 0125529b61d6ecedf749e5189460431c2fa1f420..c8899e2e74339001db4bce7515fd2c23997dd3a0 100644 --- a/cfg/encoder_lowdelay_vtm.cfg +++ b/cfg/encoder_lowdelay_vtm.cfg @@ -56,6 +56,11 @@ TransformSkipFast : 1 # Fast Transform skipping (0: OFF, 1 TransformSkipLog2MaxSize : 5 SAOLcuBoundary : 0 # SAOLcuBoundary using non-deblocked pixels (0: OFF, 1: ON) +#=========== TemporalFilter ================= +TemporalFilter : 0 # Enable/disable GOP Based Temporal Filter +TemporalFilterFutureReference : 0 # Enable/disable reading future frames +TemporalFilterStrengthFrame4 : 0.4 # Enable filter at every 4th frame with strength + #============ Slices ================ SliceMode : 0 # 0: Disable all slice options. # 1: Enforce maximum number of LCU in an slice, diff --git a/cfg/encoder_randomaccess_vtm.cfg b/cfg/encoder_randomaccess_vtm.cfg index 9548bc8e66469f0d5df536deb5ef98aca23a73b1..db7fb3002833aad1c47835327a046115a23ddd31 100644 --- a/cfg/encoder_randomaccess_vtm.cfg +++ b/cfg/encoder_randomaccess_vtm.cfg @@ -70,6 +70,12 @@ TransformSkipFast : 1 # Fast Transform skipping (0: OFF, 1 TransformSkipLog2MaxSize : 5 SAOLcuBoundary : 0 # SAOLcuBoundary using non-deblocked pixels (0: OFF, 1: ON) +#=========== TemporalFilter ================= +TemporalFilter : 0 # Enable/disable GOP Based Temporal Filter +TemporalFilterFutureReference : 1 # Enable/disable reading future frames +TemporalFilterStrengthFrame8 : 0.95 # Enable filter at every 8th frame with given strength +TemporalFilterStrengthFrame16 : 1.5 # Enable filter at every 16th frame with given strength, longer intervals has higher priority + #============ Slices ================ SliceMode : 0 # 0: Disable all slice options. # 1: Enforce maximum number of LCU in an slice, diff --git a/doc/software-manual.tex b/doc/software-manual.tex old mode 100755 new mode 100644 index 820df1b14e1df0497b45e0d89078b692af8037b6..db557a0b809463dd14c99ac25c6c777c8e84ae11 --- a/doc/software-manual.tex +++ b/doc/software-manual.tex @@ -890,6 +890,32 @@ Picture output options: output upscaled (2), decoded but in full resolution buff \end{OptionTableNoShorthand} +%% +%% GOP based temporal filter parameters +%% + +\begin{OptionTableNoShorthand}{GOP based temporal filter paramters}{tab:gop-based-temporal-filter} + +\Option{TemporalFilter} & +%\ShortOption{\None} & +\Default{false} & +Enables or disables GOP based temporal filter. +\\ +\Option{TemporalFilterFutureReference} & +%\ShortOption{\None} & +\Default{true} & +Enables or disable referencing future frames in the GOP based temporal filter. Can be used to disable future referencing for +low delay configurations. +\\ +\Option{TemporalFilterStrengthFrame*} & +%\ShortOption{\None} & +\Default{} & +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. Longer intervals overrides shorter when there are +multiple matches. +\\ +\end{OptionTableNoShorthand} + %% %% profile, level and conformance options %% diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp index 8646b6e48aebee4a43add86685dce75ede798091..7c8b5e3ec1a9ccd11e90e310060707973ea4b328 100644 --- a/source/App/EncoderApp/EncApp.cpp +++ b/source/App/EncoderApp/EncApp.cpp @@ -48,6 +48,10 @@ #include "AppEncHelper360/TExt360AppEncTop.h" #endif +#if JVET_O0549_ENCODER_ONLY_FILTER +#include "EncoderLib/EncTemporalFilter.h" +#endif + using namespace std; //! \ingroup EncoderApp @@ -692,6 +696,9 @@ void EncApp::xInitLibCfg() m_cEncLib.setCropOffsetBottom (m_cropOffsetBottom); m_cEncLib.setCalculateHdrMetrics (m_calculateHdrMetrics); #endif +#if JVET_O0549_ENCODER_ONLY_FILTER + m_cEncLib.setGopBasedTemporalFilterEnabled(m_gopBasedTemporalFilterEnabled); +#endif } void EncApp::xCreateLib( std::list<PelUnitBuf*>& recBufList @@ -792,6 +799,17 @@ void EncApp::encode() TExt360AppEncTop ext360(*this, m_cEncLib.getGOPEncoder()->getExt360Data(), *(m_cEncLib.getGOPEncoder()), orgPic); #endif +#if JVET_O0549_ENCODER_ONLY_FILTER + EncTemporalFilter temporalFilter; + if (m_gopBasedTemporalFilterEnabled) + { + temporalFilter.init(m_FrameSkip, m_inputBitDepth, m_MSBExtendedBitDepth, m_internalBitDepth, m_iSourceWidth, m_iSourceHeight, + m_aiPad, m_bClipInputVideoToRec709Range, m_inputFileName, m_chromaFormatIDC, + m_inputColourSpaceConvert, m_iQP, m_gopBasedTemporalFilterStrengths, + m_gopBasedTemporalFilterFutureReference); + } +#endif + while ( !bEos ) { // read input YUV file @@ -808,6 +826,13 @@ void EncApp::encode() m_cVideoIOYuvInputFile.read( orgPic, trueOrgPic, ipCSC, m_aiPad, m_InputChromaFormatIDC, m_bClipInputVideoToRec709Range ); #endif +#if JVET_O0549_ENCODER_ONLY_FILTER + if (m_gopBasedTemporalFilterEnabled) + { + temporalFilter.filter(&orgPic, m_iFrameRcvd); + } +#endif + // increase number of received frames m_iFrameRcvd++; diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp index 56a3813a0871afd3bd6f68222f68830c60b633f8..ae932b1b23687763ac4241fa36011265d20497b5 100644 --- a/source/App/EncoderApp/EncAppCfg.cpp +++ b/source/App/EncoderApp/EncAppCfg.cpp @@ -605,6 +605,27 @@ static inline istream& operator >> (std::istream &in, EncAppCfg::OptionalValue<T } #endif +#if JVET_O0549_ENCODER_ONLY_FILTER +template <class T1, class T2> +static inline istream& operator >> (std::istream& in, std::map<T1, T2>& map) +{ + T1 key; + T2 value; + try + { + in >> key; + in >> value; + } + catch (...) + { + in.setstate(ios::failbit); + } + + map[key] = value; + return in; +} +#endif + static void automaticallySelectRExtProfile(const bool bUsingGeneralRExtTools, const bool bUsingChromaQPAdjustment, @@ -1422,6 +1443,14 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) #endif ; +#if JVET_O0549_ENCODER_ONLY_FILTER + opts.addOptions() + ("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"); +#endif + #if EXTENSION_360_VIDEO TExt360AppEncCfg::TExt360AppEncCfgContext ext360CfgContext; m_ext360.addOptions(opts, ext360CfgContext); @@ -3474,6 +3503,12 @@ bool EncAppCfg::xCheckParameter() xConfirmPara( m_decodeBitstreams[0] == m_bitstreamFileName, "Debug bitstream and the output bitstream cannot be equal.\n" ); xConfirmPara( m_decodeBitstreams[1] == m_bitstreamFileName, "Decode2 bitstream and the output bitstream cannot be equal.\n" ); xConfirmPara(unsigned(m_LMChroma) > 1, "LMMode exceeds range (0 to 1)"); +#if JVET_O0549_ENCODER_ONLY_FILTER + if (m_gopBasedTemporalFilterEnabled) + { + xConfirmPara(m_temporalSubsampleRatio != 1, "GOP Based Temporal Filter only support Temporal sub-sample ratio 1"); + } +#endif #if EXTENSION_360_VIDEO check_failed |= m_ext360.verifyParameters(); #endif @@ -3837,7 +3872,9 @@ void EncAppCfg::xPrintParameter() #if EXTENSION_360_VIDEO m_ext360.outputConfigurationSummary(); #endif - +#if JVET_O0549_ENCODER_ONLY_FILTER + msg(VERBOSE, "TemporalFilter:%d ", m_gopBasedTemporalFilterEnabled); +#endif msg( VERBOSE, "\n\n"); msg( NOTICE, "\n"); diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h index 4c67d85fade0bb654ffee889981ea7a14a0aa670..a1aa89cba1fbadf294983666831a90b978bd2a44 100644 --- a/source/App/EncoderApp/EncAppCfg.h +++ b/source/App/EncoderApp/EncAppCfg.h @@ -39,6 +39,9 @@ #define __ENCAPPCFG__ #include "CommonLib/CommonDef.h" +#if JVET_O0549_ENCODER_ONLY_FILTER +#include "Utilities/program_options_lite.h" +#endif #include "EncoderLib/EncCfg.h" #if EXTENSION_360_VIDEO @@ -48,6 +51,9 @@ #if JVET_O0756_CALCULATE_HDRMETRICS #include "HDRLib/inc/DistortionMetric.H" #endif +#if JVET_O0549_ENCODER_ONLY_FILTER +namespace po = df::program_options_lite; +#endif #include <sstream> #include <vector> @@ -656,6 +662,12 @@ protected: int m_upscaledOutput; ////< Output upscaled (2), decoded cropped but in full resolution buffer (1) or decoded cropped (0, default) picture for RPR. #endif +#if JVET_O0549_ENCODER_ONLY_FILTER + bool m_gopBasedTemporalFilterEnabled; ///< GOP-based Temporal Filter enable/disable + bool m_gopBasedTemporalFilterFutureReference; ///< Enable/disable future frame references in the GOP-based Temporal Filter + std::map<int, double> m_gopBasedTemporalFilterStrengths; ///< Filter strength per frame for the GOP-based Temporal Filter +#endif + #if EXTENSION_360_VIDEO TExt360AppEncCfg m_ext360; friend class TExt360AppEncCfg; diff --git a/source/Lib/CommonLib/Buffer.h b/source/Lib/CommonLib/Buffer.h index e76cdd7a55ad73002fdf1f9e197290312a13548e..e2c16f95512c29203dea0d5269bc3a6fe085c614 100644 --- a/source/Lib/CommonLib/Buffer.h +++ b/source/Lib/CommonLib/Buffer.h @@ -126,6 +126,9 @@ struct AreaBuf : public Size #endif void extendSingleBorderPel(); void extendBorderPel ( unsigned margin ); +#if JVET_O0549_ENCODER_ONLY_FILTER + void extendBorderPel(unsigned marginX, unsigned marginY); +#endif void addWeightedAvg ( const AreaBuf<const T> &other1, const AreaBuf<const T> &other2, const ClpRng& clpRng, const int8_t gbiIdx); void removeWeightHighFreq ( const AreaBuf<T>& other, const bool bClip, const ClpRng& clpRng, const int8_t iGbiWeight); void addAvg ( const AreaBuf<const T> &other1, const AreaBuf<const T> &other2, const ClpRng& clpRng ); @@ -579,6 +582,46 @@ void AreaBuf<T>::updateHistogram( std::vector<int32_t>& hist ) const } } +#if JVET_O0549_ENCODER_ONLY_FILTER +template<typename T> +void AreaBuf<T>::extendBorderPel(unsigned marginX, unsigned marginY) +{ + T* p = buf; + int h = height; + int w = width; + int s = stride; + + CHECK((w + 2 * marginX) > s, "Size of buffer too small to extend"); + // do left and right margins + for (int y = 0; y < h; y++) + { + for (int x = 0; x < marginX; x++) + { + *(p - marginX + x) = p[0]; + p[w + x] = p[w - 1]; + } + p += s; + } + + // p is now the (0,height) (bottom left of image within bigger picture + p -= (s + marginX); + // p is now the (-margin, height-1) + for (int y = 0; y < marginY; y++) + { + ::memcpy(p + (y + 1) * s, p, sizeof(T) * (w + (marginX << 1))); + } + + // p is still (-marginX, height-1) + p -= ((h - 1) * s); + // p is now (-marginX, 0) + for (int y = 0; y < marginY; y++) + { + ::memcpy(p - (y + 1) * s, p, sizeof(T) * (w + (marginX << 1))); + } +} +#endif + + template<typename T> void AreaBuf<T>::extendBorderPel( unsigned margin ) { @@ -746,6 +789,9 @@ struct UnitBuf void addWeightedAvg ( const UnitBuf<const T> &other1, const UnitBuf<const T> &other2, const ClpRngs& clpRngs, const uint8_t gbiIdx = GBI_DEFAULT, const bool chromaOnly = false, const bool lumaOnly = false); void addAvg ( const UnitBuf<const T> &other1, const UnitBuf<const T> &other2, const ClpRngs& clpRngs, const bool chromaOnly = false, const bool lumaOnly = false); void extendSingleBorderPel(); +#if JVET_O0549_ENCODER_ONLY_FILTER + void extendBorderPel(unsigned marginX, unsigned marginY); +#endif void extendBorderPel ( unsigned margin ); void removeHighFreq ( const UnitBuf<T>& other, const bool bClip, const ClpRngs& clpRngs , const int8_t gbiWeight = g_GbiWeights[GBI_DEFAULT] @@ -855,6 +901,17 @@ void UnitBuf<T>::extendSingleBorderPel() } } +#if JVET_O0549_ENCODER_ONLY_FILTER +template<typename T> +void UnitBuf<T>::extendBorderPel(unsigned marginX, unsigned marginY) +{ + for (unsigned i = 0; i < bufs.size(); i++) + { + bufs[i].extendBorderPel(marginX >> getComponentScaleX(ComponentID(i), chromaFormat), marginY >> getComponentScaleY(ComponentID(i), chromaFormat)); + } +} +#endif + template<typename T> void UnitBuf<T>::extendBorderPel( unsigned margin ) { diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h index 44aa44e3ec759f0f138a27e03a423f42c41b05f5..44d23329064bc9934d30e6b53eabdf71aa174f65 100644 --- a/source/Lib/CommonLib/TypeDef.h +++ b/source/Lib/CommonLib/TypeDef.h @@ -50,6 +50,8 @@ #include <assert.h> #include <cassert> +#define JVET_O0549_ENCODER_ONLY_FILTER 1 // JVET-O0549: Encoder-only temporal filter, no decoder changes + #define JVET_O0143_BOTTOM_RIGHT_BRICK_IDX_DELTA 1 // JVET-O0143: Remove signaling of top_right_brick_idx #define JVET_O0236_PPS_PARSING_DEPENDENCY 1 // JVET-O0236: Resolves a PPS parsing dependency diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h index 5a430caa73dff7e3217346ec695e7d710a90ace9..8a5d92ec6768040d6494cf8bf98c2497b6079a80 100644 --- a/source/Lib/EncoderLib/EncCfg.h +++ b/source/Lib/EncoderLib/EncCfg.h @@ -485,6 +485,9 @@ protected: int m_PCMBitDepth[MAX_NUM_CHANNEL_TYPE]; uint32_t m_pcmLog2MaxSize; uint32_t m_uiPCMLog2MinSize; +#endif +#if JVET_O0549_ENCODER_ONLY_FILTER + bool m_gopBasedTemporalFilterEnabled; #endif //====== Slice ======== SliceConstraint m_sliceMode; @@ -1291,6 +1294,10 @@ public: uint32_t getPCMLog2MaxSize () { return m_pcmLog2MaxSize; } uint32_t getPCMLog2MinSize () { return m_uiPCMLog2MinSize; } #endif +#if JVET_O0549_ENCODER_ONLY_FILTER + void setGopBasedTemporalFilterEnabled(bool flag) { m_gopBasedTemporalFilterEnabled = flag; } + bool getGopBasedTemporalFilterEnabled() { return m_gopBasedTemporalFilterEnabled; } +#endif bool getCrossComponentPredictionEnabledFlag () const { return m_crossComponentPredictionEnabledFlag; } void setCrossComponentPredictionEnabledFlag (const bool value) { m_crossComponentPredictionEnabledFlag = value; } diff --git a/source/Lib/EncoderLib/EncGOP.cpp b/source/Lib/EncoderLib/EncGOP.cpp index c3c1348c62af595cede8e2ce8609ecd59cc89655..e66074dafdfde7cf8e4cfbc8fc22407a4dca6c0d 100644 --- a/source/Lib/EncoderLib/EncGOP.cpp +++ b/source/Lib/EncoderLib/EncGOP.cpp @@ -3779,7 +3779,11 @@ void EncGOP::xCalculateAddPSNR(Picture* pcPic, PelUnitBuf cPicD, const AccessUni const CPelUnitBuf& pic = cPicD; CHECK(!(conversion == IPCOLOURSPACE_UNCHANGED), "Unspecified error"); // const CPelUnitBuf& org = (conversion != IPCOLOURSPACE_UNCHANGED) ? pcPic->getPicYuvTrueOrg()->getBuf() : pcPic->getPicYuvOrg()->getBuf(); +#if JVET_O0549_ENCODER_ONLY_FILTER + const CPelUnitBuf& org = (sps.getUseReshaper() || m_pcCfg->getGopBasedTemporalFilterEnabled()) ? pcPic->getTrueOrigBuf() : pcPic->getOrigBuf(); +#else const CPelUnitBuf& org = sps.getUseReshaper() ? pcPic->getTrueOrigBuf() : pcPic->getOrigBuf(); +#endif #if ENABLE_QPA const bool useWPSNR = m_pcEncLib->getUseWPSNR(); #endif diff --git a/source/Lib/EncoderLib/EncTemporalFilter.cpp b/source/Lib/EncoderLib/EncTemporalFilter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5c19ef9aec3e665601684f48abb3d109c5c8cf5a --- /dev/null +++ b/source/Lib/EncoderLib/EncTemporalFilter.cpp @@ -0,0 +1,628 @@ +/* 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-2019, 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 EncTemporalFilter.cpp +\brief EncTemporalFilter class +*/ + +#include "EncTemporalFilter.h" +#include <math.h> + +#if JVET_O0549_ENCODER_ONLY_FILTER + +// ==================================================================================================================== +// Constructor / destructor / initialization / destroy +// ==================================================================================================================== + +const int EncTemporalFilter::s_range = 2; +const double EncTemporalFilter::s_chromaFactor = 0.55; +const double EncTemporalFilter::s_sigmaMultiplier = 9.0; +const double EncTemporalFilter::s_sigmaZeroPoint = 10.0; +const int EncTemporalFilter::s_motionVectorFactor = 16; +const int EncTemporalFilter::s_padding = 128; +const int EncTemporalFilter::s_interpolationFilter[16][8] = +{ + { 0, 0, 0, 64, 0, 0, 0, 0 }, //0 + { 0, 1, -3, 64, 4, -2, 0, 0 }, //1 -->--> + { 0, 1, -6, 62, 9, -3, 1, 0 }, //2 --> + { 0, 2, -8, 60, 14, -5, 1, 0 }, //3 -->--> + { 0, 2, -9, 57, 19, -7, 2, 0 }, //4 + { 0, 3, -10, 53, 24, -8, 2, 0 }, //5 -->--> + { 0, 3, -11, 50, 29, -9, 2, 0 }, //6 --> + { 0, 3, -11, 44, 35, -10, 3, 0 }, //7 -->--> + { 0, 1, -7, 38, 38, -7, 1, 0 }, //8 + { 0, 3, -10, 35, 44, -11, 3, 0 }, //9 -->--> + { 0, 2, -9, 29, 50, -11, 3, 0 }, //10--> + { 0, 2, -8, 24, 53, -10, 3, 0 }, //11-->--> + { 0, 2, -7, 19, 57, -9, 2, 0 }, //12 + { 0, 1, -5, 14, 60, -8, 2, 0 }, //13-->--> + { 0, 1, -3, 9, 62, -6, 1, 0 }, //14--> + { 0, 0, -2, 4, 64, -3, 1, 0 } //15-->--> +}; + +const double EncTemporalFilter::s_refStrengths[3][2] = +{ // abs(POC offset) + // 1, 2 + {0.85, 0.60}, // s_range * 2 + {1.20, 1.00}, // s_range + {0.30, 0.30} // otherwise +}; + +EncTemporalFilter::EncTemporalFilter() : + m_FrameSkip(0), + m_chromaFormatIDC(NUM_CHROMA_FORMAT), + m_sourceWidth(0), + m_sourceHeight(0), + m_QP(0), + m_clipInputVideoToRec709Range(false), + m_inputColourSpaceConvert(NUMBER_INPUT_COLOUR_SPACE_CONVERSIONS) +{} + +void EncTemporalFilter::init(const int frameSkip, + const int inputBitDepth[MAX_NUM_CHANNEL_TYPE], + const int MSBExtendedBitDepth[MAX_NUM_CHANNEL_TYPE], + const int internalBitDepth[MAX_NUM_CHANNEL_TYPE], + const int width, + const int height, + const int *pad, + const bool Rec709, + const std::string &filename, + const ChromaFormat inputChromaFormatIDC, + const InputColourSpaceConversion colorSpaceConv, + const int QP, + const std::map<int, double> &temporalFilterStrengths, + const bool gopBasedTemporalFilterFutureReference) +{ + m_FrameSkip = frameSkip; + for (int i = 0; i < MAX_NUM_CHANNEL_TYPE; i++) + { + m_inputBitDepth[i] = inputBitDepth[i]; + m_MSBExtendedBitDepth[i] = MSBExtendedBitDepth[i]; + m_internalBitDepth[i] = internalBitDepth[i]; + } + + m_sourceWidth = width; + m_sourceHeight = height; + for (int i = 0; i < 2; i++) + { + m_pad[i] = pad[i]; + } + m_clipInputVideoToRec709Range = Rec709; + m_inputFileName = filename; + m_chromaFormatIDC = inputChromaFormatIDC; + m_inputColourSpaceConvert = colorSpaceConv; + m_area = Area(0, 0, width, height); + m_QP = QP; + m_temporalFilterStrengths = temporalFilterStrengths; + m_gopBasedTemporalFilterFutureReference = gopBasedTemporalFilterFutureReference; +} + +// ==================================================================================================================== +// Public member functions +// ==================================================================================================================== + +bool EncTemporalFilter::filter(PelStorage *orgPic, int receivedPoc) +{ + bool isFilterThisFrame = false; + if (m_QP >= 17) // disable filter for QP < 17 + { + for (map<int, double>::iterator it = m_temporalFilterStrengths.begin(); it != m_temporalFilterStrengths.end(); ++it) + { + int filteredFrame = it->first; + if (receivedPoc % filteredFrame == 0) + { + isFilterThisFrame = true; + break; + } + } + } + + if (isFilterThisFrame) + { + int offset = m_FrameSkip; + VideoIOYuv yuvFrames; + yuvFrames.open(m_inputFileName, false, m_inputBitDepth, m_MSBExtendedBitDepth, m_internalBitDepth); + yuvFrames.skipFrames(std::max(offset + receivedPoc - s_range, 0), m_sourceWidth - m_pad[0], m_sourceHeight - m_pad[1], m_chromaFormatIDC); + + + std::deque<TemporalFilterSourcePicInfo> srcFrameInfo; + + int firstFrame = receivedPoc + offset - s_range; + int lastFrame = receivedPoc + offset + s_range; + if (!m_gopBasedTemporalFilterFutureReference) + { + lastFrame = receivedPoc + offset - 1; + } + int origOffset = -s_range; + + // subsample original picture so it only needs to be done once + PelStorage origPadded; + + origPadded.create(m_chromaFormatIDC, m_area, 0, s_padding); + origPadded.copyFrom(*orgPic); + origPadded.extendBorderPel(s_padding, s_padding); + + PelStorage origSubsampled2; + PelStorage origSubsampled4; + + subsampleLuma(origPadded, origSubsampled2); + subsampleLuma(origSubsampled2, origSubsampled4); + + // determine motion vectors + for (int poc = firstFrame; poc <= lastFrame; poc++) + { + if (poc < 0) + { + origOffset++; + continue; // frame not available + } + else if (poc == offset + receivedPoc) + { // hop over frame that will be filtered + yuvFrames.skipFrames(1, m_sourceWidth - m_pad[0], m_sourceHeight - m_pad[1], m_chromaFormatIDC); + origOffset++; + continue; + } + srcFrameInfo.push_back(TemporalFilterSourcePicInfo()); + TemporalFilterSourcePicInfo &srcPic=srcFrameInfo.back(); + + PelStorage dummyPicBufferTO; // Only used temporary in yuvFrames.read + srcPic.picBuffer.create(m_chromaFormatIDC, m_area, 0, s_padding); + dummyPicBufferTO.create(m_chromaFormatIDC, m_area, 0, s_padding); + if (!yuvFrames.read(srcPic.picBuffer, dummyPicBufferTO, m_inputColourSpaceConvert, m_pad, m_chromaFormatIDC, m_clipInputVideoToRec709Range)) + { + return false; // eof or read fail + } + srcPic.picBuffer.extendBorderPel(s_padding, s_padding); + srcPic.mvs.allocate(m_sourceWidth / 4, m_sourceHeight / 4); + + motionEstimation(srcPic.mvs, origPadded, srcPic.picBuffer, origSubsampled2, origSubsampled4); + srcPic.origOffset = origOffset; + origOffset++; + } + + // filter + PelStorage newOrgPic; + newOrgPic.create(m_chromaFormatIDC, m_area, 0, s_padding); + double overallStrength = -1.0; + for (map<int, double>::iterator it = m_temporalFilterStrengths.begin(); it != m_temporalFilterStrengths.end(); ++it) + { + int frame = it->first; + double strength = it->second; + if (receivedPoc % frame == 0) + { + overallStrength = strength; + } + } + + bilateralFilter(origPadded, srcFrameInfo, newOrgPic, overallStrength); + + // move filtered to orgPic + orgPic->copyFrom(newOrgPic); + + yuvFrames.close(); + return true; + } + return false; +} + +// ==================================================================================================================== +// Private member functions +// ==================================================================================================================== + +void EncTemporalFilter::subsampleLuma(const PelStorage &input, PelStorage &output, const int factor) const +{ + const int newWidth = input.Y().width / factor; + const int newHeight = input.Y().height / factor; + output.create(m_chromaFormatIDC, Area(0, 0, newWidth, newHeight), 0, s_padding); + + const Pel* srcRow = input.Y().buf; + const int srcStride = input.Y().stride; + Pel *dstRow = output.Y().buf; + const int dstStride = output.Y().stride; + + for (int y = 0; y < newHeight; y++, srcRow+=factor*srcStride, dstRow+=dstStride) + { + const Pel *inRow = srcRow; + const Pel *inRowBelow = srcRow+srcStride; + Pel *target = dstRow; + + for (int x = 0; x < newWidth; x++) + { + target[x] = (inRow[0] + inRowBelow[0] + inRow[1] + inRowBelow[1] + 2) >> 2; + inRow += 2; + inRowBelow += 2; + } + } + output.extendBorderPel(s_padding, s_padding); +} + +int EncTemporalFilter::motionErrorLuma(const PelStorage &orig, + const PelStorage &buffer, + const int x, + const int y, + int dx, + int dy, + const int bs, + const int besterror = 8 * 8 * 1024 * 1024) const +{ + const Pel* origOrigin = orig.Y().buf; + const int origStride = orig.Y().stride; + const Pel *buffOrigin = buffer.Y().buf; + const int buffStride = buffer.Y().stride; + + int error = 0;// dx * 10 + dy * 10; + if (((dx | dy) & 0xF) == 0) + { + dx /= s_motionVectorFactor; + dy /= s_motionVectorFactor; + for (int y1 = 0; y1 < bs; y1++) + { + const Pel* origRowStart = origOrigin + (y+y1)*origStride + x; + const Pel* bufferRowStart = buffOrigin + (y+y1+dy)*buffStride + (x+dx); + for (int x1 = 0; x1 < bs; x1 += 2) + { + int diff = origRowStart[x1] - bufferRowStart[x1]; + error += diff * diff; + diff = origRowStart[x1 + 1] - bufferRowStart[x1 + 1]; + error += diff * diff; + } + if (error > besterror) + { + return error; + } + } + } + else + { + const int *xFilter = s_interpolationFilter[dx & 0xF]; + const int *yFilter = s_interpolationFilter[dy & 0xF]; + int tempArray[64 + 8][64]; + + int sum, base; + for (int y1 = 1; y1 < bs + 7; y1++) + { + const int yOffset = y + y1 + (dy >> 4) - 3; + const Pel *sourceRow = buffOrigin + (yOffset)*buffStride + 0; + for (int x1 = 0; x1 < bs; x1++) + { + sum = 0; + base = x + x1 + (dx >> 4) - 3; + const Pel *rowStart = sourceRow + base; + + sum += xFilter[1] * rowStart[1]; + sum += xFilter[2] * rowStart[2]; + sum += xFilter[3] * rowStart[3]; + sum += xFilter[4] * rowStart[4]; + sum += xFilter[5] * rowStart[5]; + sum += xFilter[6] * rowStart[6]; + + tempArray[y1][x1] = sum; + } + } + + const Pel maxSampleValue = (1<<m_internalBitDepth[CHANNEL_TYPE_LUMA])-1; + for (int y1 = 0; y1 < bs; y1++) + { + const Pel *origRow = origOrigin + (y+y1)*origStride + 0; + for (int x1 = 0; x1 < bs; x1++) + { + sum = 0; + sum += yFilter[1] * tempArray[y1 + 1][x1]; + sum += yFilter[2] * tempArray[y1 + 2][x1]; + sum += yFilter[3] * tempArray[y1 + 3][x1]; + sum += yFilter[4] * tempArray[y1 + 4][x1]; + sum += yFilter[5] * tempArray[y1 + 5][x1]; + sum += yFilter[6] * tempArray[y1 + 6][x1]; + + sum = (sum + (1 << 11)) >> 12; + sum = sum < 0 ? 0 : (sum > maxSampleValue ? maxSampleValue : sum); + + error += (sum - origRow[x + x1]) * (sum - origRow[x + x1]); + } + if (error > besterror) + { + return error; + } + } + } + return error; +} + +void EncTemporalFilter::motionEstimationLuma(Array2D<MotionVector> &mvs, const PelStorage &orig, const PelStorage &buffer, const int blockSize, + const Array2D<MotionVector> *previous, const int factor, const bool doubleRes) const +{ + int range = 5; + const int stepSize = blockSize; + + const int origWidth = orig.Y().width; + const int origHeight = orig.Y().height; + + for (int blockY = 0; blockY + blockSize < origHeight; blockY += stepSize) + { + for (int blockX = 0; blockX + blockSize < origWidth; blockX += stepSize) + { + MotionVector best; + + if (previous == NULL) + { + range = 8; + } + else + { + for (int py = -2; py <= 2; py++) + { + int testy = blockY / (2 * blockSize) + py; + for (int px = -2; px <= 2; px++) + { + int testx = blockX / (2 * blockSize) + px; + if ((testx >= 0) && (testx < origWidth / (2 * blockSize)) && (testy >= 0) && (testy < origHeight / (2 * blockSize))) + { + MotionVector old = previous->get(testx, testy); + int error = motionErrorLuma(orig, buffer, blockX, blockY, old.x * factor, old.y * factor, blockSize, best.error); + if (error < best.error) + { + best.set(old.x * factor, old.y * factor, error); + } + } + } + } + } + MotionVector prevBest = best; + for (int y2 = prevBest.y / s_motionVectorFactor - range; y2 <= prevBest.y / s_motionVectorFactor + range; y2++) + { + for (int x2 = prevBest.x / s_motionVectorFactor - range; x2 <= prevBest.x / s_motionVectorFactor + range; x2++) + { + int error = motionErrorLuma(orig, buffer, blockX, blockY, x2 * s_motionVectorFactor, y2 * s_motionVectorFactor, blockSize, best.error); + if (error < best.error) + { + best.set(x2 * s_motionVectorFactor, y2 * s_motionVectorFactor, error); + } + } + } + if (doubleRes) + { // merge into one loop, probably with precision array (here [12, 3] or maybe [4, 1]) with setable number of iterations + prevBest = best; + int doubleRange = 3 * 4; + for (int y2 = prevBest.y - doubleRange; y2 <= prevBest.y + doubleRange; y2 += 4) + { + for (int x2 = prevBest.x - doubleRange; x2 <= prevBest.x + doubleRange; x2 += 4) + { + int error = motionErrorLuma(orig, buffer, blockX, blockY, x2, y2, blockSize, best.error); + if (error < best.error) + { + best.set(x2, y2, error); + } + + } + } + + prevBest = best; + doubleRange = 3; + for (int y2 = prevBest.y - doubleRange; y2 <= prevBest.y + doubleRange; y2++) + { + for (int x2 = prevBest.x - doubleRange; x2 <= prevBest.x + doubleRange; x2++) + { + int error = motionErrorLuma(orig, buffer, blockX, blockY, x2, y2, blockSize, best.error); + if (error < best.error) + { + best.set(x2, y2, error); + } + + } + } + + } + mvs.get(blockX / stepSize, blockY / stepSize) = best; + } + } +} + +void EncTemporalFilter::motionEstimation(Array2D<MotionVector> &mv, const PelStorage &orgPic, const PelStorage &buffer, const PelStorage &origSubsampled2, const PelStorage &origSubsampled4) const +{ + const int width = m_sourceWidth; + const int height = m_sourceHeight; + Array2D<MotionVector> mv_0(width / 16, height / 16); + Array2D<MotionVector> mv_1(width / 16, height / 16); + Array2D<MotionVector> mv_2(width / 16, height / 16); + + PelStorage bufferSub2; + PelStorage bufferSub4; + + subsampleLuma(buffer, bufferSub2); + subsampleLuma(bufferSub2, bufferSub4); + + motionEstimationLuma(mv_0, origSubsampled4, bufferSub4, 16); + motionEstimationLuma(mv_1, origSubsampled2, bufferSub2, 16, &mv_0, 2); + motionEstimationLuma(mv_2, orgPic, buffer, 16, &mv_1, 2); + + motionEstimationLuma(mv, orgPic, buffer, 8, &mv_2, 1, true); +} + +void EncTemporalFilter::applyMotion(const Array2D<MotionVector> &mvs, const PelStorage &input, PelStorage &output) const +{ + static const int lumaBlockSize=8; + + for(int c=0; c< getNumberValidComponents(m_chromaFormatIDC); c++) + { + const ComponentID compID=(ComponentID)c; + const int csx=getComponentScaleX(compID, m_chromaFormatIDC); + const int csy=getComponentScaleY(compID, m_chromaFormatIDC); + const int blockSizeX = lumaBlockSize>>csx; + const int blockSizeY = lumaBlockSize>>csy; + const int height = input.bufs[c].height; + const int width = input.bufs[c].width; + + const Pel maxValue = (1<<m_internalBitDepth[toChannelType(compID)])-1; + + const Pel *srcImage = input.bufs[c].buf; + const int srcStride = input.bufs[c].stride; + + Pel *dstImage = output.bufs[c].buf; + int dstStride = output.bufs[c].stride; + + for (int y = 0, blockNumY = 0; y + blockSizeY <= height; y += blockSizeY, blockNumY++) + { + for (int x = 0, blockNumX = 0; x + blockSizeX <= width; x += blockSizeX, blockNumX++) + { + const MotionVector &mv = mvs.get(blockNumX,blockNumY); + const int dx = mv.x >> csx ; + const int dy = mv.y >> csy ; + const int xInt = mv.x >> (4+csx) ; + const int yInt = mv.y >> (4+csy) ; + + const int *xFilter = s_interpolationFilter[dx & 0xf]; + const int *yFilter = s_interpolationFilter[dy & 0xf]; // will add 6 bit. + const int numFilterTaps=7; + const int centreTapOffset=3; + + int tempArray[lumaBlockSize + numFilterTaps][lumaBlockSize]; + + for (int by = 1; by < blockSizeY + numFilterTaps; by++) + { + const int yOffset = y + by + yInt - centreTapOffset; + const Pel *sourceRow = srcImage+yOffset*srcStride; + for (int bx = 0; bx < blockSizeX; bx++) + { + int base = x + bx + xInt - centreTapOffset; + const Pel *rowStart = sourceRow + base; + + int sum = 0; + sum += xFilter[1] * rowStart[1]; + sum += xFilter[2] * rowStart[2]; + sum += xFilter[3] * rowStart[3]; + sum += xFilter[4] * rowStart[4]; + sum += xFilter[5] * rowStart[5]; + sum += xFilter[6] * rowStart[6]; + + tempArray[by][bx] = sum; + } + } + + Pel *dstRow = dstImage+y*dstStride; + for (int by = 0; by < blockSizeY; by++, dstRow+=dstStride) + { + Pel *dstPel=dstRow+x; + for (int bx = 0; bx < blockSizeX; bx++, dstPel++) + { + int sum = 0; + + sum += yFilter[1] * tempArray[by + 1][bx]; + sum += yFilter[2] * tempArray[by + 2][bx]; + sum += yFilter[3] * tempArray[by + 3][bx]; + sum += yFilter[4] * tempArray[by + 4][bx]; + sum += yFilter[5] * tempArray[by + 5][bx]; + sum += yFilter[6] * tempArray[by + 6][bx]; + + sum = (sum + (1 << 11)) >> 12; + sum = sum < 0 ? 0 : (sum > maxValue ? maxValue : sum); + *dstPel = sum; + } + } + } + } + } +} + +void EncTemporalFilter::bilateralFilter(const PelStorage &orgPic, + const std::deque<TemporalFilterSourcePicInfo> &srcFrameInfo, + PelStorage &newOrgPic, + double overallStrength) const +{ + const int numRefs = int(srcFrameInfo.size()); + std::vector<PelStorage> correctedPics(numRefs); + for (int i = 0; i < numRefs; i++) + { + correctedPics[i].create(m_chromaFormatIDC, m_area, 0, s_padding); + applyMotion(srcFrameInfo[i].mvs, srcFrameInfo[i].picBuffer, correctedPics[i]); + } + + int refStrengthRow = 2; + if (numRefs == s_range*2) + { + refStrengthRow = 0; + } + else if (numRefs == s_range) + { + refStrengthRow = 1; + } + + const double lumaSigmaSq = (m_QP - s_sigmaZeroPoint) * (m_QP - s_sigmaZeroPoint) * s_sigmaMultiplier; + const double chromaSigmaSq = 30 * 30; + + for(int c=0; c< getNumberValidComponents(m_chromaFormatIDC); c++) + { + const ComponentID compID=(ComponentID)c; + const int height = orgPic.bufs[c].height; + const int width = orgPic.bufs[c].width; + const Pel *srcPelRow = orgPic.bufs[c].buf; + const int srcStride = orgPic.bufs[c].stride; + Pel *dstPelRow = newOrgPic.bufs[c].buf; + const int dstStride = newOrgPic.bufs[c].stride; + const double sigmaSq = isChroma(compID)? chromaSigmaSq : lumaSigmaSq; + const double weightScaling = overallStrength * (isChroma(compID) ? s_chromaFactor : 0.4); + const Pel maxSampleValue = (1<<m_internalBitDepth[toChannelType(compID)])-1; + const double bitDepthDiffWeighting=1024.0 / (maxSampleValue+1); + + for (int y = 0; y < height; y++, srcPelRow+=srcStride, dstPelRow+=dstStride) + { + const Pel *srcPel=srcPelRow; + Pel *dstPel=dstPelRow; + for (int x = 0; x < width; x++, srcPel++, dstPel++) + { + const int orgVal = (int) *srcPel; + double temporalWeightSum = 1.0; + double newVal = (double) orgVal; + for (int i = 0; i < numRefs; i++) + { + const Pel *pCorrectedPelPtr=correctedPics[i].bufs[c].buf+(y*correctedPics[i].bufs[c].stride+x); + const int refVal = (int) *pCorrectedPelPtr; + double diff = (double)(refVal - orgVal); + diff *= bitDepthDiffWeighting; + double diffSq = diff * diff; + const int index = std::min(1, std::abs(srcFrameInfo[i].origOffset) - 1); + const double weight = weightScaling * s_refStrengths[refStrengthRow][index] * exp(-diffSq / (2 * sigmaSq)); + newVal += weight * refVal; + temporalWeightSum += weight; + } + newVal /= temporalWeightSum; + Pel sampleVal = (Pel)round(newVal); + sampleVal=(sampleVal<0?0 : (sampleVal>maxSampleValue ? maxSampleValue : sampleVal)); + *dstPel = sampleVal; + } + } + } +} + +//! \} + +#endif diff --git a/source/Lib/EncoderLib/EncTemporalFilter.h b/source/Lib/EncoderLib/EncTemporalFilter.h new file mode 100644 index 0000000000000000000000000000000000000000..0a44eefa01b5c8d955c4b9e46cf982c974f21db5 --- /dev/null +++ b/source/Lib/EncoderLib/EncTemporalFilter.h @@ -0,0 +1,170 @@ +/* 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-2019, 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 EncTemporalFilter.h +\brief EncTemporalFilter class (header) +*/ + +#ifndef __TEMPORAL_FILTER__ +#define __TEMPORAL_FILTER__ +#include "EncLib.h" +#include "CommonLib/Buffer.h" +#include <sstream> +#include <map> +#include <deque> + +#if JVET_O0549_ENCODER_ONLY_FILTER + +//! \ingroup EncoderLib +//! \{ + +struct MotionVector +{ + int x, y; + int error; + MotionVector() : x(0), y(0), error(INT_LEAST32_MAX) {} + void set(int nx, int ny, int ne) { x = nx; y = ny; error = ne; } +}; + +template <class T> +struct Array2D +{ +private: + int m_width, m_height; + std::vector< T > v; +public: + Array2D() : m_width(0), m_height(0), v() { } + Array2D(int width, int height, const T& value=T()) : m_width(0), m_height(0), v() { allocate(width, height, value); } + + void allocate(int width, int height, const T& value=T()) + { + m_width=width; + m_height=height; + v.resize(std::size_t(m_width*m_height), value); + } + + T& get(int x, int y) + { + assert(x<m_width && y<m_height); + return v[y*m_width+x]; + } + + const T& get(int x, int y) const + { + assert(x<m_width && y<m_height); + return v[y*m_width+x]; + } +}; + +struct TemporalFilterSourcePicInfo +{ + TemporalFilterSourcePicInfo() : picBuffer(), mvs(), origOffset(0) { } + PelStorage picBuffer; + Array2D<MotionVector> mvs; + int origOffset; +}; + +// ==================================================================================================================== +// Class definition +// ==================================================================================================================== + +class EncTemporalFilter +{ +public: + EncTemporalFilter(); + ~EncTemporalFilter() {} + + void init(const int frameSkip, + const int inputBitDepth[MAX_NUM_CHANNEL_TYPE], + const int MSBExtendedBitDepth[MAX_NUM_CHANNEL_TYPE], const int InternalBitDepth[MAX_NUM_CHANNEL_TYPE], + const int width, + const int height, + const int *pad, + const bool Rec709, + const std::string &filename, + const ChromaFormat inputChroma, + const InputColourSpaceConversion colorSpaceConv, + const int qp, + const std::map<int, double> &temporalFilterStrengths, + const bool gopBasedTemporalFilterFutureReference); + + bool filter(PelStorage *orgPic, int frame); + +private: + // Private static member variables + static const int s_range; + static const double s_chromaFactor; + static const double s_sigmaMultiplier; + static const double s_sigmaZeroPoint; + static const int s_motionVectorFactor; + static const int s_padding; + static const int s_interpolationFilter[16][8]; + static const double s_refStrengths[3][2]; + + // Private member variables + int m_FrameSkip; + std::string m_inputFileName; + int m_inputBitDepth[MAX_NUM_CHANNEL_TYPE]; + int m_MSBExtendedBitDepth[MAX_NUM_CHANNEL_TYPE]; + int m_internalBitDepth[MAX_NUM_CHANNEL_TYPE]; + ChromaFormat m_chromaFormatIDC; + int m_sourceWidth; + int m_sourceHeight; + int m_QP; + std::map<int, double> m_temporalFilterStrengths; + int m_pad[2]; + bool m_clipInputVideoToRec709Range; + InputColourSpaceConversion m_inputColourSpaceConvert; + Area m_area; + bool m_gopBasedTemporalFilterFutureReference; + + int m_maxCUWidth; + int m_maxCUHeight; + int m_maxTotalCUDepth; + + // Private functions + void subsampleLuma(const PelStorage &input, PelStorage &output, const int factor = 2) const; + int motionErrorLuma(const PelStorage &orig, const PelStorage &buffer, const int x, const int y, int dx, int dy, const int bs, const int besterror) const; + void motionEstimationLuma(Array2D<MotionVector> &mvs, const PelStorage &orig, const PelStorage &buffer, const int bs, + const Array2D<MotionVector> *previous=0, const int factor = 1, const bool doubleRes = false) const; + void motionEstimation(Array2D<MotionVector> &mvs, const PelStorage &orgPic, const PelStorage &buffer, const PelStorage &origSubsampled2, const PelStorage &origSubsampled4) const; + + void bilateralFilter(const PelStorage &orgPic, const std::deque<TemporalFilterSourcePicInfo> &srcFrameInfo, PelStorage &newOrgPic, double overallStrength) const; + void applyMotion(const Array2D<MotionVector> &mvs, const PelStorage &input, PelStorage &output) const; +}; // END CLASS DEFINITION EncTemporalFilter + + //! \} + +#endif + +#endif // __TEMPORAL_FILTER__ diff --git a/source/Lib/Utilities/program_options_lite.cpp b/source/Lib/Utilities/program_options_lite.cpp index 0c4bba0502cc08c2caa01b4cd61f66554dfe30ab..4a380e04cc1b64761d11aed3852252fc47addca4 100644 --- a/source/Lib/Utilities/program_options_lite.cpp +++ b/source/Lib/Utilities/program_options_lite.cpp @@ -96,8 +96,22 @@ namespace df } else { +#if JVET_O0549_ENCODER_ONLY_FILTER_POL + if (opt_name.size() > 0 && opt_name.back() == '*') + { + string prefix_name = opt_name.substr(0, opt_name.size() - 1); + names->opt_prefix.push_back(prefix_name); + opt_prefix_map[prefix_name].push_back(names); + } + else + { + names->opt_long.push_back(opt_name); + opt_long_map[opt_name].push_back(names); + } +#else names->opt_long.push_back(opt_name); opt_long_map[opt_name].push_back(names); +#endif } opt_start += opt_end + 1; } @@ -150,6 +164,12 @@ namespace df { out << "--" << entry.opt_long.front(); } +#if JVET_O0549_ENCODER_ONLY_FILTER_POL + else if (!entry.opt_prefix.empty()) + { + out << "--" << entry.opt_prefix.front() << "*"; + } +#endif } /* format the help text */ @@ -271,6 +291,9 @@ namespace df bool OptionWriter::storePair(bool allow_long, bool allow_short, const string& name, const string& value) { bool found = false; +#if JVET_O0549_ENCODER_ONLY_FILTER_POL + std::string val = value; +#endif Options::NamesMap::iterator opt_it; if (allow_long) { @@ -290,15 +313,34 @@ namespace df found = true; } } - +#if JVET_O0549_ENCODER_ONLY_FILTER_POL + bool allow_prefix = allow_long; + if (allow_prefix && !found) + { + for (opt_it = opts.opt_prefix_map.begin(); opt_it != opts.opt_prefix_map.end(); opt_it++) + { + std::string name_prefix = name.substr(0, opt_it->first.size()); + if (name_prefix == opt_it->first) + { + // prepend value matching * + val = name.substr(name_prefix.size()) + std::string(" ") + val; + found = true; + break; + } + } + } +#endif if (!found) { error_reporter.error(where()) << "Unknown option `" << name << "' (value:`" << value << "')\n"; return false; } - +#if JVET_O0549_ENCODER_ONLY_FILTER_POL + setOptions((*opt_it).second, val, error_reporter); +#else setOptions((*opt_it).second, value, error_reporter); +#endif return true; } diff --git a/source/Lib/Utilities/program_options_lite.h b/source/Lib/Utilities/program_options_lite.h index 2ce2bd26ed80c6066ec93401034513b2b4b71b4a..dfd082cb73f9ca67f58d04a6ce1a2fd97b51910c 100644 --- a/source/Lib/Utilities/program_options_lite.h +++ b/source/Lib/Utilities/program_options_lite.h @@ -36,6 +36,8 @@ #include <list> #include <map> +#define JVET_O0549_ENCODER_ONLY_FILTER_POL 1 // JVET-O0549: Encoder-only GOP-based temporal filter. Program Options Lite related changes. + #ifndef __PROGRAM_OPTIONS_LITE__ #define __PROGRAM_OPTIONS_LITE__ @@ -196,6 +198,9 @@ namespace df } std::list<std::string> opt_long; std::list<std::string> opt_short; +#if JVET_O0549_ENCODER_ONLY_FILTER_POL + std::list<std::string> opt_prefix; +#endif OptionBase* opt; }; @@ -207,6 +212,9 @@ namespace df typedef std::map<std::string, NamesPtrList> NamesMap; NamesMap opt_long_map; NamesMap opt_short_map; +#if JVET_O0549_ENCODER_ONLY_FILTER_POL + NamesMap opt_prefix_map; +#endif }; /* Class with templated overloaded operator(), for use by Options::addOptions() */