简单了解iOSCVPixelBuffer(下)
1、前言
在简单了解iOSCVPixelBuffer(中)中,我们了解了颜色空间RGB和YUV的区别以及相关的背景知识,最后对CVPixelBuffer中的kCVPixelFormatType相关类型进行了解读。我们已经对CVPixelBuffer有了初步的了解,在这篇文章中,我们将继续聊聊CVPixelBuffer在使用过程中的一些格式转换;
RGB和YUV格式转换
在很多场景下,我们需要将不同的颜色空间进行转换,以此来解决对应的工程性问题。以下是转换公式:1。1YUVRGBRY1。13983VGY0。39465U0。58060VBY2。03211U1。2RGBYUVY0。299R0。587G0。114BU0。14713R0。28886G0。436BV0。615R0。51499G0。10001B2、iOS中常见格式转换
在iOS中RGB和YUV互相转换的方法会使用到libyuv开源库,打开此链接需要梯子,目前国内也有,需要的自取libyuv开源库国内仓库
iOS在CVPixelBuffer转换的上会很复杂,对buffer操作之前需要执行加锁方法CVPixelBufferLockBaseAddress进行保护,在处理完后,执行解锁buffer方法CVPixelBufferUnlockBaseAddress。
以下的方法是我在项目中以及平常的开发中所整理,仅供参考;2。1NV12toI420
核心NV12ToI420方法是使用了libyuv开源库;NV12toI420(CVPixelBufferRef)I420PixelBufferWithNV12:(CVImageBufferRef)cvpixelBufferRef{CVPixelBufferLockBaseAddress(cvpixelBufferRef,0);图像宽度(像素)sizetpixelWidthCVPixelBufferGetWidth(cvpixelBufferRef);图像高度(像素)sizetpixelHeightCVPixelBufferGetHeight(cvpixelBufferRef);获取CVPixelBufferRef中的y数据constuint8yframe(uint8)CVPixelBufferGetBaseAddressOfPlane(cvpixelBufferRef,0);获取CMVImageBufferRef中的uv数据constuint8uvframe(uint8)CVPixelBufferGetBaseAddressOfPlane(cvpixelBufferRef,1);ystridesizetplane1strideCVPixelBufferGetBytesPerRowOfPlane(cvpixelBufferRef,0);uvstridesizetplane2strideCVPixelBufferGetBytesPerRowOfPlane(cvpixelBufferRef,1);yuvsize(内存空间)sizetframesizepixelWidthpixelHeight32;开辟framesize大小的内存空间用于存放转换好的i420数据uint8buffer(unsignedchar)malloc(framesize);buffer为这段内存的首地址,plane1size代表这一帧中y数据的长度uint8dstubufferpixelWidthpixelHeight;dstu为u数据的首地,plane1size4为u数据的长度uint8dstvdstupixelWidthpixelHeight4;libyuv转换intretNV12ToI420(yframe,(int)plane1stride,uvframe,(int)plane2stride,buffer,(int)pixelWidth,dstu,(int)pixelWidth2,dstv,(int)pixelWidth2,(int)pixelWidth,(int)pixelHeight);if(ret){returnNULL;}NSDictionarypixelAttributes{(id)kCVPixelBufferIOSurfacePropertiesKey:{}};CVPixelBufferRefpixelBufferNULL;CVReturnresultCVPixelBufferCreate(kCFAllocatorDefault,pixelWidth,pixelHeight,kCVPixelFormatType420YpCbCr8Planar,(bridgeCFDictionaryRef)(pixelAttributes),pixelBuffer);CVPixelBufferLockBaseAddress(pixelBuffer,0);sizetdCVPixelBufferGetBytesPerRowOfPlane(pixelBuffer,0);sizetudCVPixelBufferGetBytesPerRowOfPlane(pixelBuffer,1);sizetvdCVPixelBufferGetBytesPerRowOfPlane(pixelBuffer,2);unsignedchardsty(unsignedchar)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer,0);unsignedchardstu(unsignedchar)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer,1);unsignedchardstv(unsignedchar)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer,2);unsignedcharsrcybuffer;for(unsignedintrIdx0;rIdxpixelHeight;rIdx,srcypixelWidth,dstyd){memcpy(dsty,srcy,pixelWidth);}unsignedcharsrcubufferpixelHeightpixelWidth;for(unsignedintrIdx0;rIdxpixelHeight2;rIdx,srcupixelWidth2,dstuud){memcpy(dstu,srcu,pixelWidth2);}unsignedcharsrcvbufferpixelHeightpixelWidth54;for(unsignedintrIdx0;rIdxpixelHeight2;rIdx,srcvpixelWidth2,dstvvd){memcpy(dstv,srcv,pixelWidth2);}CVPixelBufferUnlockBaseAddress(pixelBuffer,0);if(result!kCVReturnSuccess){NSLog(Unabletocreatecvpixelbufferd,result);}free(buffer);CVPixelBufferRelease(cvpixelBufferRef);returnpixelBuffer;}
C音视频学习资料免费获取方法:关注音视频开发T哥,点击链接即可免费获取2023年最新C音视频开发进阶独家免费学习大礼包!2。2NV12toBGRA
核心NV12ToARGB方法同样使用了libyuv开源库NV12toBGRA(CVPixelBufferRef)RGBAPixelBufferWithNV12:(CVImageBufferRef)pixelBufferNV12{CVPixelBufferLockBaseAddress(pixelBufferNV12,0);图像宽度(像素)sizetpixelWidthCVPixelBufferGetWidth(pixelBufferNV12);图像高度(像素)sizetpixelHeightCVPixelBufferGetHeight(pixelBufferNV12);ystridesizetsrcstrideyCVPixelBufferGetBytesPerRowOfPlane(pixelBufferNV12,0);uvstridesizetsrcstrideuvCVPixelBufferGetBytesPerRowOfPlane(pixelBufferNV12,1);获取CVImageBufferRef中的y数据uint8tsrcy(unsignedchar)CVPixelBufferGetBaseAddressOfPlane(pixelBufferNV12,0);获取CMVImageBufferRef中的uv数据uint8tsrcuv(unsignedchar)CVPixelBufferGetBaseAddressOfPlane(pixelBufferNV12,1);创建一个空的32BGRA格式的CVPixelBufferRefNSDictionarypixelAttributes{(id)kCVPixelBufferIOSurfacePropertiesKey:{}};CVPixelBufferRefpixelBufferRGBANULL;CVReturnresultCVPixelBufferCreate(kCFAllocatorDefault,pixelWidth,pixelHeight,kCVPixelFormatType32BGRA,(bridgeCFDictionaryRef)pixelAttributes,pixelBufferRGBA);kCVPixelFormatType32BGRAif(result!kCVReturnSuccess){NSLog(Unabletocreatecvpixelbufferd,result);returnNULL;}resultCVPixelBufferLockBaseAddress(pixelBufferRGBA,0);if(result!kCVReturnSuccess){CFRelease(pixelBufferRGBA);NSLog(Failedtolockbaseaddress:d,result);returnNULL;}得到新创建的CVPixelBufferRef中rgb数据的首地址uint8trgbdata(uint8)CVPixelBufferGetBaseAddress(pixelBufferRGBA);使用libyuv为rgbdata写入数据,将NV12转换为BGRAsizetbgraStrideCVPixelBufferGetBytesPerRowOfPlane(pixelBufferRGBA,0);intretNV12ToARGB(srcy,(int)srcstridey,srcuv,(int)srcstrideuv,rgbdata,(int)bgraStride,(int)pixelWidth,(int)pixelHeight);if(ret){NSLog(ErrorconvertingNV12VideoFrametoBGRA:d,result);CFRelease(pixelBufferRGBA);returnNULL;}CVPixelBufferUnlockBaseAddress(pixelBufferRGBA,0);CVPixelBufferUnlockBaseAddress(pixelBufferNV12,0);returnpixelBufferRGBA;}2。3CVPixelBufferReftoUIImage
以下方法可以将视频帧转成单张图片(比较适用于间隔时间长的截图,高频的使用这个方法很可能会引起内存的问题)buffertoimage(UIImage)convert:(CVPixelBufferRef)pixelBuffer{CIImageciImage〔CIImageimageWithCVPixelBuffer:pixelBuffer〕;CIContexttemporaryContext〔CIContextcontextWithOptions:nil〕;CGImageRefvideoImage〔temporaryContextcreateCGImage:ciImagefromRect:CGRectMake(0,0,CVPixelBufferGetWidth(pixelBuffer),CVPixelBufferGetHeight(pixelBuffer))〕;UIImageuiImage〔UIImageimageWithCGImage:videoImage〕;CGImageRelease(videoImage);returnuiImage;}2。4CGImageReftoCVPixelBufferRef
以下方法会通过单张图片转成一个PixelBuffer(适用于将某一帧图片转成Buffer添加字幕或者美颜贴纸等等)imagetobuffer(CVPixelBufferRef)pixelBufferFromCGImage:(CGImageRef)image{NSDictionaryoptions{(NSString)kCVPixelBufferCGImageCompatibilityKey:YES,(NSString)kCVPixelBufferCGBitmapContextCompatibilityKey:YES,(NSString)kCVPixelBufferIOSurfacePropertiesKey:〔NSDictionarydictionary〕};CVPixelBufferRefpxbufferNULL;CGFloatframeWidthCGImageGetWidth(image);CGFloatframeHeightCGImageGetHeight(image);CVReturnstatusCVPixelBufferCreate(kCFAllocatorDefault,frameWidth,frameHeight,kCVPixelFormatType32BGRA,(bridgeCFDictionaryRef)options,pxbuffer);NSParameterAssert(statuskCVReturnSuccesspxbuffer!NULL);CVPixelBufferLockBaseAddress(pxbuffer,0);voidpxdataCVPixelBufferGetBaseAddress(pxbuffer);NSParameterAssert(pxdata!NULL);CGColorSpaceRefrgbColorSpaceCGColorSpaceCreateDeviceRGB();CGContextRefcontextCGBitmapContextCreate(pxdata,frameWidth,frameHeight,8,CVPixelBufferGetBytesPerRow(pxbuffer),rgbColorSpace,(CGBitmapInfo)kCGImageAlphaNoneSkipFirst);NSParameterAssert(context);CGContextConcatCTM(context,CGAffineTransformIdentity);CGContextDrawImage(context,CGRectMake(0,0,frameWidth,frameHeight),image);CGColorSpaceRelease(rgbColorSpace);CGContextRelease(context);CVPixelBufferUnlockBaseAddress(pxbuffer,0);returnpxbuffer;}2。5BufferDatatoUIImage
以下方法会通过内存数据转成图片(根据内存的地址去取出存储的buffer并生成图片,其实这里的内存的地址指向的就是Buffer)NV12toimage(UIImage)YUVtoUIImage:(int)wh:(int)hbuffer:(unsignedchar)buffer{YUV(NV12)CIImageUIImageConversionNSDictionarypixelAttributes{(NSString)kCVPixelBufferIOSurfacePropertiesKey:{}};CVPixelBufferRefpixelBufferNULL;CVReturnresultCVPixelBufferCreate(kCFAllocatorDefault,w,h,kCVPixelFormatType420YpCbCr8BiPlanarVideoRange,(bridgeCFDictionaryRef)(pixelAttributes),pixelBuffer);CVPixelBufferLockBaseAddress(pixelBuffer,0);voidyDestPlaneCVPixelBufferGetBaseAddressOfPlane(pixelBuffer,0);Hereych0isYPlaneofYUV(NV12)data。unsignedcharych0buffer;unsignedcharych1bufferwh;memcpy(yDestPlane,ych0,wh);voiduvDestPlaneCVPixelBufferGetBaseAddressOfPlane(pixelBuffer,1);Hereych1isUVPlaneofYUV(NV12)data。memcpy(uvDestPlane,ych1,wh0。5);CVPixelBufferUnlockBaseAddress(pixelBuffer,0);if(result!kCVReturnSuccess){NSLog(Unabletocreatecvpixelbufferd,result);}CIImageConversionif(available(iOS13。0,)){CIImagecoreImage〔CIImageimageWithCVPixelBuffer:pixelBuffer〕;CIContexttemporaryContext〔CIContextcontextWithOptions:nil〕;CGImageRefvideoImage〔temporaryContextcreateCGImage:coreImagefromRect:CGRectMake(0,0,w,h)〕;UIImagefinalImage〔〔UIImagealloc〕initWithCGImage:videoImage〕;CVPixelBufferRelease(pixelBuffer);CGImageRelease(videoImage);returnfinalImage;}returnnil;};2。6BufferToNSData(NSData)dataFrompixelBuffer:(CVPixelBufferRef)pixelBuffer{CVPixelBufferLockBaseAddress(pixelBuffer,0);sizetpixelWidthCVPixelBufferGetWidth(pixelBuffer);sizetpixelHeightCVPixelBufferGetHeight(pixelBuffer);sizetysizepixelWidthpixelHeight;sizetuvsizeysize2;uint8tyuvframe(uint8t)malloc(uvsizeysize);uint8tyframe(uint8t)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer,0);memcpy(yuvframe,yframe,ysize);uint8tuvframe(uint8t)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer,1);memcpy(yuvframeysize,uvframe,uvsize);CVPixelBufferUnlockBaseAddress(pixelBuffer,0);NSDatadata〔NSDatadataWithBytesNoCopy:yuvframelength:ysizeuvsize〕;returndata;}
iOS中格式转换涉及到C、OC、C的一些方法,所以很多方法看起来会非常的冗余,需要一定的基础和持续的学习。还有很多是通过OpenGL来绘制图像的方法,更是难得看懂,所以初学者做好笔记,保持耐心,把常用的方法梳理起来,最后封装到工具类中。等到机缘巧合的时候再来深入了解。3、参考文献
一文读懂YUV的采样与格式
原文链接:简单了解iOSCVPixelBuffer(下)掘金
中芯要达到台积电的技术水平,需要几年时间?在制造芯片的圆晶体代工领域,大陆的中芯国际,要达到甚至超过我国台湾台积电的技术水平,有3种途径。第一种途径是我国突破美国的阻挠,从荷兰的阿斯麦(ASML)公司获得目前最先进的EUV
各位电脑技术员,U盘PE哪家比较好?能说说你常用的PE和理由吗?很多朋友都网上求助,使用下载的PE系统来进行系统恢复操作后,IE浏览器主页竟然莫名其妙的被修改为其他网址了,而且还被自动安装上了其他软件。那现在有没有干净的PE系统呢?1微PE,目
在东莞买社保,工厂已经倒闭,自己又找不到给买社保的工厂,该怎么办?按照当下的实际情况,至少有以下三种操作方式,找不到那是不可能找不到的。1个人以灵活就业人员的方式缴纳。个人缴纳需要自己缴纳全额,包括原来单位缴纳的那一部分,有些项目不需要。在东莞,
新家刚装修完不久,买台空气净化器除甲醛有用吗?没用,不管多好多贵的空气净化机,处理甲醛的能力都有限,不要讨论价格,我在用最贵的空气净化器要10000多,6。7000的松下也用过,五六百的也用过。空气净化器处理甲醛,无非是用活性
开孕婴店,如何引流吸引顾客主动上门?在淘宝开店打滚十几年后,也延伸到线下实体,恰巧我也是母婴作为老前辈,我分享几点实用的干货。1店铺选址,周边五公里内的楼盘至少三个,这保证了基本的市场需求2店铺装修,色调暖色七彩,最
生娃之后多久穿收腹带?哪个阶段使用效果最好?我是二胎妈妈,两个宝宝是顺产的,生产后是在出院以后开始使用收腹带的。但剖腹产妈妈是在手术结束后医生就会给绑上收腹带,帮助恢复刀口。生娃之后多久穿收腹带?如果是剖腹产,建议手术后直接
为什么有些年轻女演员不用大红色系口红?举个例子吧,我感觉很多明星都不太适合涂大红色口红。1脸型不合适赵丽颖就不经常用大红色口红,她基本上用的都是珊瑚色或者西柚色的口红,比如参加dior活动的时候,非常正式吧,穿了礼服,
07年最佳一阵有多强?一阵4个MVP,詹姆斯2766二阵?说起NBA群雄争霸的年代那绝对数00年代最为灿烂,在90年代更像是乔丹的独舞,以及奥拉朱旺巴克利等人的短暂璀璨,而论群雄争霸还要看00年代,这个时期没有乔丹那种绝对统治力的存在,奥
什么手表比较耐用,耐看,价格又不高的?什么手表耐用,耐看,不用非得太讲究牌子和花哨的款式。实用性高,简单大气的手表更能凸现一个人的品味。首先,从实用性来看,肯定要挑戴着舒服,性价比高的手表,款式不要太花哨,要大气,顺眼
游戏三国志14里有哪些有趣的细节?1。孙权黑本作黑孙权到了丧心病狂的程度,三大君主里曹操的主义是霸道,刘备是王道,而孙权是割据关键孙权手下还没几个和自己合得来的,四都督里一个割据主义的都没有,十二虎臣里也只有韩当和
八卦下医院里遇到的有趣故事?前段时间,我姐陪姐夫住院,遇到了一个神奇的老头。老头高龄90,体型瘦削,声如炮仗。老头是位有故事的老头,他来到病房的第一天,便成功勾起了病房病友和家属的好奇心。老头性格开朗,不久后
俄罗斯损失惨重!俄航天局决定空飞船接回宇航员到底要花多少钱?终于尘埃落定,俄罗斯国家航天集团周三表示,联盟MS23救援船将于2月20日在无人状态下前往国际空间站,冷却系统发生泄漏的联盟MS22号飞船在没有机组人员的情况下返回地球。俄罗斯损失
两颗气象探测卫星发射入轨西永微电园全面启建低轨卫星星座图为发射现场。西永微电园区企业供图记者10日从西部(重庆)科学城西永微电子产业园区获悉,由落户该园区的企业研制的气象探测卫星西永微电园1号和西永微电园2号,9日搭载星河动力航天公司
已经伤停一年多的卢比奥,明天复出据ESPN记者Woj报道,因前交叉韧带撕裂缺席一年多之后,骑士后卫里基卢比奥计划在周五对开拓者队的比赛中伤愈复出。Woj表示,这位32岁的老将将在今天进行训练,除非出现伤情反复,否
乐购武汉2023年跨年消费季武昌分会场活动暨斗鱼电竞跨年趴盛大揭幕1月7日,乐购武汉2023跨年消费季武昌分会场活动暨斗鱼电竞跨年趴,在武商梦时代正式启动。该活动由武汉市商务局共青团武汉市委员会指导,武昌区人民政府主办,斗鱼承办。在此,历时四个月
大帝玻尿酸与凉凉绰号析大帝玻尿酸与凉凉幽称析自俄乌战争以来,有这么一个网上获得几种费解却又幽称的大国首领。想来有点像我国古代名人中的名字号。有人称其为大帝,有人唤其玻尿酸,有人叫其凉凉那么,这些称谓到底
土地与货币一场激烈的变革在得知了地方城投隐性债务危机高达65万亿人民币,以及想到最近热议的土地财政,想着出一期合集,从土地说起,到土地货币和经济的关系,理解了土地和货币的关系,也就可以理解土地财政的历史必
岳飞尽忠报国为何被杀?罪名莫须有是什么意思?绍兴十年(1140年),岳飞的岳家军与完颜兀术率领的金兵在郾城相遇。岳飞派儿子岳云率领骑兵打头阵,岳云领着骑兵,奋勇冲杀,在金军大阵中进出几十次,杀得金兵尸横遍野。完颜兀术随即派出
位于非洲的马达加斯加,为何主体是黄种人而非黑人按照西方的标准,世界上的人主要分成黄种人白种人与黑种人三大人种,原本黄种人分布于亚洲东部与美洲,白种人分布在亚洲西部与欧洲,黑人分布于撒哈拉以南的非洲。近代因为新航路的开辟,西方人
朱元璋落魄时去算命,先生见到他写的字后称日后成就不可限量众所周知,我们有上下五千年的历史,历经24朝代422位皇帝。纵观这些皇帝中,明朝开国皇帝朱元璋是极其特殊的一位,他的崛起过程堪称奇迹,被后人称之为传奇皇帝。在历史上,农民起义虽然爆
致敬祖国,致敬革命前辈我的生活也是头条致敬祖国,致敬革命前辈。退休证刘建堂,河南省南阳市邓州市(原为鄧縣,邓县)罗庄镇罗西村人。退休待遇证1923年出生,1949年从国民革命军335师起义。黄埔同学同学
普林斯顿大学化学家成功利用定制蛋白质在室温下创造量子点自然界使用20种典型的氨基酸作为制造蛋白质的构件,将它们的序列组合起来,创造出能发挥生物功能的复杂分子。但是,自然界没有选择的序列会发生什么?在构建全新的序列以制造新的,或从头开始