解析yuv420图片并转换成RGB显示

简介

  本篇为学习yuv图片格式转化笔记。

原理讲解

参考资料

  YUV格式哟很多种,本篇记录的格式为YUV420。
有关YUV420的详细介绍,可以参考资料:1、图文详解YUV420数据格式 http://blog.csdn.net/jefry_xdz/article/details/7931018

大致讲解

  首先yuv420的存储大小为:width * height * 3 / 2。
它的分布根据前面推荐文档提到的,是又有两种方式:紧缩格式和平面格式。这里只讲解本篇使用到的平面格式。
其数据分布可以如下图所示:
  
                 (图一)                                 (图二)
  从图片中我们可以看到,这种格式下的yuv数据存放,yuv之间的数据比例为4:1:1。同时最先是存储完所有的Y数据之后,接着才存储UV数据。
uv数据的存放,有两种情况,u和v交替存放,以及先把所有的u存放之后,再继续存放y数据。
  其中Y数据的数据量为:图片Width * height;u和v的数据量都为图片图片Width * height / 4;
  所以在解析和转换yuv数据的时候,根据格式分离出YUV数据,在根据公式进入后续转换。

代码实现

具体代码

 

 
  1. #include <stdio.h>

  2. #include <opencv2/core/core.hpp>

  3. #include <opencv/cv.h>

  4. #include <opencv/highgui.h>

  5. #include <string>

  6. #include <vector>

  7. #include <opencv2/features2d/features2d.hpp>

  8. #include <opencv2/highgui/highgui.hpp>

  9. #include <opencv2/imgproc/imgproc.hpp>

  10. #include <opencv2/calib3d/calib3d.hpp>

  11. #include <opencv2/imgproc/imgproc_c.h>

  12. #include <stdio.h>

  13.  
  14. using namespace cv;

  15. using namespace std;

  16.  
  17. #define nWidth 1280

  18. #define nHeight 960

  19. #define FrameSize nWidth*nHeight*3/2

  20. #define YSize nWidth*nHeight

  21. #define USize nWidth*nHeight / 4

  22. #define VSize nWidth*nHeight / 4

  23.  
  24. int main(int argc, char* argv[]){

  25. int i, k;

  26. Mat tmp, tmpY;

  27.  
  28. FILE *f ;

  29. if(!(f = fopen("1.yuv","rb"))){

  30. printf("file open error!");

  31. }

  32.  
  33. Mat yMat = Mat(cvSize(nWidth, nHeight), CV_8UC1, cv::Scalar(0, 0, 0));

  34. Mat uMat = Mat(cvSize(nWidth/2, nHeight/2), CV_8UC1, cv::Scalar(0, 0, 0));

  35. Mat vMat = Mat(cvSize(nWidth/2, nHeight/2), CV_8UC1, cv::Scalar(0, 0, 0));

  36. Mat rgbMat = Mat(cvSize(nWidth/2, nHeight/2), CV_8UC3, cv::Scalar(0, 0, 0));

  37.  
  38. uchar* yPtr = yMat.ptr(0);

  39. uchar* uPtr = uMat.ptr(0);

  40. uchar* vPtr = vMat.ptr(0);

  41. uchar* rgbPtr = rgbMat.ptr(0);

  42.  
  43. unsigned char *pBuf = new unsigned char[FrameSize];

  44. fread(pBuf, 1, FrameSize, f);

  45.  
  46. for(i=0; i<YSize; i++){

  47. yPtr[i] = pBuf[i];

  48. }

  49.  
  50. k=0;

  51. for(i=YSize; i< FrameSize;){

  52. uPtr[k] = pBuf[i];

  53. vPtr[k] = pBuf[i+1];

  54. i += 2;

  55. k += 1;

  56. }

  57. resize(yMat, tmpY, cvSize(nWidth/2, nHeight/2));

  58. yPtr = tmpY.ptr(0);

  59.  
  60. for(i=0; i<YSize / 4; i++){

  61. rgbPtr[i*3 + 2] = yPtr[i] + 1.772 * (uPtr[i] - 128); //R

  62. rgbPtr[i*3 + 1] = yPtr[i] - 0.34413 * (uPtr[i] - 128) - 0.71414 * (vPtr[i] - 128); //G

  63. rgbPtr[i*3 + 0] = yPtr[i] + 1.402 * (vPtr[i] - 128); //G

  64. }

  65. imshow("new.jpg", rgbMat);

  66. imshow("U", uMat);

  67. imshow("v", vMat);

  68. imshow("Y", tmpY);

  69. cvWaitKey( 0 );

  70. return 0;

  71. }

代码讲解

  1、首先是打开对应图片,然后进行一些对应初始化设置
 
 
  1. FILE *f ;

  2. if(!(f = fopen("1.yuv","rb"))){

  3. printf("file open error!");

  4. }

  5.  
  6. Mat yMat = Mat(cvSize(nWidth, nHeight), CV_8UC1, cv::Scalar(0, 0, 0));

  7. Mat uMat = Mat(cvSize(nWidth/2, nHeight/2), CV_8UC1, cv::Scalar(0, 0, 0));

  8. Mat vMat = Mat(cvSize(nWidth/2, nHeight/2), CV_8UC1, cv::Scalar(0, 0, 0));

  9. Mat rgbMat = Mat(cvSize(nWidth/2, nHeight/2), CV_8UC3, cv::Scalar(0, 0, 0));

  10.  
  11. uchar* yPtr = yMat.ptr(0);

  12. uchar* uPtr = uMat.ptr(0);

  13. uchar* vPtr = vMat.ptr(0);

  14. uchar* rgbPtr = rgbMat.ptr(0);

    打开图片到f中,接着创建了yMat,uMat, vMat, rgbMat,分别用来保存y,u,v分量数据,以及最后合成出来的rgb图像数据。
  2、取出Y的数据到yMat。
 
 
  1. #define FrameSize nWidth*nHeight*3/2

  2. #define YSize nWidth*nHeight

  3.  
  4. unsigned char *pBuf = new unsigned char[FrameSize];

  5. fread(pBuf, 1, FrameSize, f);

  6.  
  7. for(i=0; i<YSize; i++){

  8. yPtr[i] = pBuf[i];

  9. }

    前面讲解时候已经说过,图像的所有数据大小为Width*Height*3/2, Y的数据大小为:Width*Height。这里首先将大小的图像文件f中,所有的数据读取到pBuf中,接着从pBuf中,取出最开始的Width*Height数据,也就对应为Y的数据。
  3、取出U、V的数据
 
 
  1. k=0;

  2. for(i=YSize; i< FrameSize;){

  3. uPtr[k] = pBuf[i];

  4. vPtr[k] = pBuf[i+1];

  5. i += 2;

  6. k += 1;

  7. }

    这里使用的范例图片为u、v交替存储格式,所有是u、v数据交替读取。最后u、v数据保存到uMat和vMat中。
  4、转换为RGB图像
 
 
  1. resize(yMat, tmpY, cvSize(nWidth/2, nHeight/2));

  2. yPtr = tmpY.ptr(0);

  3.  
  4. for(i=0; i<YSize / 4; i++){

  5. rgbPtr[i*3 + 2] = yPtr[i] + 1.772 * (uPtr[i] - 128);

  6. rgbPtr[i*3 + 1] = yPtr[i] - 0.34413 * (uPtr[i] - 128) - 0.71414 * (vPtr[i] - 128);

  7. rgbPtr[i*3 + 0] = yPtr[i] + 1.402 * (vPtr[i] - 128);

  8. }

    也就是根据yuv到RGB的转换公式,来讲图像数据转换为RGB,分别存放到rgbMat图像对应的三个通道中。需要注意下:之前已经有提到yuv420的像素比例为4:1:1,所有,在合成到RGB图像之前需要首先将u、v的数据分别扩展放大4倍,让YUV之前比值为4:4:4。                            我这里为了方便测试显示图像,所以是将Y的数据,缩放为原来图像的1/4,这会导致,最后出来的图像width和height都只有原图像1/4。
  常用的yuv转换为RGB的公式有:
          R= Y +  +1.402(V-128)
          G= Y - 0.34413(U-128) - 0.71414(V-128)
          B= Y + 1.772(U-128)+0 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值