【视频处理】YV12ToARGB
前面提到了YV12转RGB的各种实现方法和优化方法,主要是CPU上的实现。本文主要介绍基于GPU的YV12转RGB的实现。
1. 基于OpenGL的实现
利用OpenGL shader实现将YV12转RGB,将Y、U、V分量数据作为纹理数据,并构造YUV转RGB的shader代码,最终纹理数据在shader代码作用下,实现YV12转RGB。该方法适合于将YV12转RGB后直接显示,若YV12转化成RGB后,还需要进行图像处理操作,则利用OpenGL进行纹理数据的图像处理操作不方便。说明:由于本文着重于基于Cuda的实现,因而未验证基于OpenGL的代码实现。
具体资料可参考:
http://blog.csdn.net/xiaoguaihai/article/details/8672631
http://www.fourcc.org/source/YUV420P-OpenGL-GLSLang.c
2. 基于Cuda的实现
YV12转RGB的过程是逐一获取像素的Y、U、V分量,然后通过转换公式计算得RGB。基于CUDA的实现关键在于两个步骤:Y、U、V分量的获取,RGB的计算。Y、U、V分量的获取与YUV的内存布局有关,RGB的计算公式一般是固定不变。具体的代码实现如下所示,主要参考NV12ToARGB.cu的代码,在该代码的基础上,保持RGB的计算方法不变,修改了Y、U、V分量的获取方法。
|
#include "cuda.h" #include "cuda_runtime_api.h" #define COLOR_COMPONENT_BIT_SIZE 10 #define COLOR_COMPONENT_MASK 0x3FF __constant__ float constHueColorSpaceMat[9]={1.1644f,0.0f,1.596f,1.1644f,-0.3918f,-0.813f,1.1644f,2.0172f,0.0f}; __device__ staticvoid YUV2RGB(constint* yuvi,float* red,float* green,float* blue) { float luma, chromaCb, chromaCr; // Prepare for hue adjustment luma =(float)yuvi[0]; chromaCb =(float)((int)yuvi[1]-512.0f); chromaCr =(float)((int)yuvi[2]-512.0f); // Convert YUV To RGB with hue adjustment *red =(luma * constHueColorSpaceMat[0])+ (chromaCb * constHueColorSpaceMat[1])+ (chromaCr * constHueColorSpaceMat[2]); *green =(luma * constHueColorSpaceMat[3])+ (chromaCb * constHueColorSpaceMat[4])+ (chromaCr * constHueColorSpaceMat[5]); *blue =(luma * constHueColorSpaceMat[6])+ (chromaCb * constHueColorSpaceMat[7])+ (chromaCr * constHueColorSpaceMat[8]); } __device__ staticint RGBA_pack_10bit(float red,float green,float blue,int alpha) { int ARGBpixel =0; // Clamp final 10 bit results red =::fmin(::fmax(red, 0.0f),1023.f); green =::fmin(::fmax(green,0.0f),1023.f); blue =::fmin(::fmax(blue, 0.0f),1023.f); // Convert to 8 bit unsigned integers per color component ARGBpixel =(((int)blue >>2)| (((int)green >>2)<<8) | (((int)red >>2)<<16)| (int)alpha); return ARGBpixel; } __global__ void YV12ToARGB_FourPixel(constunsignedchar* pYV12,unsignedint* pARGB,int width,int height) { // Pad borders with duplicate pixels, and we multiply by 2 because we process 4 pixels per thread constint x = blockIdx.x *(blockDim.x <<1)+(threadIdx.x <<1); constint y = blockIdx.y *(blockDim.y <<1)+(threadIdx.y <<1); if((x +1)>= width ||(y +1)>= height) return; // Read 4 Luma components at a time int yuv101010Pel[4]; yuv101010Pel[0]=(pYV12[y * width + x ])<<2; yuv101010Pel[1]=(pYV12[y * width + x +1])<<2; yuv101010Pel[2]=(pYV12[(y +1)* width + x ])<<2; yuv101010Pel[3]=(pYV12[(y +1)* width + x +1])<<2; constunsignedint vOffset = width * height; constunsignedint uOffset = vOffset +(vOffset >>2); constunsignedint vPitch = width >>1; constunsignedint uPitch = vPitch; constint x_chroma = x >>1; constint y_chroma = y >>1; int chromaCb = pYV12[uOffset + y_chroma * uPitch + x_chroma]; //U int chromaCr = pYV12[vOffset + y_chroma * vPitch + x_chroma]; //V yuv101010Pel[0]|=(chromaCb <<( COLOR_COMPONENT_BIT_SIZE +2)); yuv101010Pel[0]|=(chromaCr <<((COLOR_COMPONENT_BIT_SIZE <<1)+2)); yuv101010Pel[1]|=(chromaCb <<( COLOR_COMPONENT_BIT_SIZE +2)); yuv101010Pel[1]|=(chromaCr <<((COLOR_COMPONENT_BIT_SIZE <<1)+2)); yuv101010Pel[2]|=(chromaCb <<( COLOR_COMPONENT_BIT_SIZE +2)); yuv101010Pel[2]|=(chromaCr <<((COLOR_COMPONENT_BIT_SIZE <<1)+2)); yuv101010Pel[3]|=(chromaCb <<( COLOR_COMPONENT_BIT_SIZE +2)); yuv101010Pel[3]|=(chromaCr <<((COLOR_COMPONENT_BIT_SIZE <<1)+2)); // this steps performs the color conversion int yuvi[12]; float red[4], green[4], blue[4]; yuvi[0]=(yuv101010Pel[0]& COLOR_COMPONENT_MASK ); yuvi[1]=((yuv101010Pel[0]>> COLOR_COMPONENT_BIT_SIZE) & COLOR_COMPONENT_MASK); yuvi[2]=((yuv101010Pel[0]>>(COLOR_COMPONENT_BIT_SIZE <<1))& COLOR_COMPONENT_MASK); yuvi[3]=(yuv101010Pel[1]& COLOR_COMPONENT_MASK ); yuvi[4]=((yuv101010Pel[1]>> COLOR_COMPONENT_BIT_SIZE) & COLOR_COMPONENT_MASK); yuvi[5]=((yuv101010Pel[1]>>(COLOR_COMPONENT_BIT_SIZE <<1))& COLOR_COMPONENT_MASK); yuvi[6]=(yuv101010Pel[2]& COLOR_COMPONENT_MASK ); yuvi[7]=((yuv101010Pel[2]>> COLOR_COMPONENT_BIT_SIZE) & COLOR_COMPONENT_MASK); yuvi[8]=((yuv101010Pel[2]>>(COLOR_COMPONENT_BIT_SIZE <<1))& COLOR_COMPONENT_MASK); yuvi[9]=(yuv101010Pel[3]& COLOR_COMPONENT_MASK ); yuvi[10]=((yuv101010Pel[3]>> COLOR_COMPONENT_BIT_SIZE) & COLOR_COMPONENT_MASK); yuvi[11]=((yuv101010Pel[3]>>(COLOR_COMPONENT_BIT_SIZE <<1))& COLOR_COMPONENT_MASK); // YUV to RGB Transformation conversion YUV2RGB(&yuvi[0],&red[0],&green[0],&blue[0]); YUV2RGB(&yuvi[3],&red[1],&green[1],&blue[1]); YUV2RGB(&yuvi[6],&red[2],&green[2],&blue[2]); YUV2RGB(&yuvi[9],&red[3],&green[3],&blue[3]); pARGB[y * width + x ]= RGBA_pack_10bit(red[0], green[0], blue[0],((int)0xff<<24)); pARGB[y * width + x +1]= RGBA_pack_10bit(red[1], green[1], blue[1],((int)0xff<<24)); pARGB[(y +1)* width + x ]= RGBA_pack_10bit(red[2], green[2], blue[2],((int)0xff<<24)); pARGB[(y +1)* width + x +1]= RGBA_pack_10bit(red[3], green[3], blue[3],((int)0xff<<24)); } bool YV12ToARGB(unsignedchar* pYV12,unsignedchar* pARGB,int width,int height) { unsignedchar* d_src; unsignedchar* d_dst; unsignedint srcMemSize =sizeof(unsignedchar)* width * height *3/2; unsignedint dstMemSize =sizeof(unsignedchar)* width * height *4; cudaMalloc((void**)&d_src,srcMemSize); cudaMalloc((void**)*d_dst,dstMemSize); cudaMemcpy(d_src,pYV12,srcMemSize,cudaMemcpyHostToDevice); dim3 block(32,8); int gridx =(width +2*block.x -1)/(2*block.x); int gridy =(height +2*block.y -1)/(2*block.y); dim3 grid(gridx,gridy); YV12ToARGB<<<grid,block>>>(d_src,(unsignedint*)d_dst,width,height); cudaMemcpy(pARGB,d_dst,dstMemSize,cudaMemcpyDeviceToHost); returntrue; } |
线程内存访问示意图如下所示,每个线程访问4个Y、1个U、1个V,最终转换得到4个ARGB值。由于YV12属于YUV4:2:0采样,每四个Y共用一组UV分量,即Y(0,0)、Y(0,1)、Y(1,0)、Y(1,1)共用V(0,0)和U(0,0),如红色框标注所示。

3. 基于Cuda的实现优化
优化主要关注于两个方面:单个线程处理像素粒度和数据传输。单个线程处理粒度分为:OnePixelPerThread,TwoPixelPerThread,FourPixelPerThread。数据传输优化主要采用Pageable Memory,Pinned Memory,Mapped Memory(Zero Copy)。经测试,不同实现版本的转换效率如下表所示,测试序列:1920*1080,时间统计包括内核函数执行时间和数据传输时间,单位为ms。
|
OnePixel |
TwoPixel |
FourPixel |
|
|
Pageable |
6.91691 |
6.64319 |
6.2873 |
|
Pinned |
5.31999 |
5.01890 |
4.71937 |
|
Mapped |
3.39043 |
48.5298 |
23.8327 |
由上表可知,不使用Mapped Memory(Zero Copy)时,单个线程处理像素的粒度越大,内核函数执行的时间越小,转换效率越好。使用Mapped Memory(Zero Copy)时,单线程处理单像素时,转换效率最好。
单个线程处理四个像素时,内核函数执行时间最少;使用Pinned Memory会减少数据传输时间;使用Mapped Memory消除数据传输过程,但会增加内核函数执行时间,最终优化效果与内核函数访问内存的方式有关。建议使用Pinned Memory+FourPixelPerThread的优化版本。
利用NVIDIA提供的性能分析工具,分析Pinned Memory+FourPixelPerThread版本程序,分析结果如下图所示,内核计算时间占1/4左右,数据传输时间占3/4左右,总体而言,内核计算任务过少,导致并行优化的效果无法抵消数据传输的开销。

【视频处理】YV12ToARGB的更多相关文章
- 【腾讯bugly干货分享】HTML 5 视频直播一站式扫盲
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=1277 视频直 ...
- premere cs4绿色版 安装 并且 视频导出 讲解
最近室友,开始在玩视频剪辑,用的是 premere cs4 绿色版.让他遇到的最大问题也是我之前遇到的最大问题,就是视频导出. 所以我在这里上传一套自己的一点点经验吧. 接下来,我就总结一下 我是怎么 ...
- Power BI官方视频(3) Power BI Desktop 8月份更新功能概述
Power BI Desktop 8月24日发布了更新版本.现将更新内容翻译整理如下,可以根据后面提供的链接下载最新版本使用. 1.主要功能更新 1.1 数据钻取支持在线版 以前的desktop中进行 ...
- 视频 - 在 VirtualBox 中部署 OpenStack
大家新年好,CloudMan 今天给大家带来一件新年礼物. 一直以来大家都反馈 OpenStack 学习有两大障碍:1. 实验环境难搭2. 体系复杂,难道大今天我就先帮大家解决环境问题.前两天我抽空在 ...
- canvas与html5实现视频截图功能
这段时间一直在研究canvas,突发奇想想做一个可以截屏视频的功能,然后把图片拉去做表情包,哈哈哈哈哈哈~~ 制作方法: 1.在页面中加载视频 在使用canvas制作这个截图功能时,首先必须保证页面上 ...
- html5 与视频
1.视频支持格式. 有3种视频格式被浏览器广泛支持:.ogg,.mp4,.webm. Theora+Vorbis=.ogg (Theora:视频编码器,Vorbis:音频编码器) H.264+$$$ ...
- 基于RN开发的一款视频配音APP(开源)
在如今React.ng.vue三分天下的格局下,不得不让自己加快学习的脚步.虽然经常会陷入各种迷茫,学得越多会发现不会的东西也被无限放大,不过能用新的技术作出一些小项目小Demo还是会给自己些许自信与 ...
- 脑洞大开之采用HTML5+SignalR2.0(.Net)实现原生Web视频
目录 对SignalR不了解的人可以直接移步下面的目录 SignalR系列目录 前言 - -,我又来了,今天废话不多说,我们直接来实现Web视频聊天. 采用的技术如下: HTML5 WebRTC Si ...
- duang~免费的学习视频来啦:学霸君之全栈测试
学霸君向童鞋们推荐一款 同名学霸学习 视频教程 重点是完全免费收看学习噢!!! 今天 学霸君推荐腾讯课堂的学霸君之全栈测试 复制下方链接至腾讯课堂中报名学习 https://ke.qq.com/cou ...
随机推荐
- Leetcode 4 Median of Two Sorted Arrays 二分查找(二分答案+二分下标)
貌似是去年阿里巴巴c++的笔试题,没有什么创新直接照搬的... 题意就是找出两个排序数组的中间数,其实就是找出两个排序数组的第k个数. 二分答案,先二分出一个数,再用二分算出这个数在两个排序数组排序第 ...
- Android后台保活实践总结:即时通讯应用无法根治的“顽疾”
前言 Android进程和Service的保活,是困扰Android开发人员的一大顽疾.因涉及到省电和内存管理策略,各厂商基于自家的理解,在自已ROOM发布于都对标准Android发行版作为或多或少的 ...
- rabbitMQ第一篇:rabbitMQ的安装和配置
在Windows下进行rabbitMQ的安装 第一步:软件安装 如果安装rabbitMQ首先安装基于erlang语言支持的OTP软件,然后在下载rabbitMQ软件进行安装(安装过程都是下一步,在此不 ...
- SSIS Send Mail
在SSIS中Send Mail的方法主要有三种,使用Send Mail Task,使用Script Task和使用存储过程msdb.dbo.sp_send_dbmail. 一,使用Send Mail ...
- sublime简要笔记
选中单词 [1]选中当前单词 ctrl+d [2]跳过当前单词 ctrl+k ctrl+d [3]选中相同的所有单词 alt+f3 [4]多行游标 按住shift,然后按住鼠标右键向下拖动 行操作 [ ...
- Oracle Dataguard之物理standby的基本配置
尽管网上有很多Oracle Dataguard的配置教程,但不难发现,很多采用的是rman duplicate这种方法,尽管此种方法较为简便.但在某种程度上,却也误导了初学者,虽说也能配置成功,但只知 ...
- 轻松自动化---selenium-webdriver(python) (一)
为什么选python? 之前的菜鸟系列是基于java的,一年没学其实也忘的差不多了,目前所测的产品部分也是python写的,而且团队也在推广python ,其实就测试人员来说,python也相当受欢迎 ...
- LocationManager使用细节
在使用系统的LocationManager请求地理位置的时候,请特别注意一个很小的细节,调用 requestLocationUpdates 以后,请记得[自己]设置一个timeout值,否则在某些情况 ...
- 在.NET Core程序中设置全局异常处理
以前我们想设置全局异常处理只需要这样的代码: AppDomain currentDomain = AppDomain.CurrentDomain; currentDomain.UnhandledExc ...
- PHP的反射机制
在面向对象中最经典的使用就是反射,之前在Java语言中,使用反射可以解耦,用于依赖注入. 在PHP中,同样也有如此强大的地方,我们利用反射来获取一个对象的实例. 首先我们先写一个类: class Te ...