前面提到了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的更多相关文章

  1. 【腾讯bugly干货分享】HTML 5 视频直播一站式扫盲

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=1277 视频直 ...

  2. premere cs4绿色版 安装 并且 视频导出 讲解

    最近室友,开始在玩视频剪辑,用的是 premere cs4 绿色版.让他遇到的最大问题也是我之前遇到的最大问题,就是视频导出. 所以我在这里上传一套自己的一点点经验吧. 接下来,我就总结一下 我是怎么 ...

  3. Power BI官方视频(3) Power BI Desktop 8月份更新功能概述

    Power BI Desktop 8月24日发布了更新版本.现将更新内容翻译整理如下,可以根据后面提供的链接下载最新版本使用. 1.主要功能更新 1.1 数据钻取支持在线版 以前的desktop中进行 ...

  4. 视频 - 在 VirtualBox 中部署 OpenStack

    大家新年好,CloudMan 今天给大家带来一件新年礼物. 一直以来大家都反馈 OpenStack 学习有两大障碍:1. 实验环境难搭2. 体系复杂,难道大今天我就先帮大家解决环境问题.前两天我抽空在 ...

  5. canvas与html5实现视频截图功能

    这段时间一直在研究canvas,突发奇想想做一个可以截屏视频的功能,然后把图片拉去做表情包,哈哈哈哈哈哈~~ 制作方法: 1.在页面中加载视频 在使用canvas制作这个截图功能时,首先必须保证页面上 ...

  6. html5 与视频

    1.视频支持格式. 有3种视频格式被浏览器广泛支持:.ogg,.mp4,.webm. Theora+Vorbis=.ogg  (Theora:视频编码器,Vorbis:音频编码器) H.264+$$$ ...

  7. 基于RN开发的一款视频配音APP(开源)

    在如今React.ng.vue三分天下的格局下,不得不让自己加快学习的脚步.虽然经常会陷入各种迷茫,学得越多会发现不会的东西也被无限放大,不过能用新的技术作出一些小项目小Demo还是会给自己些许自信与 ...

  8. 脑洞大开之采用HTML5+SignalR2.0(.Net)实现原生Web视频

    目录 对SignalR不了解的人可以直接移步下面的目录 SignalR系列目录 前言 - -,我又来了,今天废话不多说,我们直接来实现Web视频聊天. 采用的技术如下: HTML5 WebRTC Si ...

  9. duang~免费的学习视频来啦:学霸君之全栈测试

    学霸君向童鞋们推荐一款 同名学霸学习 视频教程 重点是完全免费收看学习噢!!! 今天 学霸君推荐腾讯课堂的学霸君之全栈测试 复制下方链接至腾讯课堂中报名学习 https://ke.qq.com/cou ...

随机推荐

  1. Java面试(1)-- Java赋值表达式

    1 class Demo01{ 2 public static void main(String[] args){ 3 //赋值运算符 = 4 5 //例1 6 int a = 1; 7 System ...

  2. Android笔记——Android框架

    本篇将站在顶级的高度--架构,来看android.我开篇就说了,这个系列适合0基础的人且我也是从0开始按照这个步骤来 学的,谈架构是不是有点螳臂挡车,自不量力呢?我觉得其实不然,如果一开始就对整个an ...

  3. 那些年我们写过的T-SQL(下篇)

    下篇的内容很多都会在工作中用到,尤其是可编程对象,那些年我们写过的存储过程,有木有?到目前为止很多大型传统企业仍然很依赖存储过程.这部分主要难理解的部分是事务和锁机制这块,本文会进行简单的阐述.虽然很 ...

  4. JS 关于(function( window, undefined ) {})(window)写法的理解

    JS 关于(function( window, undefined ) {})(window)写法的理解 [网络整理] (function( window, undefined ) {})(windo ...

  5. 用javascript动态创建并提交表单form,表格table

    <script> //helper function to create the formfunction getNewSubmitForm(){ var submitForm = doc ...

  6. 基于Metronic的Bootstrap开发框架经验总结(9)--实现Web页面内容的打印预览和保存操作

    在前面介绍了很多篇相关的<Bootstrap开发框架>的系列文章,这些内容基本上覆盖到了我这个Bootstrap框架的各个主要方面的内容,总体来说基本达到了一个稳定的状态,随着时间的推移可 ...

  7. Unity 游戏存档 PlayerPrefs类的用法

    unity3d提供了一个用于本地持久化保存与读取的类--PlayerPrefs.工作原理非常简单,以键值对的形式将数据保存在文件中,然后程序可以根据这个名称取出上次保存的数值.    PlayerPr ...

  8. PHP Log时时查看小工具

    以前Log都是打印在一个文档中,然后打开文件夹,最后打开文档查看里面的内容,每次打开文件夹感觉很烦. 前些日子看到同事开发.NET的时候,用他自己的一个小工具能够时时查看到Log的内容,非常方便,所以 ...

  9. Network - DNS

    珠玉在前,不再赘言 DNS 原理入门 从理论到实践,全方位认识DNS(理论篇) 从理论到实践,全方位认识DNS(实践篇)

  10. Struts+Hibernate+Spring实现用户登录功能

    通过登录案例实现三大框架之间的整合,登录功能是任何系统和软件必不可少的一个模块,然而通过这个模块来认识这些复杂的框架技术,理解数据流向和整个设计思路是相当容易的.只有在掌握了这些小模块的应用后,才能轻 ...