如何使用IPP将8位灰度图像转换为NV12(有限范围)色彩空间

3

视频编码器(例如Intel® 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

enter image description here

灰度图像将被称为"I平面":
IIIIII
IIIIII

enter image description here

设置UV平面很简单:将所有的U,V元素设为128值即可。
但是Y平面怎么办呢?
在完整范围的YUV情况下,我们可以将"I平面"简单地作为Y平面(即Y = I)。
在“有限”YUV格式的情况下,需要进行一次转换:
设置转换公式中的R=G=B,结果为:Y = round(I*0.859 + 16)。 使用IPP进行上述转换的有效方法是什么?

1
我会使用完整的Y范围从0..255,不进行任何转换,因为这种做法有很强的先例,即使它违反了亮度的原始定义。 - R.. GitHub STOP HELPING ICE
你确定媒体SDK不支持gray8吗?MFX_CHROMAFORMAT_MONOCHROME呢? - apalopohapa
@apalopohapa 不,我不确定... 据我回忆,当我使用H.264视频编码器时,唯一支持的格式(在不使用VPP颜色空间转换的情况下)是NV12。 - Rotem
1个回答

0

我正在回答自己的问题。
希望能看到更好的答案...

我找到了一个使用两个IPP函数的解决方案:

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

  • 通过扩展、缩放和移位来执行 0.859 缩放的定点实现。例如:b = (a*scale + (1<<7)) >> 8; [当 scale = (0.859)*2^8 时]。
    ippsMulC_8u_Sfs 函数的 val 参数设置为 round(0.859*2^8) = 220
    ippsMulC_8u_Sfs 函数的 scaleFactor 参数设置为 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],而且UV范围也为[0,255])。
使用“全范围”标准允许在Y= I的位置放置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 是设置“全范围”视频的唯一相关参数,但我们必须填充整个结构。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接