4

I need convert YUV to RGB. I also need the RGB values to be in the limited range (16-235). I try to use sws_scale function for this task.

My code you can see below. But after conversion I got the black pixel is (0, 0, 0) instead of (16, 16, 16).

Maybe there are some options to tell sws_scale function to calculate the limited range.

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;
}

Also I tried convert YUV pixel to RGB pixel manualy with formula and I got correct result. From YUV (16, 128, 128) I got (16, 16, 16) in RGB.

cmpR = y + 1.402 * (v - 128);
cmpG = y - 0.3441 * (u - 128) - 0.7141 * (v - 128);
cmpB = y + 1.772 * (u - 128);
  • "I also need the RGB values to be in the limited range (16-235)." - to my knowledge/memory, swscale does not support limited-range RGB, only limited-range YUV. – Ronald S. Bultje Apr 30 '22 at 13:30

2 Answers2

2

You may the source format to "full scale" YUVJ.

As far as I know, sws_scale has no option for selecting Studio RGB as output format.
Changing the input format is the best solution I can think of.

The color conversion formula of "JPEG: YUV -> RGB" is the same as the formula in your post.

Examples for setting the source format:

  • If src->format is PIX_FMT_YUV420P, set the format to PIX_FMT_YUVJ420P.
  • If src->format is PIX_FMT_YUV422P, set the format to PIX_FMT_YUVJ422P.
  • If src->format is PIX_FMT_YUV444P, set the format to PIX_FMT_YUVJ444P.
  • If PIX_FMT_YUV440P, use PIX_FMT_YUVJ440P.

I know the solution is not covering all the possibilists, and there might be some output pixels exceeding the range of [16, 235], so it's not the most general solution...

Rotem
  • 30,366
  • 4
  • 32
  • 65
  • Hello @Rotem, thanks! This solution seems to work for me. Could you please explain me why this solution is work? On what principle is it based? And seems "AV_PIX_FMT_YUVJ420P " is deprecated "planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV420P and setting color_range" – Валентин Никин Mar 01 '21 at 05:36
  • As I mentioned in my post, I based the solution on your the conversion formula in your question. The formula is identical to the JPEG YUV conversion (described in Wikipedia). The `J` letter of `YUVJ`, applies JPEG conversion (JPEG uses full range YUV and not the common video conversion formula were Y is limited to [16, 235]). – Rotem Mar 01 '21 at 16:51
  • My solution is not ideal, in part because "AV_PIX_FMT_YUVJ420P " is deprecated. I think you better use [sws_setColorspaceDetails()](http://man.hubwiz.com/docset/FFmpeg.docset/Contents/Resources/Documents/api/group__libsws.html#ga541bdffa8149f5f9203664f955faa040). Set `srcRange` to `1`. There is probably a better solution (Note: I never used sws_scale directly - only with FFmpeg command line). Please post the solution you found. – Rotem Mar 01 '21 at 17:01
  • Thanks for reply. I see you. I accept your answer as solution. It's definitely work, but maybe I will try manually get formulas YUV to RGB conversation (for main standards BT.601, BT.709, and BT.2020, and for 8, 10 and 12 bit depth), to have more control. I came across an interesting document, where the conclusion of these formulas is described in understandable form https://poynton.ca/ColorFAQ.html – Валентин Никин Mar 02 '21 at 05:17
0

yuv to rgb conversion using FFMPEG I see lot of information given already for this above. However for code completeness I am re-sharing the code with missing allocPicture() function, header & library to include, it works for me like a charm. Thanks to @Валентин Никин & @Rotem for most of the info & code.

Headers:

#include <libswscale/swscale.h> 

Link FFMPEG Library:

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);
naveenKumar
  • 367
  • 4
  • 10