您当前的位置: 首页 > 

鱼儿-1226

暂无认证

  • 0浏览

    0关注

    1100博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

最简单的基于FFmpeg的libswscale的示例(YUV转RGB)

鱼儿-1226 发布时间:2020-08-11 10:28:10 ,浏览量:0

简单的初始化方法

Libswscale使用起来很方便,最主要的函数只有3个: (1)       sws_getContext():使用参数初始化SwsContext结构体。 (2)       sws_scale():转换一帧图像。 (3)       sws_freeContext():释放SwsContext结构体。 其中sws_getContext()也可以用另一个接口函数sws_getCachedContext()取代。  

复杂但是更灵活的初始化方法

初始化SwsContext除了调用sws_getContext()之外还有另一种方法,更加灵活,可以配置更多的参数。该方法调用的函数如下所示。 (1)       sws_alloc_context():为SwsContext结构体分配内存。 (2)       av_opt_set_XXX():通过av_opt_set_int(),av_opt_set()…等等一系列方法设置SwsContext结构体的值。在这里需要注意,SwsContext结构体的定义看不到,所以不能对其中的成员变量直接进行赋值,必须通过av_opt_set()这类的API才能对其进行赋值。 (3)       sws_init_context():初始化SwsContext结构体。 这种复杂的方法可以配置一些sws_getContext()配置不了的参数。比如说设置图像的YUV像素的取值范围是JPEG标准(Y、U、V取值范围都是0-255)还是MPEG标准(Y取值范围是16-235,U、V的取值范围是16-240)。  

几个知识点

下文记录几个图像像素数据处理过程中的几个知识点:像素格式,图像拉伸,YUV像素取值范围,色域。

像素格式

像素格式的知识此前已经记录过,不再重复。在这里记录一下FFmpeg支持的像素格式。有几点注意事项: (1)       所有的像素格式的名称都是以“AV_PIX_FMT_”开头

(2)       像素格式名称后面有“P”的,代表是planar格式,否则就是packed格式。Planar格式不同的分量分别存储在不同的数组中,例如AV_PIX_FMT_YUV420P存储方式如下:

data[0]: Y1, Y2, Y3, Y4, Y5, Y6, Y7, Y8…… data[1]: U1, U2, U3, U4……

data[2]: V1, V2, V3, V4……

Packed格式的数据都存储在同一个数组中,例如AV_PIX_FMT_RGB24存储方式如下:

data[0]: R1, G1, B1, R2, G2, B2, R3, G3, B3, R4, G4, B4……   (3)       像素格式名称后面有“BE”的,代表是Big Endian格式;名称后面有“LE”的,代表是Little Endian格式。   FFmpeg支持的像素格式的定义位于libavutil\pixfmt.h,是一个名称为AVPixelFormat的枚举类型,如下所示。

 
  1. /**

  2. * Pixel format.

  3. *

  4. * @note

  5. * AV_PIX_FMT_RGB32 is handled in an endian-specific manner. An RGBA

  6. * color is put together as:

  7. * (A >log2_chroma_w)

  8.  
  9. /**

  10. * Amount to shift the luma height right to find the chroma height.

  11. * For YV12 this is 1 for example.

  12. * chroma_height= -((-luma_height) >> log2_chroma_h)

  13. * The note above is needed to ensure rounding up.

  14. * This value only refers to the chroma components.

  15. */

  16. uint8_t log2_chroma_h;

  17. uint8_t flags;

  18.  
  19. /**

  20. * Parameters that describe how pixels are packed.

  21. * If the format has 2 or 4 components, then alpha is last.

  22. * If the format has 1 or 2 components, then luma is 0.

  23. * If the format has 3 or 4 components,

  24. * if the RGB flag is set then 0 is red, 1 is green and 2 is blue;

  25. * otherwise 0 is luma, 1 is chroma-U and 2 is chroma-V.

  26. */

  27. AVComponentDescriptor comp[4];

  28. }AVPixFmtDescriptor;

关于AVPixFmtDescriptor这个结构体不再做过多解释。它的定义比较简单,看注释就可以理解。通过av_pix_fmt_desc_get()可以获得指定像素格式的AVPixFmtDescriptor结构体。

 
  1. /**

  2. * @return a pixel format descriptor for provided pixel format or NULL if

  3. * this pixel format is unknown.

  4. */

  5. const AVPixFmtDescriptor *av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt);

  通过AVPixFmtDescriptor结构体可以获得不同像素格式的一些信息。例如下文中用到了av_get_bits_per_pixel(),通过该函数可以获得指定像素格式每个像素占用的比特数(Bit Per Pixel)。

 
  1. /**

  2. * Return the number of bits per pixel used by the pixel format

  3. * described by pixdesc. Note that this is not the same as the number

  4. * of bits per sample.

  5. *

  6. * The returned number of bits refers to the number of bits actually

  7. * used for storing the pixel information, that is padding bits are

  8. * not counted.

  9. */

  10. int av_get_bits_per_pixel(const AVPixFmtDescriptor *pixdesc);

其他的API在这里不做过多记录。  

图像拉伸

FFmpeg支持多种像素拉伸的方式。这些方式的定义位于libswscale\swscale.h中,如下所示。

 
  1. #define SWS_FAST_BILINEAR 1

  2. #define SWS_BILINEAR 2

  3. #define SWS_BICUBIC 4

  4. #define SWS_X 8

  5. #define SWS_POINT 0x10

  6. #define SWS_AREA 0x20

  7. #define SWS_BICUBLIN 0x40

  8. #define SWS_GAUSS 0x80

  9. #define SWS_SINC 0x100

  10. #define SWS_LANCZOS 0x200

  11. #define SWS_SPLINE 0x400

其中SWS_BICUBIC性能比较好;SWS_FAST_BILINEAR在性能和速度之间有一个比好好的平衡, 而SWS_POINT的效果比较差。

有关这些方法的评测可以参考文章:

《ffmpeg中的sws_scale算法性能测试》

简单解释一下SWS_BICUBIC、SWS_BILINEAR和SWS_POINT的原理。

SWS_POINT(Nearest-neighbor interpolation, 邻域插值)

领域插值可以简单说成“1个点确定插值的点”。例如当图像放大后,新的样点根据距离它最近的样点的值取得自己的值。换句话说就是简单拷贝附近距离它最近的样点的值。领域插值是一种最基础的插值方法,速度最快,插值效果最不好,一般情况下不推荐使用。一般情况下使用邻域插值之后,画面会产生很多的“锯齿”。下图显示了4x4=16个彩色样点经过邻域插值后形成的图形。

SWS_BILINEAR(Bilinear interpolation, 双线性插值)

双线性插值可以简单说成“4个点确定插值的点”。它的计算过程可以简单用下图表示。图中绿色的P点是需要插值的点。首先通过Q11,Q21求得R1;Q12,Q22求得R2。然后根据R1,R2求得P。

其中求值的过程是一个简单的加权计算的过程。 设定Q11 = (x1, y1),Q12 = (x1, y2),Q21 = (x2, y1),Q22 = (x2, y2)则各点的计算公式如下。

可以看出距离插值的点近一些的样点权值会大一些,远一些的样点权值要小一些。 下面看一个维基百科上的双线性插值的实例。该例子根据坐标为(20, 14), (20, 15), (21, 14),(21, 15)的4个样点计算坐标为(20.2, 14.5)的插值点的值。

 

 

SWS_BICUBIC(Bicubic interpolation, 双三次插值)

双三次插值可以简单说成“16个点确定插值的点”。该插值算法比前两种算法复杂很多,插值后图像的质量也是最好的。有关它的插值方式比较复杂不再做过多记录。它的差值方法可以简单表述为下述公式。

其中aij的过程依赖于插值数据的特性。   维基百科上使用同样的样点进行邻域插值,双线性插值,双三次插值对比如下图所示。

Nearest-neighbor interpolation,邻域插值

Bilinear interpolation,双线性插值

Bicubic interpolation,双三次插值

 

YUV像素取值范围

FFmpeg中可以通过使用av_opt_set()设置“src_range”和“dst_range”来设置输入和输出的YUV的取值范围。如果“dst_range”字段设置为“1”的话,则代表输出的YUV的取值范围遵循“jpeg”标准;如果“dst_range”字段设置为“0”的话,则代表输出的YUV的取值范围遵循“mpeg”标准。下面记录一下YUV的取值范围的概念。

与RGB每个像素点的每个分量取值范围为0-255不同(每个分量占8bit),YUV取值范围有两种:

(1)       以Rec.601为代表(还包括BT.709 / BT.2020)的广播电视标准中,Y的取值范围是16-235,U、V的取值范围是16-240。FFmpeg中称之为“mpeg”范围。

(2)       以JPEG为代表的标准中,Y、U、V的取值范围都是0-255。FFmpeg中称之为“jpeg” 范围。

实际中最常见的是第1种取值范围的YUV(可以自己观察一下YUV的数据,会发现其中亮度分量没有取值为0、255这样的数值)。很多人在这个地方会有疑惑,为什么会去掉“两边”的取值呢?

在广播电视系统中不传输很低和很高的数值,实际上是为了防止信号变动造成过载,因而把这“两边”的数值作为“保护带”。下面这张图是数字电视中亮度信号量化后的电平分配图。从图中可以看出,对于8bit量化来说,信号的白电平为235,对应模拟电平为700mV;黑电平为16,对应模拟电平为0mV。信号上方的“保护带”取值范围是236至254,而信号下方的“保护带”取值范围是1-15。最边缘的0和255两个电平是保护电平,是不允许出现在数据流中的。与之类似,10bit量化的时候,白电平是235*4=940,黑电平是16*4=64。

下面两张图是数字电视中色度信号量化后的电平分配图。可以看出,色度最大正电平为240,对应模拟电平为+350mV;色度最大负电平为16,对应模拟电平为-350mV。需要注意的是,色度信号数字电平128对应的模拟电平是0mV。

 

 

色域

Libswscale支持色域的转换。有关色域的转换我目前还没有做太多的研究,仅记录一下目前最常见的三个标准中的色域:BT.601,BT.709,BT.2020。这三个标准中的色域逐渐增大。 在这里先简单解释一下CIE 1931颜色空间。这个空间围绕的区域像一个“舌头”,其中包含了自然界所有的颜色。CIE 1931颜色空间中的横坐标是x,纵坐标是y,x、y、z满足如下关系:

x + y + z = 1

“舌头”的边缘叫做“舌形曲线”,代表着饱和度为100%的光谱色。“舌头”的中心点(1/3,1/3)对应着白色,饱和度为0。 受显示器件性能的限制,电视屏幕是无法重现所有的颜色的,尤其是位于“舌形曲线”上的100% 饱和度的光谱色一般情况下是无法显示出来的。因此电视屏幕只能根据其具体的荧光粉的配方,有选择性的显示一部分的颜色,这部分可以显示的颜色称为色域。下文分别比较标清电视、高清电视和超高清电视标准中规定的色域。可以看出随着技术的进步,色域的范围正变得越来越大。 标清电视(SDTV)色域的规定源自于BT.601。高清电视(HDTV)色域的规定源自于BT.709。他们两个标准中的色域在CIE 1931颜色空间中的对比如下图所示。从图中可以看出,BT.709和BT.601色域差别不大,BT.709的色域要略微大于BT.601。

超高清电视(UHDTV)色域的规定源自于BT.2020。BT.2020和BT.709的色域在CIE 1931 颜色空间中的对比如下图所示。从图中可以看出,BT.2020的色域要远远大于BT.709。

从上面的对比也可以看出,对超高清电视(UHDTV)的显示器件的性能的要求更高了。这样超高清电视可以还原出一个更“真实”的世界。

下面这张图则使用实际的例子反映出色域范围大的重要性。图中的两个黑色三角形分别标识出了BT.709(小三角形)和BT.2020(大三角形)标准中的色域。从图中可以看出,如果使用色域较小的显示设备显示图片的话,将会损失掉很多的颜色。

源代码

本示例程序包含一个输入和一个输出,实现了从输入图像格式(YUV420P)到输出图像格式(RGB24)之间的转换;同时将输入视频的分辨率从480x272拉伸为1280x720。  

 
  1. /**

  2. * 最简单的基于FFmpeg的Swscale示例

  3. * Simplest FFmpeg Swscale

  4. *

  5. * 雷霄骅 Lei Xiaohua

  6. * leixiaohua1020@126.com

  7. * 中国传媒大学/数字电视技术

  8. * Communication University of China / Digital TV Technology

  9. * http://blog.csdn.net/leixiaohua1020

  10. *

  11. * 本程序使用libswscale对像素数据进行缩放转换等处理。

  12. * 它中实现了YUV420P格式转换为RGB24格式,

  13. * 同时将分辨率从480x272拉伸为1280x720

  14. * 它是最简单的libswscale的教程。

  15. *

  16. * This software uses libswscale to scale / convert pixels.

  17. * It convert YUV420P format to RGB24 format,

  18. * and changes resolution from 480x272 to 1280x720.

  19. * It's the simplest tutorial about libswscale.

  20. */

  21.  
  22. #include

  23.  
  24. #define __STDC_CONSTANT_MACROS

  25.  
  26. #ifdef _WIN32

  27. //Windows

  28. extern "C"

  29. {

  30. #include "libswscale/swscale.h"

  31. #include "libavutil/opt.h"

  32. #include "libavutil/imgutils.h"

  33. };

  34. #else

  35. //Linux...

  36. #ifdef __cplusplus

  37. extern "C"

  38. {

  39. #endif

  40. #include

  41. #include

  42. #include

  43. #ifdef __cplusplus

  44. };

  45. #endif

  46. #endif

  47.  
  48.  
  49. int main(int argc, char* argv[])

  50. {

  51. //Parameters

  52. FILE *src_file =fopen("sintel_480x272_yuv420p.yuv", "rb");

  53. const int src_w=480,src_h=272;

  54. AVPixelFormat src_pixfmt=AV_PIX_FMT_YUV420P;

  55.  
  56. int src_bpp=av_get_bits_per_pixel(av_pix_fmt_desc_get(src_pixfmt));

  57.  
  58. FILE *dst_file = fopen("sintel_1280x720_rgb24.rgb", "wb");

  59. const int dst_w=1280,dst_h=720;

  60. AVPixelFormat dst_pixfmt=AV_PIX_FMT_RGB24;

  61. int dst_bpp=av_get_bits_per_pixel(av_pix_fmt_desc_get(dst_pixfmt));

  62.  
  63. //Structures

  64. uint8_t *src_data[4];

  65. int src_linesize[4];

  66.  
  67. uint8_t *dst_data[4];

  68. int dst_linesize[4];

  69.  
  70. int rescale_method=SWS_BICUBIC;

  71. struct SwsContext *img_convert_ctx;

  72. uint8_t *temp_buffer=(uint8_t *)malloc(src_w*src_h*src_bpp/8);

  73.  
  74. int frame_idx=0;

  75. int ret=0;

  76. ret= av_image_alloc(src_data, src_linesize,src_w, src_h, src_pixfmt, 1);

  77. if (ret< 0) {

  78. printf( "Could not allocate source image\n");

  79. return -1;

  80. }

  81. ret = av_image_alloc(dst_data, dst_linesize,dst_w, dst_h, dst_pixfmt, 1);

  82. if (ret< 0) {

  83. printf( "Could not allocate destination image\n");

  84. return -1;

  85. }

  86. //-----------------------------

  87. //Init Method 1

  88. img_convert_ctx =sws_alloc_context();

  89. //Show AVOption

  90. av_opt_show2(img_convert_ctx,stdout,AV_OPT_FLAG_VIDEO_PARAM,0);

  91. //Set Value

  92. av_opt_set_int(img_convert_ctx,"sws_flags",SWS_BICUBIC|SWS_PRINT_INFO,0);

  93. av_opt_set_int(img_convert_ctx,"srcw",src_w,0);

  94. av_opt_set_int(img_convert_ctx,"srch",src_h,0);

  95. av_opt_set_int(img_convert_ctx,"src_format",src_pixfmt,0);

  96. //'0' for MPEG (Y:0-235);'1' for JPEG (Y:0-255)

  97. av_opt_set_int(img_convert_ctx,"src_range",1,0);

  98. av_opt_set_int(img_convert_ctx,"dstw",dst_w,0);

  99. av_opt_set_int(img_convert_ctx,"dsth",dst_h,0);

  100. av_opt_set_int(img_convert_ctx,"dst_format",dst_pixfmt,0);

  101. av_opt_set_int(img_convert_ctx,"dst_range",1,0);

  102. sws_init_context(img_convert_ctx,NULL,NULL);

  103.  
  104. //Init Method 2

  105. //img_convert_ctx = sws_getContext(src_w, src_h,src_pixfmt, dst_w, dst_h, dst_pixfmt,

  106. // rescale_method, NULL, NULL, NULL);

  107. //-----------------------------

  108. /*

  109. //Colorspace

  110. ret=sws_setColorspaceDetails(img_convert_ctx,sws_getCoefficients(SWS_CS_ITU601),0,

  111. sws_getCoefficients(SWS_CS_ITU709),0,

  112. 0, 1

关注
打赏
1604459285
查看更多评论
0.0745s