From 0921297ae360c58b44dbffa83705a9b60908eb53 Mon Sep 17 00:00:00 2001
From: Jiahao Li <lijiahao.cn@bytedance.com>
Date: Fri, 19 Apr 2019 06:29:26 +0200
Subject: [PATCH] JVET-N0247:  Improvement of Hash Motion Estimation

---
 source/Lib/CommonLib/Hash.cpp         |  84 ++++++-
 source/Lib/CommonLib/Hash.h           |  17 +-
 source/Lib/CommonLib/Picture.cpp      |   8 +-
 source/Lib/CommonLib/Slice.cpp        |   3 +
 source/Lib/CommonLib/TypeDef.h        |   2 +
 source/Lib/EncoderLib/EncCu.cpp       |   6 +-
 source/Lib/EncoderLib/EncModeCtrl.cpp |   5 +
 source/Lib/EncoderLib/InterSearch.cpp | 342 +++++++++++++++++++++++++-
 source/Lib/EncoderLib/InterSearch.h   |   9 +
 9 files changed, 468 insertions(+), 8 deletions(-)

diff --git a/source/Lib/CommonLib/Hash.cpp b/source/Lib/CommonLib/Hash.cpp
index 2301f6845..86806b62c 100644
--- a/source/Lib/CommonLib/Hash.cpp
+++ b/source/Lib/CommonLib/Hash.cpp
@@ -109,6 +109,12 @@ TComHash::TComHash()
 {
   m_lookupTable = NULL;
   tableHasContent = false;
+#if JVET_N0247_HASH_IMPROVE
+  for (int i = 0; i < 5; i++)
+  {
+    hashPic[i] = NULL;
+  }
+#endif
 }
 
 TComHash::~TComHash()
@@ -120,7 +126,25 @@ TComHash::~TComHash()
     m_lookupTable = NULL;
   }
 }
-
+#if JVET_N0247_HASH_IMPROVE
+void TComHash::create(int picWidth, int picHeight)
+{
+  if (m_lookupTable)
+  {
+    clearAll();
+  }
+  if (!hashPic[0])
+  {
+    for (int k = 0; k < 5; k++)
+    {
+      hashPic[k] = new uint16_t[picWidth*picHeight];
+    }
+  }
+  if (m_lookupTable)
+  {
+    return;
+  }
+#else
 void TComHash::create()
 {
   if (m_lookupTable != NULL)
@@ -128,6 +152,7 @@ void TComHash::create()
     clearAll();
     return;
   }
+#endif
   int maxAddr = 1 << (m_CRCBits + m_blockSizeBits);
   m_lookupTable = new std::vector<BlockHash>*[maxAddr];
   memset(m_lookupTable, 0, sizeof(std::vector<BlockHash>*) * maxAddr);
@@ -136,6 +161,16 @@ void TComHash::create()
 
 void TComHash::clearAll()
 {
+#if JVET_N0247_HASH_IMPROVE
+  if (hashPic[0])
+  {
+    for (int k = 0; k < 5; k++)
+    {
+      delete[] hashPic[k];
+      hashPic[k] = NULL;
+    }
+  }
+#endif
   tableHasContent = false;
   if (m_lookupTable == NULL)
   {
@@ -251,6 +286,7 @@ void TComHash::generateBlock2x2HashValue(const PelUnitBuf &curPicBuf, int picWid
 
   delete[] p;
 }
+#if !JVET_N0247_HASH_IMPROVE
 void TComHash::generateRectangleHashValue(int picWidth, int picHeight, int width, int height, uint32_t* srcPicBlockHash[2], uint32_t* dstPicBlockHash[2], bool* srcPicBlockSameInfo[3], bool* dstPicBlockSameInfo[3])
 {
   //at present, only support 1:2(2:1) retangle hash value
@@ -328,6 +364,7 @@ void TComHash::generateRectangleHashValue(int picWidth, int picHeight, int width
 
   delete[] p;
 }
+#endif
 
 void TComHash::generateBlockHashValue(int picWidth, int picHeight, int width, int height, uint32_t* srcPicBlockHash[2], uint32_t* dstPicBlockHash[2], bool* srcPicBlockSameInfo[3], bool* dstPicBlockSameInfo[3])
 {
@@ -372,15 +409,21 @@ void TComHash::generateBlockHashValue(int picWidth, int picHeight, int width, in
 
   if (width >= 4)
   {
+#if !JVET_N0247_HASH_IMPROVE
     int widthMinus1 = width - 1;
     int heightMinus1 = height - 1;
+#endif
     pos = 0;
 
     for (int yPos = 0; yPos < yEnd; yPos++)
     {
       for (int xPos = 0; xPos < xEnd; xPos++)
       {
+#if JVET_N0247_HASH_IMPROVE
+        dstPicBlockSameInfo[2][pos] = (!dstPicBlockSameInfo[0][pos] && !dstPicBlockSameInfo[1][pos]);
+#else
         dstPicBlockSameInfo[2][pos] = (!dstPicBlockSameInfo[0][pos] && !dstPicBlockSameInfo[1][pos]) || (((xPos & widthMinus1) == 0) && ((yPos & heightMinus1) == 0));
+#endif
         pos++;
       }
       pos += width - 1;
@@ -404,12 +447,18 @@ void TComHash::addToHashMapByRowWithPrecalData(uint32_t* picHash[2], bool* picIs
   addValue <<= m_CRCBits;
   int crcMask = 1 << m_CRCBits;
   crcMask -= 1;
+#if JVET_N0247_HASH_IMPROVE
+  int blockIdx = g_aucLog2[width] - 2;
+#endif
 
   for (int xPos = 0; xPos < xEnd; xPos++)
   {
     for (int yPos = 0; yPos < yEnd; yPos++)
     {
       int pos = yPos * picWidth + xPos;
+#if JVET_N0247_HASH_IMPROVE
+      hashPic[blockIdx][pos] = (uint16_t)(srcHash[1][pos] & crcMask);
+#endif
       //valid data
       if (srcIsAdded[pos])
       {
@@ -557,7 +606,38 @@ bool TComHash::isBlock2x2ColSameValue(unsigned char* p, bool includeAllComponent
 
   return true;
 }
+#if JVET_N0247_HASH_IMPROVE
+bool TComHash::isHorizontalPerfectLuma(const Pel* srcPel, int stride, int width, int height)
+{
+  for (int i = 0; i < height; i++)
+  {
+    for (int j = 1; j < width; j++)
+    {
+      if (srcPel[j] != srcPel[0])
+      {
+        return false;
+      }
+    }
+    srcPel += stride;
+  }
+  return true;
+}
 
+bool TComHash::isVerticalPerfectLuma(const Pel* srcPel, int stride, int width, int height)
+{
+  for (int i = 0; i < width; i++)
+  {
+    for (int j = 1; j < height; j++)
+    {
+      if (srcPel[j*stride + i] != srcPel[i])
+      {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+#endif
 bool TComHash::getBlockHashValue(const PelUnitBuf &curPicBuf, int width, int height, int xStart, int yStart, const BitDepths bitDepths, uint32_t& hashValue1, uint32_t& hashValue2)
 {
   int addValue = m_blockSizeToIndex[width][height];
@@ -712,8 +792,10 @@ void TComHash::initBlockSizeToIndex()
   m_blockSizeToIndex[32][32] = 2;
   m_blockSizeToIndex[64][64] = 3;
   m_blockSizeToIndex[4][4] = 4;
+#if !JVET_N0247_HASH_IMPROVE
   m_blockSizeToIndex[4][8] = 5;
   m_blockSizeToIndex[8][4] = 6;
+#endif
 }
 
 uint32_t TComHash::getCRCValue1(unsigned char* p, int length)
diff --git a/source/Lib/CommonLib/Hash.h b/source/Lib/CommonLib/Hash.h
index d69787cfc..b86322d90 100644
--- a/source/Lib/CommonLib/Hash.h
+++ b/source/Lib/CommonLib/Hash.h
@@ -91,7 +91,11 @@ struct TComHash
 public:
   TComHash();
   ~TComHash();
+#if JVET_N0247_HASH_IMPROVE
+  void create(int picWidth, int picHeight);
+#else
   void create();
+#endif
   void clearAll();
   void addToTable(uint32_t hashValue, const BlockHash& blockHash);
   int count(uint32_t hashValue);
@@ -102,11 +106,15 @@ public:
 
   void generateBlock2x2HashValue(const PelUnitBuf &curPicBuf, int picWidth, int picHeight, const BitDepths bitDepths, uint32_t* picBlockHash[2], bool* picBlockSameInfo[3]);
   void generateBlockHashValue(int picWidth, int picHeight, int width, int height, uint32_t* srcPicBlockHash[2], uint32_t* dstPicBlockHash[2], bool* srcPicBlockSameInfo[3], bool* dstPicBlockSameInfo[3]);
+#if !JVET_N0247_HASH_IMPROVE
   void generateRectangleHashValue(int picWidth, int picHeight, int width, int height, uint32_t* srcPicBlockHash[2], uint32_t* dstPicBlockHash[2], bool* srcPicBlockSameInfo[3], bool* dstPicBlockSameInfo[3]);
+#endif
   void addToHashMapByRowWithPrecalData(uint32_t* srcHash[2], bool* srcIsSame, int picWidth, int picHeight, int width, int height);
   bool isInitial() { return tableHasContent; }
   void setInitial() { tableHasContent = true; }
-
+#if JVET_N0247_HASH_IMPROVE
+  uint16_t* getHashPic(int baseSize) const { return hashPic[g_aucLog2[baseSize] - 2]; }
+#endif
 
 
 public:
@@ -117,10 +125,17 @@ public:
   static bool isBlock2x2ColSameValue(unsigned char* p, bool includeAllComponent = true);
   static bool getBlockHashValue(const PelUnitBuf &curPicBuf, int width, int height, int xStart, int yStart, const BitDepths bitDepths, uint32_t& hashValue1, uint32_t& hashValue2);
   static void initBlockSizeToIndex();
+#if JVET_N0247_HASH_IMPROVE
+  static bool isHorizontalPerfectLuma(const Pel* srcPel, int stride, int width, int height);
+  static bool isVerticalPerfectLuma(const Pel* srcPel, int stride, int width, int height);
+#endif
 
 private:
   std::vector<BlockHash>** m_lookupTable;
   bool tableHasContent;
+#if JVET_N0247_HASH_IMPROVE
+  uint16_t* hashPic[5];//4x4 ~ 64x64
+#endif
 
 private:
   static const int m_CRCBits = 16;
diff --git a/source/Lib/CommonLib/Picture.cpp b/source/Lib/CommonLib/Picture.cpp
index 62baca75a..f583805bf 100644
--- a/source/Lib/CommonLib/Picture.cpp
+++ b/source/Lib/CommonLib/Picture.cpp
@@ -1188,17 +1188,21 @@ void Picture::addPictureToHashMapForInter()
       bIsBlockSame[i][j] = new bool[picWidth*picHeight];
     }
   }
-
+#if JVET_N0247_HASH_IMPROVE
+  m_hashMap.create(picWidth, picHeight);
+#else
   m_hashMap.create();
+#endif
   m_hashMap.generateBlock2x2HashValue(getOrigBuf(), picWidth, picHeight, slices[0]->getSPS()->getBitDepths(), blockHashValues[0], bIsBlockSame[0]);//2x2
   m_hashMap.generateBlockHashValue(picWidth, picHeight, 4, 4, blockHashValues[0], blockHashValues[1], bIsBlockSame[0], bIsBlockSame[1]);//4x4
   m_hashMap.addToHashMapByRowWithPrecalData(blockHashValues[1], bIsBlockSame[1][2], picWidth, picHeight, 4, 4);
-
+#if !JVET_N0247_HASH_IMPROVE
   m_hashMap.generateRectangleHashValue(picWidth, picHeight, 8, 4, blockHashValues[1], blockHashValues[0], bIsBlockSame[1], bIsBlockSame[0]);//8x4
   m_hashMap.addToHashMapByRowWithPrecalData(blockHashValues[0], bIsBlockSame[0][2], picWidth, picHeight, 8, 4);
 
   m_hashMap.generateRectangleHashValue(picWidth, picHeight, 4, 8, blockHashValues[1], blockHashValues[0], bIsBlockSame[1], bIsBlockSame[0]);//4x8
   m_hashMap.addToHashMapByRowWithPrecalData(blockHashValues[0], bIsBlockSame[0][2], picWidth, picHeight, 4, 8);
+#endif
 
   m_hashMap.generateBlockHashValue(picWidth, picHeight, 8, 8, blockHashValues[1], blockHashValues[0], bIsBlockSame[1], bIsBlockSame[0]);//8x8
   m_hashMap.addToHashMapByRowWithPrecalData(blockHashValues[0], bIsBlockSame[0][2], picWidth, picHeight, 8, 8);
diff --git a/source/Lib/CommonLib/Slice.cpp b/source/Lib/CommonLib/Slice.cpp
index 2e3d9d158..927383a31 100644
--- a/source/Lib/CommonLib/Slice.cpp
+++ b/source/Lib/CommonLib/Slice.cpp
@@ -1164,6 +1164,9 @@ void Slice::applyReferencePictureSet( PicList& rcListPic, const ReferencePicture
 
     if( ! pcPic->referenced)
     {
+#if JVET_N0247_HASH_IMPROVE
+      pcPic->getHashMap()->clearAll();
+#endif
       continue;
     }
 
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index c00888c88..d8b6bbb45 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -133,6 +133,8 @@
 
 #define JVET_N0325_BDOF                                   1  // unified right-shifts for BDOF derivation
 
+#define JVET_N0247_HASH_IMPROVE                           1  // Improve hash motion estimation
+
 #define JVET_N0449_MMVD_SIMP                              1 // Configurable number of mmvd distance entries used
 
 #define JVET_N0363_INTRA_COST_MOD                         1 // Modified cost criterion for intra encoder search
diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp
index 69ca8817e..bab69d051 100644
--- a/source/Lib/EncoderLib/EncCu.cpp
+++ b/source/Lib/EncoderLib/EncCu.cpp
@@ -1662,8 +1662,12 @@ void EncCu::xCheckRDCostHashInter( CodingStructure *&tempCS, CodingStructure *&b
     }
   }
   tempCS->initStructData(encTestMode.qp, encTestMode.lossless);
-
+#if JVET_N0247_HASH_IMPROVE
+  int minSize = min(cu.lwidth(), cu.lheight());
+  if (minSize < 64)
+#else
   if (cu.lwidth() != 64)
+#endif
   {
     isPerfectMatch = false;
   }
diff --git a/source/Lib/EncoderLib/EncModeCtrl.cpp b/source/Lib/EncoderLib/EncModeCtrl.cpp
index 505cd72b1..92bb4f3b5 100644
--- a/source/Lib/EncoderLib/EncModeCtrl.cpp
+++ b/source/Lib/EncoderLib/EncModeCtrl.cpp
@@ -1396,7 +1396,12 @@ void EncModeCtrlMTnoRQT::initCULevel( Partitioner &partitioner, const CodingStru
       }
       if (m_pcEncCfg->getUseHashME())
       {
+#if JVET_N0247_HASH_IMPROVE
+        int minSize = min(cs.area.lwidth(), cs.area.lheight());
+        if (minSize < 128 && minSize >= 4)
+#else
         if ((cs.area.lwidth() == cs.area.lheight() && cs.area.lwidth() <= 64 && cs.area.lwidth() >= 4) || (cs.area.lwidth() == 4 && cs.area.lheight() == 8) || (cs.area.lwidth() == 8 && cs.area.lheight() == 4))
+#endif
         {
           m_ComprCUCtxList.back().testModes.push_back({ ETM_HASH_INTER, ETO_STANDARD, qp, lossless });
         }
diff --git a/source/Lib/EncoderLib/InterSearch.cpp b/source/Lib/EncoderLib/InterSearch.cpp
index 9bc95e794..a954f59f1 100644
--- a/source/Lib/EncoderLib/InterSearch.cpp
+++ b/source/Lib/EncoderLib/InterSearch.cpp
@@ -1799,7 +1799,301 @@ void InterSearch::selectMatchesInter(const MapIterator& itBegin, int count, std:
     }
   }
 }
+#if JVET_N0247_HASH_IMPROVE
+void InterSearch::selectRectangleMatchesInter(const MapIterator& itBegin, int count, std::list<BlockHash>& listBlockHash, const BlockHash& currBlockHash, int width, int height, int idxNonSimple, unsigned int* &hashValues, int baseNum, int picWidth, int picHeight, bool isHorizontal, uint16_t* curHashPic)
+{
+  const int maxReturnNumber = 5;
+  int baseSize = min(width, height);
+  unsigned int crcMask = 1 << 16;
+  crcMask -= 1;
+
+  listBlockHash.clear();
+  std::list<int> listCost;
+  listCost.clear();
+
+  MapIterator it = itBegin;
+
+  for (int i = 0; i < count; i++, it++)
+  {
+    if ((*it).hashValue2 != currBlockHash.hashValue2)
+    {
+      continue;
+    }
+    int xRef = (*it).x;
+    int yRef = (*it).y;
+    if (isHorizontal)
+    {
+      xRef -= idxNonSimple * baseSize;
+    }
+    else
+    {
+      yRef -= idxNonSimple * baseSize;
+    }
+    if (xRef < 0 || yRef < 0 || xRef + width >= picWidth || yRef + height >= picHeight)
+    {
+      continue;
+    }
+    //check Other baseSize hash values
+    uint16_t* refHashValue = curHashPic + yRef * picWidth + xRef;
+    bool isSame = true;
+
+    for (int k = 0; k < baseNum; k++)
+    {
+      if ((*refHashValue) != (uint16_t)(hashValues[k] & crcMask))
+      {
+        isSame = false;
+        break;
+      }
+      refHashValue += (isHorizontal ? baseSize : (baseSize*picWidth));
+    }
+    if (!isSame)
+    {
+      continue;
+    }
+
+    int currCost = RdCost::xGetExpGolombNumberOfBits(xRef - currBlockHash.x) +
+      RdCost::xGetExpGolombNumberOfBits(yRef - currBlockHash.y);
+
+    BlockHash refBlockHash;
+    refBlockHash.hashValue2 = (*it).hashValue2;
+    refBlockHash.x = xRef;
+    refBlockHash.y = yRef;
+
+    if (listBlockHash.size() < maxReturnNumber)
+    {
+      addToSortList(listBlockHash, listCost, currCost, refBlockHash);
+    }
+    else if (!listCost.empty() && currCost < listCost.back())
+    {
+      listCost.pop_back();
+      listBlockHash.pop_back();
+      addToSortList(listBlockHash, listCost, currCost, refBlockHash);
+    }
+  }
+}
+
+bool InterSearch::xRectHashInterEstimation(PredictionUnit& pu, RefPicList& bestRefPicList, int& bestRefIndex, Mv& bestMv, Mv& bestMvd, int& bestMVPIndex, bool& isPerfectMatch)
+{
+  int width = pu.cu->lumaSize().width;
+  int height = pu.cu->lumaSize().height;
+
+  int baseSize = min(width, height);
+  bool isHorizontal = true;;
+  int baseNum = 0;
+  if (height < width)
+  {
+    isHorizontal = true;
+    baseNum = 1 << (g_aucLog2[width] - g_aucLog2[height]);
+  }
+  else
+  {
+    isHorizontal = false;
+    baseNum = 1 << (g_aucLog2[height] - g_aucLog2[width]);
+  }
+
+  int xPos = pu.cu->lumaPos().x;
+  int yPos = pu.cu->lumaPos().y;
+  const int currStride = pu.cs->picture->getOrigBuf().get(COMPONENT_Y).stride;
+  const Pel* curPel = pu.cs->picture->getOrigBuf().get(COMPONENT_Y).buf + yPos * currStride + xPos;
+  int picWidth = pu.cu->slice->getSPS()->getPicWidthInLumaSamples();
+  int picHeight = pu.cu->slice->getSPS()->getPicHeightInLumaSamples();
+
+  int xBase = xPos;
+  int yBase = yPos;
+  const Pel* basePel = curPel;
+  int idxNonSimple = -1;
+  unsigned int* hashValue1s = new unsigned int[baseNum];
+  unsigned int* hashValue2s = new unsigned int[baseNum];
+
+  for (int k = 0; k < baseNum; k++)
+  {
+    if (isHorizontal)
+    {
+      xBase = xPos + k * baseSize;
+      basePel = curPel + k * baseSize;
+    }
+    else
+    {
+      yBase = yPos + k * baseSize;
+      basePel = curPel + k * baseSize * currStride;
+    }
+
+    if (idxNonSimple == -1 && !TComHash::isHorizontalPerfectLuma(basePel, currStride, baseSize, baseSize) && !TComHash::isVerticalPerfectLuma(basePel, currStride, baseSize, baseSize))
+    {
+      idxNonSimple = k;
+    }
+    TComHash::getBlockHashValue((pu.cs->picture->getOrigBuf()), baseSize, baseSize, xBase, yBase, pu.cu->slice->getSPS()->getBitDepths(), hashValue1s[k], hashValue2s[k]);
+  }
+  if (idxNonSimple == -1)
+  {
+    idxNonSimple = 0;
+  }
+
+  Distortion bestCost = UINT64_MAX;
+
+  BlockHash currBlockHash;
+  currBlockHash.x = xPos;//still use the first base block location
+  currBlockHash.y = yPos;
+
+  currBlockHash.hashValue2 = hashValue2s[idxNonSimple];
+
+  m_pcRdCost->setDistParam(m_cDistParam, pu.cs->getOrgBuf(pu).Y(), 0, 0, m_lumaClpRng.bd, COMPONENT_Y, 0, 1, false);
+
+  int imvBest = 0;
+  int numPredDir = pu.cu->slice->isInterP() ? 1 : 2;
+  for (int refList = 0; refList < numPredDir; refList++)
+  {
+    RefPicList eRefPicList = (refList == 0) ? REF_PIC_LIST_0 : REF_PIC_LIST_1;
+    int refPicNumber = pu.cu->slice->getNumRefIdx(eRefPicList);
+
+    for (int refIdx = 0; refIdx < refPicNumber; refIdx++)
+    {
+      int bitsOnRefIdx = 1;
+      if (refPicNumber > 1)
+      {
+        bitsOnRefIdx += refIdx + 1;
+        if (refIdx == refPicNumber - 1)
+        {
+          bitsOnRefIdx--;
+        }
+      }
+      m_numHashMVStoreds[eRefPicList][refIdx] = 0;
+
+      if (refList == 0 || pu.cu->slice->getList1IdxToList0Idx(refIdx) < 0)
+      {
+        int count = static_cast<int>(pu.cu->slice->getRefPic(eRefPicList, refIdx)->getHashMap()->count(hashValue1s[idxNonSimple]));
+        if (count == 0)
+        {
+          continue;
+        }
+
+        list<BlockHash> listBlockHash;
+        selectRectangleMatchesInter(pu.cu->slice->getRefPic(eRefPicList, refIdx)->getHashMap()->getFirstIterator(hashValue1s[idxNonSimple]), count, listBlockHash, currBlockHash, width, height, idxNonSimple, hashValue2s, baseNum, picWidth, picHeight, isHorizontal, pu.cu->slice->getRefPic(eRefPicList, refIdx)->getHashMap()->getHashPic(baseSize));
+
+        m_numHashMVStoreds[eRefPicList][refIdx] = int(listBlockHash.size());
+        if (listBlockHash.empty())
+        {
+          continue;
+        }
+        AMVPInfo currAMVPInfoPel;
+        AMVPInfo currAMVPInfo4Pel;
+        AMVPInfo currAMVPInfoQPel;
+        pu.cu->imv = 2;
+        PU::fillMvpCand(pu, eRefPicList, refIdx, currAMVPInfo4Pel);
+        pu.cu->imv = 1;
+        PU::fillMvpCand(pu, eRefPicList, refIdx, currAMVPInfoPel);
+        pu.cu->imv = 0;
+        PU::fillMvpCand(pu, eRefPicList, refIdx, currAMVPInfoQPel);
+
+        const Pel* refBufStart = pu.cu->slice->getRefPic(eRefPicList, refIdx)->getRecoBuf().get(COMPONENT_Y).buf;
+        const int refStride = pu.cu->slice->getRefPic(eRefPicList, refIdx)->getRecoBuf().get(COMPONENT_Y).stride;
+        m_cDistParam.cur.stride = refStride;
 
+        m_pcRdCost->selectMotionLambda(pu.cu->transQuantBypass);
+        m_pcRdCost->setCostScale(0);
+
+        list<BlockHash>::iterator it;
+        int countMV = 0;
+        for (it = listBlockHash.begin(); it != listBlockHash.end(); ++it)
+        {
+          int curMVPIdx = 0;
+          unsigned int curMVPbits = MAX_UINT;
+          Mv cMv((*it).x - currBlockHash.x, (*it).y - currBlockHash.y);
+          m_hashMVStoreds[eRefPicList][refIdx][countMV++] = cMv;
+          cMv.changePrecision(MV_PRECISION_INT, MV_PRECISION_QUARTER);
+
+          for (int mvpIdxTemp = 0; mvpIdxTemp < 2; mvpIdxTemp++)
+          {
+            Mv cMvPredPel = currAMVPInfoQPel.mvCand[mvpIdxTemp];
+            m_pcRdCost->setPredictor(cMvPredPel);
+
+            unsigned int tempMVPbits = m_pcRdCost->getBitsOfVectorWithPredictor(cMv.getHor(), cMv.getVer(), 0);
+
+            if (tempMVPbits < curMVPbits)
+            {
+              curMVPbits = tempMVPbits;
+              curMVPIdx = mvpIdxTemp;
+              pu.cu->imv = 0;
+            }
+
+            if (pu.cu->slice->getSPS()->getAMVREnabledFlag())
+            {
+              unsigned int bitsMVP1Pel = MAX_UINT;
+              Mv mvPred1Pel = currAMVPInfoPel.mvCand[mvpIdxTemp];
+              m_pcRdCost->setPredictor(mvPred1Pel);
+              bitsMVP1Pel = m_pcRdCost->getBitsOfVectorWithPredictor(cMv.getHor(), cMv.getVer(), 2);
+              if (bitsMVP1Pel < curMVPbits)
+              {
+                curMVPbits = bitsMVP1Pel;
+                curMVPIdx = mvpIdxTemp;
+                pu.cu->imv = 1;
+              }
+
+              if ((cMv.getHor() % 16 == 0) && (cMv.getVer() % 16 == 0))
+              {
+                unsigned int bitsMVP4Pel = MAX_UINT;
+                Mv mvPred4Pel = currAMVPInfo4Pel.mvCand[mvpIdxTemp];
+                m_pcRdCost->setPredictor(mvPred4Pel);
+                bitsMVP4Pel = m_pcRdCost->getBitsOfVectorWithPredictor(cMv.getHor(), cMv.getVer(), 4);
+                if (bitsMVP4Pel < curMVPbits)
+                {
+                  curMVPbits = bitsMVP4Pel;
+                  curMVPIdx = mvpIdxTemp;
+                  pu.cu->imv = 2;
+                }
+              }
+            }
+          }
+          curMVPbits += bitsOnRefIdx;
+
+          m_cDistParam.cur.buf = refBufStart + (*it).y*refStride + (*it).x;
+          Distortion currSad = m_cDistParam.distFunc(m_cDistParam);
+          Distortion currCost = currSad + m_pcRdCost->getCost(curMVPbits);
+
+          if (!isPerfectMatch)
+          {
+            if (pu.cu->slice->getRefPic(eRefPicList, refIdx)->slices[0]->getSliceQp() <= pu.cu->slice->getSliceQp())
+            {
+              isPerfectMatch = true;
+            }
+          }
+
+          if (currCost < bestCost)
+          {
+            bestCost = currCost;
+            bestRefPicList = eRefPicList;
+            bestRefIndex = refIdx;
+            bestMv = cMv;
+            bestMVPIndex = curMVPIdx;
+            imvBest = pu.cu->imv;
+            if (pu.cu->imv == 2)
+            {
+              bestMvd = cMv - currAMVPInfo4Pel.mvCand[curMVPIdx];
+            }
+            else if (pu.cu->imv == 1)
+            {
+              bestMvd = cMv - currAMVPInfoPel.mvCand[curMVPIdx];
+            }
+            else
+            {
+              bestMvd = cMv - currAMVPInfoQPel.mvCand[curMVPIdx];
+            }
+          }
+        }
+      }
+    }
+  }
+  delete[] hashValue1s;
+  delete[] hashValue2s;
+  pu.cu->imv = imvBest;
+  if (bestMvd == Mv(0, 0))
+  {
+    pu.cu->imv = 0;
+    return false;
+  }
+  return (bestCost < MAX_INT);
+}
+#else
 int InterSearch::xHashInterPredME(const PredictionUnit& pu, RefPicList currRefPicList, int currRefPicIndex, Mv bestMv[5])
 {
   int width = pu.cu->lumaSize().width;
@@ -1843,11 +2137,18 @@ int InterSearch::xHashInterPredME(const PredictionUnit& pu, RefPicList currRefPi
 
   return totalSize;
 }
+#endif
 
 bool InterSearch::xHashInterEstimation(PredictionUnit& pu, RefPicList& bestRefPicList, int& bestRefIndex, Mv& bestMv, Mv& bestMvd, int& bestMVPIndex, bool& isPerfectMatch)
 {
   int width = pu.cu->lumaSize().width;
   int height = pu.cu->lumaSize().height;
+#if JVET_N0247_HASH_IMPROVE
+  if (width != height)
+  {
+    return xRectHashInterEstimation(pu, bestRefPicList, bestRefIndex, bestMv, bestMvd, bestMVPIndex, isPerfectMatch);
+  }
+#endif
   int xPos = pu.cu->lumaPos().x;
   int yPos = pu.cu->lumaPos().y;
 
@@ -1887,6 +2188,9 @@ bool InterSearch::xHashInterEstimation(PredictionUnit& pu, RefPicList& bestRefPi
           bitsOnRefIdx--;
         }
       }
+#if JVET_N0247_HASH_IMPROVE
+      m_numHashMVStoreds[eRefPicList][refIdx] = 0;
+#endif
 
       if (refList == 0 || pu.cu->slice->getList1IdxToList0Idx(refIdx) < 0)
       {
@@ -1898,7 +2202,9 @@ bool InterSearch::xHashInterEstimation(PredictionUnit& pu, RefPicList& bestRefPi
 
         list<BlockHash> listBlockHash;
         selectMatchesInter(pu.cu->slice->getRefPic(eRefPicList, refIdx)->getHashMap()->getFirstIterator(hashValue1), count, listBlockHash, currBlockHash);
-
+#if JVET_N0247_HASH_IMPROVE
+        m_numHashMVStoreds[eRefPicList][refIdx] = (int)listBlockHash.size();
+#endif
         if (listBlockHash.empty())
         {
           continue;
@@ -1923,11 +2229,17 @@ bool InterSearch::xHashInterEstimation(PredictionUnit& pu, RefPicList& bestRefPi
         m_pcRdCost->setCostScale(0);
 
         list<BlockHash>::iterator it;
+#if JVET_N0247_HASH_IMPROVE
+        int countMV = 0;
+#endif
         for (it = listBlockHash.begin(); it != listBlockHash.end(); ++it)
         {
           int curMVPIdx = 0;
           unsigned int curMVPbits = MAX_UINT;
           Mv cMv((*it).x - currBlockHash.x, (*it).y - currBlockHash.y);
+#if JVET_N0247_HASH_IMPROVE
+          m_hashMVStoreds[eRefPicList][refIdx][countMV++] = cMv;
+#endif
           cMv.changePrecision(MV_PRECISION_INT, MV_PRECISION_QUARTER);
 
           for (int mvpIdxTemp = 0; mvpIdxTemp < 2; mvpIdxTemp++)
@@ -3500,6 +3812,18 @@ void InterSearch::xTZSearch( const PredictionUnit& pu,
       , cStruct
     );
   }
+#if JVET_N0247_HASH_IMPROVE
+  if (m_pcEncCfg->getUseHashME() && (m_currRefPicList == 0 || pu.cu->slice->getList1IdxToList0Idx(m_currRefPicIndex) < 0))
+  {
+    int minSize = min(pu.cu->lumaSize().width, pu.cu->lumaSize().height);
+    if (minSize < 128 && minSize >= 4)
+    {
+      int numberOfOtherMvps = m_numHashMVStoreds[m_currRefPicList][m_currRefPicIndex];
+      for (int i = 0; i < numberOfOtherMvps; i++)
+      {
+        xTZSearchHelp(cStruct, m_hashMVStoreds[m_currRefPicList][m_currRefPicIndex][i].getHor(), m_hashMVStoreds[m_currRefPicList][m_currRefPicIndex][i].getVer(), 0, 0);
+      }
+#else
   if (m_pcEncCfg->getUseHashME())
   {
     int width = pu.cu->lumaSize().width;
@@ -3513,6 +3837,7 @@ void InterSearch::xTZSearch( const PredictionUnit& pu,
       {
         xTZSearchHelp(cStruct, otherMvps[i].getHor(), otherMvps[i].getVer(), 0, 0);
       }
+#endif
       if (numberOfOtherMvps > 0)
       {
         // write out best match
@@ -3779,7 +4104,18 @@ void InterSearch::xTZSearchSelective( const PredictionUnit& pu,
       , cStruct
     );
   }
-
+#if JVET_N0247_HASH_IMPROVE
+  if (m_pcEncCfg->getUseHashME() && (m_currRefPicList == 0 || pu.cu->slice->getList1IdxToList0Idx(m_currRefPicIndex) < 0))
+  {
+    int minSize = min(pu.cu->lumaSize().width, pu.cu->lumaSize().height);
+    if (minSize < 128 && minSize >= 4)
+    {
+      int numberOfOtherMvps = m_numHashMVStoreds[m_currRefPicList][m_currRefPicIndex];
+      for (int i = 0; i < numberOfOtherMvps; i++)
+      {
+        xTZSearchHelp(cStruct, m_hashMVStoreds[m_currRefPicList][m_currRefPicIndex][i].getHor(), m_hashMVStoreds[m_currRefPicList][m_currRefPicIndex][i].getVer(), 0, 0);
+      }
+#else
   if (m_pcEncCfg->getUseHashME())
   {
     int width = pu.cu->lumaSize().width;
@@ -3793,7 +4129,7 @@ void InterSearch::xTZSearchSelective( const PredictionUnit& pu,
       {
         xTZSearchHelp(cStruct, otherMvps[i].getHor(), otherMvps[i].getVer(), 0, 0);
       }
-
+#endif
       if (numberOfOtherMvps > 0)
       {
         // write out best match
diff --git a/source/Lib/EncoderLib/InterSearch.h b/source/Lib/EncoderLib/InterSearch.h
index acc2861c1..0115d2505 100644
--- a/source/Lib/EncoderLib/InterSearch.h
+++ b/source/Lib/EncoderLib/InterSearch.h
@@ -143,6 +143,10 @@ protected:
   RefPicList      m_currRefPicList;
   int             m_currRefPicIndex;
   bool            m_skipFracME;
+#if JVET_N0247_HASH_IMPROVE
+  int             m_numHashMVStoreds[NUM_REF_PIC_LIST_01][MAX_NUM_REF];
+  Mv              m_hashMVStoreds[NUM_REF_PIC_LIST_01][MAX_NUM_REF][5];
+#endif
 
   // Misc.
   Pel            *m_pTempPel;
@@ -308,7 +312,12 @@ public:
   void addToSortList(std::list<BlockHash>& listBlockHash, std::list<int>& listCost, int cost, const BlockHash& blockHash);
   bool predInterHashSearch(CodingUnit& cu, Partitioner& partitioner, bool& isPerfectMatch);
   bool xHashInterEstimation(PredictionUnit& pu, RefPicList& bestRefPicList, int& bestRefIndex, Mv& bestMv, Mv& bestMvd, int& bestMVPIndex, bool& isPerfectMatch);
+#if JVET_N0247_HASH_IMPROVE
+  bool xRectHashInterEstimation(PredictionUnit& pu, RefPicList& bestRefPicList, int& bestRefIndex, Mv& bestMv, Mv& bestMvd, int& bestMVPIndex, bool& isPerfectMatch);
+  void selectRectangleMatchesInter(const MapIterator& itBegin, int count, std::list<BlockHash>& listBlockHash, const BlockHash& currBlockHash, int width, int height, int idxNonSimple, unsigned int* &hashValues, int baseNum, int picWidth, int picHeight, bool isHorizontal, uint16_t* curHashPic);
+#else
   int  xHashInterPredME(const PredictionUnit& pu, RefPicList currRefPicList, int currRefPicIndex, Mv bestMv[5]);
+#endif
   void selectMatchesInter(const MapIterator& itBegin, int count, std::list<BlockHash>& vecBlockHash, const BlockHash& currBlockHash);
 protected:
 
-- 
GitLab