【OpenGL】用OpenGL shader实现将YUV(YUV420,YV12)转RGB-(直接调用GPU实现,纯硬件方式,效率高) 这段时间一直在搞视频格式的转换问题,终于最近将一个图片的YUV

【OpenGL】用OpenGL shader实现将YUV(YUV420,YV12)转RGB-(直接调用GPU实现,纯硬件方式,效率高)

这段时间一直在搞视频格式的转换问题,终于最近将一个图片的YUV格式转RGB格式转换成功了。下面就来介绍一下:


由于我的工程是在vs2008中的,其中包含一些相关头文件和库,所以下面只是列出部分核心代码,并不是全部代码。


1、下载一个包含YUV数据的文件也可以自己制作一个该文件

下载地址: 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、将纹理传入

上述片段shader中就是根据yuv转rgb的公式得来的。也就是说是在shader中实现转换的。
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";
上述片段shader中就是根据yuv转rgb的公式得来的。也就是说是在shader中实现转换的。

4、显示结果

结果如下:

注意:该shader是OpenGL格式的shader有一点差别。

--------------------------------------------------------------------------------------------------------------------------------

YV12格式与YUV格式只是在UV的存储位置上不同,需要注意一下

YV12,I420,YUV420P的区别

YV12和I420的区别
一般来说,直接采集到的视频数据是RGB24的格式,RGB24一帧的大小size=width×heigth×3 Byte,RGB32的size=width×heigth×4,如果是I420(即YUV标准格式4:2:0)的数据量是 size=width×heigth×1.5 Byte。
在采集到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。

补充=============
详细的格式之间的差异可以参考:
 
附一个YUV播放器的源代码: http://download.csdn.net/detail/leixiaohua1020/6374065
查看YUV的时候也可以下载使用成熟的YUV播放器 ——YUV Player Deluxe: http://www.yuvplayer.com/
 

yuv420p就是I420格式,使用极其广泛,它的示意图:

 

【图像-视频处理】YUV420、YV12与RGB24的转换公式


[cpp]  view plain  copy print?
    1. bool YV12ToBGR24_Native(unsigned char* pYUV,unsigned char* pBGR24,int width,int height)  
    2. {  
    3.     if (width < 1 || height < 1 || pYUV == NULL || pBGR24 == NULL)  
    4.         return false;  
    5.     const long len = width * height;  
    6.     unsigned char* yData = pYUV;  
    7.     unsigned char* vData = &yData[len];  
    8.     unsigned char* uData = &vData[len >> 2];  
    9.   
    10.     int bgr[3];  
    11.     int yIdx,uIdx,vIdx,idx;  
    12.     for (int i = 0;i < height;i++){  
    13.         for (int j = 0;j < width;j++){  
    14.             yIdx = i * width + j;  
    15.             vIdx = (i/2) * (width/2) + (j/2);  
    16.             uIdx = vIdx;  
    17.   
    18.             bgr[0] = (int)(yData[yIdx] + 1.732446 * (uData[vIdx] - 128));                                    // b分量  
    19.             bgr[1] = (int)(yData[yIdx] - 0.698001 * (uData[uIdx] - 128) - 0.703125 * (vData[vIdx] - 128));    // g分量  
    20.             bgr[2] = (int)(yData[yIdx] + 1.370705 * (vData[uIdx] - 128));                                    // r分量  
    21.   
    22.             for (int k = 0;k < 3;k++){  
    23.                 idx = (i * width + j) * 3 + k;  
    24.                 if(bgr[k] >= 0 && bgr[k] <= 255)  
    25.                     pBGR24[idx] = bgr[k];  
    26.                 else  
    27.                     pBGR24[idx] = (bgr[k] < 0)?0:255;  
    28.             }  
    29.         }  
    30.     }  
    31.     return true;  

以上是yv12到RGB24的转换算法,如果是yuv420到RGB24转换,秩序u,v反过来就可以了。

即:

[cpp]  view plain  copy print?
  1. unsigned char* uData = &yData[nYLen];  
  2. unsigned char* vData = &vData[nYLen>>2];  
 注:海康威视网络摄像头一般就是yu12格式的!

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);
}
 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值