[x265] [PATCH] noise reduction feature, ported from x264
praveen at multicorewareinc.com
praveen at multicorewareinc.com
Tue May 20 09:20:43 CEST 2014
# HG changeset patch
# User Praveen Tiwari
# Date 1400570170 -19800
# Node ID 5dd459c0b7a0c4d7a4c194323d00fdc044f2ba13
# Parent b35a5d8f012b5d2d45865bf2a1419df2cd8087d6
noise reduction feature, ported from x264
diff -r b35a5d8f012b -r 5dd459c0b7a0 doc/reST/cli.rst
--- a/doc/reST/cli.rst Sun May 18 15:02:27 2014 +0900
+++ b/doc/reST/cli.rst Tue May 20 12:46:10 2014 +0530
@@ -184,6 +184,14 @@
picture. Only applicable when the input bit depth is larger than
8bits and internal bit depth is 8bits. Default disabled
+.. option:: --nr
+
+ It's just an adaptive deadzone applied after DCT (subtracting from DCT coefficients),
+ before quantization, on inter blocks. It does no pixel-level filtering, doesn't cross
+ DCT block boundaries, has no overlap, doesn't affect intra blocks.
+
+ **Values:** any value in range of 100 to 1000. Default disabled.
+
**CLI ONLY**
.. option:: --input-res <wxh>
diff -r b35a5d8f012b -r 5dd459c0b7a0 source/Lib/TLibCommon/TComRom.h
--- a/source/Lib/TLibCommon/TComRom.h Sun May 18 15:02:27 2014 +0900
+++ b/source/Lib/TLibCommon/TComRom.h Tue May 20 12:46:10 2014 +0530
@@ -278,6 +278,10 @@
// CABAC tables
extern const uint8_t g_lpsTable[64][4];
extern const uint8_t x265_exp2_lut[64];
+
+// DCT denoise tables
+extern const uint32_t g_dctDenoiseWeight4x4[16];
+extern const uint32_t g_dctDenoiseWeight8x8[64];
}
#endif //ifndef X265_TCOMROM_H
diff -r b35a5d8f012b -r 5dd459c0b7a0 source/Lib/TLibCommon/TComTrQuant.cpp
--- a/source/Lib/TLibCommon/TComTrQuant.cpp Sun May 18 15:02:27 2014 +0900
+++ b/source/Lib/TLibCommon/TComTrQuant.cpp Tue May 20 12:46:10 2014 +0530
@@ -95,6 +95,19 @@
destroyScalingList();
}
+static void denoiseDct(coeff_t* dctCoef, uint32_t* resSum, uint16_t* offset, int size)
+{
+ for (int i = 0; i < size; i++)
+ {
+ int level = dctCoef[i];
+ int sign = level >> 31;
+ level = (level + sign) ^ sign;
+ resSum[i] += level;
+ level -= offset[i];
+ dctCoef[i] = level < 0 ? 0 : (level ^ sign) - sign;
+ }
+}
+
/** Set qP for Quantization.
* \param qpy QPy
* \param bLowpass
@@ -300,16 +313,17 @@
m_useTransformSkipFast = useTransformSkipFast;
}
-uint32_t TComTrQuant::transformNxN(TComDataCU* cu,
- int16_t* residual,
- uint32_t stride,
- coeff_t* coeff,
- uint32_t trSize,
- TextType ttype,
- uint32_t absPartIdx,
- int32_t* lastPos,
- bool useTransformSkip,
- bool curUseRDOQ)
+uint32_t TComTrQuant::transformNxN(TComDataCU* cu,
+ int16_t* residual,
+ uint32_t stride,
+ coeff_t* coeff,
+ uint32_t trSize,
+ TextType ttype,
+ uint32_t absPartIdx,
+ int32_t* lastPos,
+ NoiseReduction* nr,
+ bool useTransformSkip,
+ bool curUseRDOQ)
{
if (cu->getCUTransquantBypass(absPartIdx))
{
@@ -346,6 +360,15 @@
// TODO: this may need larger data types for X265_DEPTH > 8
const uint32_t log2BlockSize = g_convertToBit[trSize];
primitives.dct[DCT_4x4 + log2BlockSize - ((trSize == 4) && (mode != REG_DCT))](residual, m_tmpCoeff, stride);
+ if (nr->bNoiseReduction)
+ {
+ int index = (DCT_4x4 + log2BlockSize - ((trSize == 4) && (mode != REG_DCT)));
+ if (index > 0 && index < 5)
+ {
+ denoiseDct(m_tmpCoeff, nr->residualSum[index - 1], nr->offset[index - 1], (16 << (index - 1) * 2 ));
+ nr->count[index - 1]++;
+ }
+ }
}
return xQuant(cu, m_tmpCoeff, coeff, trSize, ttype, absPartIdx, lastPos, curUseRDOQ);
}
diff -r b35a5d8f012b -r 5dd459c0b7a0 source/Lib/TLibCommon/TComTrQuant.h
--- a/source/Lib/TLibCommon/TComTrQuant.h Sun May 18 15:02:27 2014 +0900
+++ b/source/Lib/TLibCommon/TComTrQuant.h Tue May 20 12:46:10 2014 +0530
@@ -128,7 +128,7 @@
// transform & inverse transform functions
uint32_t transformNxN(TComDataCU* cu, int16_t* residual, uint32_t stride, coeff_t* coeff, uint32_t trSize,
- TextType ttype, uint32_t absPartIdx, int32_t* lastPos, bool useTransformSkip = false, bool curUseRDOQ = true);
+ TextType ttype, uint32_t absPartIdx, int32_t* lastPos, NoiseReduction* nr, bool useTransformSkip = false, bool curUseRDOQ = true);
void invtransformNxN(bool transQuantBypass, uint32_t mode, int16_t* residual, uint32_t stride, coeff_t* coeff, uint32_t trSize, int scalingListType, bool useTransformSkip = false, int lastPos = MAX_INT);
diff -r b35a5d8f012b -r 5dd459c0b7a0 source/Lib/TLibEncoder/TEncSearch.cpp
--- a/source/Lib/TLibEncoder/TEncSearch.cpp Sun May 18 15:02:27 2014 +0900
+++ b/source/Lib/TLibEncoder/TEncSearch.cpp Tue May 20 12:46:10 2014 +0530
@@ -465,7 +465,7 @@
m_trQuant->setQPforQuant(cu->getQP(0), TEXT_LUMA, QP_BD_OFFSET, 0, chFmt);
m_trQuant->selectLambda(TEXT_LUMA);
- absSum = m_trQuant->transformNxN(cu, residual, stride, coeff, tuSize, TEXT_LUMA, absPartIdx, &lastPos, useTransformSkip);
+ absSum = m_trQuant->transformNxN(cu, residual, stride, coeff, tuSize, TEXT_LUMA, absPartIdx, &lastPos, &(m_cfg->m_nr), useTransformSkip);
//--- set coded block flag ---
cu->setCbfSubParts((absSum ? 1 : 0) << trDepth, TEXT_LUMA, absPartIdx, fullDepth);
@@ -589,7 +589,7 @@
m_trQuant->selectLambda(TEXT_CHROMA);
- absSum = m_trQuant->transformNxN(cu, residual, stride, coeff, tuSize, ttype, absPartIdx, &lastPos, useTransformSkipChroma);
+ absSum = m_trQuant->transformNxN(cu, residual, stride, coeff, tuSize, ttype, absPartIdx, &lastPos, &(m_cfg->m_nr), useTransformSkipChroma);
//--- set coded block flag ---
cu->setCbfPartRange((((absSum > 0) ? 1 : 0) << origTrDepth), ttype, absPartIdx, absPartIdxStep);
@@ -902,7 +902,7 @@
m_trQuant->setQPforQuant(cu->getQP(0), TEXT_LUMA, QP_BD_OFFSET, 0, chFmt);
m_trQuant->selectLambda(TEXT_LUMA);
- absSum = m_trQuant->transformNxN(cu, residual, stride, coeff, tuSize, TEXT_LUMA, absPartIdx, &lastPos, useTransformSkip);
+ absSum = m_trQuant->transformNxN(cu, residual, stride, coeff, tuSize, TEXT_LUMA, absPartIdx, &lastPos, &(m_cfg->m_nr), useTransformSkip);
//--- set coded block flag ---
cu->setCbfSubParts((absSum ? 1 : 0) << trDepth, TEXT_LUMA, absPartIdx, fullDepth);
@@ -1497,7 +1497,7 @@
m_trQuant->selectLambda(TEXT_CHROMA);
- absSum = m_trQuant->transformNxN(cu, residual, stride, coeff, tuSize, ttype, absTUPartIdxC, &lastPos, useTransformSkipChroma);
+ absSum = m_trQuant->transformNxN(cu, residual, stride, coeff, tuSize, ttype, absTUPartIdxC, &lastPos, &(m_cfg->m_nr), useTransformSkipChroma);
//--- set coded block flag ---
cu->setCbfPartRange((((absSum > 0) ? 1 : 0) << origTrDepth), ttype, absTUPartIdxC, tuIterator.m_absPartIdxStep);
@@ -2901,7 +2901,7 @@
m_trQuant->selectLambda(TEXT_LUMA);
absSumY = m_trQuant->transformNxN(cu, resiYuv->getLumaAddr(absTUPartIdx), resiYuv->m_width, coeffCurY,
- trWidth, TEXT_LUMA, absPartIdx, &lastPosY, false, curuseRDOQ);
+ trWidth, TEXT_LUMA, absPartIdx, &lastPosY, &(m_cfg->m_nr), false, curuseRDOQ);
cu->setCbfSubParts(absSumY ? setCbf : 0, TEXT_LUMA, absPartIdx, depth);
@@ -2945,12 +2945,12 @@
m_trQuant->selectLambda(TEXT_CHROMA);
absSumU = m_trQuant->transformNxN(cu, resiYuv->getCbAddr(absTUPartIdxC), resiYuv->m_cwidth, coeffCurU + subTUBufferOffset,
- trWidthC, TEXT_CHROMA_U, absTUPartIdxC, &lastPosU, false, curuseRDOQ);
+ trWidthC, TEXT_CHROMA_U, absTUPartIdxC, &lastPosU, &(m_cfg->m_nr), false, curuseRDOQ);
curChromaQpOffset = cu->getSlice()->getPPS()->getChromaCrQpOffset() + cu->getSlice()->getSliceQpDeltaCr();
m_trQuant->setQPforQuant(cu->getQP(0), TEXT_CHROMA, cu->getSlice()->getSPS()->getQpBDOffsetC(), curChromaQpOffset, chFmt);
absSumV = m_trQuant->transformNxN(cu, resiYuv->getCrAddr(absTUPartIdxC), resiYuv->m_cwidth, coeffCurV + subTUBufferOffset,
- trWidthC, TEXT_CHROMA_V, absTUPartIdxC, &lastPosV, false, curuseRDOQ);
+ trWidthC, TEXT_CHROMA_V, absTUPartIdxC, &lastPosV, &(m_cfg->m_nr), false, curuseRDOQ);
cu->setCbfPartRange(absSumU ? setCbf : 0, TEXT_CHROMA_U, absTUPartIdxC, tuIterator.m_absPartIdxStep);
cu->setCbfPartRange(absSumV ? setCbf : 0, TEXT_CHROMA_V, absTUPartIdxC, tuIterator.m_absPartIdxStep);
@@ -3118,7 +3118,7 @@
m_trQuant->selectLambda(TEXT_LUMA);
absSum[TEXT_LUMA][0] = m_trQuant->transformNxN(cu, resiYuv->getLumaAddr(absTUPartIdx), resiYuv->m_width, coeffCurY,
- trWidth, TEXT_LUMA, absPartIdx, &lastPos[TEXT_LUMA][0], false, curuseRDOQ);
+ trWidth, TEXT_LUMA, absPartIdx, &lastPos[TEXT_LUMA][0], &(m_cfg->m_nr), false, curuseRDOQ);
cu->setCbfSubParts(absSum[TEXT_LUMA][0] ? setCbf : 0, TEXT_LUMA, absPartIdx, depth);
@@ -3156,12 +3156,12 @@
m_trQuant->selectLambda(TEXT_CHROMA);
absSum[TEXT_CHROMA_U][tuIterator.m_section] = m_trQuant->transformNxN(cu, resiYuv->getCbAddr(tuIterator.m_absPartIdxTURelCU), resiYuv->m_cwidth, coeffCurU + subTUBufferOffset,
- widthC, TEXT_CHROMA_U, tuIterator.m_absPartIdxTURelCU, &lastPos[TEXT_CHROMA_U][tuIterator.m_section], false, curuseRDOQ);
+ widthC, TEXT_CHROMA_U, tuIterator.m_absPartIdxTURelCU, &lastPos[TEXT_CHROMA_U][tuIterator.m_section], &(m_cfg->m_nr), false, curuseRDOQ);
//Cr transform
curChromaQpOffset = cu->getSlice()->getPPS()->getChromaCrQpOffset() + cu->getSlice()->getSliceQpDeltaCr();
m_trQuant->setQPforQuant(cu->getQP(0), TEXT_CHROMA, cu->getSlice()->getSPS()->getQpBDOffsetC(), curChromaQpOffset, chFmt);
absSum[TEXT_CHROMA_V][tuIterator.m_section] = m_trQuant->transformNxN(cu, resiYuv->getCrAddr(tuIterator.m_absPartIdxTURelCU), resiYuv->m_cwidth, coeffCurV + subTUBufferOffset,
- widthC, TEXT_CHROMA_V, tuIterator.m_absPartIdxTURelCU, &lastPos[TEXT_CHROMA_V][tuIterator.m_section], false, curuseRDOQ);
+ widthC, TEXT_CHROMA_V, tuIterator.m_absPartIdxTURelCU, &lastPos[TEXT_CHROMA_V][tuIterator.m_section], &(m_cfg->m_nr), false, curuseRDOQ);
cu->setCbfPartRange(absSum[TEXT_CHROMA_U][tuIterator.m_section] ? setCbf : 0, TEXT_CHROMA_U, tuIterator.m_absPartIdxTURelCU, tuIterator.m_absPartIdxStep);
cu->setCbfPartRange(absSum[TEXT_CHROMA_V][tuIterator.m_section] ? setCbf : 0, TEXT_CHROMA_V, tuIterator.m_absPartIdxTURelCU, tuIterator.m_absPartIdxStep);
@@ -3451,7 +3451,7 @@
m_trQuant->selectLambda(TEXT_LUMA);
absSumTransformSkipY = m_trQuant->transformNxN(cu, resiYuv->getLumaAddr(absTUPartIdx), resiYuv->m_width, coeffCurY,
- trWidth, TEXT_LUMA, absPartIdx, &lastPosTransformSkip[TEXT_LUMA][0], true, curuseRDOQ);
+ trWidth, TEXT_LUMA, absPartIdx, &lastPosTransformSkip[TEXT_LUMA][0], &(m_cfg->m_nr), true, curuseRDOQ);
cu->setCbfSubParts(absSumTransformSkipY ? setCbf : 0, TEXT_LUMA, absPartIdx, depth);
if (absSumTransformSkipY)
@@ -3544,11 +3544,11 @@
m_trQuant->selectLambda(TEXT_CHROMA);
absSumTransformSkipU = m_trQuant->transformNxN(cu, resiYuv->getCbAddr(tuIterator.m_absPartIdxTURelCU), resiYuv->m_cwidth, coeffCurU + subTUBufferOffset,
- widthC, TEXT_CHROMA_U, tuIterator.m_absPartIdxTURelCU, &lastPosTransformSkip[TEXT_CHROMA_U][tuIterator.m_section], true, curuseRDOQ);
+ widthC, TEXT_CHROMA_U, tuIterator.m_absPartIdxTURelCU, &lastPosTransformSkip[TEXT_CHROMA_U][tuIterator.m_section], &(m_cfg->m_nr), true, curuseRDOQ);
curChromaQpOffset = cu->getSlice()->getPPS()->getChromaCrQpOffset() + cu->getSlice()->getSliceQpDeltaCr();
m_trQuant->setQPforQuant(cu->getQP(0), TEXT_CHROMA, cu->getSlice()->getSPS()->getQpBDOffsetC(), curChromaQpOffset, chFmt);
absSumTransformSkipV = m_trQuant->transformNxN(cu, resiYuv->getCrAddr(tuIterator.m_absPartIdxTURelCU), resiYuv->m_cwidth, coeffCurV + subTUBufferOffset,
- widthC, TEXT_CHROMA_V, tuIterator.m_absPartIdxTURelCU, &lastPosTransformSkip[TEXT_CHROMA_V][tuIterator.m_section], true, curuseRDOQ);
+ widthC, TEXT_CHROMA_V, tuIterator.m_absPartIdxTURelCU, &lastPosTransformSkip[TEXT_CHROMA_V][tuIterator.m_section], &(m_cfg->m_nr), true, curuseRDOQ);
cu->setCbfPartRange(absSumTransformSkipU ? setCbf : 0, TEXT_CHROMA_U, tuIterator.m_absPartIdxTURelCU, tuIterator.m_absPartIdxStep);
cu->setCbfPartRange(absSumTransformSkipV ? setCbf : 0, TEXT_CHROMA_V, tuIterator.m_absPartIdxTURelCU, tuIterator.m_absPartIdxStep);
diff -r b35a5d8f012b -r 5dd459c0b7a0 source/common/common.h
--- a/source/common/common.h Sun May 18 15:02:27 2014 +0900
+++ b/source/common/common.h Tue May 20 12:46:10 2014 +0530
@@ -177,6 +177,21 @@
#define X265_LOG2(x) log2(x)
#endif
+struct NoiseReduction
+{
+ bool bNoiseReduction;
+
+ /* 0 = luma 4x4, 1 = luma 8x8, 2 = luma 16x16, 3 = luma 32x32
+ * 4 = chroma 4x4, 5 = chroma 8x8, 6 = chroma 16x16, 7 = chroma 32x32 */
+ uint16_t (*offset)[1024];
+ uint32_t (*residualSum)[1024];
+ uint32_t *count;
+
+ uint16_t offsetDenoise[8][1024];
+ uint32_t residualSumBuf[4][8][1024];
+ uint32_t countBuf[4][8];
+};
+
/* defined in common.cpp */
int64_t x265_mdate(void);
void x265_log(const x265_param *param, int level, const char *fmt, ...);
diff -r b35a5d8f012b -r 5dd459c0b7a0 source/common/param.cpp
--- a/source/common/param.cpp Sun May 18 15:02:27 2014 +0900
+++ b/source/common/param.cpp Tue May 20 12:46:10 2014 +0530
@@ -694,6 +694,7 @@
&p->vui.defDispWinRightOffset,
&p->vui.defDispWinBottomOffset) != 4;
}
+ OPT("nr") p->noiseReduction = atoi(value);
else
return X265_PARAM_BAD_NAME;
#undef OPT
@@ -986,6 +987,8 @@
CHECK(param->rc.bitrate < 0,
"Target bitrate can not be less than zero");
CHECK(param->bFrameBias < 0, "Bias towards B frame decisions must be 0 or greater");
+ if (param->noiseReduction)
+ CHECK(100 > param->noiseReduction || param->noiseReduction > 1000, "Valid noise reduction range 100 - 1000");
return check_failed;
}
diff -r b35a5d8f012b -r 5dd459c0b7a0 source/encoder/encoder.cpp
--- a/source/encoder/encoder.cpp Sun May 18 15:02:27 2014 +0900
+++ b/source/encoder/encoder.cpp Tue May 20 12:46:10 2014 +0530
@@ -64,6 +64,14 @@
m_csvfpt = NULL;
param = NULL;
+ memset(m_nr.offsetDenoise, 0, sizeof(m_nr.offsetDenoise[0][0]) * 8 * 1024);
+ memset(m_nr.residualSumBuf, 0, sizeof(m_nr.residualSumBuf[0][0][0]) * 4 * 8 * 1024);
+ memset(m_nr.countBuf, 0, sizeof(m_nr.countBuf[0][0]) * 4 * 8);
+
+ m_nr.offset = m_nr.offsetDenoise;
+ m_nr.residualSum = m_nr.residualSumBuf[0];
+ m_nr.count = m_nr.countBuf[0];
+
#if ENC_DEC_TRACE
g_hTrace = fopen("TraceEnc.txt", "wb");
g_bJustDoIt = g_bEncDecTraceDisable;
@@ -186,6 +194,7 @@
}
m_lookahead->init();
m_encodeStartTime = x265_mdate();
+ m_nr.bNoiseReduction = !!param->noiseReduction;
}
int Encoder::getStreamHeaders(NALUnitEBSP **nalunits)
@@ -218,6 +227,44 @@
}
}
+/****************************************************************************
+ * DCT-domain noise reduction / adaptive deadzone
+ * from libavcodec
+ ****************************************************************************/
+
+void Encoder::noiseReductionUpdate(NoiseReduction* h)
+{
+ h->offset = h->offsetDenoise;
+ h->residualSum = h->residualSumBuf[0];
+ h->count = h->countBuf[0];
+
+ int transformSize[4] = {16, 64, 256, 1024};
+ uint32_t blockCount[4] = { 1 << 18, 1 << 16, 1 << 14, 1 << 12 };
+
+ int isCspI444 = (param->internalCsp == X265_CSP_I444) ? 1 : 0;
+ for (int cat = 0; cat < 7 + isCspI444; cat++)
+ {
+ int index = cat % 4;
+ int size = transformSize[index];
+
+ if (h->count[cat] > blockCount[index])
+ {
+ for (int i = 0; i < size; i++)
+ h->residualSum[cat][i] >>= 1;
+ h->count[cat] >>= 1;
+ }
+
+ for (int i = 0; i < size; i++)
+ h->offset[cat][i] =
+ (uint16_t)(((uint64_t)param->noiseReduction * h->count[cat]
+ + h->residualSum[cat][i] / 2)
+ / ((uint64_t)h->residualSum[cat][i] + 1));
+
+ // Don't denoise DC coefficients
+ h->offset[cat][0] = 0;
+ }
+}
+
#define VERBOSE_RATE 0
#if VERBOSE_RATE
static const char* nalUnitTypeToString(NalUnitType type)
@@ -434,6 +481,9 @@
m_rateControl->rateControlEnd(out, bits, &curEncoder->m_rce);
finishFrameStats(out, curEncoder, bits);
+ if (m_nr.bNoiseReduction)
+ noiseReductionUpdate(&m_nr);
+
// Allow this frame to be recycled if no frame encoders are using it for reference
ATOMIC_DEC(&out->m_countRefEncoders);
m_dpb->recycleUnreferenced(m_freeList);
diff -r b35a5d8f012b -r 5dd459c0b7a0 source/encoder/encoder.h
--- a/source/encoder/encoder.h Sun May 18 15:02:27 2014 +0900
+++ b/source/encoder/encoder.h Tue May 20 12:46:10 2014 +0530
@@ -194,6 +194,8 @@
x265_nal* m_nals;
char* m_packetData;
+ NoiseReduction m_nr;
+
Encoder();
virtual ~Encoder();
@@ -227,6 +229,8 @@
void updateVbvPlan(RateControl* rc);
+ void noiseReductionUpdate(NoiseReduction* nr);
+
protected:
void finishFrameStats(TComPic* pic, FrameEncoder *curEncoder, uint64_t bits);
diff -r b35a5d8f012b -r 5dd459c0b7a0 source/x265.cpp
--- a/source/x265.cpp Sun May 18 15:02:27 2014 +0900
+++ b/source/x265.cpp Tue May 20 12:46:10 2014 +0530
@@ -176,6 +176,7 @@
{ "qpfile", required_argument, NULL, 0 },
{ "b-intra", no_argument, NULL, 0 },
{ "no-b-intra", no_argument, NULL, 0 },
+ { "nr", required_argument, NULL, 0 },
{ 0, 0, 0, 0 }
};
@@ -414,6 +415,8 @@
H0("\nReconstructed video options (debugging):\n");
H0("-r/--recon <filename> Reconstructed raw image YUV or Y4M output file name\n");
H0(" --recon-depth <integer> Bit-depth of reconstructed raw image file. Defaults to input bit depth, or 8 if Y4M\n");
+ H0("\nNoiseReduction option:\n");
+ H0(" --nr <integer> An integer value in range of 100 to 1000, which denotes strength of noise reduction. Default disabled\n");
#undef OPT
#undef H0
printf("\n\nFull documentation may be found at http://x265.readthedocs.org/en/default/cli.html\n");
diff -r b35a5d8f012b -r 5dd459c0b7a0 source/x265.h
--- a/source/x265.h Sun May 18 15:02:27 2014 +0900
+++ b/source/x265.h Tue May 20 12:46:10 2014 +0530
@@ -646,6 +646,10 @@
* regardless of this setting. */
int bIntraInBFrames;
+ /* An integer value in range of 100 to 1000, which denotes strength of noise
+ * reduction */
+ int noiseReduction;
+
/*== Rate Control ==*/
struct
More information about the x265-devel
mailing list