首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >具有BT.709矩阵的H.264编码视频是否包含任何伽马调整?

具有BT.709矩阵的H.264编码视频是否包含任何伽马调整?
EN

Stack Overflow用户
提问于 2018-12-24 17:41:05
回答 1查看 3.5K关注 0票数 7

我已经读了BT.709 spec很多次了,我不清楚的是,编码的H.264码流是否真的应该对编码的数据应用任何伽马曲线?请注意,BT.709规范中特别提到了类gamma公式。苹果提供了从CoreVideo读取YUV数据的OpenGL或金属着色器的示例,前提是缓冲区不做任何类型的gamma调整。YUV值被读取和处理,就像它们是简单的线性值一样。我还检查了ffmpeg的源代码,发现在BT.709缩放步骤之后没有应用gamma调整。然后我用两个线性灰度颜色5和26进行created a test video,分别对应2%和10%的水平。当转换为同时具有ffmpeg和iMovie的H.264时,输出BT.709值是(YCbCr) (20128)和(38128),并且这些值与BT.709转换矩阵的输出完全匹配,而无需任何伽马调整。

关于这个主题的一篇很棒的背景文章可以在Quicktime Gamma Bug上找到。似乎Quicktime和Adobe编码器的一些历史问题不正确地进行了不同的gamma调整,结果使视频流在不同的播放器上看起来很糟糕。这真的很令人困惑,因为如果你与sRGB相比,它清楚地指示了如何应用gamma编码,然后解码以在sRGB和线性之间进行转换。如果在创建h.264数据流时,在矩阵步骤之后没有应用伽马调整,为什么BT.709会进入关于同一种伽马调整曲线的如此多的细节?h.264流中的所有色阶是否都要编码为直线(伽马1.0)值?

在特定的例子输入会让事情变得更清楚,我附加了3个颜色条图像,不同颜色的精确值可以用这些图像文件在图像编辑器中显示。

第一个图像位于sRGB色彩空间中,并被标记为sRGB。

第二幅图像已转换为线性RGB色彩空间,并使用线性RGB配置文件进行标记。

第三个图像已通过elles_icc_profiles 的Rec709-elle-V4-rec709.icc转换为REC.709配置文件级别。这似乎是一个人需要做的事情来模拟BT.709中描述的“相机”伽马。

请注意右下角的RGB值(0x555555)如何变为线性sRGB (0x171717),而BT.709伽马编码值如何变为(0x464646)。尚不清楚的是,我是否应该将线性RGB值传递到ffmpeg中,或者是否应该传递已经BT.709伽马编码值,然后在线性转换矩阵步骤返回RGB之前,需要在客户端对其进行解码。

更新:

根据反馈,我已经更新了我的基于C的实现和金属着色器,并作为iOS示例项目MetalBT709Decoder上传到github。

对标准化的线性RGB值进行编码的实现方式如下:

代码语言:javascript
运行
复制
static inline
int BT709_convertLinearRGBToYCbCr(
                            float Rn,
                            float Gn,
                            float Bn,
                            int *YPtr,
                            int *CbPtr,
                            int *CrPtr,
                            int applyGammaMap)
{
  // Gamma adjustment to non-linear value

  if (applyGammaMap) {
    Rn = BT709_linearNormToNonLinear(Rn);
    Gn = BT709_linearNormToNonLinear(Gn);
    Bn = BT709_linearNormToNonLinear(Bn);
  }

  // https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.709-6-201506-I!!PDF-E.pdf

  float Ey = (Kr * Rn) + (Kg * Gn) + (Kb * Bn);
  float Eb = (Bn - Ey) / Eb_minus_Ey_Range;
  float Er = (Rn - Ey) / Er_minus_Ey_Range;

  // Quant Y to range [16, 235] (inclusive 219 values)
  // Quant Eb, Er to range [16, 240] (inclusive 224 values, centered at 128)

  float AdjEy = (Ey * (YMax-YMin)) + 16;
  float AdjEb = (Eb * (UVMax-UVMin)) + 128;
  float AdjEr = (Er * (UVMax-UVMin)) + 128;

  *YPtr = (int) round(AdjEy);
  *CbPtr = (int) round(AdjEb);
  *CrPtr = (int) round(AdjEr);

  return 0;
}

从YCbCr到线性RGB的解码实现如下:

代码语言:javascript
运行
复制
static inline
int BT709_convertYCbCrToLinearRGB(
                             int Y,
                             int Cb,
                             int Cr,
                             float *RPtr,
                             float *GPtr,
                             float *BPtr,
                             int applyGammaMap)
{
  // https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.709_conversion
  // http://www.niwa.nu/2013/05/understanding-yuv-values/

  // Normalize Y to range [0 255]
  //
  // Note that the matrix multiply will adjust
  // this byte normalized range to account for
  // the limited range [16 235]

  float Yn = (Y - 16) * (1.0f / 255.0f);

  // Normalize Cb and CR with zero at 128 and range [0 255]
  // Note that matrix will adjust to limited range [16 240]

  float Cbn = (Cb - 128) * (1.0f / 255.0f);
  float Crn = (Cr - 128) * (1.0f / 255.0f);

  const float YScale = 255.0f / (YMax-YMin);
  const float UVScale = 255.0f / (UVMax-UVMin);

  const
  float BT709Mat[] = {
    YScale,   0.000f,  (UVScale * Er_minus_Ey_Range),
    YScale, (-1.0f * UVScale * Eb_minus_Ey_Range * Kb_over_Kg),  (-1.0f * UVScale * Er_minus_Ey_Range * Kr_over_Kg),
    YScale, (UVScale * Eb_minus_Ey_Range),  0.000f,
  };

  // Matrix multiply operation
  //
  // rgb = BT709Mat * YCbCr

  // Convert input Y, Cb, Cr to normalized float values

  float Rn = (Yn * BT709Mat[0]) + (Cbn * BT709Mat[1]) + (Crn * BT709Mat[2]);
  float Gn = (Yn * BT709Mat[3]) + (Cbn * BT709Mat[4]) + (Crn * BT709Mat[5]);
  float Bn = (Yn * BT709Mat[6]) + (Cbn * BT709Mat[7]) + (Crn * BT709Mat[8]);

  // Saturate normalzied linear (R G B) to range [0.0, 1.0]

  Rn = saturatef(Rn);
  Gn = saturatef(Gn);
  Bn = saturatef(Bn);

  // Gamma adjustment for RGB components after matrix transform

  if (applyGammaMap) {
    Rn = BT709_nonLinearNormToLinear(Rn);
    Gn = BT709_nonLinearNormToLinear(Gn);
    Bn = BT709_nonLinearNormToLinear(Bn);
  }

  *RPtr = Rn;
  *GPtr = Gn;
  *BPtr = Bn;

  return 0;
}

我相信这个逻辑是正确实现的,但我在验证结果时遇到了很大的困难。当我生成一个包含gamma调整颜色值(osxcolor_test_image_24bit_BT709.m4v)的.m4v文件时,结果与预期一致。但是像(bars_709_Frame01.m4v)这样的测试用例,我发现here似乎不起作用,因为颜色条值似乎编码为线性(没有伽马调整)。

对于SMPTE测试模式,0.75灰度级别是线性RGB (191 191 191),是否应将此RGB编码为(Y Cb Cr) (180 128 128)而不进行伽马调整,或者比特流中的值是否应显示为伽马调整后的(Y Cb Cr) (206 128 128)?

(跟进)在对这个伽马问题做了额外的研究后,很明显,苹果在AVFoundation中实际做的是使用1.961伽马函数。这是使用AVAssetWriterInputPixelBufferAdaptor编码、使用vImage或CoreVideo API时的情况。该分段伽马函数定义如下:

代码语言:javascript
运行
复制
#define APPLE_GAMMA_196 (1.960938f)

static inline
float Apple196_nonLinearNormToLinear(float normV) {
  const float xIntercept = 0.05583828f;

  if (normV < xIntercept) {
    normV *= (1.0f / 16.0f);
  } else {
    const float gamma = APPLE_GAMMA_196;
    normV = pow(normV, gamma);
  }

  return normV;
}

static inline
float Apple196_linearNormToNonLinear(float normV) {
  const float yIntercept = 0.00349f;

  if (normV < yIntercept) {
    normV *= 16.0f;
  } else {
    const float gamma = 1.0f / APPLE_GAMMA_196;
    normV = pow(normV, gamma);
  }

  return normV;
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-01-04 05:00:12

你最初的问题:带有BT.709矩阵的H.264编码视频是否包含任何gamma调整?

编码的视频只包含gamma调整-如果您提供给编码器gamma调整的值。

H.264编码器并不关心传输特性。所以如果你压缩线性,然后再解压,你就会得到线性。所以如果你用gamma压缩,然后再解压缩,你就会得到gamma。

或者如果您的比特是用Rec编码的。709传递函数-编码器不会改变伽马。

但是您可以将H.264流中的传输特性指定为元数据。(记录:ITU-T H.264 (04/2017) E.1.1 VUI参数语法)。因此,编码的流携带颜色空间信息,但它不用于编码或解码。

我假设8位视频总是包含一个非线性传递函数。否则,您将非常不明智地使用8位。

如果你转换成线性来做特效和构图-我建议增加位深度或线性化成浮点数。

颜色空间由基色、传递函数和矩阵系数组成。伽马调整在传递函数中编码(而不是在矩阵中)。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53911662

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档