首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >图像贴片(NV12)颜色转换-无效边框

图像贴片(NV12)颜色转换-无效边框
EN

Stack Overflow用户
提问于 2019-06-01 17:55:28
回答 1查看 827关注 0票数 1

目标是将NV12转换为BGR24映像,更确切地说是图像修补程序(x:0,y:0,w:220,h:220)。

问题是转换后的补丁程序右侧的未定义像素列,如下所示:

问题是,为什么会发生这种情况(即使补丁的坐标和维度有偶数)?(有趣的是,对于奇数宽度值来说,这个问题并不存在)

该修补程序具有以下边界框:(x:0,y:0,w:220,h:220)。

这种行为应该与任何图像都是可复制的。可以使用ppm转换页进行转换。

下面的代码从一个nv12映像创建一个bgr24映像,然后将一个nv12修补程序转换回bgr24修补程序。如果一切正常工作,输出应该与源映像相同。

代码语言:javascript
复制
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>

void readPPM(const char* filename, uint8_t** bgrData, int* stride, int* w, int* h)
{
    FILE* fp = fopen(filename, "rb");
    fscanf(fp, "%*s\n"); //skip format check

    fscanf(fp, "%d %d\n", w, h);
    fscanf(fp, "%*d\n"); //skip max value check

    *stride = *w * 3;
    *bgrData = av_malloc(*h * *stride);

    for (int r = 0; r < *h; r++)
    {
        uint8_t* rowData = *bgrData + r * *stride;
        for (int c = 0; c < *w; c++)
        {
            //rgb -> bgr
            fread(&rowData[2], 1, 1, fp);
            fread(&rowData[1], 1, 1, fp);
            fread(&rowData[0], 1, 1, fp);

            rowData += 3;
        }
    }

    fclose(fp);
}

void writePPM(const char* filename, uint8_t* bgrData, int stride, int w, int h)
{
    FILE* fp = fopen(filename, "wb");
    fprintf(fp, "P6\n");
    fprintf(fp, "%d %d\n", w, h);
    fprintf(fp, "%d\n", 255);

    for (int r = 0; r < h; r++)
    {
        uint8_t* rowData = bgrData + r * stride;
        for (int c = 0; c < w; c++)
        {
            //bgr -> rgb
            fwrite(&rowData[2], 1, 1, fp);
            fwrite(&rowData[1], 1, 1, fp);
            fwrite(&rowData[0], 1, 1, fp);

            rowData += 3;       
        }
    }

    fclose(fp);
}


void bgrToNV12(uint8_t* srcData[4], int srcStride[4], 
               uint8_t* tgtData[4], int tgtStride[4],
               int w, int h)
{
    struct SwsContext* context = sws_getContext(w, h, AV_PIX_FMT_BGR24,
                                                w, h, AV_PIX_FMT_NV12, SWS_POINT, NULL, NULL, NULL);
    {
        sws_scale(context,
                  srcData, srcStride, 0, h,
                  tgtData, tgtStride);
    }
    sws_freeContext(context);
}

void nv12ToBgr(uint8_t* srcData[4], int srcStride[4],
               uint8_t* tgtData[4], int tgtStride[4],
               int w, int h)
{
    struct SwsContext* context = sws_getContext(w, h, AV_PIX_FMT_NV12,
                                                w, h, AV_PIX_FMT_BGR24, SWS_POINT, NULL, NULL, NULL);
    {
        sws_scale(context,
                  srcData, srcStride, 0, h,
                  tgtData, tgtStride);
    }
    sws_freeContext(context);
}


int main()
{
    //load BGR image
    uint8_t* bgrData[4]; int bgrStride[4]; int bgrW, bgrH;
    readPPM("sample.ppm", &bgrData[0], &bgrStride[0], &bgrW, &bgrH);

    //create NV12 image from the BGR image
    uint8_t* nv12Data[4]; int nv12Stride[4];
    av_image_alloc(nv12Data, nv12Stride, bgrW, bgrH, AV_PIX_FMT_NV12, 16);
    bgrToNV12(bgrData, bgrStride, nv12Data, nv12Stride, bgrW, bgrH);

    //convert nv12 patch to bgr patch
    nv12ToBgr(nv12Data, nv12Stride, bgrData, bgrStride, 220, 220);   //invalid result (random column stripe)
    //nv12ToBgr(nv12Data, nv12Stride, bgrData, bgrStride, 221, 220); //valid result

    //save bgr image (should be exactly as original BGR image)
    writePPM("sample-out.ppm", bgrData[0], bgrStride[0], bgrW, bgrH);

    //cleanup
    av_freep(bgrData);
    av_freep(nv12Data);
    return 0;
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-06-04 05:02:33

sws_scale同时进行颜色转换和缩放。

大多数使用的算法都需要在计算目标像素时包括相邻像素。当然,如果图像维数不是x的倍数,这可能会导致边缘问题,其中x取决于所使用的算法。

如果您在这里将图像维度设置为8的倍数(下一个倍数为8= 224),那么它可以不使用工件。

代码语言:javascript
复制
nv12ToBgr(nv12Data, nv12Stride, bgrData, bgrStride, 224, 224);

Demo

使用图像尺寸220 x 220在左边,给出了在转换补丁的右侧边缘的伪影。

如果一个人选择224 x 224,它没有提供一个人工制品,请在截图中看到比较这两种程序的正确图像。

理论上要求最小对准

让我们看一看YVU420格式:

为每个像素确定luma值。颜色信息被划分为Cb和Cr,是从一个2x2像素块计算出来的。因此,最小图像大小将是一个2x2图像块,产生6个字节(即每字节12像素= 12 *4=48位=6字节),参见此处的图形:

因此,最低技术要求是图像的宽度和高度都是均匀的。

您已经定义了用于缩放的SWS_POINT标志,即使用最近的邻居方法。因此,理论上对每个输出像素确定并使用最接近的输入像素,这不会造成任何对齐限制。

性能

但是,算法实际实现的一个重要方面往往是性能。在这种情况下,例如可以同时处理几个相邻的像素。也不要忘记硬件加速操作的可能性.

替代解决方案

如果出于某种原因,您需要坚持220x220格式,您可以选择使用SWS_BITEXACT标志。

它确实:

启用位精确输出。

请参阅005f选项

因此,在nv12ToBgr中,您可以使用如下内容:

代码语言:javascript
复制
struct SwsContext* context = sws_getContext(w, h, AV_PIX_FMT_NV12,
                                            w, h, AV_PIX_FMT_BGR24, SWS_POINT | SWS_BITEXACT, NULL, NULL, NULL);

这也不会给出任何文物。如果你要转换很多帧,我会看一看表演。

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

https://stackoverflow.com/questions/56409122

复制
相关文章

相似问题

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