3

英特尔® Media SDK 等视频编码器不接受 8 位灰度图像作为输入格式。
8 位灰度格式在 [0, 255] 范围内每个像素应用一个字节。

问题上下文中的 8 位 YUV 格式适用YCbCr(BT.601 或 BT.709)。
虽然有一个全范围的 YUV 标准,但常用的格式是“有限范围”YUV,其中 Y 的范围为 [16, 235],U,V 的范围为 [16, 240]。

在这种情况下, NV12 格式是常见的输入格式。
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进行上述转换的有效方法是什么?

4

1 回答 1

0

我正在为我自己的问题添加一个答案。
希望能看到更好的答案...

我找到了使用两个 IPP 功能的解决方案:

我选择了使用定点数学的函数,以获得更好的性能。

  • 缩放的定点实现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_Sfs82^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)。

使用 Intel Media 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是设置“全范围”视频的唯一相关参数,但我们必须填充整个结构。

于 2016-06-26T20:57:31.997 回答