diff --git a/cfg/sei_vui/post_filter_hint_info.cfg b/cfg/sei_vui/post_filter_hint_info.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c943e157ac26c39da6eb65f2ad4f430d6fd006f7
--- /dev/null
+++ b/cfg/sei_vui/post_filter_hint_info.cfg
@@ -0,0 +1,9 @@
+#======== post-filter hint SEI message =====================
+SEIPostFilterHintEnabled                    : 1
+SEIPostFilterHintCancelFlag                 : 0
+SEIPostFilterHintPersistenceFlag            : 0
+SEIPostFilterHintSizeY                      : 5
+SEIPostFilterHintSizeX                      : 5
+SEIPostFilterHintType                       : 0
+SEIPostFilterHintChromaCoeffPresentFlag     : 0
+SEIPostFilterHintValue                      : 5 9 10 9 5 9 13 14 13 9 10 14 16 14 10 9 13 14 13 9 5 9 10 9 5
diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp
index 652fa97f4eaa7a655a30a8a376a0edce6309428b..c211ef19eed628568be29ee7ae9fdbb842e58b0a 100644
--- a/source/App/EncoderApp/EncApp.cpp
+++ b/source/App/EncoderApp/EncApp.cpp
@@ -1307,6 +1307,17 @@ void EncApp::xInitLibCfg( int layerIdx )
   m_cEncLib.setPoSEIProcessingOrder                              (m_poSEIProcessingOrder);
   m_cEncLib.setPoSEINumofSeiMessages                             (m_numofSEIMessages);
 
+#if JVET_AB0070_POST_FILTER_HINT
+  m_cEncLib.setPostFilterHintSEIEnabled(m_postFilterHintSEIEnabled);
+  m_cEncLib.setPostFilterHintSEICancelFlag(m_postFilterHintSEICancelFlag);
+  m_cEncLib.setPostFilterHintSEIPersistenceFlag(m_postFilterHintSEIPersistenceFlag);
+  m_cEncLib.setPostFilterHintSEISizeY(m_postFilterHintSEISizeY);
+  m_cEncLib.setPostFilterHintSEISizeX(m_postFilterHintSEISizeX);
+  m_cEncLib.setPostFilterHintSEIType(m_postFilterHintSEIType);
+  m_cEncLib.setPostFilterHintSEIChromaCoeffPresentFlag(m_postFilterHintSEIChromaCoeffPresentFlag);
+  m_cEncLib.setPostFilterHintSEIValues(m_postFilterHintValues);
+#endif
+
   m_cEncLib.setVuiParametersPresentFlag                          ( m_vuiParametersPresentFlag );
   m_cEncLib.setSamePicTimingInAllOLS                             (m_samePicTimingInAllOLS);
   m_cEncLib.setAspectRatioInfoPresentFlag                        ( m_aspectRatioInfoPresentFlag);
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index 1c956b727e8d79b62ba0e062e16f9212fa2c56e1..60d741c5563a16be5858a285c935058edd9c0664 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -738,6 +738,10 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   SMultiValueInput<uint16_t>   cfg_poSEIProcessingOrder (0, 255, 0, 256);
 #endif
 
+#if JVET_AB0070_POST_FILTER_HINT
+  SMultiValueInput<int32_t> cfg_postFilterHintSEIValues(INT32_MIN + 1, INT32_MAX, 1 * 1 * 1, 15 * 15 * 3);
+#endif
+
 #if JVET_AB0058_NN_FRAME_RATE_UPSAMPLING
   std::vector<SMultiValueInput<uint32_t>>   cfg_nnPostFilterSEICharacteristicsInterpolatedPicturesList;
   for (int i = 0; i < MAX_NUM_NN_POST_FILTERS; i++)
@@ -1592,6 +1596,17 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   ("SEIPOPayLoadType",                                cfg_poSEIPayloadType,               cfg_poSEIPayloadType, "List of payloadType for processing")
   ("SEIPOProcessingOrder",                            cfg_poSEIProcessingOrder,       cfg_poSEIProcessingOrder, "List of payloadType processing order")
 
+#if JVET_AB0070_POST_FILTER_HINT
+  ("SEIPostFilterHintEnabled",                        m_postFilterHintSEIEnabled,                        false, "Control generation of post-filter Hint SEI message")
+  ("SEIPostFilterHintCancelFlag",                     m_postFilterHintSEICancelFlag,                     false, "Specifies the persistence of any previous post-filter Hint SEI message in output order")
+  ("SEIPostFilterHintPersistenceFlag",                m_postFilterHintSEIPersistenceFlag,                false, "Specifies the persistence of the post-filter Hint SEI message for the current layer")
+  ("SEIPostFilterHintSizeY",                          m_postFilterHintSEISizeY,                             1u, "Specifies the vertical size of the post-filter coefficient or correlation array")
+  ("SEIPostFilterHintSizeX",                          m_postFilterHintSEISizeX,                             1u, "Specifies the horizontal size of the post-filter coefficient or correlation array")
+  ("SEIPostFilterHintType",                           m_postFilterHintSEIType,                              0u, "Specifies the type of the post-filter: 2D-FIR filter (0, default), 1D-FIR filters (1) or Cross-correlation matrix (0)")
+  ("SEIPostFilterHintChromaCoeffPresentFlag",         m_postFilterHintSEIChromaCoeffPresentFlag,         false, "Specifies the presence of post-filter coefficients for chroma")
+  ("SEIPostFilterHintValue",                          cfg_postFilterHintSEIValues, cfg_postFilterHintSEIValues, "Specifies post-filter coefficients or elements of a cross-correlation matrix")
+#endif
+
 #if JVET_T0056_SEI_MANIFEST
   //SEI manifest
   ("SEISEIManifestEnabled",                           m_SEIManifestSEIEnabled,                           false, "Controls if SEI Manifest SEI messages enabled")
@@ -3388,6 +3403,22 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
     assert(m_poSEIProcessingOrder.size() == m_poSEIPayloadType.size());
   }
 
+#if JVET_AB0070_POST_FILTER_HINT
+  if (m_postFilterHintSEIEnabled)
+  {
+    assert(cfg_postFilterHintSEIValues.values.size() > 0);
+    assert(cfg_postFilterHintSEIValues.values.size()
+           == (m_postFilterHintSEIChromaCoeffPresentFlag ? 3 : 1) * m_postFilterHintSEISizeY
+                * m_postFilterHintSEISizeX);
+    m_postFilterHintValues.resize(cfg_postFilterHintSEIValues.values.size());
+
+    for (uint32_t i = 0; i < m_postFilterHintValues.size(); i++)
+    {
+      m_postFilterHintValues[i] = cfg_postFilterHintSEIValues.values[i];
+    }
+  }
+#endif
+
   if( m_costMode == COST_LOSSLESS_CODING )
   {
     bool firstSliceLossless = false;
diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h
index f912b2a20a2b91e1d4436631093bbb0e1436d2d4..c64c3bec0645797457ba3c6508d325ee77c56806 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -785,6 +785,17 @@ protected:
 #endif
   uint32_t              m_numofSEIMessages;
 
+#if JVET_AB0070_POST_FILTER_HINT
+  bool                 m_postFilterHintSEIEnabled;
+  bool                 m_postFilterHintSEICancelFlag;
+  bool                 m_postFilterHintSEIPersistenceFlag;
+  uint32_t             m_postFilterHintSEISizeY;
+  uint32_t             m_postFilterHintSEISizeX;
+  uint32_t             m_postFilterHintSEIType;
+  bool                 m_postFilterHintSEIChromaCoeffPresentFlag;
+  std::vector<int32_t> m_postFilterHintValues;
+#endif
+
   bool                  m_constrainedRaslEncoding;
 
   bool                  m_sampleAspectRatioInfoSEIEnabled;
diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h
index 5e3f731b81755a045e7ed6c4243705bf2cd79812..e59037a1496adca9e1109180064944fe81d0dcb2 100644
--- a/source/Lib/EncoderLib/EncCfg.h
+++ b/source/Lib/EncoderLib/EncCfg.h
@@ -876,6 +876,17 @@ protected:
 #endif
   uint32_t              m_numofSEIMessages;
 
+#if JVET_AB0070_POST_FILTER_HINT
+  bool                 m_postFilterHintSEIEnabled;
+  bool                 m_postFilterHintSEICancelFlag;
+  bool                 m_postFilterHintSEIPersistenceFlag;
+  uint32_t             m_postFilterHintSEISizeY;
+  uint32_t             m_postFilterHintSEISizeX;
+  uint32_t             m_postFilterHintSEIType;
+  bool                 m_postFilterHintSEIChromaCoeffPresentFlag;
+  std::vector<int32_t> m_postFilterHintValues;
+#endif
+
   bool      m_constrainedRaslEncoding;
 
   //====== Weighted Prediction ========
@@ -2490,6 +2501,25 @@ public:
   void     setPoSEINumofSeiMessages(uint32_t i)                      { m_numofSEIMessages = i; }
   uint32_t getPoSEINumofSeiMessages()                          const { return m_numofSEIMessages; }
 
+#if JVET_AB0070_POST_FILTER_HINT
+  void     setPostFilterHintSEIEnabled(bool b) { m_postFilterHintSEIEnabled = b; }
+  bool     getPostFilterHintSEIEnabled() { return m_postFilterHintSEIEnabled; }
+  void     setPostFilterHintSEICancelFlag(bool b) { m_postFilterHintSEICancelFlag = b; }
+  bool     getPostFilterHintSEICancelFlag() { return m_postFilterHintSEICancelFlag; }
+  void     setPostFilterHintSEIPersistenceFlag(bool b) { m_postFilterHintSEIPersistenceFlag = b; }
+  bool     getPostFilterHintSEIPersistenceFlag() { return m_postFilterHintSEIPersistenceFlag; }
+  void     setPostFilterHintSEISizeY(uint32_t i) { m_postFilterHintSEISizeY = i; }
+  uint32_t getPostFilterHintSEISizeY() { return m_postFilterHintSEISizeY; }
+  void     setPostFilterHintSEISizeX(uint32_t i) { m_postFilterHintSEISizeX = i; }
+  uint32_t getPostFilterHintSEISizeX() { return m_postFilterHintSEISizeX; }
+  void     setPostFilterHintSEIType(uint32_t i) { m_postFilterHintSEIType = i; }
+  uint32_t getPostFilterHintSEIType() { return m_postFilterHintSEIType; }
+  void     setPostFilterHintSEIChromaCoeffPresentFlag(bool b) { m_postFilterHintSEIChromaCoeffPresentFlag = b; }
+  bool     getPostFilterHintSEIChromaCoeffPresentFlag() { return m_postFilterHintSEIChromaCoeffPresentFlag; }
+  void     setPostFilterHintSEIValues(const std::vector<int32_t> &b) { m_postFilterHintValues = b; }
+  int32_t  getPostFilterHintSEIValues(int32_t idx) const { return m_postFilterHintValues[idx]; }
+#endif
+
   void         setUseWP               ( bool b )                     { m_useWeightedPred   = b;    }
   void         setWPBiPred            ( bool b )                     { m_useWeightedBiPred = b;    }
   bool         getUseWP               ()                             { return m_useWeightedPred;   }
diff --git a/source/Lib/EncoderLib/EncGOP.cpp b/source/Lib/EncoderLib/EncGOP.cpp
index e7be5d21fa56ffba206220a782dc4395007ed6ec..0d159484737389f1868467a97a02b7ef8a8ae8d3 100644
--- a/source/Lib/EncoderLib/EncGOP.cpp
+++ b/source/Lib/EncoderLib/EncGOP.cpp
@@ -1008,6 +1008,16 @@ void EncGOP::xCreatePerPictureSEIMessages (int picInGOP, SEIMessages& seiMessage
     m_seiEncoder.initSEINeuralNetworkPostFilterActivation(nnpfActivationSEI);
     seiMessages.push_back(nnpfActivationSEI);
   }
+
+#if JVET_AB0070_POST_FILTER_HINT
+  if (m_pcCfg->getPostFilterHintSEIEnabled())
+  {
+    SEIPostFilterHint *postFilterHintSEI = new SEIPostFilterHint;
+
+    m_seiEncoder.initSEIPostFilterHint(postFilterHintSEI);
+    seiMessages.push_back(postFilterHintSEI);
+  }
+#endif
 }
 
 void EncGOP::xCreatePhaseIndicationSEIMessages(SEIMessages& seiMessages, Slice* slice, int ppsId)
diff --git a/source/Lib/EncoderLib/SEIEncoder.cpp b/source/Lib/EncoderLib/SEIEncoder.cpp
index 9ff1ef6c6511c3b97f755258f64711b8d8c5550f..9b077b20c0520aa7ff16cfbc56a5f4b491567273 100644
--- a/source/Lib/EncoderLib/SEIEncoder.cpp
+++ b/source/Lib/EncoderLib/SEIEncoder.cpp
@@ -618,6 +618,29 @@ void SEIEncoder::initSEIProcessingOrderInfo(SEIProcessingOrderInfo *seiProcessin
   }
 }
 
+#if JVET_AB0070_POST_FILTER_HINT
+void SEIEncoder::initSEIPostFilterHint(SEIPostFilterHint *seiPostFilterHint)
+{
+  assert(m_isInitialized);
+  assert(seiPostFilterHint != nullptr);
+
+  seiPostFilterHint->m_filterHintCancelFlag             = m_pcCfg->getPostFilterHintSEICancelFlag();
+  seiPostFilterHint->m_filterHintPersistenceFlag        = m_pcCfg->getPostFilterHintSEIPersistenceFlag();
+  seiPostFilterHint->m_filterHintSizeY                  = m_pcCfg->getPostFilterHintSEISizeY();
+  seiPostFilterHint->m_filterHintSizeX                  = m_pcCfg->getPostFilterHintSEISizeX();
+  seiPostFilterHint->m_filterHintType                   = m_pcCfg->getPostFilterHintSEIType();
+  seiPostFilterHint->m_filterHintChromaCoeffPresentFlag = m_pcCfg->getPostFilterHintSEIChromaCoeffPresentFlag();
+
+  seiPostFilterHint->m_filterHintValues.resize((seiPostFilterHint->m_filterHintChromaCoeffPresentFlag ? 3 : 1)
+                                               * seiPostFilterHint->m_filterHintSizeY
+                                               * seiPostFilterHint->m_filterHintSizeX);
+  for (uint32_t i = 0; i < seiPostFilterHint->m_filterHintValues.size(); i++)
+  {
+    seiPostFilterHint->m_filterHintValues[i] = m_pcCfg->getPostFilterHintSEIValues(i);
+  }
+}
+#endif
+
 template <typename T>
 static void readTokenValue(T            &returnedValue, /// value returned
                            bool         &failed,        /// used and updated
diff --git a/source/Lib/EncoderLib/SEIEncoder.h b/source/Lib/EncoderLib/SEIEncoder.h
index ba4c1d896540a937b221f4111690b68f94cf5942..a0b4317ddbb3ea6306251bc9279ccb3f25662382 100644
--- a/source/Lib/EncoderLib/SEIEncoder.h
+++ b/source/Lib/EncoderLib/SEIEncoder.h
@@ -100,6 +100,9 @@ public:
   void initSEINeuralNetworkPostFilterCharacteristics(SEINeuralNetworkPostFilterCharacteristics *sei, int filterIdx);
   void initSEINeuralNetworkPostFilterActivation(SEINeuralNetworkPostFilterActivation *sei);
   void initSEIProcessingOrderInfo(SEIProcessingOrderInfo *sei);
+#if JVET_AB0070_POST_FILTER_HINT
+  void initSEIPostFilterHint(SEIPostFilterHint *sei);
+#endif
 #if GREEN_METADATA_SEI_ENABLED
   void initSEIGreenMetadataInfo(SEIGreenMetadataInfo *sei, FeatureCounterStruct featureCounter, SEIQualityMetrics metrics, SEIComplexityMetrics greenMetadata);
 #endif