前面提到了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. SQL将JSON转成列

    好久不写东西,这个也没什么技术含量,放上来玩玩,也许有人用的着. /** * create procedure for get all fields from json * * Mark * * 20 ...

  2. 使用Spring的Validator接口进行校验

    你可以使用Spring提供的validator接口进行对象的校验.Validator接口与Errors协同工作,在Spring做校验的时候,它会将所有的校验错误汇总到Errors对象中去. 来看这个简 ...

  3. Lookup 组件异常

    Lookup组件有两个数据源,一个是上流组件的输出,一个是组件lookup的数据源,这个数据源是在Connection选项卡中进行配置.在开发package的过程中,我发现一个异常,当Lookup数据 ...

  4. CSS计数器

    使用CSS计数器就像使用变量一样. 它有以下几个属性: counter-reset 创建或重置计数器 counter-increment 增长计数器 content 生成内容 counter() 将计 ...

  5. poj 2226 Muddy Fields(合理建图+二分匹配)

    /* 题意:用木板盖住泥泞的地方,不能盖住草.木板任意长!可以重叠覆盖! '*'表示泥泞的地方,'.'表示草! 思路: 首先让我们回忆一下HDU 2119 Matrix这一道题,一个矩阵中只有0, 1 ...

  6. 《BI那点儿事》数据挖掘各类算法——准确性验证

    准确性验证示例1:——基于三国志11数据库 数据准备: 挖掘模型:依次为:Naive Bayes 算法.聚类分析算法.决策树算法.神经网络算法.逻辑回归算法.关联算法提升图: 依次排名为: 1. 神经 ...

  7. .NET应用服务器

    昨天参加Oracle的一个活动,知道了WebLogic的强大,于是对应用服务器产生了兴趣. WebLogic是一个Java EE应用服务器(与Java EE对应的另外一个技术平台就是.NET). 为什 ...

  8. 在Abp中执行sql语句

    目录 前言 最近使用ABP(ASP.NET Boilerplate)做项目,有些查询(或存储过程)直接使用sql语句会更方便一点. 定义一个接口 在Core项目中定义一个接口,如:ISqlExecut ...

  9. Spark入门实战系列--8.Spark MLlib(下)--机器学习库SparkMLlib实战

    [注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .MLlib实例 1.1 聚类实例 1.1.1 算法说明 聚类(Cluster analys ...

  10. JS兼容所有浏览器的一段加入收藏代码,设置为首页

    <script language="javascript" type="text/javascript"> function addfavorite ...