关于这个主题的一个很好的背景可以在Quicktime Gamma Bug找到.似乎Quicktime和Adobe编码器的一些历史问题不正确地进行不同的伽玛调整,结果使得视频流在不同的播放器上看起来很糟糕.这真的令人困惑,因为如果你比较sRGB,它清楚地表明如何应用伽玛编码然后解码它以在sRGB和线性之间进行转换.如果在创建h.264数据流的矩阵步骤之后没有应用伽玛调整,为什么BT.709会详细介绍相同类型的伽玛调整曲线? h.264流中的所有颜色步骤是否都要编码为直线(gamma 1.0)值?
如果特定的示例输入会使事情更清楚,我附加了3个彩条图像,不同颜色的确切值可以在带有这些图像文件的图像编辑器中显示.
第一张图像位于sRGB色彩空间中,并标记为sRGB.
第二个图像已转换为线性RGB色彩空间,并标有线性RGB配置文件.
第三张图像已经从elles_icc_profiles
用Rec709-elle-V4-rec709.icc转换为REC.709配置文件等级.这似乎是模拟“相机”伽玛所需要做的,如BT.709中所述.
注意右下角的sRGB值(0x555555)如何变为线性RGB(0x171717),BT.709伽马编码值变为(0x464646).不清楚的是我是否应该将线性RGB值传递给ffmpeg,或者我是否应该传递已经BT.709伽马编码的值,然后需要在线性转换矩阵步骤之前在客户端中对其进行解码以返回RGB .
更新:
根据反馈,我更新了基于C的实现和Metal shader,并作为iOS示例项目MetalBT709Decoder上传到github.
对规范化线性RGB值进行编码的方式如下:
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的解码实现如下:
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; }
我相信这个逻辑是正确实现的,但我很难验证结果.当我生成包含伽马调整颜色值(osxcolor_test_image_24bit_BT709.m4v)的.m4v文件时,结果将按预期显示.但是我发现here这样的测试用例(bars_709_Frame01.m4v)似乎不起作用,因为颜色条值似乎被编码为线性(无伽玛调整).
对于SMPTE测试图案,0.75灰度级是线性RGB(191 191 191),如果这个RGB编码没有伽马调整为(Y Cb Cr)(180 128 128),或者比特流中的值应该显示为调整的伽玛(Y Cb Cr)(206 128 128)?
(跟进)
在对这个伽玛问题做进一步研究后,很明显Apple在AVFoundation中实际做的是使用1.961伽马功能.使用AVIsetWriterInputPixelBufferAdaptor进行编码时,使用vImage或使用CoreVideo API时就是这种情况.该分段伽玛函数定义如下:
#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; }您的原始问题:带有BT.709矩阵的H.264编码视频是否包含任何伽玛调整?
编码视频仅包含伽玛调整 – 如果您输入编码器伽玛调整值.
H.264编码器不关心传输特性.
因此,如果您压缩线性然后解压缩 – 您将获得线性.
因此,如果您使用gamma压缩然后解压缩 – 您将获得伽玛.
或者,如果您的位使用Rec进行编码. 709传递函数 – 编码器不会改变伽玛.
但您可以将H.264流中的传输特性指定为元数据. (ITU-T H.264建议书(04/2017)E.1.1 VUI参数语法).因此,编码流携带颜色空间信息,但不用于编码或解码.
我认为8位视频总是包含非线性传递函数.否则你会非常不明智地使用8位.
如果你转换为线性来做效果和组合 – 我建议增加位深度或线性化为浮点数.
颜色空间由原色,传递函数和矩阵系数组成.伽马调整在传递函数中编码(而不是在矩阵中).