[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