diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp index 54cfe178e2cfab8cf953d86c3f2a5ab06c59bc3b..b5d6ad17068129e75a6f7ff423780becb0a92f25 100644 --- a/source/App/EncoderApp/EncApp.cpp +++ b/source/App/EncoderApp/EncApp.cpp @@ -257,6 +257,10 @@ void EncApp::xInitLibCfg() #endif m_cEncLib.setUseMHIntra ( m_MHIntra ); m_cEncLib.setUseTriangle ( m_Triangle ); +#if JVET_M0253_HASH_ME + m_cEncLib.setUseHashME ( m_HashME ); +#endif + #if JVET_M0255_FRACMMVD_SWITCH m_cEncLib.setAllowDisFracMMVD ( m_allowDisFracMMVD ); #endif diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp index f31fad8636275253c227ef20c31d5251b929fa5e..b79df7b356c4edc63d4d5edd172393362df1749c 100644 --- a/source/App/EncoderApp/EncAppCfg.cpp +++ b/source/App/EncoderApp/EncAppCfg.cpp @@ -864,6 +864,10 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) #endif ("MHIntra", m_MHIntra, false, "Enable MHIntra mode") ("Triangle", m_Triangle, false, "Enable triangular shape motion vector prediction (0:off, 1:on)") +#if JVET_M0253_HASH_ME + ("HashME", m_HashME, false, "Enable hash motion estimation (0:off, 1:on)") +#endif + #if JVET_M0255_FRACMMVD_SWITCH ("AllowDisFracMMVD", m_allowDisFracMMVD, false, "Disable fractional MVD in MMVD mode adaptively") #endif @@ -1943,6 +1947,9 @@ bool EncAppCfg::xCheckParameter() xConfirmPara( m_MTT, "Multi type tree is only allowed with NEXT profile" ); xConfirmPara( m_ImvMode, "IMV is only allowed with NEXT profile" ); xConfirmPara(m_IBCMode, "IBC Mode only allowed with NEXT profile"); +#if JVET_M0253_HASH_ME + xConfirmPara( m_HashME, "Hash motion estimation only allowed with NEXT profile" ); +#endif xConfirmPara( m_useFastLCTU, "Fast large CTU can only be applied when encoding with NEXT profile" ); #if JVET_M0464_UNI_MTS xConfirmPara( m_MTS, "MTS only allowed with NEXT profile" ); @@ -3165,6 +3172,9 @@ void EncAppCfg::xPrintParameter() #endif } msg(VERBOSE, "IBC:%d ", m_IBCMode); +#if JVET_M0253_HASH_ME + msg( VERBOSE, "HashME:%d ", m_HashME ); +#endif msg( VERBOSE, "WrapAround:%d ", m_wrapAround); if( m_wrapAround ) { diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h index c91c7ec580402ba1b0a7941bb7222fb0e4404b7f..b7df630eb5e7fb3aeb7419b3f69d561b2eb73f6c 100644 --- a/source/App/EncoderApp/EncAppCfg.h +++ b/source/App/EncoderApp/EncAppCfg.h @@ -237,6 +237,9 @@ protected: bool m_MHIntra; bool m_Triangle; +#if JVET_M0253_HASH_ME + bool m_HashME; +#endif #if JVET_M0255_FRACMMVD_SWITCH bool m_allowDisFracMMVD; #endif diff --git a/source/Lib/CommonLib/Hash.cpp b/source/Lib/CommonLib/Hash.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4dba4d867293dc18e850da56fe39dc324567d243 --- /dev/null +++ b/source/Lib/CommonLib/Hash.cpp @@ -0,0 +1,732 @@ +/* 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 TEncHash.cpp + \brief hash encoder class + */ +#include "CommonLib/dtrace_codingstruct.h" +#include "CommonLib/Picture.h" +#include "CommonLib/UnitTools.h" +#include "Hash.h" + + + + // ==================================================================================================================== + // Constructor / destructor / create / destroy + // ==================================================================================================================== + +int TComHash::m_blockSizeToIndex[65][65]; +TCRCCalculatorLight TComHash::m_crcCalculator1(24, 0x5D6DCB); +TCRCCalculatorLight TComHash::m_crcCalculator2(24, 0x864CFB); + +TCRCCalculatorLight::TCRCCalculatorLight(unsigned int bits, unsigned int truncPoly) +{ + m_remainder = 0; + m_bits = bits; + m_truncPoly = truncPoly; + m_finalResultMask = (1 << bits) - 1; + + xInitTable(); +} + +TCRCCalculatorLight::~TCRCCalculatorLight() +{ + +} + +void TCRCCalculatorLight::xInitTable() +{ + const unsigned int highBit = 1 << (m_bits - 1); + const unsigned int ByteHighBit = 1 << (8 - 1); + + for (unsigned int value = 0; value < 256; value++) + { + unsigned int remainder = 0; + for (unsigned char mask = ByteHighBit; mask != 0; mask >>= 1) + { + if (value & mask) + { + remainder ^= highBit; + } + + if (remainder & highBit) + { + remainder <<= 1; + remainder ^= m_truncPoly; + } + else + { + remainder <<= 1; + } + } + + m_table[value] = remainder; + } +} + +void TCRCCalculatorLight::processData(unsigned char* curData, unsigned int dataLength) +{ + for (unsigned int i = 0; i < dataLength; i++) + { + unsigned char index = (m_remainder >> (m_bits - 8)) ^ curData[i]; + m_remainder <<= 8; + m_remainder ^= m_table[index]; + } +} + + +TComHash::TComHash() +{ + m_lookupTable = NULL; + tableHasContent = false; +} + +TComHash::~TComHash() +{ + clearAll(); + if (m_lookupTable != NULL) + { + delete[] m_lookupTable; + m_lookupTable = NULL; + } +} + +void TComHash::create() +{ + if (m_lookupTable != NULL) + { + clearAll(); + return; + } + int maxAddr = 1 << (m_CRCBits + m_blockSizeBits); + m_lookupTable = new std::vector<BlockHash>*[maxAddr]; + memset(m_lookupTable, 0, sizeof(std::vector<BlockHash>*) * maxAddr); + tableHasContent = false; +} + +void TComHash::clearAll() +{ + tableHasContent = false; + if (m_lookupTable == NULL) + { + return; + } + int maxAddr = 1 << (m_CRCBits + m_blockSizeBits); + for (int i = 0; i < maxAddr; i++) + { + if (m_lookupTable[i] != NULL) + { + delete m_lookupTable[i]; + m_lookupTable[i] = NULL; + } + } +} + +void TComHash::addToTable(unsigned int hashValue, const BlockHash& blockHash) +{ + if (m_lookupTable[hashValue] == NULL) + { + m_lookupTable[hashValue] = new std::vector<BlockHash>; + m_lookupTable[hashValue]->push_back(blockHash); + } + else + { + m_lookupTable[hashValue]->push_back(blockHash); + } +} + +int TComHash::count(unsigned int hashValue) +{ + if (m_lookupTable[hashValue] == NULL) + { + return 0; + } + else + { + return static_cast<int>(m_lookupTable[hashValue]->size()); + } +} + +int TComHash::count(unsigned int hashValue) const +{ + if (m_lookupTable[hashValue] == NULL) + { + return 0; + } + else + { + return static_cast<int>(m_lookupTable[hashValue]->size()); + } +} + +MapIterator TComHash::getFirstIterator(unsigned int hashValue) +{ + return m_lookupTable[hashValue]->begin(); +} + +const MapIterator TComHash::getFirstIterator(unsigned int hashValue) const +{ + return m_lookupTable[hashValue]->begin(); +} + +bool TComHash::hasExactMatch(unsigned int hashValue1, unsigned int hashValue2) +{ + if (m_lookupTable[hashValue1] == NULL) + { + return false; + } + std::vector<BlockHash>::iterator it; + for (it = m_lookupTable[hashValue1]->begin(); it != m_lookupTable[hashValue1]->end(); it++) + { + if ((*it).hashValue2 == hashValue2) + { + return true; + } + } + return false; +} + +void TComHash::generateBlock2x2HashValue(const PelUnitBuf &curPicBuf, int picWidth, int picHeight, const BitDepths bitDepths, unsigned int* picBlockHash[2], bool* picBlockSameInfo[3]) +{ + const int width = 2; + const int height = 2; + int xEnd = picWidth - width + 1; + int yEnd = picHeight - height + 1; + + int length = width * 2; + bool includeChroma = false; + if ((curPicBuf).chromaFormat == CHROMA_444) + { + length *= 3; + includeChroma = true; + } + unsigned char* p = new unsigned char[length]; + + int pos = 0; + for (int yPos = 0; yPos < yEnd; yPos++) + { + for (int xPos = 0; xPos < xEnd; xPos++) + { + TComHash::getPixelsIn1DCharArrayByBlock2x2(curPicBuf, p, xPos, yPos, bitDepths, includeChroma); + picBlockSameInfo[0][pos] = isBlock2x2RowSameValue(p, includeChroma); + picBlockSameInfo[1][pos] = isBlock2x2ColSameValue(p, includeChroma); + + picBlockHash[0][pos] = TComHash::getCRCValue1(p, length * sizeof(unsigned char)); + picBlockHash[1][pos] = TComHash::getCRCValue2(p, length * sizeof(unsigned char)); + + pos++; + } + pos += width - 1; + } + + delete[] p; +} +void TComHash::generateRectangleHashValue(int picWidth, int picHeight, int width, int height, unsigned int* srcPicBlockHash[2], unsigned int* dstPicBlockHash[2], bool* srcPicBlockSameInfo[3], bool* dstPicBlockSameInfo[3]) +{ + //at present, only support 1:2(2:1) retangle hash value + CHECK(width != (height << 1) && (width << 1) != height, "Wrong") + bool isHorizontal = width == (height << 1) ? true : false; + + int xEnd = picWidth - width + 1; + int yEnd = picHeight - height + 1; + + int srcWidth = width >> 1; + int quadWidth = width >> 2; + int srcHeight = height >> 1; + int quadHeight = height >> 2; + + int length = 2 * sizeof(unsigned int); + unsigned int* p = new unsigned int[2]; + int pos = 0; + if (isHorizontal) + { + for (int yPos = 0; yPos < yEnd; yPos++) + { + for (int xPos = 0; xPos < xEnd; xPos++) + { + p[0] = srcPicBlockHash[0][pos]; + p[1] = srcPicBlockHash[0][pos + srcWidth]; + dstPicBlockHash[0][pos] = TComHash::getCRCValue1((unsigned char*)p, length); + + p[0] = srcPicBlockHash[1][pos]; + p[1] = srcPicBlockHash[1][pos + srcWidth]; + dstPicBlockHash[1][pos] = TComHash::getCRCValue2((unsigned char*)p, length); + + dstPicBlockSameInfo[0][pos] = srcPicBlockSameInfo[0][pos] && srcPicBlockSameInfo[0][pos + quadWidth] && srcPicBlockSameInfo[0][pos + srcWidth]; + dstPicBlockSameInfo[1][pos] = srcPicBlockSameInfo[1][pos] && srcPicBlockSameInfo[1][pos + srcWidth]; + pos++; + } + pos += width - 1; + } + } + else + { + for (int yPos = 0; yPos < yEnd; yPos++) + { + for (int xPos = 0; xPos < xEnd; xPos++) + { + p[0] = srcPicBlockHash[0][pos]; + p[1] = srcPicBlockHash[0][pos + srcHeight * picWidth]; + dstPicBlockHash[0][pos] = TComHash::getCRCValue1((unsigned char*)p, length); + + p[0] = srcPicBlockHash[1][pos]; + p[1] = srcPicBlockHash[1][pos + srcHeight * picWidth]; + dstPicBlockHash[1][pos] = TComHash::getCRCValue2((unsigned char*)p, length); + + dstPicBlockSameInfo[0][pos] = srcPicBlockSameInfo[0][pos] && srcPicBlockSameInfo[0][pos + srcHeight * picWidth]; + dstPicBlockSameInfo[1][pos] = srcPicBlockSameInfo[1][pos] && srcPicBlockSameInfo[1][pos + quadHeight * picWidth] && srcPicBlockSameInfo[1][pos + srcHeight * picWidth]; + + pos++; + } + pos += width - 1; + } + } + + int widthMinus1 = width - 1; + int heightMinus1 = height - 1; + pos = 0; + + for (int yPos = 0; yPos < yEnd; yPos++) + { + for (int xPos = 0; xPos < xEnd; xPos++) + { + dstPicBlockSameInfo[2][pos] = (!dstPicBlockSameInfo[0][pos] && !dstPicBlockSameInfo[1][pos]) || (((xPos & widthMinus1) == 0) && ((yPos & heightMinus1) == 0)); + pos++; + } + pos += width - 1; + } + + delete[] p; +} + +void TComHash::generateBlockHashValue(int picWidth, int picHeight, int width, int height, unsigned int* srcPicBlockHash[2], unsigned int* dstPicBlockHash[2], bool* srcPicBlockSameInfo[3], bool* dstPicBlockSameInfo[3]) +{ + int xEnd = picWidth - width + 1; + int yEnd = picHeight - height + 1; + + int srcWidth = width >> 1; + int quadWidth = width >> 2; + int srcHeight = height >> 1; + int quadHeight = height >> 2; + + int length = 4 * sizeof(unsigned int); + + unsigned int* p = new unsigned int[4]; + int pos = 0; + for (int yPos = 0; yPos < yEnd; yPos++) + { + for (int xPos = 0; xPos < xEnd; xPos++) + { + p[0] = srcPicBlockHash[0][pos]; + p[1] = srcPicBlockHash[0][pos + srcWidth]; + p[2] = srcPicBlockHash[0][pos + srcHeight * picWidth]; + p[3] = srcPicBlockHash[0][pos + srcHeight * picWidth + srcWidth]; + dstPicBlockHash[0][pos] = TComHash::getCRCValue1((unsigned char*)p, length); + + p[0] = srcPicBlockHash[1][pos]; + p[1] = srcPicBlockHash[1][pos + srcWidth]; + p[2] = srcPicBlockHash[1][pos + srcHeight * picWidth]; + p[3] = srcPicBlockHash[1][pos + srcHeight * picWidth + srcWidth]; + dstPicBlockHash[1][pos] = TComHash::getCRCValue2((unsigned char*)p, length); + + dstPicBlockSameInfo[0][pos] = srcPicBlockSameInfo[0][pos] && srcPicBlockSameInfo[0][pos + quadWidth] && srcPicBlockSameInfo[0][pos + srcWidth] + && srcPicBlockSameInfo[0][pos + srcHeight * picWidth] && srcPicBlockSameInfo[0][pos + srcHeight * picWidth + quadWidth] && srcPicBlockSameInfo[0][pos + srcHeight * picWidth + srcWidth]; + + dstPicBlockSameInfo[1][pos] = srcPicBlockSameInfo[1][pos] && srcPicBlockSameInfo[1][pos + srcWidth] && srcPicBlockSameInfo[1][pos + quadHeight * picWidth] + && srcPicBlockSameInfo[1][pos + quadHeight * picWidth + srcWidth] && srcPicBlockSameInfo[1][pos + srcHeight * picWidth] && srcPicBlockSameInfo[1][pos + srcHeight * picWidth + srcWidth]; + + pos++; + } + pos += width - 1; + } + + if (width >= 4) + { + int widthMinus1 = width - 1; + int heightMinus1 = height - 1; + pos = 0; + + for (int yPos = 0; yPos < yEnd; yPos++) + { + for (int xPos = 0; xPos < xEnd; xPos++) + { + dstPicBlockSameInfo[2][pos] = (!dstPicBlockSameInfo[0][pos] && !dstPicBlockSameInfo[1][pos]) || (((xPos & widthMinus1) == 0) && ((yPos & heightMinus1) == 0)); + pos++; + } + pos += width - 1; + } + } + + delete[] p; + +} + +void TComHash::addToHashMapByRowWithPrecalData(unsigned int* picHash[2], bool* picIsSame, int picWidth, int picHeight, int width, int height) +{ + int xEnd = picWidth - width + 1; + int yEnd = picHeight - height + 1; + + bool* srcIsAdded = picIsSame; + unsigned int* srcHash[2] = { picHash[0], picHash[1] }; + + int addValue = m_blockSizeToIndex[width][height]; + CHECK(addValue < 0, "Wrong") + addValue <<= m_CRCBits; + int crcMask = 1 << m_CRCBits; + crcMask -= 1; + + for (int xPos = 0; xPos < xEnd; xPos++) + { + for (int yPos = 0; yPos < yEnd; yPos++) + { + int pos = yPos * picWidth + xPos; + //valid data + if (srcIsAdded[pos]) + { + BlockHash blockHash; + blockHash.x = xPos; + blockHash.y = yPos; + + unsigned int hashValue1 = (srcHash[0][pos] & crcMask) + addValue; + blockHash.hashValue2 = srcHash[1][pos]; + + addToTable(hashValue1, blockHash); + } + } + } +} + +void TComHash::getPixelsIn1DCharArrayByBlock2x2(const PelUnitBuf &curPicBuf, unsigned char* pixelsIn1D, int xStart, int yStart, const BitDepths& bitDepths, bool includeAllComponent) +{ + ChromaFormat fmt = (curPicBuf).chromaFormat; + if (fmt != CHROMA_444) + { + includeAllComponent = false; + } + + if (bitDepths.recon[CHANNEL_TYPE_LUMA] == 8 && bitDepths.recon[CHANNEL_TYPE_CHROMA] == 8) + { + Pel* curPel[3]; + int stride[3]; + for (int id = 0; id < 3; id++) + { + ComponentID compID = ComponentID(id); + stride[id] = (curPicBuf).get(compID).stride; + curPel[id] = (curPicBuf).get(compID).buf; + curPel[id] += (yStart >> getComponentScaleY(compID, fmt)) * stride[id] + (xStart >> getComponentScaleX(compID, fmt)); + } + + int index = 0; + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < 2; j++) + { + pixelsIn1D[index++] = static_cast<unsigned char>(curPel[0][j]); + if (includeAllComponent) + { + pixelsIn1D[index++] = static_cast<unsigned char>(curPel[1][j]); + pixelsIn1D[index++] = static_cast<unsigned char>(curPel[2][j]); + } + } + curPel[0] += stride[0]; + if (includeAllComponent) + { + curPel[1] += stride[1]; + curPel[2] += stride[2]; + } + } + } + else + { + int shift = bitDepths.recon[CHANNEL_TYPE_LUMA] - 8; + int shiftc = bitDepths.recon[CHANNEL_TYPE_CHROMA] - 8; + Pel* curPel[3]; + int stride[3]; + for (int id = 0; id < 3; id++) + { + ComponentID compID = ComponentID(id); + stride[id] = (curPicBuf).get(compID).stride; + curPel[id] = (curPicBuf).get(compID).buf; + curPel[id] += (yStart >> getComponentScaleY(compID, fmt)) * stride[id] + (xStart >> getComponentScaleX(compID, fmt)); + } + + int index = 0; + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < 2; j++) + { + pixelsIn1D[index++] = static_cast<unsigned char>(curPel[0][j] >> shift); + if (includeAllComponent) + { + pixelsIn1D[index++] = static_cast<unsigned char>(curPel[1][j] >> shiftc); + pixelsIn1D[index++] = static_cast<unsigned char>(curPel[2][j] >> shiftc); + } + } + curPel[0] += stride[0]; + if (includeAllComponent) + { + curPel[1] += stride[1]; + curPel[2] += stride[2]; + } + } + } +} + +bool TComHash::isBlock2x2RowSameValue(unsigned char* p, bool includeAllComponent) +{ + if (includeAllComponent) + { + if (p[0] != p[3] || p[6] != p[9]) + { + return false; + } + if (p[1] != p[4] || p[7] != p[10]) + { + return false; + } + if (p[2] != p[5] || p[8] != p[11]) + { + return false; + } + } + else + { + if (p[0] != p[1] || p[2] != p[3]) + { + return false; + } + } + + return true; +} + +bool TComHash::isBlock2x2ColSameValue(unsigned char* p, bool includeAllComponent) +{ + if (includeAllComponent) + { + if (p[0] != p[6] || p[3] != p[9]) + { + return false; + } + if (p[1] != p[7] || p[4] != p[10]) + { + return false; + } + if (p[2] != p[8] || p[5] != p[11]) + { + return false; + } + } + else + { + if ((p[0] != p[2]) || (p[1] != p[3])) + { + return false; + } + } + + return true; +} + +bool TComHash::getBlockHashValue(const PelUnitBuf &curPicBuf, int width, int height, int xStart, int yStart, const BitDepths bitDepths, unsigned int& hashValue1, unsigned int& hashValue2) +{ + int addValue = m_blockSizeToIndex[width][height]; + + CHECK(addValue < 0, "Wrong") + addValue <<= m_CRCBits; + int crcMask = 1 << m_CRCBits; + crcMask -= 1; + int length = 4; + bool includeChroma = false; + if ((curPicBuf).chromaFormat == CHROMA_444) + { + length *= 3; + includeChroma = true; + } + + unsigned char* p = new unsigned char[length]; + unsigned int* toHash = new unsigned int[4]; + + int block2x2Num = (width*height) >> 2; + + unsigned int* hashValueBuffer[2][2]; + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < 2; j++) + { + hashValueBuffer[i][j] = new unsigned int[block2x2Num]; + } + } + + //2x2 subblock hash values in current CU + int subBlockInWidth = (width >> 1); + int subBlockInHeight = (height >> 1); + for (int yPos = 0; yPos < height; yPos += 2) + { + for (int xPos = 0; xPos < width; xPos += 2) + { + int pos = (yPos >> 1)*subBlockInWidth + (xPos >> 1); + TComHash::getPixelsIn1DCharArrayByBlock2x2(curPicBuf, p, xStart + xPos, yStart + yPos, bitDepths, includeChroma); + + hashValueBuffer[0][0][pos] = TComHash::getCRCValue1(p, length * sizeof(unsigned char)); + hashValueBuffer[1][0][pos] = TComHash::getCRCValue2(p, length * sizeof(unsigned char)); + } + } + + int srcSubBlockInWidth = subBlockInWidth; + subBlockInWidth >>= 1; + subBlockInHeight >>= 1; + length = 4 * sizeof(unsigned int); + + int srcIdx = 1; + int dstIdx = 0; + + //4x4 subblock hash values to current block hash values + int minSize = std::min(height, width); + for (int subWidth = 4; subWidth <= minSize; subWidth *= 2) + { + srcIdx = 1 - srcIdx; + dstIdx = 1 - dstIdx; + + int dstPos = 0; + for (int yPos = 0; yPos < subBlockInHeight; yPos++) + { + for (int xPos = 0; xPos < subBlockInWidth; xPos++) + { + int srcPos = (yPos << 1)*srcSubBlockInWidth + (xPos << 1); + + toHash[0] = hashValueBuffer[0][srcIdx][srcPos]; + toHash[1] = hashValueBuffer[0][srcIdx][srcPos + 1]; + toHash[2] = hashValueBuffer[0][srcIdx][srcPos + srcSubBlockInWidth]; + toHash[3] = hashValueBuffer[0][srcIdx][srcPos + srcSubBlockInWidth + 1]; + + hashValueBuffer[0][dstIdx][dstPos] = TComHash::getCRCValue1((unsigned char*)toHash, length); + + toHash[0] = hashValueBuffer[1][srcIdx][srcPos]; + toHash[1] = hashValueBuffer[1][srcIdx][srcPos + 1]; + toHash[2] = hashValueBuffer[1][srcIdx][srcPos + srcSubBlockInWidth]; + toHash[3] = hashValueBuffer[1][srcIdx][srcPos + srcSubBlockInWidth + 1]; + hashValueBuffer[1][dstIdx][dstPos] = TComHash::getCRCValue2((unsigned char*)toHash, length); + + dstPos++; + } + } + + srcSubBlockInWidth = subBlockInWidth; + subBlockInWidth >>= 1; + subBlockInHeight >>= 1; + } + + if (width != height)//currently support 1:2 or 2:1 block size + { + CHECK(width != (height << 1) && (width << 1) != height, "Wrong") + bool isHorizontal = width == (height << 1) ? true : false; + length = 2 * sizeof(unsigned int); + srcIdx = 1 - srcIdx; + dstIdx = 1 - dstIdx; + if (isHorizontal) + { + toHash[0] = hashValueBuffer[0][srcIdx][0]; + toHash[1] = hashValueBuffer[0][srcIdx][1]; + + hashValueBuffer[0][dstIdx][0] = TComHash::getCRCValue1((unsigned char*)toHash, length); + + toHash[0] = hashValueBuffer[1][srcIdx][0]; + toHash[1] = hashValueBuffer[1][srcIdx][1]; + hashValueBuffer[1][dstIdx][0] = TComHash::getCRCValue2((unsigned char*)toHash, length); + } + else + { + CHECK(srcSubBlockInWidth != 1, "Wrong") + toHash[0] = hashValueBuffer[0][srcIdx][0]; + toHash[1] = hashValueBuffer[0][srcIdx][srcSubBlockInWidth]; + + hashValueBuffer[0][dstIdx][0] = TComHash::getCRCValue1((unsigned char*)toHash, length); + + toHash[0] = hashValueBuffer[1][srcIdx][0]; + toHash[1] = hashValueBuffer[1][srcIdx][srcSubBlockInWidth]; + hashValueBuffer[1][dstIdx][0] = TComHash::getCRCValue2((unsigned char*)toHash, length); + } + } + + hashValue1 = (hashValueBuffer[0][dstIdx][0] & crcMask) + addValue; + hashValue2 = hashValueBuffer[1][dstIdx][0]; + + delete[] toHash; + + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < 2; j++) + { + delete[] hashValueBuffer[i][j]; + } + } + + delete[] p; + + return true; +} + +void TComHash::initBlockSizeToIndex() +{ + for (int i = 0; i < 65; i++) + { + for (int j = 0; j < 65; j++) + { + m_blockSizeToIndex[i][j] = -1; + } + } + + m_blockSizeToIndex[8][8] = 0; + m_blockSizeToIndex[16][16] = 1; + m_blockSizeToIndex[32][32] = 2; + m_blockSizeToIndex[64][64] = 3; + m_blockSizeToIndex[4][4] = 4; + m_blockSizeToIndex[4][8] = 5; + m_blockSizeToIndex[8][4] = 6; +} + +unsigned int TComHash::getCRCValue1(unsigned char* p, int length) +{ + m_crcCalculator1.reset(); + m_crcCalculator1.processData(p, length); + return m_crcCalculator1.getCRC(); +} + +unsigned int TComHash::getCRCValue2(unsigned char* p, int length) +{ + m_crcCalculator2.reset(); + m_crcCalculator2.processData(p, length); + return m_crcCalculator2.getCRC(); +} +//! \} diff --git a/source/Lib/CommonLib/Hash.h b/source/Lib/CommonLib/Hash.h new file mode 100644 index 0000000000000000000000000000000000000000..f47f14469f04ef6efd2a47b2eb5d0df5ad290e05 --- /dev/null +++ b/source/Lib/CommonLib/Hash.h @@ -0,0 +1,134 @@ +/* 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 Hash.h + \brief Hash class (header) + */ + + +#ifndef __HASH__ +#define __HASH__ + + // Include files + +#include "CommonLib/Buffer.h" +#include "CommonLib/CommonDef.h" +#include "CommonLib/TrQuant.h" +#include "CommonLib/Unit.h" +#include "CommonLib/UnitPartitioner.h" +#include <vector> + + +struct BlockHash +{ + short x; + short y; + unsigned int hashValue2; +}; + +typedef std::vector<BlockHash>::iterator MapIterator; + +// ==================================================================================================================== +// Class definitions +// ==================================================================================================================== + + +struct TCRCCalculatorLight +{ +public: + TCRCCalculatorLight(unsigned int bits, unsigned int truncPoly); + ~TCRCCalculatorLight(); + +public: + void processData(unsigned char* curData, unsigned int dataLength); + void reset() { m_remainder = 0; } + unsigned int getCRC() { return m_remainder & m_finalResultMask; } + +private: + void xInitTable(); + +private: + unsigned int m_remainder; + unsigned int m_truncPoly; + unsigned int m_bits; + unsigned int m_table[256]; + unsigned int m_finalResultMask; +}; + + +struct TComHash +{ +public: + TComHash(); + ~TComHash(); + void create(); + void clearAll(); + void addToTable(unsigned int hashValue, const BlockHash& blockHash); + int count(unsigned int hashValue); + int count(unsigned int hashValue) const; + MapIterator getFirstIterator(unsigned int hashValue); + const MapIterator getFirstIterator(unsigned int hashValue) const; + bool hasExactMatch(unsigned int hashValue1, unsigned int hashValue2); + + void generateBlock2x2HashValue(const PelUnitBuf &curPicBuf, int picWidth, int picHeight, const BitDepths bitDepths, unsigned int* picBlockHash[2], bool* picBlockSameInfo[3]); + void generateBlockHashValue(int picWidth, int picHeight, int width, int height, unsigned int* srcPicBlockHash[2], unsigned int* dstPicBlockHash[2], bool* srcPicBlockSameInfo[3], bool* dstPicBlockSameInfo[3]); + void generateRectangleHashValue(int picWidth, int picHeight, int width, int height, unsigned int* srcPicBlockHash[2], unsigned int* dstPicBlockHash[2], bool* srcPicBlockSameInfo[3], bool* dstPicBlockSameInfo[3]); + void addToHashMapByRowWithPrecalData(unsigned int* srcHash[2], bool* srcIsSame, int picWidth, int picHeight, int width, int height); + bool isInitial() { return tableHasContent; } + void setInitial() { tableHasContent = true; } + + + +public: + static unsigned int getCRCValue1(unsigned char* p, int length); + static unsigned int getCRCValue2(unsigned char* p, int length); + static void getPixelsIn1DCharArrayByBlock2x2(const PelUnitBuf &curPicBuf, unsigned char* pixelsIn1D, int xStart, int yStart, const BitDepths& bitDepths, bool includeAllComponent = true); + static bool isBlock2x2RowSameValue(unsigned char* p, bool includeAllComponent = true); + 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, unsigned int& hashValue1, unsigned int& hashValue2); + static void initBlockSizeToIndex(); + +private: + std::vector<BlockHash>** m_lookupTable; + bool tableHasContent; + +private: + static const int m_CRCBits = 16; + static const int m_blockSizeBits = 3; + static int m_blockSizeToIndex[65][65]; + + static TCRCCalculatorLight m_crcCalculator1; + static TCRCCalculatorLight m_crcCalculator2; +}; + +#endif // __HASH__ diff --git a/source/Lib/CommonLib/Picture.cpp b/source/Lib/CommonLib/Picture.cpp index d319f1f7ed99cae3d0b589db280489359e00733d..aed517f64659a82a65186d61b243c79ff5ecd93c 100644 --- a/source/Lib/CommonLib/Picture.cpp +++ b/source/Lib/CommonLib/Picture.cpp @@ -747,6 +747,9 @@ void Picture::create(const ChromaFormat &_chromaFormat, const Size &size, const m_ctuArea = UnitArea( _chromaFormat, Area( Position{ 0, 0 }, Size( _maxCUSize, _maxCUSize ) ) ); #endif +#if JVET_M0253_HASH_ME + m_hashMap.clearAll(); +#endif } void Picture::destroy() @@ -762,7 +765,9 @@ void Picture::destroy() { M_BUFS( jId, t ).destroy(); } - +#if JVET_M0253_HASH_ME + m_hashMap.clearAll(); +#endif if( cs ) { cs->destroy(); @@ -1162,3 +1167,64 @@ bool Picture::getSpliceFull() return false; return true; } + +#if JVET_M0253_HASH_ME +void Picture::addPictureToHashMapForInter() +{ + int picWidth = slices[0]->getSPS()->getPicWidthInLumaSamples(); + int picHeight = slices[0]->getSPS()->getPicHeightInLumaSamples(); + unsigned int* blockHashValues[2][2]; + bool* bIsBlockSame[2][3]; + + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < 2; j++) + { + blockHashValues[i][j] = new unsigned int[picWidth*picHeight]; + } + + for (int j = 0; j < 3; j++) + { + bIsBlockSame[i][j] = new bool[picWidth*picHeight]; + } + } + + m_hashMap.create(); + 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); + + 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); + + 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); + + m_hashMap.generateBlockHashValue(picWidth, picHeight, 16, 16, blockHashValues[0], blockHashValues[1], bIsBlockSame[0], bIsBlockSame[1]);//16x16 + m_hashMap.addToHashMapByRowWithPrecalData(blockHashValues[1], bIsBlockSame[1][2], picWidth, picHeight, 16, 16); + + m_hashMap.generateBlockHashValue(picWidth, picHeight, 32, 32, blockHashValues[1], blockHashValues[0], bIsBlockSame[1], bIsBlockSame[0]);//32x32 + m_hashMap.addToHashMapByRowWithPrecalData(blockHashValues[0], bIsBlockSame[0][2], picWidth, picHeight, 32, 32); + + m_hashMap.generateBlockHashValue(picWidth, picHeight, 64, 64, blockHashValues[0], blockHashValues[1], bIsBlockSame[0], bIsBlockSame[1]);//64x64 + m_hashMap.addToHashMapByRowWithPrecalData(blockHashValues[1], bIsBlockSame[1][2], picWidth, picHeight, 64, 64); + + m_hashMap.setInitial(); + + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < 2; j++) + { + delete[] blockHashValues[i][j]; + } + + for (int j = 0; j < 3; j++) + { + delete[] bIsBlockSame[i][j]; + } + } +} +#endif diff --git a/source/Lib/CommonLib/Picture.h b/source/Lib/CommonLib/Picture.h index 2e90cc5382746f8a7675372eceb892748202fa98..1b6797720ae5d2ab1dc5d9de55ce2d42c1d7d369 100644 --- a/source/Lib/CommonLib/Picture.h +++ b/source/Lib/CommonLib/Picture.h @@ -46,7 +46,9 @@ #include "Unit.h" #include "Slice.h" #include "CodingStructure.h" - +#if JVET_M0253_HASH_ME +#include "Hash.h" +#endif #include <deque> #if ENABLE_WPP_PARALLELISM || ENABLE_SPLIT_PARALLELISM @@ -259,6 +261,13 @@ public: PelStorage m_bufs[NUM_PIC_TYPES]; #endif +#if JVET_M0253_HASH_ME + TComHash m_hashMap; + TComHash* getHashMap() { return &m_hashMap; } + const TComHash* getHashMap() const { return &m_hashMap; } + void addPictureToHashMapForInter(); +#endif + CodingStructure* cs; std::deque<Slice*> slices; SEIMessages SEIs; diff --git a/source/Lib/CommonLib/Slice.cpp b/source/Lib/CommonLib/Slice.cpp index fe1541e008a14867e3b0faad4cff4c47f894fd1f..218c6b78b0b491e7beb269407aa3718bf0d1f6d5 100644 --- a/source/Lib/CommonLib/Slice.cpp +++ b/source/Lib/CommonLib/Slice.cpp @@ -695,6 +695,9 @@ void Slice::decodingRefreshMarking(int& pocCRA, bool& bRefreshPending, PicList& if (rpcPic->getPOC() != pocCurr) { rpcPic->referenced = false; +#if JVET_M0253_HASH_ME + rpcPic->getHashMap()->clearAll(); +#endif } iterPic++; } @@ -722,6 +725,9 @@ void Slice::decodingRefreshMarking(int& pocCRA, bool& bRefreshPending, PicList& if (rpcPic->getPOC() != pocCurr && rpcPic->getPOC() != m_iLastIDR) { rpcPic->referenced = false; +#if JVET_M0253_HASH_ME + rpcPic->getHashMap()->clearAll(); +#endif } iterPic++; } @@ -739,6 +745,9 @@ void Slice::decodingRefreshMarking(int& pocCRA, bool& bRefreshPending, PicList& if (rpcPic->getPOC() != pocCurr && rpcPic->getPOC() != pocCRA) { rpcPic->referenced = false; +#if JVET_M0253_HASH_ME + rpcPic->getHashMap()->clearAll(); +#endif } iterPic++; } @@ -1150,6 +1159,9 @@ void Slice::applyReferencePictureSet( PicList& rcListPic, const ReferencePicture pcPic->referenced = false; pcPic->usedByCurr = false; pcPic->longTerm = false; +#if JVET_M0253_HASH_ME + pcPic->getHashMap()->clearAll(); +#endif } // sanity checks diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h index cb1dc56721259efdd479c56284673317f95b2bfc..ba16df39f1f33060176be94dd1b4b52196f726ed 100644 --- a/source/Lib/CommonLib/TypeDef.h +++ b/source/Lib/CommonLib/TypeDef.h @@ -53,6 +53,8 @@ #define JVET_M0471_LONG_DEBLOCKING_FILTERS 1 #define JVET_M0470 1 // Fixed GR/TU+EG-k transition point, use limited prefix length for escape codes +#define JVET_M0253_HASH_ME 1 + #define JVET_M0257 1 // Scan only non zero-out regions of large TUs #define JVET_M0193_PAIR_AVG_REDUCTION 1 //Use only one pairwise average candidate diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h index bbfd74821a63dea93ca14b8dec8a7450abc75930..d61e6dd6ffe60b7a205888dbfbd1b59400b9e6c0 100644 --- a/source/Lib/EncoderLib/EncCfg.h +++ b/source/Lib/EncoderLib/EncCfg.h @@ -243,6 +243,8 @@ protected: #endif #if JVET_M0246_AFFINE_AMVR bool m_AffineAmvr; +#endif#if JVET_M0253_HASH_ME +bool m_HashME; #endif #if JVET_M0247_AFFINE_AMVR_ENCOPT bool m_AffineAmvrEncOpt; @@ -766,6 +768,10 @@ public: void setAllowDisFracMMVD ( bool b ) { m_allowDisFracMMVD = b; } bool getAllowDisFracMMVD () const { return m_allowDisFracMMVD; } #endif +#if JVET_M0253_HASH_ME + void setUseHashME ( bool b ) { m_HashME = b; } + bool getUseHashME () const { return m_HashME; } +#endif #if JVET_M0246_AFFINE_AMVR void setUseAffineAmvr ( bool b ) { m_AffineAmvr = b; } bool getUseAffineAmvr () const { return m_AffineAmvr; } diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp index 32639339932f9997b2656b341fd10f747d5831a7..8b2d43ec02b6ce81dae3087976e3a577d6890b77 100644 --- a/source/Lib/EncoderLib/EncCu.cpp +++ b/source/Lib/EncoderLib/EncCu.cpp @@ -754,6 +754,12 @@ void EncCu::xCompressCU( CodingStructure *&tempCS, CodingStructure *&bestCS, Par } } +#if JVET_M0253_HASH_ME + else if (currTestMode.type == ETM_HASH_INTER) + { + xCheckRDCostHashInter( tempCS, bestCS, partitioner, currTestMode ); + } +#endif else if( currTestMode.type == ETM_AFFINE ) { xCheckRDCostAffineMerge2Nx2N( tempCS, bestCS, partitioner, currTestMode ); @@ -1721,7 +1727,56 @@ void EncCu::xFillPCMBuffer( CodingUnit &cu ) } } } +#if JVET_M0253_HASH_ME +void EncCu::xCheckRDCostHashInter( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode ) +{ + bool isPerfectMatch = false; + + tempCS->initStructData(encTestMode.qp, encTestMode.lossless); + m_pcInterSearch->resetBufferedUniMotions(); + m_pcInterSearch->setAffineModeSelected(false); + CodingUnit &cu = tempCS->addCU(tempCS->area, partitioner.chType); + + partitioner.setCUData(cu); + cu.slice = tempCS->slice; + cu.skip = false; + cu.predMode = MODE_INTER; + cu.transQuantBypass = encTestMode.lossless; + cu.chromaQpAdj = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1; + cu.qp = encTestMode.qp; + cu.ibc = false; + CU::addPUs(cu); + cu.mmvdSkip = false; + cu.firstPU->mmvdMergeFlag = false; + + if (m_pcInterSearch->predInterHashSearch(cu, partitioner, isPerfectMatch)) + { + const unsigned wIdx = gp_sizeIdxInfo->idxFrom(tempCS->area.lwidth()); + double equGBiCost = MAX_DOUBLE; +#if JVET_M0464_UNI_MTS + xEncodeInterResidual(tempCS, bestCS, partitioner, encTestMode, 0 + , m_pImvTempCS ? m_pImvTempCS[wIdx] : NULL + , 0 + , &equGBiCost +#else + xEncodeInterResidual(tempCS, bestCS, partitioner, encTestMode, 0 + , m_pImvTempCS ? m_pImvTempCS[wIdx] : NULL + , 1 + , 0 + , &equGBiCost +#endif + ); + } + tempCS->initStructData(encTestMode.qp, encTestMode.lossless); + + if (cu.lwidth() != 64) + { + isPerfectMatch = false; + } + m_modeCtrl->setIsHashPerfectMatch(isPerfectMatch); +} +#endif void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode ) { @@ -2148,10 +2203,25 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *& } } +#if !JVET_M0253_HASH_ME const uint32_t iteration = encTestMode.lossless ? 1 : 2; // 2. Pass: check candidates using full RD test for( uint32_t uiNoResidualPass = 0; uiNoResidualPass < iteration; uiNoResidualPass++ ) +#else + uint32_t iteration; + uint32_t iterationBegin = m_modeCtrl->getIsHashPerfectMatch() ? 1 : 0; + if (encTestMode.lossless) + { + iteration = 1; + iterationBegin = 0; + } + else + { + iteration = 2; + } + for (uint32_t uiNoResidualPass = iterationBegin; uiNoResidualPass < iteration; ++uiNoResidualPass) +#endif { for( uint32_t uiMrgHADIdx = 0; uiMrgHADIdx < uiNumMrgSATDCand; uiMrgHADIdx++ ) { @@ -2514,8 +2584,23 @@ void EncCu::xCheckRDCostMergeTriangle2Nx2N( CodingStructure *&tempCS, CodingStru } { +#if !JVET_M0253_HASH_ME const uint8_t iteration = encTestMode.lossless ? 1 : 2; for( uint8_t noResidualPass = 0; noResidualPass < iteration; noResidualPass++ ) +#else + uint8_t iteration; + uint8_t iterationBegin = m_modeCtrl->getIsHashPerfectMatch() ? 1 : 0; + if (encTestMode.lossless) + { + iteration = 1; + iterationBegin = 0; + } + else + { + iteration = 2; + } + for (uint8_t noResidualPass = iterationBegin; noResidualPass < iteration; ++noResidualPass) +#endif { for( uint8_t mrgHADIdx = 0; mrgHADIdx < triangleNumMrgSATDCand; mrgHADIdx++ ) { @@ -2774,10 +2859,25 @@ void EncCu::xCheckRDCostAffineMerge2Nx2N( CodingStructure *&tempCS, CodingStruct } } +#if !JVET_M0253_HASH_ME const uint32_t iteration = encTestMode.lossless ? 1 : 2; // 2. Pass: check candidates using full RD test for ( uint32_t uiNoResidualPass = 0; uiNoResidualPass < iteration; uiNoResidualPass++ ) +#else + uint32_t iteration; + uint32_t iterationBegin = m_modeCtrl->getIsHashPerfectMatch() ? 1 : 0; + if (encTestMode.lossless) + { + iteration = 1; + iterationBegin = 0; + } + else + { + iteration = 2; + } + for (uint32_t uiNoResidualPass = iterationBegin; uiNoResidualPass < iteration; ++uiNoResidualPass) +#endif { for ( uint32_t uiMrgHADIdx = 0; uiMrgHADIdx < uiNumMrgSATDCand; uiMrgHADIdx++ ) { diff --git a/source/Lib/EncoderLib/EncCu.h b/source/Lib/EncoderLib/EncCu.h index 920e0eca595016e272842c99c0eb4cf9f29e72dd..a700255dc5cd032c0c45d355d22e9f573a23d081 100644 --- a/source/Lib/EncoderLib/EncCu.h +++ b/source/Lib/EncoderLib/EncCu.h @@ -210,6 +210,9 @@ protected: void xCheckDQP ( CodingStructure& cs, Partitioner& partitioner, bool bKeepCtx = false); void xFillPCMBuffer ( CodingUnit &cu); +#if JVET_M0253_HASH_ME + void xCheckRDCostHashInter ( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm, const EncTestMode& encTestMode ); +#endif void xCheckRDCostAffineMerge2Nx2N ( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode ); void xCheckRDCostInter ( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm, const EncTestMode& encTestMode ); diff --git a/source/Lib/EncoderLib/EncGOP.cpp b/source/Lib/EncoderLib/EncGOP.cpp index bc863bb6c78e3f4f93fcdc40f4ef2558c89ccb71..62866d4e686312cb5e2db017977bca34fadfdffe 100644 --- a/source/Lib/EncoderLib/EncGOP.cpp +++ b/source/Lib/EncoderLib/EncGOP.cpp @@ -1643,6 +1643,75 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic, // Set reference list pcSlice->setRefPicList ( rcListPic ); +#if JVET_M0253_HASH_ME + if (m_pcCfg->getUseHashME()) + { + PicList::iterator iterPic = rcListPic.begin(); + while (iterPic != rcListPic.end()) + { + Picture* refPic = *(iterPic++); + + if (refPic->poc != pcPic->poc && refPic->referenced) + { + if (!refPic->getHashMap()->isInitial()) + { + if (refPic->getPOC() == 0) + { + Pel* picSrc = refPic->getOrigBuf().get(COMPONENT_Y).buf; + int stridePic = refPic->getOrigBuf().get(COMPONENT_Y).stride; + int picWidth = pcSlice->getSPS()->getPicWidthInLumaSamples(); + int picHeight = pcSlice->getSPS()->getPicHeightInLumaSamples(); + int blockSize = 4; + int allNum = 0; + int simpleNum = 0; + for (int j = 0; j <= picHeight - blockSize; j += blockSize) + { + for (int i = 0; i <= picWidth - blockSize; i += blockSize) + { + Pel* curBlock = picSrc + j * stridePic + i; + bool isHorSame = true; + for (int m = 0; m < blockSize&&isHorSame; m++) + { + for (int n = 1; n < blockSize&&isHorSame; n++) + { + if (curBlock[m*stridePic] != curBlock[m*stridePic + n]) + { + isHorSame = false; + } + } + } + bool isVerSame = true; + for (int m = 1; m < blockSize&&isVerSame; m++) + { + for (int n = 0; n < blockSize&&isVerSame; n++) + { + if (curBlock[n] != curBlock[m*stridePic + n]) + { + isVerSame = false; + } + } + } + allNum++; + if (isHorSame || isVerSame) + { + simpleNum++; + } + } + } + + if (simpleNum < 0.3*allNum) + { + m_pcCfg->setUseHashME(false); + break; + } + } + refPic->addPictureToHashMapForInter(); + } + } + } + + } +#endif if( m_pcCfg->getUseAMaxBT() ) { if( !pcSlice->isIRAP() ) diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp index 4d8506f0217d3292f6c752caf591887c6d7e76a7..4176ced62d04ec2191e9deae1a3b5aca194dae38 100644 --- a/source/Lib/EncoderLib/EncLib.cpp +++ b/source/Lib/EncoderLib/EncLib.cpp @@ -84,9 +84,9 @@ void EncLib::create () { // initialize global variables initROM(); - - - +#if JVET_M0253_HASH_ME + TComHash::initBlockSizeToIndex(); +#endif m_iPOCLast = m_compositeRefEnabled ? -2 : -1; // create processing unit classes m_cGOPEncoder. create( ); @@ -756,6 +756,9 @@ void EncLib::xGetNewPicBuffer ( std::list<PelUnitBuf*>& rcListPicYuvRecOut, Pict rpcPic->setBorderExtension( false ); rpcPic->reconstructed = false; rpcPic->referenced = true; +#if JVET_M0253_HASH_ME + rpcPic->getHashMap()->clearAll(); +#endif m_iPOCLast += (m_compositeRefEnabled ? 2 : 1); m_iNumPicRcvd++; diff --git a/source/Lib/EncoderLib/EncModeCtrl.cpp b/source/Lib/EncoderLib/EncModeCtrl.cpp index ecd823301016b267b8f0fe099da56a9820289c67..560a59cf5cb805694c73e311f5cbabb52ee2baeb 100644 --- a/source/Lib/EncoderLib/EncModeCtrl.cpp +++ b/source/Lib/EncoderLib/EncModeCtrl.cpp @@ -1183,6 +1183,15 @@ void EncModeCtrlMTnoRQT::initCULevel( Partitioner &partitioner, const CodingStru m_ComprCUCtxList.back().testModes.push_back( { ETM_AFFINE, ETO_STANDARD, qp, lossless } ); } } +#if JVET_M0253_HASH_ME + if (m_pcEncCfg->getUseHashME()) + { + 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)) + { + m_ComprCUCtxList.back().testModes.push_back({ ETM_HASH_INTER, ETO_STANDARD, qp, lossless }); + } + } +#endif } } @@ -1206,6 +1215,12 @@ bool EncModeCtrlMTnoRQT::tryMode( const EncTestMode& encTestmode, const CodingSt ComprCUCtx& cuECtx = m_ComprCUCtxList.back(); // Fast checks, partitioning depended +#if JVET_M0253_HASH_ME + if (cuECtx.isHashPerfectMatch && encTestmode.type != ETM_MERGE_SKIP && encTestmode.type != ETM_AFFINE && encTestmode.type != ETM_MERGE_TRIANGLE) + { + return false; + } +#endif // if early skip detected, skip all modes checking but the splits if( cuECtx.earlySkip && m_pcEncCfg->getUseEarlySkipDetection() && !isModeSplit( encTestmode ) && !( isModeInter( encTestmode ) ) ) diff --git a/source/Lib/EncoderLib/EncModeCtrl.h b/source/Lib/EncoderLib/EncModeCtrl.h index 422e4c80840c1bd6b3960c1b1abe75129e6c0249..b44f18c7d9a35d84d4cec94f83c7c85b4ddb6f8f 100644 --- a/source/Lib/EncoderLib/EncModeCtrl.h +++ b/source/Lib/EncoderLib/EncModeCtrl.h @@ -54,6 +54,9 @@ enum EncTestModeType { +#if JVET_M0253_HASH_ME + ETM_HASH_INTER, +#endif ETM_MERGE_SKIP, ETM_INTER_ME, ETM_AFFINE, @@ -137,6 +140,9 @@ inline bool isModeInter( const EncTestMode& encTestmode ) // perhaps remove || encTestmode.type == ETM_MERGE_SKIP || encTestmode.type == ETM_AFFINE || encTestmode.type == ETM_MERGE_TRIANGLE +#if JVET_M0253_HASH_ME + || encTestmode.type == ETM_HASH_INTER +#endif ); } @@ -178,6 +184,10 @@ struct ComprCUCtx , testModes ( ) , lastTestMode ( ) , earlySkip ( false ) +#if JVET_M0253_HASH_ME + , isHashPerfectMatch + ( false ) +#endif , bestCS ( nullptr ) , bestCU ( nullptr ) , bestTU ( nullptr ) @@ -212,6 +222,9 @@ struct ComprCUCtx std::vector<EncTestMode> testModes; EncTestMode lastTestMode; bool earlySkip; +#if JVET_M0253_HASH_ME + bool isHashPerfectMatch; +#endif CodingStructure *bestCS; CodingUnit *bestCU; TransformUnit *bestTU; @@ -287,6 +300,10 @@ public: EncTestMode currTestMode () const; EncTestMode lastTestMode () const; void setEarlySkipDetected (); +#if JVET_M0253_HASH_ME + void setIsHashPerfectMatch( bool b ) { m_ComprCUCtxList.back().isHashPerfectMatch = b; } + bool getIsHashPerfectMatch() { return m_ComprCUCtxList.back().isHashPerfectMatch; } +#endif virtual void setBest ( CodingStructure& cs ); bool anyMode () const; diff --git a/source/Lib/EncoderLib/InterSearch.cpp b/source/Lib/EncoderLib/InterSearch.cpp index 65d35faf49c6580fbe3c340ae2ce1b958e9ae45e..814e65b01a56d07be0eea1f012548819e6f02f45 100644 --- a/source/Lib/EncoderLib/InterSearch.cpp +++ b/source/Lib/EncoderLib/InterSearch.cpp @@ -697,6 +697,12 @@ Distortion InterSearch::xPatternRefinement( const CPelBuf* pcPatternKey, const Mv* pcMvRefine = (iFrac == 2 ? s_acMvRefineH : s_acMvRefineQ); for (uint32_t i = 0; i < 9; i++) { +#if JVET_M0253_HASH_ME + if (m_bSkipFracME && i > 0) + { + break; + } +#endif Mv cMvTest = pcMvRefine[i]; cMvTest += baseRefMv; @@ -1538,6 +1544,337 @@ void InterSearch::xxIBCHashSearch(PredictionUnit& pu, Mv* mvPred, int numMvPred, } +#if JVET_M0253_HASH_ME +void InterSearch::addToSortList(std::list<BlockHash>& listBlockHash, std::list<int>& listCost, int cost, const BlockHash& blockHash) +{ + std::list<BlockHash>::iterator itBlockHash = listBlockHash.begin(); + std::list<int>::iterator itCost = listCost.begin(); + + while (itCost != listCost.end()) + { + if (cost < (*itCost)) + { + listCost.insert(itCost, cost); + listBlockHash.insert(itBlockHash, blockHash); + return; + } + + ++itCost; + ++itBlockHash; + } + + listCost.push_back(cost); + listBlockHash.push_back(blockHash); +} + +void InterSearch::selectMatchesInter(const MapIterator& itBegin, int count, std::list<BlockHash>& listBlockHash, const BlockHash& currBlockHash) +{ + const int maxReturnNumber = 5; + + 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 currCost = RdCost::xGetExpGolombNumberOfBits((*it).x - currBlockHash.x) + + RdCost::xGetExpGolombNumberOfBits((*it).y - currBlockHash.y); + + if (listBlockHash.size() < maxReturnNumber) + { + addToSortList(listBlockHash, listCost, currCost, (*it)); + } + else if (!listCost.empty() && currCost < listCost.back()) + { + listCost.pop_back(); + listBlockHash.pop_back(); + addToSortList(listBlockHash, listCost, currCost, (*it)); + } + } +} + +Distortion InterSearch::getSAD(const Pel* refPel, const int refStride, const Pel* curPel, const int curStride, int width, int height, const BitDepths& bitDepths) +{ + Distortion dist = 0; + + for (int i = 0; i < height; i++) + { + for (int j = 0; j < width; j++) + { + dist += abs(refPel[j] - curPel[j]); + } + refPel += refStride; + curPel += curStride; + } + + if (bitDepths.recon[CHANNEL_TYPE_LUMA] == 8) + { + return dist; + } + else + { + int shift = DISTORTION_PRECISION_ADJUSTMENT(bitDepths.recon[CHANNEL_TYPE_LUMA]); + return dist >> shift; + } + return 0; +} + +int InterSearch::xHashInterPredME(const PredictionUnit& pu, RefPicList currRefPicList, int currRefPicIndex, Mv bestMv[5]) +{ + int width = pu.cu->lumaSize().width; + int height = pu.cu->lumaSize().height; + int xPos = pu.cu->lumaPos().x; + int yPos = pu.cu->lumaPos().y; + + unsigned int hashValue1; + unsigned int hashValue2; + + if (!TComHash::getBlockHashValue((pu.cs->picture->getOrigBuf()), width, height, xPos, yPos, pu.cu->slice->getSPS()->getBitDepths(), hashValue1, hashValue2)) + { + return 0; + } + BlockHash currBlockHash; + currBlockHash.x = xPos; + currBlockHash.y = yPos; + currBlockHash.hashValue2 = hashValue2; + + int count = static_cast<int>(pu.cu->slice->getRefPic(currRefPicList, currRefPicIndex)->getHashMap()->count(hashValue1)); + if (count == 0) + { + return 0; + } + + list<BlockHash> listBlockHash; + selectMatchesInter(pu.cu->slice->getRefPic(currRefPicList, currRefPicIndex)->getHashMap()->getFirstIterator(hashValue1), count, listBlockHash, currBlockHash); + + if (listBlockHash.empty()) + { + return 0; + } + + int totalSize = 0; + list<BlockHash>::iterator it = listBlockHash.begin(); + for (int i = 0; i < 5 && i < listBlockHash.size(); i++, it++) + { + bestMv[i].set((*it).x - currBlockHash.x, (*it).y - currBlockHash.y); + totalSize++; + } + + return totalSize; +} + +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; + int xPos = pu.cu->lumaPos().x; + int yPos = pu.cu->lumaPos().y; + + unsigned int hashValue1; + unsigned int hashValue2; + Distortion bestCost = UINT64_MAX; + + if (!TComHash::getBlockHashValue((pu.cs->picture->getOrigBuf()), width, height, xPos, yPos, pu.cu->slice->getSPS()->getBitDepths(), hashValue1, hashValue2)) + { + return false; + } + + BlockHash currBlockHash; + currBlockHash.x = xPos; + currBlockHash.y = yPos; + currBlockHash.hashValue2 = hashValue2; + + const int currStride = pu.cs->picture->getOrigBuf().get(COMPONENT_Y).stride; + const Pel* pCurr = pu.cs->picture->getOrigBuf().get(COMPONENT_Y).buf + (currBlockHash.y)*currStride + (currBlockHash.x); + + 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); + + if (pu.cs->slice->getSPS()->getSpsNext().getIBCMode() && eRefPicList == REF_PIC_LIST_0) + { + refPicNumber--; + } + + for (int refIdx = 0; refIdx < refPicNumber; refIdx++) + { + int bitsOnRefIdx = refIdx + 1; + if (refIdx + 1 == refPicNumber) + { + bitsOnRefIdx--; + } + + if (refList == 0 || pu.cu->slice->getList1IdxToList0Idx(refIdx) < 0) + { + int count = static_cast<int>(pu.cu->slice->getRefPic(eRefPicList, refIdx)->getHashMap()->count(hashValue1)); + if (count == 0) + { + continue; + } + + list<BlockHash> listBlockHash; + selectMatchesInter(pu.cu->slice->getRefPic(eRefPicList, refIdx)->getHashMap()->getFirstIterator(hashValue1), count, listBlockHash, currBlockHash); + + if (listBlockHash.empty()) + { + continue; + } + AMVPInfo currAMVPInfoPel; + AMVPInfo currAMVPInfo4Pel; + pu.cu->imv = 2; + PU::fillMvpCand(pu, eRefPicList, refIdx, currAMVPInfo4Pel); + pu.cu->imv = 1; + PU::fillMvpCand(pu, eRefPicList, refIdx, currAMVPInfoPel); + CHECK(currAMVPInfoPel.numCand <= 1, "Wrong") + + const Pel* pRefStart = 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_pcRdCost->selectMotionLambda(pu.cu->transQuantBypass); + m_pcRdCost->setCostScale(0); + + list<BlockHash>::iterator it; + 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); + + for (int mvpIdxTemp = 0; mvpIdxTemp < 2; mvpIdxTemp++) + { + Mv cMvPredPel = currAMVPInfoPel.mvCand[mvpIdxTemp]; + cMvPredPel >>= 2; + 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 = 1; + } + + if ((cMv.getHor() % 4 == 0) && (cMv.getVer() % 4 == 0)) + { + unsigned int bitsMVP4Pel = MAX_UINT; + Mv mvPredQuadPel = currAMVPInfo4Pel.mvCand[mvpIdxTemp]; + mvPredQuadPel >>= 4; + m_pcRdCost->setPredictor(mvPredQuadPel); + bitsMVP4Pel = m_pcRdCost->getBitsOfVectorWithPredictor(cMv.getHor() >> 2, cMv.getVer() >> 2, 0); + + if (bitsMVP4Pel < curMVPbits) + { + curMVPbits = bitsMVP4Pel; + curMVPIdx = mvpIdxTemp; + pu.cu->imv = 2; + } + } + + } + + const Pel* refPel = pRefStart + (*it).y*refStride + (*it).x; + Distortion currSad = getSAD(refPel, refStride, pCurr, currStride, width, height, pu.cu->slice->getSPS()->getBitDepths()); + curMVPbits += bitsOnRefIdx; + 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) + { + cMv <<= 2; + 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]; + } + } + } + } + } + } + pu.cu->imv = imvBest; + if (bestMvd == Mv(0, 0)) + { + pu.cu->imv = 0; + return false; + } + return (bestCost < MAX_INT); +} + +bool InterSearch::predInterHashSearch(CodingUnit& cu, Partitioner& partitioner, bool& isPerfectMatch) +{ + Mv bestMv, bestMvd; + RefPicList bestRefPicList; + int bestRefIndex; + int bestMVPIndex; + + auto &pu = *cu.firstPU; + + Mv cMvZero; + pu.mv[REF_PIC_LIST_0] = Mv(); + pu.mv[REF_PIC_LIST_1] = Mv(); + pu.mvd[REF_PIC_LIST_0] = cMvZero; + pu.mvd[REF_PIC_LIST_1] = cMvZero; + pu.refIdx[REF_PIC_LIST_0] = NOT_VALID; + pu.refIdx[REF_PIC_LIST_1] = NOT_VALID; + pu.mvpIdx[REF_PIC_LIST_0] = NOT_VALID; + pu.mvpIdx[REF_PIC_LIST_1] = NOT_VALID; + pu.mvpNum[REF_PIC_LIST_0] = NOT_VALID; + pu.mvpNum[REF_PIC_LIST_1] = NOT_VALID; + + if (xHashInterEstimation(pu, bestRefPicList, bestRefIndex, bestMv, bestMvd, bestMVPIndex, isPerfectMatch)) + { + pu.interDir = static_cast<int>(bestRefPicList) + 1; + pu.mv[bestRefPicList] = bestMv; + pu.mv[bestRefPicList].hor <<= MV_FRACTIONAL_BITS_DIFF; + pu.mv[bestRefPicList].ver <<= MV_FRACTIONAL_BITS_DIFF; + + pu.mvd[bestRefPicList] = bestMvd; + pu.refIdx[bestRefPicList] = bestRefIndex; + pu.mvpIdx[bestRefPicList] = bestMVPIndex; + + pu.mvpNum[bestRefPicList] = 2; + + PU::spanMotionInfo(pu); + PelUnitBuf predBuf = pu.cs->getPredBuf(pu); + motionCompensation(pu, predBuf, REF_PIC_LIST_X); + return true; + } + else + { + return false; + } + + return true; +} +#endif + //! search of the best candidate for inter prediction void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner) @@ -2656,7 +2993,11 @@ void InterSearch::xMotionEstimation(PredictionUnit& pu, PelUnitBuf& origBuf, Ref { setWpScalingDistParam(iRefIdxPred, eRefPicList, pu.cu->slice); } - +#if JVET_M0253_HASH_ME + m_currRefPicList = eRefPicList; + m_currRefPicIndex = iRefIdxPred; + m_bSkipFracME = false; +#endif // Do integer search if( ( m_motionEstimationSearchMethod == MESEARCH_FULL ) || bBi || bQTBTMV ) { @@ -2939,6 +3280,31 @@ void InterSearch::xTZSearch( const PredictionUnit& pu, , cStruct ); } +#if JVET_M0253_HASH_ME + if (m_pcEncCfg->getUseHashME()) + { + int width = pu.cu->lumaSize().width; + int height = pu.cu->lumaSize().height; + if ((width == height && width <= 64 && width >= 4) || (width == 8 && height == 4) || (width == 4 && height == 8)) + { + Mv otherMvps[5]; + int numberOfOtherMvps; + numberOfOtherMvps = xHashInterPredME(pu, m_currRefPicList, m_currRefPicIndex, otherMvps); + for (int i = 0; i < numberOfOtherMvps; i++) + { + xTZSearchHelp(cStruct, otherMvps[i].getHor(), otherMvps[i].getVer(), 0, 0); + } + if (numberOfOtherMvps > 0) + { + // write out best match + rcMv.set(cStruct.iBestX, cStruct.iBestY); + ruiSAD = cStruct.uiBestSad - m_pcRdCost->getCostOfVectorWithPredictor(cStruct.iBestX, cStruct.iBestY, cStruct.imvShift); + m_bSkipFracME = true; + return; + } + } + } +#endif // start search int iDist = 0; @@ -3196,6 +3562,33 @@ void InterSearch::xTZSearchSelective( const PredictionUnit& pu, ); } +#if JVET_M0253_HASH_ME + if (m_pcEncCfg->getUseHashME()) + { + int width = pu.cu->lumaSize().width; + int height = pu.cu->lumaSize().height; + if ((width == height && width <= 64 && width >= 4) || (width == 8 && height == 4) || (width == 4 && height == 8)) + { + Mv otherMvps[5]; + int numberOfOtherMvps; + numberOfOtherMvps = xHashInterPredME(pu, m_currRefPicList, m_currRefPicIndex, otherMvps); + for (int i = 0; i < numberOfOtherMvps; i++) + { + xTZSearchHelp(cStruct, otherMvps[i].getHor(), otherMvps[i].getVer(), 0, 0); + } + + if (numberOfOtherMvps > 0) + { + // write out best match + rcMv.set(cStruct.iBestX, cStruct.iBestY); + ruiSAD = cStruct.uiBestSad - m_pcRdCost->getCostOfVectorWithPredictor(cStruct.iBestX, cStruct.iBestY, cStruct.imvShift); + m_bSkipFracME = true; + return; + } + } + } +#endif + // Initial search int iBestX = cStruct.iBestX; int iBestY = cStruct.iBestY; @@ -3383,6 +3776,18 @@ void InterSearch::xPatternSearchFracDIF( // Reference pattern initialization (integer scale) int iOffset = rcMvInt.getHor() + rcMvInt.getVer() * cStruct.iRefStride; CPelBuf cPatternRoi(cStruct.piRefY + iOffset, cStruct.iRefStride, *cStruct.pcPatternKey); +#if JVET_M0253_HASH_ME + if (m_bSkipFracME) + { + Mv baseRefMv(0, 0); + rcMvHalf.setZero(); + m_pcRdCost->setCostScale(0); + xExtDIFUpSamplingH(&cPatternRoi); + rcMvQter = rcMvInt; rcMvQter <<= 2; // for mv-cost + ruiCost = xPatternRefinement(cStruct.pcPatternKey, baseRefMv, 1, rcMvQter, !bIsLosslessCoded); + return; + } +#endif if (cStruct.imvShift || (pu.cs->sps->getSpsNext().getUseCompositeRef() && cStruct.zeroMV)) @@ -5235,11 +5640,24 @@ void InterSearch::xExtDIFUpSamplingH( CPelBuf* pattern ) const ChromaFormat chFmt = m_currChromaFormat; m_if.filterHor(COMPONENT_Y, srcPtr, srcStride, m_filteredBlockTmp[0][0], intStride, width + 1, height + filterSize, 0 << MV_FRACTIONAL_BITS_DIFF, false, chFmt, clpRng); +#if JVET_M0253_HASH_ME + if (!m_bSkipFracME) + { +#endif m_if.filterHor(COMPONENT_Y, srcPtr, srcStride, m_filteredBlockTmp[2][0], intStride, width + 1, height + filterSize, 2 << MV_FRACTIONAL_BITS_DIFF, false, chFmt, clpRng); +#if JVET_M0253_HASH_ME + } +#endif intPtr = m_filteredBlockTmp[0][0] + halfFilterSize * intStride + 1; dstPtr = m_filteredBlock[0][0][0]; m_if.filterVer(COMPONENT_Y, intPtr, intStride, dstPtr, dstStride, width + 0, height + 0, 0 << MV_FRACTIONAL_BITS_DIFF, false, true, chFmt, clpRng); +#if JVET_M0253_HASH_ME + if (m_bSkipFracME) + { + return; + } +#endif intPtr = m_filteredBlockTmp[0][0] + (halfFilterSize - 1) * intStride + 1; dstPtr = m_filteredBlock[2][0][0]; diff --git a/source/Lib/EncoderLib/InterSearch.h b/source/Lib/EncoderLib/InterSearch.h index da9abdf1bea40095271e8b8f1c0eede5da81fddc..51fbd0954f4ef07228ef9de472b4d33979e327c5 100644 --- a/source/Lib/EncoderLib/InterSearch.h +++ b/source/Lib/EncoderLib/InterSearch.h @@ -52,6 +52,9 @@ #include "CommonLib/AffineGradientSearch.h" #include "CommonLib/IbcHashMap.h" +#if JVET_M0253_HASH_ME +#include "CommonLib/Hash.h" +#endif #include <unordered_map> #include <vector> //! \ingroup EncoderLib @@ -138,6 +141,12 @@ protected: CtxCache* m_CtxCache; DistParam m_cDistParam; +#if JVET_M0253_HASH_ME + RefPicList m_currRefPicList; + int m_currRefPicIndex; + bool m_bSkipFracME; +#endif + // Misc. Pel *m_pTempPel; @@ -264,6 +273,14 @@ public: void xIBCEstimation ( PredictionUnit& pu, PelUnitBuf& origBuf, Mv *pcMvPred, Mv &rcMv, Distortion &ruiCost, const int localSearchRangeX, const int localSearchRangeY); void xIBCSearchMVCandUpdate ( Distortion uiSad, int x, int y, Distortion* uiSadBestCand, Mv* cMVCand); int xIBCSearchMVChromaRefine( PredictionUnit& pu, int iRoiWidth, int iRoiHeight, int cuPelX, int cuPelY, Distortion* uiSadBestCand, Mv* cMVCand); +#if JVET_M0253_HASH_ME + void addToSortList(std::list<BlockHash>& listBlockHash, std::list<int>& listCost, int cost, const BlockHash& blockHash); + Distortion getSAD(const Pel* refPel, const int refStride, const Pel* curPel, const int curStride, int width, int height, const BitDepths& bitDepths); + bool predInterHashSearch(CodingUnit& cu, Partitioner& partitioner, bool& isPerfectMatch); + bool xHashInterEstimation(PredictionUnit& pu, RefPicList& bestRefPicList, int& bestRefIndex, Mv& bestMv, Mv& bestMvd, int& bestMVPIndex, bool& isPerfectMatch); + int xHashInterPredME(const PredictionUnit& pu, RefPicList currRefPicList, int currRefPicIndex, Mv bestMv[5]); + void selectMatchesInter(const MapIterator& itBegin, int count, std::list<BlockHash>& vecBlockHash, const BlockHash& currBlockHash); +#endif protected: // -------------------------------------------------------------------------------------------------------------------