sws_scale,YUV转RGB转换

4
我需要将YUV转换为RGB,并且需要RGB值处于有限范围(16-235)之间。我尝试使用sws_scale函数来完成此任务。
您可以在下面看到我的代码。但是,转换后,我得到的黑色像素是(0, 0, 0),而不是(16, 16, 16)。
也许有一些选项可以告诉sws_scale函数计算有限范围。
AVFrame* frameRGB = avFrameConvertPixelFormat(_decodedBuffer[i].pAVFrame, AV_PIX_FMT_RGB24);

AVFrame* Decoder::avFrameConvertPixelFormat(const AVFrame* src, AVPixelFormat dstFormat) {
    int width = src->width;
    int height = src->height;

    AVFrame* dst = allocPicture(dstFormat, width, height);

    SwsContext* conversion = sws_getContext(width,
                                            height,
                                            (AVPixelFormat)src->format,
                                            width,
                                            height,
                                            dstFormat,
                                            SWS_FAST_BILINEAR,
                                            NULL,
                                            NULL,
                                            NULL);
    sws_scale(conversion, src->data, src->linesize, 0, height, dst->data, dst->linesize);
    sws_freeContext(conversion);

    dst->format = dstFormat;
    dst->width = src->width;
    dst->height = src->height;

    return dst;
}

我也尝试使用公式手动将YUV像素转换为RGB像素,并且得到了正确的结果。从YUV(16,128,128)到RGB(16,16,16)。

cmpR = y + 1.402 * (v - 128);
cmpG = y - 0.3441 * (u - 128) - 0.7141 * (v - 128);
cmpB = y + 1.772 * (u - 128);

据我所知/记得,swscale不支持有限范围的RGB,只支持有限范围的YUV。此外,我还需要RGB值在有限范围(16-235)内。 - Ronald S. Bultje
2个回答

2
您可以将源格式设置为“全尺寸”YUVJ
据我所知,sws_scale没有选择Studio RGB作为输出格式的选项。改变输入格式是我能想到的最好的解决方案。
“JPEG: YUV->RGB”的颜色转换公式与您的帖子中的公式相同。
以下是设置源格式的示例:
如果src->formatPIX_FMT_YUV420P,请将格式设置为PIX_FMT_YUVJ420P
如果src->formatPIX_FMT_YUV422P,请将格式设置为PIX_FMT_YUVJ422P
如果src->formatPIX_FMT_YUV444P,请将格式设置为PIX_FMT_YUVJ444P
如果PIX_FMT_YUV440P,请使用PIX_FMT_YUVJ440P
我知道这个解决方案并不能涵盖所有可能性,并且可能会有一些输出像素超出[16, 235]的范围,因此它不是最通用的解决方案...

你好@Rotem,谢谢!这个解决方案对我很有效。你能否解释一下为什么这个解决方案有效吗?它基于什么原理?而且,“AV_PIX_FMT_YUVJ420P”已经被弃用,“平面YUV 4:2:0,12bpp,全幅(JPEG),已被AV_PIX_FMT_YUV420P和设置色彩范围所取代”。 - Валентин Никин
正如我在帖子中提到的,我基于您在问题中提供的转换公式来解决问题。该公式与JPEG YUV转换公式相同(在维基百科上有描述)。 YUVJ 中的 J 字母表示应用了JPEG转换(JPEG使用完整范围的YUV而不是常见的视频转换公式,其中Y限制为[16、235])。 - Rotem
我的解决方案并不理想,部分原因是因为“AV_PIX_FMT_YUVJ420P”已经被弃用。我认为你最好使用sws_setColorspaceDetails()。将srcRange设置为1。可能有更好的解决方案(注意:我从未直接使用过sws_scale - 只使用FFmpeg命令行)。请发布您找到的解决方案。 - Rotem
感谢回复。我看到你了。 我接受你的答案作为解决方案。它肯定有效,但也许我会尝试手动获取公式YUV到RGB转换(对于主要标准BT.601、BT.709和BT.2020,以及8、10和12位深度),以获得更多控制权。 我偶然发现了一份有趣的文件,其中这些公式的结论以易懂的形式描述。 https://poynton.ca/ColorFAQ.html - Валентин Никин

0

使用FFMPEG进行yuv到rgb转换我已经看到很多关于这个问题的信息了。然而,为了完整性,我重新分享了代码,并补充了缺失的allocPicture()函数、头文件和库。它对我来说非常好用。感谢@Валентин Никин和@Rotem提供的大部分信息和代码。

头文件:

#include <libswscale/swscale.h> 

链接FFMPEG库:

libswscale

static AVFrame* allocPicture(enum AVPixelFormat pix_fmt, int width, int height)
{
  // Allocate a frame
  AVFrame* frame = av_frame_alloc();

  if (frame == NULL)
  {
    fprintf(stderr, "avcodec_alloc_frame failed");
  }

  if (av_image_alloc(frame->data, frame->linesize, width, height, pix_fmt, 1) < 0)
  {
    fprintf(stderr, "av_image_alloc failed");
  }

  frame->width = width;
  frame->height = height;
  frame->format = pix_fmt;

  return frame;
}

static AVFrame* avFrameConvertPixelFormat(const AVFrame* src, enum AVPixelFormat dstFormat)
{
  int width = src->width;
  int height = src->height;

  AVFrame* dst = allocPicture(dstFormat, width, height);

  struct SwsContext* conversion = sws_getContext(width,
                                        height,
                                        (enum AVPixelFormat)src->format,
                                        width,
                                        height,
                                        dstFormat,
                                        SWS_FAST_BILINEAR | SWS_FULL_CHR_H_INT | SWS_ACCURATE_RND,
                                        NULL,
                                        NULL,
                                        NULL);
  sws_scale(conversion, src->data, src->linesize, 0, height, dst->data, dst->linesize);
  sws_freeContext(conversion);

  dst->format = dstFormat;
  dst->width = src->width;
  dst->height = src->height;

  return dst;
}

// convert yuv420p10le to rgb24 (or any other RGB formats)
AVFrame* frame = avFrameConvertPixelFormat(frame, AV_PIX_FMT_RGB24);

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