iOS|iOS YUV数据与RGBA数据互转

在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.*/ vImagePixelCountheight; /* The height (in pixels) of the buffer*/ vImagePixelCountwidth; /* The width (in pixels) of the buffer*/ size_trowBytes; /* The number of bytes in a pixel row, including any unused space between one row and the next. */ }vImage_Buffer;

Buffer初始化函数如下,对于需要转换的数据可以直接对结构体赋值,对于存储目标数据的结构体才需要使用初始化函数进行初始化。
VIMAGE_PF vImage_ErrorvImageBuffer_Init( vImage_Buffer *buf, vImagePixelCountheight, vImagePixelCountwidth, uint32_tpixelBits, vImage_Flagsflags)

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

【iOS|iOS YUV数据与RGBA数据互转】随后我们就可以从argbBuff中取出转换后的数据,到此转换完成。
demo地址:https://github.com/XMSECODE/ESCLibyuvDemo

    推荐阅读