[x265] [PATCH] Add Luma and Chroma offsets for HDR/WCG content
gopi.satykrishna at multicorewareinc.com
gopi.satykrishna at multicorewareinc.com
Wed Feb 15 11:58:54 CET 2017
# HG changeset patch
# User Gopi Satykrishna Akisetty <gopi.satykrishna at multicorewareinc.com>
# Date 1485948689 -19800
# Wed Feb 01 17:01:29 2017 +0530
# Node ID 959318c42635cec036b9fed8cf1dca5109b5343a
# Parent 912dd749bdb53cdd1e251bc3a69e4c41ece3b308
Add Luma and Chroma offsets for HDR/WCG content
diff -r 912dd749bdb5 -r 959318c42635 doc/reST/cli.rst
--- a/doc/reST/cli.rst Wed Feb 15 12:04:41 2017 +0530
+++ b/doc/reST/cli.rst Wed Feb 01 17:01:29 2017 +0530
@@ -414,6 +414,15 @@
4. nv12
5. nv16
+.. option:: --capture-csp <integer|string>
+
+ Specify color primaries of the Capture device. Default
+ bt2020.
+
+ 1. bt709
+ 2. p3d65
+ 3. bt2020
+
.. option:: --fps <integer|float|numerator/denominator>
YUV only: Source frame rate
@@ -1845,6 +1854,13 @@
automatically when :option`--master-display` or :option`--max-cll` is
specified. Useful when there is a desire to signal 0 values for max-cll
and max-fall. Default disabled.
+
+.. option:: --hdr-opt, --no-hdr-opt
+
+ Add luma and chroma offsets for HDR/WCG content.
+ Input video should be 10 bit 4:2:0. Also set the appropriate color primaries
+ of the capture device using :option:`--capture-csp` option. Applicable for HDR content.
+ Default disabled. **Experimental Feature**
.. option:: --min-luma <integer>
diff -r 912dd749bdb5 -r 959318c42635 source/CMakeLists.txt
--- a/source/CMakeLists.txt Wed Feb 15 12:04:41 2017 +0530
+++ b/source/CMakeLists.txt Wed Feb 01 17:01:29 2017 +0530
@@ -29,7 +29,7 @@
option(STATIC_LINK_CRT "Statically link C runtime for release builds" OFF)
mark_as_advanced(FPROFILE_USE FPROFILE_GENERATE NATIVE_BUILD)
# X265_BUILD must be incremented each time the public API is changed
-set(X265_BUILD 110)
+set(X265_BUILD 111)
configure_file("${PROJECT_SOURCE_DIR}/x265.def.in"
"${PROJECT_BINARY_DIR}/x265.def")
configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in"
diff -r 912dd749bdb5 -r 959318c42635 source/common/param.cpp
--- a/source/common/param.cpp Wed Feb 15 12:04:41 2017 +0530
+++ b/source/common/param.cpp Wed Feb 01 17:01:29 2017 +0530
@@ -270,6 +270,8 @@
param->bOptRefListLengthPPS = 1;
param->bOptCUDeltaQP = 0;
param->bAQMotion = 0;
+ param->bHDROpt = 0;
+ param->captureColorPrim = 3;
}
@@ -943,6 +945,11 @@
}
}
OPT("hdr") p->bEmitHDRSEI = atobool(value);
+ OPT("hdr-opt") p->bHDROpt = atobool(value);
+ OPT("capture-csp")
+ {
+ p->captureColorPrim = parseName(value, x265_capture_colorprim_names, bError);
+ }
else
return X265_PARAM_BAD_NAME;
}
@@ -1281,6 +1288,10 @@
"qpmin exceeds supported range (0 to 69)");
CHECK(param->log2MaxPocLsb < 4 || param->log2MaxPocLsb > 16,
"Supported range for log2MaxPocLsb is 4 to 16");
+ CHECK(param->captureColorPrim < 0
+ || param->captureColorPrim > 3,
+ "Capture Color Primaries must be bt709, p3d65,"
+ " bt2020");
#if !X86_64
CHECK(param->searchMethod == X265_SEA && (param->sourceWidth > 840 || param->sourceHeight > 480),
"SEA motion search does not support resolutions greater than 480p in 32 bit build");
@@ -1648,6 +1659,8 @@
BOOL(p->bOptCUDeltaQP, "opt-cu-delta-qp");
BOOL(p->bAQMotion, "aq-motion");
BOOL(p->bEmitHDRSEI, "hdr");
+ BOOL(p->bHDROpt, "hdr-opt");
+ s += sprintf(s, " Capture-colorprim=%d", p->captureColorPrim);
#undef BOOL
return buf;
}
diff -r 912dd749bdb5 -r 959318c42635 source/common/quant.cpp
--- a/source/common/quant.cpp Wed Feb 15 12:04:41 2017 +0530
+++ b/source/common/quant.cpp Wed Feb 01 17:01:29 2017 +0530
@@ -225,8 +225,8 @@
m_rdoqLevel = ctu.m_encData->m_param->rdoqLevel;
if (ctu.m_chromaFormat != X265_CSP_I400)
{
- setChromaQP(qp + ctu.m_slice->m_pps->chromaQpOffset[0], TEXT_CHROMA_U, ctu.m_chromaFormat);
- setChromaQP(qp + ctu.m_slice->m_pps->chromaQpOffset[1], TEXT_CHROMA_V, ctu.m_chromaFormat);
+ setChromaQP(qp + ctu.m_slice->m_pps->chromaQpOffset[0] + ctu.m_slice->m_chromaQpOffset[0], TEXT_CHROMA_U, ctu.m_chromaFormat);
+ setChromaQP(qp + ctu.m_slice->m_pps->chromaQpOffset[1] + ctu.m_slice->m_chromaQpOffset[1], TEXT_CHROMA_V, ctu.m_chromaFormat);
}
}
diff -r 912dd749bdb5 -r 959318c42635 source/common/slice.h
--- a/source/common/slice.h Wed Feb 15 12:04:41 2017 +0530
+++ b/source/common/slice.h Wed Feb 01 17:01:29 2017 +0530
@@ -289,6 +289,7 @@
bool bPicDisableDeblockingFilter;
int numRefIdxDefault[2];
+ bool pps_slice_chroma_qp_offsets_present_flag;
};
struct WeightParam
@@ -339,6 +340,7 @@
NalUnitType m_nalUnitType;
SliceType m_sliceType;
int m_sliceQp;
+ int m_chromaQpOffset[2];
int m_poc;
int m_lastIDR;
int m_rpsIdx;
@@ -372,6 +374,7 @@
numRefIdxDefault[0] = 1;
numRefIdxDefault[1] = 1;
m_rpsIdx = -1;
+ m_chromaQpOffset[0] = m_chromaQpOffset[1] = 0;
}
void disableWeights();
diff -r 912dd749bdb5 -r 959318c42635 source/encoder/encoder.cpp
--- a/source/encoder/encoder.cpp Wed Feb 15 12:04:41 2017 +0530
+++ b/source/encoder/encoder.cpp Wed Feb 01 17:01:29 2017 +0530
@@ -79,6 +79,8 @@
m_iFrameNum = 0;
m_iPPSQpMinus26 = 0;
m_rpsInSpsCount = 0;
+ m_cB = 0.0;
+ m_cR = 0.0;
for (int i = 0; i < X265_MAX_FRAME_THREADS; i++)
m_frameEncoder[i] = NULL;
@@ -1829,6 +1831,7 @@
pps->chromaQpOffset[0] = m_param->cbQpOffset;
pps->chromaQpOffset[1] = m_param->crQpOffset;
+ pps->pps_slice_chroma_qp_offsets_present_flag = m_param->bHDROpt;
pps->bConstrainedIntraPred = m_param->bEnableConstrainedIntra;
pps->bUseWeightPred = m_param->bEnableWeightedPred;
@@ -2294,6 +2297,37 @@
x265_log(p, X265_LOG_WARNING, "maxSlices can not be more than min(rows, MAX_NAL_UNITS-1), force set to %d\n", slicesLimit);
p->maxSlices = slicesLimit;
}
+ if (p->bHDROpt)
+ {
+ if (p->internalCsp != X265_CSP_I420 || p->internalBitDepth != 10 || p->vui.colorPrimaries != 9 ||
+ p->vui.transferCharacteristics != 16 || p->vui.matrixCoeffs != 9)
+ {
+ x265_log(p, X265_LOG_ERROR, "Recommended Settings for HDR: colour primaries should be BT.2020,\n"
+ " transfer characteristics should be SMPTE ST.2084,\n"
+ " matrix coeffs should be BT.2020,\n"
+ " the input video should be 10 bit 4:2:0\n"
+ " Disabling offset tuning for HDR videos\n");
+ p->bHDROpt = 0;
+ }
+ else
+ {
+ if (p->captureColorPrim == 1)
+ {
+ m_cB = 1.14;
+ m_cR = 1.78;
+ }
+ else if (p->captureColorPrim == 2)
+ {
+ m_cB = 1.04;
+ m_cR = 1.39;
+ }
+ else if (p->captureColorPrim == 3)
+ {
+ m_cB = 1.0;
+ m_cR = 1.0;
+ }
+ }
+ }
}
void Encoder::allocAnalysis(x265_analysis_data* analysis)
diff -r 912dd749bdb5 -r 959318c42635 source/encoder/encoder.h
--- a/source/encoder/encoder.h Wed Feb 15 12:04:41 2017 +0530
+++ b/source/encoder/encoder.h Wed Feb 01 17:01:29 2017 +0530
@@ -168,6 +168,9 @@
Lock m_rpsInSpsLock;
int m_rpsInSpsCount;
+ /* For HDR*/
+ double m_cB;
+ double m_cR;
Encoder();
~Encoder() {}
diff -r 912dd749bdb5 -r 959318c42635 source/encoder/entropy.cpp
--- a/source/encoder/entropy.cpp Wed Feb 15 12:04:41 2017 +0530
+++ b/source/encoder/entropy.cpp Wed Feb 01 17:01:29 2017 +0530
@@ -349,7 +349,7 @@
WRITE_SVLC(pps.chromaQpOffset[0], "pps_cb_qp_offset");
WRITE_SVLC(pps.chromaQpOffset[1], "pps_cr_qp_offset");
- WRITE_FLAG(0, "pps_slice_chroma_qp_offsets_present_flag");
+ WRITE_FLAG(pps.pps_slice_chroma_qp_offsets_present_flag, "pps_slice_chroma_qp_offsets_present_flag");
WRITE_FLAG(pps.bUseWeightPred, "weighted_pred_flag");
WRITE_FLAG(pps.bUseWeightedBiPred, "weighted_bipred_flag");
@@ -692,6 +692,11 @@
int code = sliceQp - (slice.m_iPPSQpMinus26 + 26);
WRITE_SVLC(code, "slice_qp_delta");
+ if (slice.m_pps->pps_slice_chroma_qp_offsets_present_flag)
+ {
+ WRITE_SVLC(slice.m_chromaQpOffset[0], "slice_cb_qp_offset");
+ WRITE_SVLC(slice.m_chromaQpOffset[1], "slice_cr_qp_offset");
+ }
// TODO: Enable when pps_loop_filter_across_slices_enabled_flag==1
// We didn't support filter across slice board, so disable it now
diff -r 912dd749bdb5 -r 959318c42635 source/encoder/frameencoder.cpp
--- a/source/encoder/frameencoder.cpp Wed Feb 15 12:04:41 2017 +0530
+++ b/source/encoder/frameencoder.cpp Wed Feb 01 17:01:29 2017 +0530
@@ -483,6 +483,13 @@
/* Clip slice QP to 0-51 spec range before encoding */
slice->m_sliceQp = x265_clip3(-QP_BD_OFFSET, QP_MAX_SPEC, qp);
+ if (m_param->bHDROpt)
+ {
+ int qpCb = x265_clip3(-12, 0, (int)round(m_top->m_cB * ((-.46) * qp + 9.26)));
+ int qpCr = x265_clip3(-12, 0, (int)round(m_top->m_cR * ((-.46) * qp + 9.26)));
+ slice->m_chromaQpOffset[0] = slice->m_pps->chromaQpOffset[0] + qpCb < -12 ? (qpCb + (-12 - (slice->m_pps->chromaQpOffset[0] + qpCb))) : qpCb;
+ slice->m_chromaQpOffset[1] = slice->m_pps->chromaQpOffset[1] + qpCr < -12 ? (qpCr + (-12 - (slice->m_pps->chromaQpOffset[1] + qpCr))) : qpCr;
+ }
if (m_param->bOptQpPPS && m_param->bRepeatHeaders)
{
diff -r 912dd749bdb5 -r 959318c42635 source/encoder/rdcost.h
--- a/source/encoder/rdcost.h Wed Feb 15 12:04:41 2017 +0530
+++ b/source/encoder/rdcost.h Wed Feb 01 17:01:29 2017 +0530
@@ -67,13 +67,13 @@
int qpCb, qpCr;
if (slice.m_sps->chromaFormatIdc == X265_CSP_I420)
{
- qpCb = (int)g_chromaScale[x265_clip3(QP_MIN, QP_MAX_MAX, qp + slice.m_pps->chromaQpOffset[0])];
- qpCr = (int)g_chromaScale[x265_clip3(QP_MIN, QP_MAX_MAX, qp + slice.m_pps->chromaQpOffset[1])];
+ qpCb = (int)g_chromaScale[x265_clip3(QP_MIN, QP_MAX_MAX, qp + slice.m_pps->chromaQpOffset[0] + slice.m_chromaQpOffset[0])];
+ qpCr = (int)g_chromaScale[x265_clip3(QP_MIN, QP_MAX_MAX, qp + slice.m_pps->chromaQpOffset[1] + slice.m_chromaQpOffset[1])];
}
else
{
- qpCb = x265_clip3(QP_MIN, QP_MAX_SPEC, qp + slice.m_pps->chromaQpOffset[0]);
- qpCr = x265_clip3(QP_MIN, QP_MAX_SPEC, qp + slice.m_pps->chromaQpOffset[1]);
+ qpCb = x265_clip3(QP_MIN, QP_MAX_SPEC, qp + slice.m_pps->chromaQpOffset[0] + slice.m_chromaQpOffset[0]);
+ qpCr = x265_clip3(QP_MIN, QP_MAX_SPEC, qp + slice.m_pps->chromaQpOffset[1] + slice.m_chromaQpOffset[1]);
}
if (slice.m_sps->chromaFormatIdc == X265_CSP_I444)
diff -r 912dd749bdb5 -r 959318c42635 source/encoder/sao.cpp
--- a/source/encoder/sao.cpp Wed Feb 15 12:04:41 2017 +0530
+++ b/source/encoder/sao.cpp Wed Feb 01 17:01:29 2017 +0530
@@ -1232,10 +1232,9 @@
int qpCb = qp;
if (m_param->internalCsp == X265_CSP_I420)
- qpCb = x265_clip3(m_param->rc.qpMin, m_param->rc.qpMax, (int)g_chromaScale[qp + slice->m_pps->chromaQpOffset[0]]);
+ qpCb = x265_clip3(m_param->rc.qpMin, m_param->rc.qpMax, (int)g_chromaScale[qp + slice->m_pps->chromaQpOffset[0] + slice->m_chromaQpOffset[0]]);
else
- qpCb = X265_MIN(qp + slice->m_pps->chromaQpOffset[0], QP_MAX_SPEC);
-
+ qpCb = X265_MIN(qp + slice->m_pps->chromaQpOffset[0] + slice->m_chromaQpOffset[0], QP_MAX_SPEC);
lambda[0] = (int64_t)floor(256.0 * x265_lambda2_tab[qp]);
lambda[1] = (int64_t)floor(256.0 * x265_lambda2_tab[qpCb]); // Use Cb QP for SAO chroma
diff -r 912dd749bdb5 -r 959318c42635 source/encoder/slicetype.cpp
--- a/source/encoder/slicetype.cpp Wed Feb 15 12:04:41 2017 +0530
+++ b/source/encoder/slicetype.cpp Wed Feb 01 17:01:29 2017 +0530
@@ -105,6 +105,21 @@
x265_emms();
return var;
}
+/* Find the sum of pixels of each block for luma plane */
+uint32_t LookaheadTLD::lumaSumCu(Frame* curFrame, uint32_t blockX, uint32_t blockY, uint32_t qgSize)
+{
+ intptr_t stride = curFrame->m_fencPic->m_stride;
+ intptr_t blockOffsetLuma = blockX + (blockY * stride);
+ uint64_t sum_ssd;
+
+ if (qgSize == 8)
+ sum_ssd = primitives.cu[BLOCK_8x8].var(curFrame->m_fencPic->m_picOrg[0] + blockOffsetLuma, stride);
+ else
+ sum_ssd = primitives.cu[BLOCK_16x16].var(curFrame->m_fencPic->m_picOrg[0] + blockOffsetLuma, stride);
+
+ x265_emms();
+ return (uint32_t)sum_ssd;
+}
void LookaheadTLD::calcAdaptiveQuantFrame(Frame *curFrame, x265_param* param)
{
@@ -227,6 +242,30 @@
uint32_t energy = acEnergyCu(curFrame, blockX, blockY, param->internalCsp,param->rc.qgSize);
qp_adj = strength * (X265_LOG2(X265_MAX(energy, 1)) - (modeOneConst + 2 * (X265_DEPTH - 8)));
}
+
+ if (param->bHDROpt)
+ {
+ uint32_t sum = lumaSumCu(curFrame, blockX, blockY, param->rc.qgSize);
+ uint32_t lumaAvg = sum / (loopIncr * loopIncr);
+ if (lumaAvg < 301)
+ qp_adj += 3;
+ else if (lumaAvg >= 301 && lumaAvg < 367)
+ qp_adj += 2;
+ else if (lumaAvg >= 367 && lumaAvg < 434)
+ qp_adj += 1;
+ else if (lumaAvg >= 501 && lumaAvg < 567)
+ qp_adj -= 1;
+ else if (lumaAvg >= 567 && lumaAvg < 634)
+ qp_adj -= 2;
+ else if (lumaAvg >= 634 && lumaAvg < 701)
+ qp_adj -= 3;
+ else if (lumaAvg >= 701 && lumaAvg < 767)
+ qp_adj -= 4;
+ else if (lumaAvg >= 767 && lumaAvg < 834)
+ qp_adj -= 5;
+ else if (lumaAvg >= 834)
+ qp_adj -= 6;
+ }
if (quantOffsets != NULL)
qp_adj += quantOffsets[blockXY];
curFrame->m_lowres.qpAqOffset[blockXY] = qp_adj;
diff -r 912dd749bdb5 -r 959318c42635 source/encoder/slicetype.h
--- a/source/encoder/slicetype.h Wed Feb 15 12:04:41 2017 +0530
+++ b/source/encoder/slicetype.h Wed Feb 01 17:01:29 2017 +0530
@@ -91,6 +91,7 @@
protected:
uint32_t acEnergyCu(Frame* curFrame, uint32_t blockX, uint32_t blockY, int csp, uint32_t qgSize);
+ uint32_t lumaSumCu(Frame* curFrame, uint32_t blockX, uint32_t blockY, uint32_t qgSize);
uint32_t weightCostLuma(Lowres& fenc, Lowres& ref, WeightParam& wp);
bool allocWeightedRef(Lowres& fenc);
};
diff -r 912dd749bdb5 -r 959318c42635 source/x265.h
--- a/source/x265.h Wed Feb 15 12:04:41 2017 +0530
+++ b/source/x265.h Wed Feb 01 17:01:29 2017 +0530
@@ -492,6 +492,7 @@
"32:11", "80:33", "18:11", "15:11", "64:33", "160:99", "4:3", "3:2", "2:1", 0 };
static const char * const x265_interlace_names[] = { "prog", "tff", "bff", 0 };
static const char * const x265_analysis_names[] = { "off", "save", "load", 0 };
+static const char * const x265_capture_colorprim_names[] = { "", "bt709", "p3d65", "bt2020", 0 };
/* Zones: override ratecontrol for specific sections of the video.
* If zones overlap, whichever comes later in the list takes precedence. */
@@ -1379,6 +1380,13 @@
* Auto-enabled when max-cll, max-fall, or mastering display info is specified.
* Default is disabled */
int bEmitHDRSEI;
+
+ /* Color primaries of the capture device */
+ int captureColorPrim;
+
+ /* Enable luma and chroma offsets for HDR/WCG content.
+ * Default is disabled */
+ int bHDROpt;
} x265_param;
/* x265_param_alloc:
diff -r 912dd749bdb5 -r 959318c42635 source/x265cli.h
--- a/source/x265cli.h Wed Feb 15 12:04:41 2017 +0530
+++ b/source/x265cli.h Wed Feb 01 17:01:29 2017 +0530
@@ -263,6 +263,9 @@
{ "no-ssim-rd", no_argument, NULL, 0 },
{ "hdr", no_argument, NULL, 0 },
{ "no-hdr", no_argument, NULL, 0 },
+ { "capture-csp", required_argument, NULL, 0 },
+ { "hdr-opt", no_argument, NULL, 0 },
+ { "no-hdr-opt", no_argument, NULL, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
@@ -471,6 +474,8 @@
H0(" format: G(x,y)B(x,y)R(x,y)WP(x,y)L(max,min)\n");
H0(" --max-cll <string> Emit content light level info SEI as \"cll,fall\" (HDR)\n");
H0(" --[no-]hdr Control dumping of HDR SEI packet. If max-cll or master-display has non-zero values, this is enabled. Default %s\n", OPT(param->bEmitHDRSEI));
+ H0(" --capture-csp <string> Specify color primaries from bt709, p3d65, bt2020 for the capture device. Default bt709\n");
+ H0(" --[no-]hdr-opt Add luma and chroma offsets for HDR/WCG content. Default %s\n", OPT(param->bHDROpt));
H0(" --min-luma <integer> Minimum luma plane value of input source picture\n");
H0(" --max-luma <integer> Maximum luma plane value of input source picture\n");
H0("\nBitstream options:\n");
More information about the x265-devel
mailing list