如何使用 IPP 将 8 位灰度图像转换为 NV12(有限范围)色彩空间
Posted
技术标签:
【中文标题】如何使用 IPP 将 8 位灰度图像转换为 NV12(有限范围)色彩空间【英文标题】:How to convert 8 bits Grayscale image to NV12 (limited range) color space using IPP 【发布时间】:2016-10-25 00:17:31 【问题描述】:英特尔® 媒体 SDK 等视频编码器不接受 8 位灰度图像作为输入格式。 8 位 Grayscale 格式在 [0, 255] 范围内每个像素应用一个字节。
问题上下文中的 8 位 YUV 格式适用于 YCbCr(BT.601 或 BT.709)。 虽然有一个全范围的 YUV 标准,但常用的格式是“有限范围”YUV,其中 Y 的范围为 [16, 235],U,V 的范围为 [16, 240]。
NV12 format 是本例常用的输入格式。
NV12 格式是在内存中排序的 YUV 4:2:0 格式,首先是 Y 平面,然后是交错 UV 平面中的打包色度样本:YYYYYY
YYYYYY
UVUVUV
灰度图像将被称为“I平面”:IIIIII
IIIIII
设置 UV 平面很简单:将所有 U、V 元素设置为 128 值。
但是 Y 平面呢?
在全范围YUV的情况下,我们可以简单地将“I平面”作为Y平面(即Y = I)。
在“受限”YUV 格式的情况下,需要进行转换: 在转换公式中设置 R=G=B 结果:Y = round(I*0.859 + 16)。
使用IPP进行上述转换的有效方法是什么?
【问题讨论】:
我会使用从 0..255 开始的整个 Y 范围而不进行转换,因为这种做法有很强的先例,即使它违背了亮度的原始定义。 您确定 Media SDK 不接受 gray8 吗?MFX_CHROMAFORMAT_MONOCHROME
呢?
@apalopohapa 不,我不确定...我记得当我使用 H.264 视频编码器时,唯一支持的格式(不使用 VPP 颜色空间转换)是 NV12。
【参考方案1】:
我正在为自己的问题添加答案。 希望能看到更好的答案……
我找到了使用两个 IPP 函数的解决方案:
ippsMulC_8u_Sfs - 将向量的每个元素乘以一个常数值。 ippsAddC_8u_ISfs - 为向量的每个元素添加一个常数值。我选择了使用定点数学的函数,以获得更好的性能。
0.859
缩放的定点实现是通过扩展、缩放和移位来执行的。示例:b = (a*scale + (1<<7)) >> 8;
[当scale
= (0.859)*2^8
]。val
参数设置为ippsMulC_8u_Sfs
设置为round(0.859*2^8)
= 220
。scaleFactor
参数设置为ippsMulC_8u_Sfs
8
(缩放结果除以2^8
)。
代码示例:
void GrayscaleToNV12(const unsigned char I[],
int image_width,
int image_height,
unsigned char J[])
IppStatus ipp_status;
const int image_size = image_width*image_height;
unsigned char *UV = &J[image_size]; //In NV12 format, UV plane starts below Y.
const Ipp8u expanded_scaling = (Ipp8u)(0.859 * 256.0 + 0.5);
//J[x] = (expanded_scaling * I[x] + 128u) >> 8u;
ipp_status = ippsMulC_8u_Sfs(I, //const Ipp8u* pSrc,
expanded_scaling, //Ipp8u val,
J, //Ipp8u* pDst,
image_size, //int len,
8); //int scaleFactor);
//Check ipp_status, and handle errors...
//J[x] += 16;
//ippsAddC_8u_ISfs is deprecated, I used it to keep the code simple.
ipp_status = ippsAddC_8u_ISfs(16, //Ipp8u val,
J, //Ipp8u* pSrcDst,
image_size, //int len,
0); //int scaleFactor);
//Check ipp_status, and handle errors...
//2. Fill all UV plane with 128 value - "gray color".
memset(UV, 128, image_width*image_height/2);
题外话:
有一种方法可以将视频流标记为“全范围”(其中Y
范围是 [0, 255] 而不是 [16
, 235
],U
,V
范围也是 [ 0, 255]).
使用“全范围”标准允许使用I
代替Y
(即Y = I)。
使用英特尔媒体 SDK 将流标记为“全范围”是可能的(但没有很好的记录)。
将 H.264 流标记为“全范围”需要添加指向 mfxExtBuffer **ExtParam
列表的指针(在结构 mfxVideoParam
中):
指向mfxExtVideoSignalInfo
类型结构的指针应添加以下值:
typedef struct
mfxExtBuffer Header; //MFX_EXTBUFF_VIDEO_SIGNAL_INFO and sizeof(mfxExtVideoSignalInfo)
mfxU16 VideoFormat; //Most likely 5 ("Unspecified video format")
mfxU16 VideoFullRange; //1 (video_full_range_flag is equal to 1)
mfxU16 ColourDescriptionPresent; //0 (description_present_flag equal to 0)
mfxU16 ColourPrimaries; //0 (no affect when ColourDescriptionPresent = 0)
mfxU16 TransferCharacteristics; //0 (no affect when ColourDescriptionPresent = 0)
mfxU16 MatrixCoefficients; //0 (no affect when ColourDescriptionPresent = 0)
mfxExtVideoSignalInfo;
VideoFullRange = 1
是设置“全幅”视频的唯一相关参数,但我们必须填充整个结构。
【讨论】:
以上是关于如何使用 IPP 将 8 位灰度图像转换为 NV12(有限范围)色彩空间的主要内容,如果未能解决你的问题,请参考以下文章