diff --git a/source/App/DecoderApp/DecApp.cpp b/source/App/DecoderApp/DecApp.cpp
index 4ee4e10ba7f615236e5c5591a3bf02dd0c8d8795..ec9dac43f40b4dc1e219884b6b990c238e68e9c0 100644
--- a/source/App/DecoderApp/DecApp.cpp
+++ b/source/App/DecoderApp/DecApp.cpp
@@ -480,6 +480,11 @@ uint32_t DecApp::decode()
                                                          m_outputBitDepth, bitDepths.recon);   // write mode
           m_jsonFile << " \"suffix_bs\": \"_bs.yuv\",\n";
 #endif
+#if JVET_AJ0124_QP_BLOCK
+          m_cVideoIOYuvQpMapFile[nalu.m_nuhLayerId].open(m_dumpBasename + "_qp.yuv", true, m_outputBitDepth,
+                                                         m_outputBitDepth, bitDepths.recon);   // write mode
+          m_jsonFile << " \"suffix_qp_block\": \"_qp.yuv\",\n";
+#endif
 #if NNVC_USE_QP
           m_qpFile.open(m_dumpBasename + "_qp.dat", std::ios::binary);   // write mode
           m_jsonFile << " \"suffix_qp\": \"_qp.dat\",\n";
@@ -833,6 +838,12 @@ void DecApp::xDestroyDecLib()
       bsMapFile.second.close();
     }
 #endif
+#if JVET_AJ0124_QP_BLOCK
+    for (auto &qpMapFile: m_cVideoIOYuvQpMapFile)
+    {
+      qpMapFile.second.close();
+    }
+#endif
 #if NNVC_USE_QP
     m_qpFile.close();
 #endif
@@ -1130,6 +1141,16 @@ void DecApp::xWriteOutput( PicList* pcListPic, uint32_t tId )
             conf.getWindowBottomOffset() * SPS::getWinUnitY(chromaFormatIDC), NUM_CHROMA_FORMAT,
             m_bClipOutputVideoToRec709Range);
 #endif
+#if JVET_AJ0124_QP_BLOCK
+          m_cVideoIOYuvQpMapFile[pcPic->layerId].write(
+            pcPic->getBlockQpBuf().get(COMPONENT_Y).width, pcPic->getBlockQpBuf().get(COMPONENT_Y).height,
+            pcPic->getBlockQpBuf(), m_outputColourSpaceConvert, m_packedYUVMode,
+            conf.getWindowLeftOffset() * SPS::getWinUnitX(chromaFormatIDC),
+            conf.getWindowRightOffset() * SPS::getWinUnitX(chromaFormatIDC),
+            conf.getWindowTopOffset() * SPS::getWinUnitY(chromaFormatIDC),
+            conf.getWindowBottomOffset() * SPS::getWinUnitY(chromaFormatIDC), NUM_CHROMA_FORMAT,
+            m_bClipOutputVideoToRec709Range);
+#endif
 #if NNVC_USE_QP
           int qp = pcPic->cs->slice->getSliceQp();
           m_qpFile.write((const char *) &qp, sizeof(qp));
@@ -1469,6 +1490,16 @@ void DecApp::xFlushOutput( PicList* pcListPic, const int layerId )
               conf.getWindowBottomOffset() * SPS::getWinUnitY(chromaFormatIDC), NUM_CHROMA_FORMAT,
               m_bClipOutputVideoToRec709Range);
 #endif
+#if JVET_AJ0124_QP_BLOCK
+            m_cVideoIOYuvQpMapFile[pcPic->layerId].write(
+              pcPic->getBlockQpBuf().get(COMPONENT_Y).width, pcPic->getBlockQpBuf().get(COMPONENT_Y).height,
+              pcPic->getBlockQpBuf(), m_outputColourSpaceConvert, m_packedYUVMode,
+              conf.getWindowLeftOffset() * SPS::getWinUnitX(chromaFormatIDC),
+              conf.getWindowRightOffset() * SPS::getWinUnitX(chromaFormatIDC),
+              conf.getWindowTopOffset() * SPS::getWinUnitY(chromaFormatIDC),
+              conf.getWindowBottomOffset() * SPS::getWinUnitY(chromaFormatIDC), NUM_CHROMA_FORMAT,
+              m_bClipOutputVideoToRec709Range);
+#endif
 #if NNVC_USE_QP
             int qp = pcPic->cs->slice->getSliceQp();
             m_qpFile.write((const char *) &qp, sizeof(qp));
diff --git a/source/App/DecoderApp/DecApp.h b/source/App/DecoderApp/DecApp.h
index 289a9e3c05eb09712df4b9fe4fc1c6dec0dd20c4..7b2d69e178d8afe9ee39d0e09a793ebea3f055da 100644
--- a/source/App/DecoderApp/DecApp.h
+++ b/source/App/DecoderApp/DecApp.h
@@ -91,6 +91,9 @@ private:
 #if NNVC_USE_BS
   std::unordered_map<int, VideoIOYuv>      m_cVideoIOYuvBsMapFile;        ///< bs map
 #endif
+#if JVET_AJ0124_QP_BLOCK
+  std::unordered_map<int, VideoIOYuv>      m_cVideoIOYuvQpMapFile;        ///< qp map
+#endif
 #if NNVC_USE_QP
   std::ofstream                            m_qpFile;    ///< qp slice
 #endif
diff --git a/source/Lib/CommonLib/CodingStructure.h b/source/Lib/CommonLib/CodingStructure.h
index 9be812b338e91e08f4b8a8e1bd5ec1d6be6a45a1..f4d27ba9c90a2de11b6460b4d67b4d9f016eae00 100644
--- a/source/Lib/CommonLib/CodingStructure.h
+++ b/source/Lib/CommonLib/CodingStructure.h
@@ -85,6 +85,9 @@ enum PictureType
 #endif
 #if NN_POST_FILTERING
     PIC_NN_POST_FILTERED,
+#endif
+#if JVET_AJ0124_QP_BLOCK
+   PIC_BLOCK_QP,
 #endif
   NUM_PIC_TYPES
 };
diff --git a/source/Lib/CommonLib/NNFilterUnified.cpp b/source/Lib/CommonLib/NNFilterUnified.cpp
index 358c7f8a19afc46dfa86a7cfd7ac76a2fe7d9e20..95d2720b6ee3f8476ffd9b52849bc580ae94381f 100644
--- a/source/Lib/CommonLib/NNFilterUnified.cpp
+++ b/source/Lib/CommonLib/NNFilterUnified.cpp
@@ -51,7 +51,11 @@ struct Input
     Pred,
     BS,
     QPbase,
+#if JVET_AJ0124_QP_BLOCK
+      QPBlock,
+#else
     QPSlice,
+#endif
     IPB,
 #if JVET_AH0096_CONTENT_ADAPTIVE_LOP
     MultiplierParam,
@@ -197,7 +201,11 @@ void NNFilterUnified::resizeInputs(int width, int height)
     m_inputs[Input::Pred].resize(sadl::Dimensions({ 1, sizeH / dct_sizeh, sizeW / dct_sizew, dct_ch + dct_ch / 2 }));
     m_inputs[Input::BS].resize(sadl::Dimensions({ 1, sizeH / dct_sizeh, sizeW / dct_sizew, dct_ch + dct_ch / 2 }));
     m_inputs[Input::QPbase].resize(sadl::Dimensions({ 1, sizeH / dct_sizeh, sizeW / dct_sizew, 1 }));
+#if JVET_AJ0124_QP_BLOCK
+    m_inputs[Input::QPBlock].resize(sadl::Dimensions({ 1, sizeH / dct_sizeh, sizeW / dct_sizew, 1 }));
+#else
     m_inputs[Input::QPSlice].resize(sadl::Dimensions({ 1, sizeH / dct_sizeh, sizeW / dct_sizew, 1 }));
+#endif
     m_inputs[Input::IPB].resize(sadl::Dimensions({ 1, sizeH / dct_sizeh, sizeW / dct_sizew, 1 * dct_ch }));
   }
   else
@@ -208,7 +216,11 @@ void NNFilterUnified::resizeInputs(int width, int height)
     m_inputs[Input::Pred].resize(sadl::Dimensions({ 1, sizeH, sizeW, 3 }));
     m_inputs[Input::BS].resize(sadl::Dimensions({ 1, sizeH, sizeW, 3 }));
     m_inputs[Input::QPbase].resize(sadl::Dimensions({ 1, sizeH, sizeW, 1 }));
+#if JVET_AJ0124_QP_BLOCK
+    m_inputs[Input::QPBlock].resize(sadl::Dimensions({ 1, sizeH, sizeW, 1 }));
+#else
     m_inputs[Input::QPSlice].resize(sadl::Dimensions({ 1, sizeH, sizeW, 1 }));
+#endif
     m_inputs[Input::IPB].resize(sadl::Dimensions({ 1, sizeH, sizeW, 1 }));
 #if JVET_AH0080_TRANS_INPUT
   }
@@ -634,7 +646,11 @@ void NNFilterUnified::filterBlock(Picture &pic, UnitArea inferArea, int extLeft,
   listInputData.push_back({ NN_INPUT_PRED, 1, inputScalePred, m_input_quantizer[1] - log2InputBitdepth, true, true });
   listInputData.push_back({ NN_INPUT_BS, 2, inputScalePred, m_input_quantizer[2] - log2InputBitdepth, true, true });
   listInputData.push_back({ NN_INPUT_GLOBAL_QP, 3, inputScaleQp, m_input_quantizer[3] - log2InputQpScale, true, false });
+#if JVET_AJ0124_QP_BLOCK
+  listInputData.push_back({ NN_INPUT_LOCAL_QP_BLOCK, 4, inputScaleQp, m_input_quantizer[4] - log2InputQpScale, true, false });
+#else
   listInputData.push_back({ NN_INPUT_LOCAL_QP, 4, inputScaleQp, m_input_quantizer[4] - log2InputQpScale, true, false });
+#endif
 #if NN_LF_FORCE_USE
   if (m_forceIntraType) {
     listInputData.push_back({ NN_INPUT_ZERO, 5, inputScaleIpb, m_input_quantizer[5] - log2InputIbpScale, true, false });
diff --git a/source/Lib/CommonLib/NNInference.h b/source/Lib/CommonLib/NNInference.h
index f80695d02d4f88ee542f23d40a2ddba0f6198411..f695825dea9206b191a408a0b485ac6fca2f229b 100644
--- a/source/Lib/CommonLib/NNInference.h
+++ b/source/Lib/CommonLib/NNInference.h
@@ -734,6 +734,11 @@ public:
         fillInputFromBufIpb<T>(pic, inferArea, inputs[inputData.index], pic->getBlockPredModeBuf(inferArea), inputData.luma, inputData.chroma, inputData.scale, inputData.shift);
         break;
 #endif
+#if JVET_AJ0124_QP_BLOCK
+      case NN_INPUT_LOCAL_QP_BLOCK:
+        fillInputFromBuf<T>(pic, inferArea, inputs[inputData.index], pic->getBlockQpBuf(inferArea), inputData.luma, inputData.chroma, inputData.scale, inputData.shift);
+        break;
+#endif
 #if NN_LF_FORCE_USE
       case NN_INPUT_ZERO:
         fillInputFromConstant<T>(pic, inferArea, inputs[inputData.index], 0, inputData.luma, inputData.scale, inputData.shift);
@@ -773,6 +778,11 @@ public:
       case NN_INPUT_LOCAL_QP:
         fillInputFromConstant<T>(pic, inferArea, inputs[inputData.index], localQp, inputData.luma, inputData.scale, inputData.shift);
         break;
+#if JVET_AJ0124_QP_BLOCK
+      case NN_INPUT_LOCAL_QP_BLOCK:
+          fillInputFromBuf<T>(pic, inferArea, inputs[inputData.index], pic->getBlockQpBuf(inferArea), inputData.luma, inputData.chroma, inputData.scale, inputData.shift, flip);
+          break;
+#endif
       case NN_INPUT_SLICE_TYPE:
         fillInputFromConstant<T>(pic, inferArea, inputs[inputData.index], sliceType, inputData.luma, inputData.scale, inputData.shift);
         break;
@@ -828,6 +838,11 @@ public:
       case NN_INPUT_LOCAL_QP:
         fillInputFromConstantTrans<T>(pic, inferArea, inputs[inputData.index], localQp, inputData.luma, inputData.scale, inputData.shift);
         break;
+#if JVET_AJ0124_QP_BLOCK
+      case NN_INPUT_LOCAL_QP_BLOCK:
+        fillInputFromBufTrans<T>(pic, inferArea, inputs[inputData.index], pic->getBlockQpBuf(inferArea), inputData.luma, inputData.chroma, inputData.scale, inputData.shift);
+        break;
+#endif
       case NN_INPUT_SLICE_TYPE:
         fillInputFromConstantTrans<T>(pic, inferArea, inputs[inputData.index], sliceType, inputData.luma, inputData.scale, inputData.shift);
         break;
diff --git a/source/Lib/CommonLib/Picture.cpp b/source/Lib/CommonLib/Picture.cpp
index 44bc871150b3af50189745c388b03e37f35728fd..ae20896967ebe4dd724541730e052aaa69ee3360 100644
--- a/source/Lib/CommonLib/Picture.cpp
+++ b/source/Lib/CommonLib/Picture.cpp
@@ -230,6 +230,9 @@ void Picture::create(const bool useWrapAround, const ChromaFormat &_chromaFormat
 #if NNVC_USE_REC_BEFORE_DBF
   M_BUFS( 0, PIC_REC_BEFORE_DBF ).create( _chromaFormat, a, _maxCUSize, margin, MEMORY_ALIGN_DEF_SIZE );
 #endif
+#if JVET_AJ0124_QP_BLOCK
+  M_BUFS( 0, PIC_BLOCK_QP ).create( _chromaFormat, a, _maxCUSize, margin, MEMORY_ALIGN_DEF_SIZE );
+#endif
 #if NNVC_USE_REC_AFTER_DBF
   M_BUFS( 0, PIC_REC_AFTER_DBF ).create( _chromaFormat, a, _maxCUSize, margin, MEMORY_ALIGN_DEF_SIZE );
 #endif
@@ -320,6 +323,14 @@ void Picture::createTempBuffers( const unsigned _maxCUSize )
       M_BUFS( jId, PIC_PREDICTION_CUSTOM          ).create( chromaFormat, aTemp,   _maxCUSize );
 #endif
 #endif
+#if JVET_AJ0124_QP_BLOCK
+    if (getBlockQpBuf().bufs.empty())
+#if JVET_AF0043_AF0205_PADDING
+          M_BUFS( jId, PIC_BLOCK_QP          ).create( chromaFormat, aTemp,   _maxCUSize, 8, MEMORY_ALIGN_DEF_SIZE);
+#else
+      M_BUFS( jId, PIC_BLOCK_QP          ).create( chromaFormat, aTemp,   _maxCUSize );
+#endif
+#endif
 #if ENABLE_SPLIT_PARALLELISM
     if (jId > 0)
     {
@@ -426,7 +437,14 @@ const CPelBuf     Picture::getRecBeforeDbfBuf(const CompArea &blk)  const { retu
        PelUnitBuf Picture::getRecBeforeDbfBuf(const UnitArea &unit)       { return getBuf(unit, PIC_REC_BEFORE_DBF); }
 const CPelUnitBuf Picture::getRecBeforeDbfBuf(const UnitArea &unit) const { return getBuf(unit, PIC_REC_BEFORE_DBF);}
 #endif
-
+#if JVET_AJ0124_QP_BLOCK
+      PelBuf     Picture::getBlockQpBuf(const ComponentID compID, bool /*wrap*/)       { return getBuf(compID,               PIC_BLOCK_QP); }
+      PelUnitBuf Picture::getBlockQpBuf(bool /*wrap*/)                                 { return M_BUFS(scheduler.getSplitPicId(), PIC_BLOCK_QP); }
+      PelBuf     Picture::getBlockQpBuf(const CompArea &blk)        { return getBuf(blk,  PIC_BLOCK_QP); }
+      const CPelBuf     Picture::getBlockQpBuf(const CompArea &blk)  const { return getBuf(blk,  PIC_BLOCK_QP); }
+      PelUnitBuf Picture::getBlockQpBuf(const UnitArea &unit)       { return getBuf(unit, PIC_BLOCK_QP); }
+      const CPelUnitBuf Picture::getBlockQpBuf(const UnitArea &unit) const { return getBuf(unit, PIC_BLOCK_QP);}
+#endif
 
 #if NNVC_USE_REC_AFTER_DBF
        PelBuf     Picture::getRecAfterDbfBuf(const ComponentID compID, bool /*wrap*/)       { return getBuf(compID,               PIC_REC_AFTER_DBF); }
@@ -679,6 +697,72 @@ void Picture::dumpPicCuAverage()
   }
 }
 #endif
+#if JVET_AJ0124_QP_BLOCK
+void Picture::dumpQpBlock()
+{
+
+  CodingStructure     &cs  = *(this->cs);
+  const PreCalcValues &pcv = *cs.pcv;
+  // Defining a scanning area that covers the picture
+//  const UnitArea PICarea(cs.area.chromaFormat, Area(0, 0, pcv.lumaWidth, pcv.lumaHeight));
+  // Traversing the CUs in the picture and filling a buffer with extracted info
+  for (int y = 0; y < pcv.heightInCtus; y++)
+   {
+        for (int x = 0; x < pcv.widthInCtus; x++)
+        {
+            const UnitArea ctuArea(pcv.chrFormat,
+                                   Area(x << pcv.maxCUWidthLog2, y << pcv.maxCUHeightLog2, pcv.maxCUWidth, pcv.maxCUWidth));
+            for (auto &currCU: cs.traverseCUs(CS::getArea(cs, ctuArea, CH_L), CH_L))
+            {
+                if (currCU.Y().valid()) {
+                    Pel to_fill = currCU.qp;   // sould also use chroma
+                    CompArea y_area(COMPONENT_Y, cs.area.chromaFormat, currCU.lx(), currCU.ly(), currCU.lwidth(), currCU.lheight(),
+                                    true);
+                    auto     target_buff = currCU.slice->getPic()->getBlockQpBuf(y_area);
+                    target_buff.fill(to_fill);
+                }
+                if (currCU.Cb().valid()) {
+                    const bool  useJQP = (abs(TU::getICTMode(*currCU.firstTU)) == 2);
+                    ComponentID ch[2]  = { COMPONENT_Cb, COMPONENT_Cr };
+                    for (auto compID: ch)
+                    {
+                        int chromaQpOffset = cs.pps->getQpOffset(useJQP ? JOINT_CbCr : compID);
+                        chromaQpOffset += currCU.slice->getSliceChromaQpDelta(useJQP ? JOINT_CbCr : compID);
+                        chromaQpOffset += cs.pps->getChromaQpOffsetListEntry(currCU.chromaQpAdj).u.offset[int(useJQP ? JOINT_CbCr : compID) - 1];
+                        int      qp      = currCU.qp;   // cs->getTU(currCU.lumaPos(),CHANNEL_TYPE_CHROMA).;
+                        int      qpc     = currCU.slice->getSPS()->getMappedChromaQpValue(compID, qp) + chromaQpOffset;
+                        Pel      to_fill = qpc;
+                        CompArea area(compID, cs.area.chromaFormat, currCU.chromaPos().x, currCU.chromaPos().y, currCU.chromaSize().width, currCU.chromaSize().height,false);
+                        auto     target_buff = currCU.slice->getPic()->getBlockQpBuf(area);
+                        target_buff.fill(to_fill);
+                    }
+                }
+            }
+            if (!pcv.ISingleTree) {
+                for (auto &currCU: cs.traverseCUs(CS::getArea(cs, ctuArea, CH_C), CH_C))
+                {
+                    if (currCU.Cb().valid()) {
+                        const bool  useJQP = (abs(TU::getICTMode(*currCU.firstTU)) == 2);
+                        ComponentID ch[2]  = { COMPONENT_Cb, COMPONENT_Cr };
+                        for (auto compID: ch)
+                        {
+                            int chromaQpOffset = cs.pps->getQpOffset(useJQP ? JOINT_CbCr : compID);
+                            chromaQpOffset += currCU.slice->getSliceChromaQpDelta(useJQP ? JOINT_CbCr : compID);
+                            chromaQpOffset += cs.pps->getChromaQpOffsetListEntry(currCU.chromaQpAdj).u.offset[int(useJQP ? JOINT_CbCr : compID) - 1];
+                            int      qp      = currCU.qp;   // cs->getTU(currCU.lumaPos(),CHANNEL_TYPE_CHROMA).;
+                            int      qpc     = currCU.slice->getSPS()->getMappedChromaQpValue(compID, qp) + chromaQpOffset;
+                            Pel      to_fill = qpc;
+                            CompArea area(compID, cs.area.chromaFormat, currCU.chromaPos().x, currCU.chromaPos().y, currCU.chromaSize().width, currCU.chromaSize().height,false);
+                            auto     target_buff = currCU.slice->getPic()->getBlockQpBuf(area);
+                            target_buff.fill(to_fill);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+#endif
 
 #if JVET_AC0089_NNVC_USE_BPM_INFO
 void Picture::dumpPicBpmInfo()
@@ -1570,8 +1654,14 @@ void Picture::restoreSubPicBorder(int POC, int subPicX0, int subPicY0, int subPi
 }
 
 #if JVET_AF0043_AF0205_PADDING
+#if JVET_AJ0124_QP_BLOCK
+void Picture::paddingPicBufBorder(const PictureType &picType, const int padSize, int value)
+{
+#else
 void Picture::paddingPicBufBorder(const PictureType &picType, const int &padSize)
 {
+  constexpr int value=0;
+#endif
   for(int comp=0; comp<getNumberValidComponents( cs->area.chromaFormat ); comp++)
   {
     ComponentID compID = ComponentID( comp );
@@ -1586,8 +1676,8 @@ void Picture::paddingPicBufBorder(const PictureType &picType, const int &padSize
     {
       for (int x = 0; x < xmargin; x++)
       {
-        pi[-xmargin + x] = 0;
-        pi[p.width + x]  = 0;
+        pi[-xmargin + x] = value;
+        pi[p.width + x]  = value;
       }
       pi += p.stride;
     }
@@ -1597,7 +1687,7 @@ void Picture::paddingPicBufBorder(const PictureType &picType, const int &padSize
     // pi is now the (-marginX, height), set zero all
     for (int x = 0; x < (p.width + (xmargin << 1)); x++)
     {
-      pi[x] = 0;
+      pi[x] = value;
     }
     for (int y = 0; y < ymargin - 1; y++ )
     {
@@ -1609,7 +1699,7 @@ void Picture::paddingPicBufBorder(const PictureType &picType, const int &padSize
     // pi is now (-marginX, -1), set zero all
     for (int x = 0; x < (p.width + (xmargin << 1)); x++)
     {
-      pi[x] = 0;
+      pi[x] = value;
     }
     for (int y = 0; y < ymargin - 1; y++ )
     {
diff --git a/source/Lib/CommonLib/Picture.h b/source/Lib/CommonLib/Picture.h
index 80c36e786318252a85c2ee1ee44297cdca5a6d95..ee2076e8e74d6ebd007e96d1982e5b92430fd06f 100644
--- a/source/Lib/CommonLib/Picture.h
+++ b/source/Lib/CommonLib/Picture.h
@@ -185,6 +185,15 @@ const   CPelBuf     getBsMapBuf(const CompArea &blk) const;
   PelUnitBuf        getRecBeforeDbfBuf(const UnitArea &unit);
   const CPelUnitBuf getRecBeforeDbfBuf(const UnitArea &unit) const;
 #endif
+#if JVET_AJ0124_QP_BLOCK
+  PelBuf            getBlockQpBuf(const ComponentID compID, bool wrap=false);
+  PelUnitBuf        getBlockQpBuf(bool wrap=false);
+  PelBuf            getBlockQpBuf(const CompArea &blk);
+  const CPelBuf     getBlockQpBuf(const CompArea &blk)  const;
+  PelUnitBuf        getBlockQpBuf(const UnitArea &unit);
+  const CPelUnitBuf getBlockQpBuf(const UnitArea &unit) const;
+  void              dumpQpBlock();
+#endif
 #if NNVC_USE_REC_AFTER_DBF
          PelBuf     getRecAfterDbfBuf(const ComponentID compID, bool wrap=false);
          PelUnitBuf getRecAfterDbfBuf(bool wrap=false);
@@ -229,11 +238,20 @@ const   CPelBuf     getBsMapBuf(const CompArea &blk) const;
   const CPelUnitBuf getBuf(const UnitArea &unit,     const PictureType &type) const;
 
 #if JVET_AF0043_AF0205_PADDING
+#if JVET_AJ0124_QP_BLOCK
+  void paddingPicBufBorder(const PictureType& type, const int padSize, int value);
+  void paddingBsMapBufBorder(const int padSize) { paddingPicBufBorder(PIC_BS_MAP, padSize,0); }
+  void paddingRecBeforeDbfBufBorder(const int padSize) { paddingPicBufBorder(PIC_REC_BEFORE_DBF, padSize,0); }
+  void paddingPredBufBorder(const int padSize) { paddingPicBufBorder(PIC_PREDICTION_CUSTOM, padSize,0); }
+  void paddingBPMBufBorder(const int padSize) { paddingPicBufBorder(PIC_BLOCK_PRED_MODE, padSize,0); }
+  void paddingBlockQPBufBorder(const int padSize,int value) { paddingPicBufBorder(PIC_BLOCK_QP, padSize,value); }
+#else
   void paddingPicBufBorder(const PictureType& type, const int& padSize);
-  void paddingBsMapBufBorder(const int& padSize) { paddingPicBufBorder(PIC_BS_MAP, padSize); };
-  void paddingRecBeforeDbfBufBorder(const int& padSize) { paddingPicBufBorder(PIC_REC_BEFORE_DBF, padSize); };
-  void paddingPredBufBorder(const int& padSize) { paddingPicBufBorder(PIC_PREDICTION_CUSTOM, padSize); };
-  void paddingBPMBufBorder(const int& padSize) { paddingPicBufBorder(PIC_BLOCK_PRED_MODE, padSize); };
+  void paddingBsMapBufBorder(const int& padSize) { paddingPicBufBorder(PIC_BS_MAP, padSize); }
+  void paddingRecBeforeDbfBufBorder(const int& padSize) { paddingPicBufBorder(PIC_REC_BEFORE_DBF, padSize); }
+  void paddingPredBufBorder(const int& padSize) { paddingPicBufBorder(PIC_PREDICTION_CUSTOM, padSize); }
+  void paddingBPMBufBorder(const int& padSize) { paddingPicBufBorder(PIC_BLOCK_PRED_MODE, padSize); }
+#endif
 #endif
 
   void extendPicBorder( const PPS *pps );
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index b2c7a036678cbbb521002641123d59da4176be15..a58b63558c6e0bdb0989eb1b983f9eee3d2a7f99 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -54,6 +54,8 @@
 
 
 
+
+
 #define NN_LF_UNIFIED        1
 #if NN_LF_UNIFIED
 using TypeSadlLFUnified=int16_t;
@@ -122,9 +124,10 @@ using TypeSadlIntra = float;
 #define NNVC_USE_PRED                                     1 // prediction
 #define NNVC_USE_BS                                       1 // BS of DBF
 #define NNVC_USE_PARTITION_AS_CU_AVERAGE                  0 // average on the CU
-#define NNVC_USE_QP                                       1 // QP slice
+#define NNVC_USE_QP                                       0 // QP slice
 #define NNVC_USE_SLICETYPE                                0 // slice type
 #define JVET_AC0089_NNVC_USE_BPM_INFO                     1 // JVET-AC0089: dump Block Prediction Mode
+#define JVET_AJ0124_QP_BLOCK                              1 // use QP per block for the QP input
 
 #define SR_DATA_DUMP                                      0 // only used for dumping training data for super resolution
 #if SR_DATA_DUMP
@@ -626,6 +629,9 @@ enum NNInputType
   NN_INPUT_REF_LIST_1,
 #if NN_LF_FORCE_USE
   NN_INPUT_ZERO,
+#endif
+#if JVET_AJ0124_QP_BLOCK
+  NN_INPUT_LOCAL_QP_BLOCK,
 #endif
   MAX_NUM_NN_INPUT
 };
diff --git a/source/Lib/DecoderLib/DecLib.cpp b/source/Lib/DecoderLib/DecLib.cpp
index 81a89718a6e6ae4cfa308ed91551c56b337345f4..3f0901a58e279c741b74fccaa1f75b654f61bd57 100644
--- a/source/Lib/DecoderLib/DecLib.cpp
+++ b/source/Lib/DecoderLib/DecLib.cpp
@@ -784,6 +784,10 @@ void DecLib::executeLoopFilters()
     m_pcPic->paddingRecBeforeDbfBufBorder(8);
     m_pcPic->paddingPredBufBorder(8);
     m_pcPic->paddingBPMBufBorder(8);
+#if JVET_AJ0124_QP_BLOCK
+    int qp=cs.slice->getSliceQp();
+    m_pcPic->paddingBlockQPBufBorder(8,qp);
+#endif
 #endif
     m_nnfilterUnified.setPicprms(&m_pcPic->m_picprm);
     m_nnfilterUnified.setSliceprms(m_pcPic->cs->slice->getNnlfUnifiedParameters());
@@ -868,7 +872,10 @@ void DecLib::executeLoopFilters()
         throw std::runtime_error("Model reconstruction failed");
       }
 
-      m_nnfilterUnified.init(recoModelFileName, cs.sps->getMaxPicWidthInLumaSamples(), cs.sps->getMaxPicHeightInLumaSamples(), cs.sps->getChromaFormatIdc(), cs.sps->getNnlfUnifiedMaxNumPrms(), true, true);
+      m_nnfilterUnified.init(recoModelFileName, cs.sps->getMaxPicWidthInLumaSamples(),
+                             cs.sps->getMaxPicHeightInLumaSamples(), cs.sps->getChromaFormatIdc(),
+                             cs.sps->getNnlfUnifiedMaxNumPrms(),
+                             true, true);
       m_newNnfu = false;
       m_nnfuIpUsesNnfu[picIntraPeriod] = true;
     }
@@ -1950,7 +1957,9 @@ void DecLib::xActivateParameterSets( const InputNALUnit nalu )
     if (pSlice->getPOC() == 0)
     {
 #endif
-      m_nnfilterUnified.init(m_nnModel[NnModel::LOP_UNIFIED_FILTER], sps->getMaxPicWidthInLumaSamples(), sps->getMaxPicHeightInLumaSamples(), sps->getChromaFormatIdc(), sps->getNnlfUnifiedMaxNumPrms());
+      m_nnfilterUnified.init(m_nnModel[NnModel::LOP_UNIFIED_FILTER], sps->getMaxPicWidthInLumaSamples(),
+                             sps->getMaxPicHeightInLumaSamples(), sps->getChromaFormatIdc(),
+                             sps->getNnlfUnifiedMaxNumPrms());
 #if JVET_AH0096_CONTENT_ADAPTIVE_LOP
     }
 #endif
diff --git a/source/Lib/DecoderLib/DecSlice.cpp b/source/Lib/DecoderLib/DecSlice.cpp
index e5d4dae6a3dc4f2c334e917b8468fe1ef83ece5d..bffc7f05e9092f07df46c7803df4bf4ff8c101bb 100644
--- a/source/Lib/DecoderLib/DecSlice.cpp
+++ b/source/Lib/DecoderLib/DecSlice.cpp
@@ -301,6 +301,9 @@ void DecSlice::decompressSlice( Slice* slice, InputBitstream* bitstream, int deb
 #endif
 #if JVET_AC0089_NNVC_USE_BPM_INFO
   pic->dumpPicBpmInfo();
+#endif
+#if JVET_AJ0124_QP_BLOCK
+  pic->dumpQpBlock();
 #endif
   // deallocate all created substreams, including internal buffers.
   for( auto substr: ppcSubstreams )
diff --git a/source/Lib/EncoderLib/EncGOP.cpp b/source/Lib/EncoderLib/EncGOP.cpp
index 32e1eb35c8da12caecb8435c976441c50705ad89..63692b97bb41981e6d7c6d02fa6154f639a40e3d 100644
--- a/source/Lib/EncoderLib/EncGOP.cpp
+++ b/source/Lib/EncoderLib/EncGOP.cpp
@@ -258,7 +258,10 @@ void EncGOP::init ( EncLib* pcEncLib )
 #if NN_LF_UNIFIED_ID
     m_nnfilterUnified.nnlfTransInput(m_pcCfg->getUseNnlfId()==NNLFUnifiedID::LOP3);
 #endif
-    m_nnfilterUnified.init(m_pcCfg->getNnModel(NnModel::LOP_UNIFIED_FILTER), m_pcCfg->getSourceWidth(), m_pcCfg->getSourceHeight(), m_pcCfg->getChromaFormatIdc(), m_pcCfg->getNnlfUnifiedMaxNumPrms());
+    m_nnfilterUnified.init(m_pcCfg->getNnModel(NnModel::LOP_UNIFIED_FILTER), m_pcCfg->getSourceWidth(),
+                           m_pcCfg->getSourceHeight(), m_pcCfg->getChromaFormatIdc(),
+                           m_pcCfg->getNnlfUnifiedMaxNumPrms()
+    );
   }
 #if NN_HOP_UNIFIED_TEMPORAL_FILTERING
   if (m_pcCfg->getUseNnlfHopTemporalFiltering())
@@ -3248,6 +3251,10 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic,
         pcPic->paddingRecBeforeDbfBufBorder(8);
         pcPic->paddingPredBufBorder(8);
         pcPic->paddingBPMBufBorder(8);
+#if JVET_AJ0124_QP_BLOCK
+        int qp=cs.slice->getSliceQp();
+        pcPic->paddingBlockQPBufBorder(8,qp);
+#endif
 #endif
 
         m_nnfilterUnified.initCabac( m_pcEncLib->getCABACEncoder(), m_pcEncLib->getCtxCache(), *pcSlice);
@@ -3260,7 +3267,10 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic,
 #if JVET_AH0096_CONTENT_ADAPTIVE_LOP
           if (m_pcCfg->getUseNnfu() && m_pcCfg->getNumNnfus() > 0 && m_newNnfu)
           {
-            m_nnfilterUnified.init(m_pcCfg->getNnfuModelFileNames().front(), m_pcCfg->getSourceWidth(), m_pcCfg->getSourceHeight(), m_pcCfg->getChromaFormatIdc(), m_pcCfg->getNnlfUnifiedMaxNumPrms(), true, true);
+            m_nnfilterUnified.init(m_pcCfg->getNnfuModelFileNames().front(), m_pcCfg->getSourceWidth(),
+                                   m_pcCfg->getSourceHeight(), m_pcCfg->getChromaFormatIdc(),
+                                   m_pcCfg->getNnlfUnifiedMaxNumPrms(),
+                                   true, true);
             m_pcCfg->getNnfuModelFileNames().pop_front();
           }
 #endif
diff --git a/source/Lib/EncoderLib/EncSlice.cpp b/source/Lib/EncoderLib/EncSlice.cpp
index 61c9fd542de6b378da5505daa30a0d30de4f96dc..41c2de0736d1d8e5120960356f98a8d36becaee9 100644
--- a/source/Lib/EncoderLib/EncSlice.cpp
+++ b/source/Lib/EncoderLib/EncSlice.cpp
@@ -2143,6 +2143,11 @@ void EncSlice::encodeCtus( Picture* pcPic, const bool bCompressEntireSlice, cons
 #if JVET_AC0089_NNVC_USE_BPM_INFO
   pcPic->dumpPicBpmInfo();
 #endif
+
+#if JVET_AJ0124_QP_BLOCK
+  pcPic->dumpQpBlock();
+#endif
+
   // this is wpp exclusive section
 
 //  m_uiPicTotalBits += actualBits;
diff --git a/training/data_loader/data_loader.py b/training/data_loader/data_loader.py
index 93e1f6aaaa30f9a2ca1f43d962ccd4da3e31c5d0..f109b47e55c2fd514c18e0c887e8d66344e8f63c 100644
--- a/training/data_loader/data_loader.py
+++ b/training/data_loader/data_loader.py
@@ -249,7 +249,12 @@ class DataLoader:
                 self.components.append("partition_cu_average_Y")
                 self.components.append("partition_cu_average_U")
                 self.components.append("partition_cu_average_V")
-            if "suffix_qp" in dcontent:
+            if "suffix_qp_block" in dcontent:
+                self.components.append("qp_block_Y")
+                self.components.append("qp_block_U")
+                self.components.append("qp_block_V")
+                self.suffix["qp_block"] = dcontent["suffix_qp_block"]
+            elif "suffix_qp" in dcontent:
                 self.components.append("qp_slice")
                 self.suffix["qp_slice"] = dcontent["suffix_qp"]
             self.components.append("qp_base")  # always here
@@ -364,6 +369,7 @@ class DataLoader:
                 or "pred" in c
                 or "partition_cu_average" in c
                 or "bs" in c
+                or "qp_block" in c
             ):
                 fn = os.path.join(d["dirname"], d["basename"] + self.suffix[c[:-2]])
                 nbb = 2  # 16 bits data
@@ -380,6 +386,8 @@ class DataLoader:
                     norm = self.normalizer_bs
                 elif "partition_cu_average" in c:
                     norm = self.normalizer_cu_average
+                elif "qp_block" in c:
+                    norm = self.normalizer_qp
 
                 v = readData(psize, bsize, norm, fn, off, nbb, "uint16", h, w, x0, y0)
 
@@ -504,6 +512,7 @@ class DataLoader:
                 or "partition_cu_average" in c
                 or "bs" in c
                 or "bpm" in c
+                or "qp_block" in c
             ):
                 fn = os.path.join(d["dirname"], d["basename"] + self.suffix[c[:-2]])
                 nbb = 2  # 16 bits data