在iOS视频开发的时候,我们会遇到YUV与RGBA数据转换的需求,解决这个需求我们有许多方法,我们可以自己根据公式写转换函数,可以使用第三方,也可以使用系统库实现。后附demo。
1、自己实现转换函数
一般情况下是遍历YUV数据生成RGBA数据,或者遍历RGBA数据生成YUV数据。普通函数是由CPU执行,执行时间和数据的大小成正相关。如果不是专家级人物,性能是没有第三方和系统的快的。
转换公式如下:
1.小数形式,未量化 ( U~[-0.5-0.5] , R~[0,1] )
R = Y + 1.4075 * V; G = Y - 0.3455 * U - 0.7169*V; B = Y + 1.779 * U;
Y = 0.299*R + 0.587*G + 0.114*B;
U = (B-Y)/1.772;
V = (R-Y)/1.402; 或写为:Y = 0.299*R + 0.587*G + 0.114*B;
U = -0.169*R - 0.331*G + 0.5 *B ;
V = 0.5 *R - 0.419*G - 0.081*B;
相关详细资料参考如下博客:
https://www.cnblogs.com/luoyinjie/p/7219319.html
2、第三方库
第三方库有比较多的库,我们熟悉的ffmpeg可以做yuv和rgba的数据转换,另外还有一个比较出名的libyuv库也可以做各种像素格式转换。这里我们介绍libyuv库。
Libyuv库在文章末尾的demo里面已经编译好,可以直接使用,需要的朋友可以直接下载。
Libyuv使用比较简单,引入头文件直接调用函数即可,比如YUV转RGBA的函数如下:
#import "libyuv.h"
int I420ToRGBA(const uint8* src_y,
int src_stride_y,
const uint8* src_u,
int src_stride_u,
const uint8* src_v,
int src_stride_v,
uint8* dst_rgba,
int dst_stride_rgba,
int width,
int height);
前面几个数据为yuv数据源,后面两个参数为RGBA数据地址,最后面填写的是图像的宽高。RGBA转YUV函数如下:
#import "libyuv.h"
int RGBAToI420(const uint8* src_frame,
int src_stride_frame,
uint8* dst_y,
int dst_stride_y,
uint8* dst_u,
int dst_stride_u,
uint8* dst_v,
int dst_stride_v,
int width,
int height);
参数同上。需要注意的是,目标数据的指针需要提前分配好内存空间,转换函数不进行空间分配。
Ffmpeg数据转换的这里不说了,可以参考:https://github.com/XMSECODE/FFMPEG_STUDY
3、系统库转换
系统库转换是速度最快的,消耗性能最少的。如可以满足需求,建议使用系统库进行转换。
iOS系统提供了Accelerate库进行复杂计算操作,可以大量提高计算性能。提供的计算功能比较多,其中有一项功能就是提供图像数据格式转换。过程如下:
初始化缓存区-》创建转换器对象-》转换数据
a、初始化缓存区
我们这里示例YUV转换RGBA,RGBA转换YUV过程也是一样的,demo里有详细代码。
buffer结构体如下:
typedef struct vImage_Buffer
{
void *data; /* Pointer to the top left pixel of the buffer. */
vImagePixelCount height; /* The height (in pixels) of the buffer */
vImagePixelCount width; /* The width (in pixels) of the buffer */
size_t rowBytes; /* The number of bytes in a pixel row, including any unused space between one row and the next. */
}vImage_Buffer;
Buffer初始化函数如下,对于需要转换的数据可以直接对结构体赋值,对于存储目标数据的结构体才需要使用初始化函数进行初始化。
VIMAGE_PF vImage_Error vImageBuffer_Init( vImage_Buffer * buf,
vImagePixelCount height,
vImagePixelCount width,
uint32_t pixelBits,
vImage_Flags flags)
b、创建转换器对象
创建转换器的函数如下:
VIMAGE_PF vImage_Error vImageConvert_YpCbCrToARGB_GenerateConversion(const vImage_YpCbCrToARGBMatrix *matrix, const vImage_YpCbCrPixelRange *pixelRange, vImage_YpCbCrToARGB *outInfo, vImageYpCbCrType inYpCbCrType, vImageARGBType outARGBType, vImage_Flags flags) VIMAGE_NON_NULL(1,2,3) API_AVAILABLE(macos(10.10), ios(8.0), watchos(1.0), tvos(8.0));
我们的代码调用过程如下:
vImage_YpCbCrToARGB infoyuvoargb;
vImage_YpCbCrPixelRange range;
range.Yp_bias = 16;
range.CbCr_bias = 128;
range.YpRangeMax = 235;
range.CbCrRangeMax = 240;
range.YpMax = 235;
range.YpMin = 16;
range.CbCrMax = 240;
range.CbCrMin = 16;
vImageConvert_YpCbCrToARGB_GenerateConversion(kvImage_YpCbCrToARGBMatrix_ITU_R_601_4, &range, &infoyuvoargb, kvImage420Yp8_Cb8_Cr8, kvImageARGB8888, kvImageNoFlags);
c、转换数据
转换数据也为一个函数,如下:
VIMAGE_PF vImage_Error vImageConvert_420Yp8_Cb8_Cr8ToARGB8888(const vImage_Buffer *srcYp, const vImage_Buffer *srcCb, const vImage_Buffer *srcCr, const vImage_Buffer *dest, const vImage_YpCbCrToARGB *info, const uint8_t permuteMap[4], const uint8_t alpha, vImage_Flags flags) VIMAGE_NON_NULL(1,2,3,4,5) API_AVAILABLE(macos(10.10), ios(8.0), watchos(1.0), tvos(8.0));
我们调用如下:
vImage_Error result = vImageConvert_420Yp8_Cb8_Cr8ToARGB8888(&srcsBuff_y, &srcsBuff_v, &srcsBuff_u, &argbBuff, &infoyuvoargb, NULL, 255, kvImagePrintDiagnosticsToConsole);
随后我们就可以从argbBuff中取出转换后的数据,到此转换完成。
demo地址:https://github.com/XMSECODE/ESCLibyuvDemo