前面提到了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. 使用反射将DataTable的数据转成实体类

    利用反射避免了硬编码出现的错误,但是实体类的属性名必须和数据库名字对应(相同) 1.利用反射把DataTable的数据写到单个实体类 /// <summary> ///利用反射把DataT ...

  2. 移动端调试工具DebugGap推荐。

    因为现在项目大部分都是在写移动端,但是调试起来实在是不方便,虽然可以用chrome来模拟手机端,但实际上差别还是有点大的,最近找到了一款比较不错的调试工具,这里分享一下了,虽然网上已经有分享过了,但还 ...

  3. 模拟java.util.Collection一些简单的用法

    /* 需求:模拟java.util.Collection一些简单的用法! 注意:java虚拟机中并没有泛型类型的对象.泛型是通过编译器执行一个被称为类型擦除的前段转换来实现的. 1)用泛型的原生类型替 ...

  4. 【踩坑经历】一次Asp.NET小网站部署踩坑和解决经历

    2013年给1个大学的小客户部署过一个小型的Asp.NET网站,非常小,用的sqlite数据库,今年人家说要换台服务器,要重新部署一下,好吧,虽然早就过了服务时间,但无奈谁叫人家是客户了,二话不说,上 ...

  5. 序列sequence中的cache问题

    Oracle中序列Sequence的创建语法如下: CREATE SEQUENCE [ schema. ] sequence [ { INCREMENT BY | START WITH } integ ...

  6. backbone库学习-Collection

    backbone库的结构: http://www.cnblogs.com/nuysoft/archive/2012/03/19/2404274.html 本文所有例子来自于http://blog.cs ...

  7. Linux的IO性能监控

    一般使用iostat命令监控I/O性能1.iostat命令可用参数列表: OPTIONS -c Display the CPU utilization report. -d Display the d ...

  8. 使用etcd+confd管理nginx配置

    1.前言 最近在项目中用nginx做反向代理,需要动态生成nginx的配置.大概流程是用户在页面上新增域名.http或https协议以及端口信息,后台会根据域名自动生成一个nginx的server配置 ...

  9. 基于HT for Web矢量实现2D叶轮旋转

    之前在拓扑上的应用都是些静态的图元,今天我们将在拓扑上设计一个会动的图元——叶轮旋转. 我们先来看下这个叶轮模型长什么样 从模型上看,这个叶轮模型有三个叶片,每一个叶片都是不规则图形,显然无法用上我们 ...

  10. Log4net 日志使用介绍

    概述 Log4net 有三个主要组件:loggers,appenders 和 layouts.这三个组件一起工作使得开发者能够根据信息类型和等级(Level)记录信息,以及在运行时控制信息的格式化和信 ...