【OpenGL】用OpenGL shader实现将YUV(YUV420,YV12)转RGB-(直接调用GPU实现,纯硬件方式,效率高)
这段时间一直在搞视频格式的转换问题,终于最近将一个图片的YUV格式转RGB格式转换成功了。下面就来介绍一下:
由于我的工程是在vs2008中的,其中包含一些相关头文件和库,所以下面只是列出部分核心代码,并不是全部代码。
1、下载一个包含YUV数据的文件也可以自己制作一个该文件
2、读入YUV数据文件中的yuv数据:
2.1读文件代码
unsigned char * readYUV(char *path)
{ FILE *fp;
unsigned char * buffer;
long size = 1280 * 720 * 3 / 2; if((fp=fopen(path,"rb"))==NULL)
{
printf("cant open the file");
exit(0);
} buffer = new unsigned char[size];
memset(buffer,'\0',size);
fread(buffer,size,1,fp);
fclose(fp);
return buffer;
}
2.2读入数据,并将YUV数据分别制作成3个纹理
GLuint texYId;
GLuint texUId;
GLuint texVId; void loadYUV(){
int width ;
int height ; width = 640;
height = 480; unsigned char *buffer = NULL;
buffer = readYUV("1.yuv"); glGenTextures ( 1, &texYId );
glBindTexture ( GL_TEXTURE_2D, texYId );
glTexImage2D ( GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, buffer );
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); glGenTextures ( 1, &texUId );
glBindTexture ( GL_TEXTURE_2D, texUId );
glTexImage2D ( GL_TEXTURE_2D, 0, GL_LUMINANCE, width / 2, height / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, buffer + width * height);
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); glGenTextures ( 1, &texVId );
glBindTexture ( GL_TEXTURE_2D, texVId );
glTexImage2D ( GL_TEXTURE_2D, 0, GL_LUMINANCE, width / 2, height / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, buffer + width * height * 5 / 4 );
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); }
上述代码中1.yuv就是YUV数据文件
3、将纹理传入
3.1 顶点shader和片段shader代码
GLbyte vShaderStr[] =
"attribute vec4 vPosition; \n"
"attribute vec2 a_texCoord; \n"
"varying vec2 tc; \n"
"void main() \n"
"{ \n"
" gl_Position = vPosition; \n"
" tc = a_texCoord; \n"
"} \n"; GLbyte fShaderStr[] =
"precision mediump float; \n"
"uniform sampler2D tex_y; \n"
"uniform sampler2D tex_u; \n"
"uniform sampler2D tex_v; \n"
"varying vec2 tc; \n"
"void main() \n"
"{ \n"
" vec4 c = vec4((texture2D(tex_y, tc).r - 16./255.) * 1.164);\n"
" vec4 U = vec4(texture2D(tex_u, tc).r - 128./255.);\n"
" vec4 V = vec4(texture2D(tex_v, tc).r - 128./255.);\n"
" c += V * vec4(1.596, -0.813, 0, 0);\n"
" c += U * vec4(0, -0.392, 2.017, 0);\n"
" c.a = 1.0;\n"
" gl_FragColor = c;\n"
"} \n";
4、显示结果
注意:该shader是OpenGL格式的shader有一点差别。
--------------------------------------------------------------------------------------------------------------------------------
YV12格式与YUV格式只是在UV的存储位置上不同,需要注意一下
YV12,I420,YUV420P的区别
在采集到RGB24数据后,需要对这个格式的数据进行第一次压缩。即将图像的颜色空间由RGB2YUV。因为,X264在进行编码的时候需要标准的YUV(4:2:0)。但是这里需要注意的是,虽然YV12也是(4:2:0),但是YV12和I420的却是不同的,在存储空间上面有些区别。如下:
YV12 : 亮度(行×列) + V(行×列/4) + U(行×列/4)
I420 : 亮度(行×列) + U(行×列/4) + V(行×列/4)
可以看出,YV12和I420基本上是一样的,就是UV的顺序不同。
继续我们的话题,经过第一次数据压缩后RGB24->YUV(I420)。这样,数据量将减少一半,为什么呢?呵呵,这个就太基础了,我就不多写了。同样,如果是RGB24->YUV(YV12),也是减少一半。但是,虽然都是一半,如果是YV12的话效果就有很大损失。然后,经过X264编码后,数据量将大大减少。将编码后的数据打包,通过RTP实时传送。到达目的地后,将数据取出,进行解码。完成解码后,数据仍然是YUV格式的,所以,还需要一次转换,这样windows的驱动才可以处理,就是YUV2RGB24。
yuv420p就是I420格式,使用极其广泛,它的示意图:
【图像-视频处理】YUV420、YV12与RGB24的转换公式
- bool YV12ToBGR24_Native(unsigned char* pYUV,unsigned char* pBGR24,int width,int height)
- {
- if (width < 1 || height < 1 || pYUV == NULL || pBGR24 == NULL)
- return false;
- const long len = width * height;
- unsigned char* yData = pYUV;
- unsigned char* vData = &yData[len];
- unsigned char* uData = &vData[len >> 2];
- int bgr[3];
- int yIdx,uIdx,vIdx,idx;
- for (int i = 0;i < height;i++){
- for (int j = 0;j < width;j++){
- yIdx = i * width + j;
- vIdx = (i/2) * (width/2) + (j/2);
- uIdx = vIdx;
- bgr[0] = (int)(yData[yIdx] + 1.732446 * (uData[vIdx] - 128)); // b分量
- bgr[1] = (int)(yData[yIdx] - 0.698001 * (uData[uIdx] - 128) - 0.703125 * (vData[vIdx] - 128)); // g分量
- bgr[2] = (int)(yData[yIdx] + 1.370705 * (vData[uIdx] - 128)); // r分量
- for (int k = 0;k < 3;k++){
- idx = (i * width + j) * 3 + k;
- if(bgr[k] >= 0 && bgr[k] <= 255)
- pBGR24[idx] = bgr[k];
- else
- pBGR24[idx] = (bgr[k] < 0)?0:255;
- }
- }
- }
- return true;
- }
以上是yv12到RGB24的转换算法,如果是yuv420到RGB24转换,秩序u,v反过来就可以了。
即:
- unsigned char* uData = &yData[nYLen];
- unsigned char* vData = &vData[nYLen>>2];
2016-9-22 19:53
张朋艺 pyZhangBIT2010@126.com
找到的英文参考资料:
yv12 to rgb using glsl in iOS ,result image attached
https://stackoverflow.com/questions/11093061/yv12-to-rgb-using-glsl-in-ios-result-image-attached
following is my code for uploading the three planar data to textures:
- (GLuint) textureY: (Byte*)imageData
widthType: (int) width
heightType: (int) height
{
GLuint texName;
glGenTextures( 1, &texName );
glBindTexture(GL_TEXTURE_2D, texName);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D( GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, imageData );
//free(imageData);
return texName;
}
- (GLuint) textureU: (Byte*)imageData
widthType: (int) width
heightType: (int) height
{
GLuint texName;
glGenTextures( 1, &texName );
glBindTexture(GL_TEXTURE_2D, texName);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D( GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, imageData );
//free(imageData);
return texName;
}
- (GLuint) textureV: (Byte*)imageData
widthType: (int) width
heightType: (int) height
{
GLuint texName;
glGenTextures( 1, &texName );
glBindTexture(GL_TEXTURE_2D, texName);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D( GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, imageData );
//free(imageData);
return texName;
}
- (void) readYUVFile
{
NSString *file = [[NSBundle mainBundle] pathForResource:@"video" ofType:@"yv12"];
NSLog(@"%@",file);
NSData* fileData = [NSData dataWithContentsOfFile:file];
//NSLog(@"%@",[fileData description]);
NSInteger width = 352;
NSInteger height = 288;
NSInteger uv_width = width / 2;
NSInteger uv_height = height / 2;
NSInteger dataSize = [fileData length];
NSLog(@"%i\n",dataSize);
GLint nYsize = width * height;
GLint nUVsize = uv_width * uv_height;
GLint nCbOffSet = nYsize;
GLint nCrOffSet = nCbOffSet + nUVsize;
Byte *spriteData = (Byte *)malloc(dataSize);
[fileData getBytes:spriteData length:dataSize];
Byte* uData = spriteData + nCbOffSet;
//NSLog(@"%@\n",[[NSData dataWithBytes:uData length:nUVsize] description]);
Byte* vData = spriteData + nCrOffSet;
//NSLog(@"%@\n",[[NSData dataWithBytes:vData length:nUVsize] description]);
/**
Byte *YPlanarData = (Byte *)malloc(nYsize);
for (int i=0; i<nYsize; i++) {
YPlanarData[i]= spriteData[i];
}
Byte *UPlanarData = (Byte *)malloc(nYsize);
for (int i=0; i<height; i++) {
for (int j=0; j<width; j++) {
int numInUVsize = (i/2)*uv_width+j/2;
UPlanarData[i*width+j]=uData[numInUVsize];
}
}
Byte *VPlanarData = (Byte *)malloc(nYsize);
for (int i=0; i<height; i++) {
for (int j=0; j<width; j++) {
int numInUVsize = (i/2)*uv_width+j/2;
VPlanarData[i*width+j]=vData[numInUVsize];
}
}
**/
_textureUniformY = glGetUniformLocation(programHandle, "SamplerY");
_textureUniformU = glGetUniformLocation(programHandle, "SamplerU");
_textureUniformV = glGetUniformLocation(programHandle, "SamplerV");
free(spriteData);
}
and my fragment shaders code:
precision highp float;
uniform sampler2D SamplerY;
uniform sampler2D SamplerU;
uniform sampler2D SamplerV;
varying highp vec2 coordinate;
void main()
{
highp vec3 yuv,yuv1;
highp vec3 rgb;
yuv.x = texture2D(SamplerY, coordinate).r;
yuv.y = texture2D(SamplerU, coordinate).r-0.5;
yuv.z = texture2D(SamplerV, coordinate).r-0.5 ;
rgb = mat3( 1, 1, 1,
0, -.34414, 1.772,
1.402, -.71414, 0) * yuv;
gl_FragColor = vec4(rgb, 1);
}
【OpenGL】用OpenGL shader实现将YUV(YUV420,YV12)转RGB-(直接调用GPU实现,纯硬件方式,效率高)的更多相关文章
- OpenGL 和OpenGL ES简介
OpenGL的全称是Open Graphics Library,即开放的图形库接口,它定义了一个跨编程语言.跨平台的编程接口的规范,它主要用于三维图形(实际上二维图形也可以)变成.OpenGL的前 ...
- opengl笔记——OpenGL好资料备忘
Plane Equation 注:面可理解为:连接面上的点与原点,投影相同(为:a*x1+b*x2+c*x3) OpenGL Matrix Class (C++) Overview OpenGL fi ...
- 有关于OpenGL、OpenGL ES、WebGL的小结
转自原文 有关于OpenGL.OpenGL ES.WebGL的小结 一. OpenGL简介 OpenGL(全写Open Graphics Library)是个定义了一个跨编程语言.跨平台的编程接口 ...
- YUV图解 (YUV444, YUV422, YUV420, YV12, NV12, NV21)
背景: 最近在研究音视频,了解YUV这样的格式对于音视频开发比较重要. 虽然这篇文章大部分是转载别人的,但是经过了校对以后,重新排版并补充了一部分内容 概览: 之所以提出yuv格式的原因,是为了解 ...
- Android camera2 回调imagereader 从Image拿到YUV数据转化成RGB,生成bitmap并保存
ImageUtil.java import android.graphics.ImageFormat; import android.media.Image; import android.os.Bu ...
- 色彩空间RGB/CMYK/HSL/HSB/HSV/Lab/YUV基础理论及转换方法:RGB与YUV
之前做个设计,现在从事IT,脑子里面关于RGB,RGBA,CMY,CMYK,YUV,但是具体理论还是不扎实.若干年前之前写过<水煮RGB与CMYK色彩模型—色彩与光学相关物理理论浅叙>&l ...
- Unity在OpenGL模式下Shader编译报错
报错信息 GLSL compilation failed: 0(21) : error C7528: OpenGL reserves names containing '__' 双击报错VS自动打开V ...
- OpenGL笔记<5> shader 调试信息获取 Debug
我们今天来讲调试信息,这个东西讲起来会比较无聊,因为都是一些函数调用,没啥可讲的,函数就是那样用的,不过其效果挺好玩的,同时在程序设计中也是很必要的,所以还是来写一下,不过,就是因为知识比较固定且简单 ...
- GLSL 在OpenGL中向shader传递信息【转】
http://blog.csdn.net/hgl868/article/details/7872219 引言 一个OpenGL程序可以用多种方式和shader通信.注意这种通信是单向的,因为shade ...
随机推荐
- HDU-5335
Walk Out Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Su ...
- for in 对象时,属性为非负整数的情况
在我做一个需求的时候 for in 一个对象,对象的属性都是数字 但是我想给这个对象加一个默认的属性跟值 原对象是{5446:"广州市"}.....类似于下去 然后我想给我页面展示 ...
- NET应用——你的数据安全有必要升级
最近又被[现场破解共享单车系统]刷了一脸,不得不开始后怕:如何防止类似的情况发生? 想来想去,始终觉得将程序加密是最简单的做法.但是摩拜.ofo也有加密,为什么仍然被破解?那是因为请求在传输过程中被篡 ...
- CSS布局和居中常用技巧
1.常用居中方法 居中在布局中很常见,我们假设DOM文档结构如下,子元素要在父元素中居中: <div class="parent"> <div class=&qu ...
- Python进程间通信:Queue
Python进程间通信Queue 1.Queue使用方法: Queue.qsize():返回当前队列包含的消息数量: Queue.empty():如果队列为空,返回True,反之False : Que ...
- SpringBoot学习:读取yml和properties文件的内容
一.在SpringBoot实现属性注入: 1).添加pom依赖jar包: <!-- 支持 @ConfigurationProperties 注解 --> <!-- https://m ...
- react native 手势响应
参考地址:https://www.jianshu.com/p/935e5c6a5064 官方文档地址:https://facebook.github.io/react-native/docs/panr ...
- 验收测试与UI
CRS 如果功能复杂的情况下,是不是先写验收测试,然后写单元测试,最后写代码? STST 是的 从高往低走,无论是分析,还是测试,还是开发 从高往低走,带来的是干净无累赘的,底层依赖高层的优雅的结果 ...
- Comet OJ CCPC-Wannafly Winter Camp Day1 (Div2, online mirror) F.爬爬爬山-最短路(Dijkstra)(两个板子)+思维(mdzz...) zhixincode
爬爬爬山 已经提交 已经通过 9.83% Total Submission:417 Total Accepted:41 题目描述 爬山是wlswls最喜欢的活动之一. 在一个神奇的世界里,一共有nn座 ...
- 第7天-javascript内置对象
数组相关方法 concat 用来连接多个数组 <script> var a = [1,2,3]; var b = [3,4,5]; var c = a.concat(b); console ...