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.    */
    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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342