转自:http://www.latelee.org/my-study/yuv-learning-yuv420p-to-rgb24.html

  对于YUV420的格式,网上有一大堆资料,这里就不说了。直奔主题,给出如何转换的函数,一如既往,只用代码说事。
YUV420有打包格式(Packed),一如前文所述。同时还有平面格式(Planar),即Y、U、V是分开存储的,每个分量占一块地方,其中Y为
width*height,而U、V合占Y的一半,该种格式每个像素占12比特。根据U、V的顺序,分出2种格式,U前V后即YUV420P,也叫
I420,V前U后,叫YV12(YV表示Y后面跟着V,12表示12bit)。另外,还有一种半平面格式(Semi-planar),即Y单独占一块地
方,但其后U、V又紧挨着排在一起,根据U、V的顺序,又有2种,U前V后叫NV12,在国内好像很多人叫它为YUV420SP格式;V前U后叫
NV21。这种格式似乎比NV16稍受欢迎。
首先给出转换查询表的生成函数,代码是在网上找来的,如下:

static long int crv_tab[256];
static long int cbu_tab[256];
static long int cgu_tab[256];
static long int cgv_tab[256];
static long int tab_76309[256];
static unsigned char clp[1024]; //for clip in CCIR601 void init_yuv420p_table()
{
long int crv,cbu,cgu,cgv;
int i,ind;
static int init = 0; if (init == 1) return; crv = 104597; cbu = 132201; /* fra matrise i global.h */
cgu = 25675; cgv = 53279; for (i = 0; i < 256; i++)
{
crv_tab[i] = (i-128) * crv;
cbu_tab[i] = (i-128) * cbu;
cgu_tab[i] = (i-128) * cgu;
cgv_tab[i] = (i-128) * cgv;
tab_76309[i] = 76309*(i-16);
} for (i = 0; i < 384; i++)
clp[i] = 0;
ind = 384;
for (i = 0;i < 256; i++)
clp[ind++] = i;
ind = 640;
for (i = 0;i < 384; i++)
clp[ind++] = 255; init = 1;
}

下面给出YUV420平面格式的转换函数,如下:

/**
内存分布
w
+--------------------+
|Y0Y1Y2Y3... |
|... | h
|... |
| |
+--------------------+
|U0U1 |
|... | h/2
|... |
| |
+----------+
|V0V1 |
|... | h/2
|... |
| |
+----------+
w/2
*/
void yuv420p_to_rgb24(YUV_TYPE type, unsigned char* yuvbuffer,unsigned char* rgbbuffer, int width,int height)
{
int y1, y2, u, v;
unsigned char *py1, *py2;
int i, j, c1, c2, c3, c4;
unsigned char *d1, *d2;
unsigned char *src_u, *src_v;
static int init_yuv420p = 0; src_u = yuvbuffer + width * height; // u
src_v = src_u + width * height / 4; // v if (type == FMT_YV12)
{
src_v = yuvbuffer + width * height; // v
src_u = src_u + width * height / 4; // u
}
py1 = yuvbuffer; // y
py2 = py1 + width;
d1 = rgbbuffer;
d2 = d1 + 3 * width; init_yuv420p_table(); for (j = 0; j < height; j += 2)
{
for (i = 0; i < width; i += 2)
{
u = *src_u++;
v = *src_v++; c1 = crv_tab[v];
c2 = cgu_tab[u];
c3 = cgv_tab[v];
c4 = cbu_tab[u]; //up-left
y1 = tab_76309[*py1++];
*d1++ = clp[384+((y1 + c1)>>16)];
*d1++ = clp[384+((y1 - c2 - c3)>>16)];
*d1++ = clp[384+((y1 + c4)>>16)]; //down-left
y2 = tab_76309[*py2++];
*d2++ = clp[384+((y2 + c1)>>16)];
*d2++ = clp[384+((y2 - c2 - c3)>>16)];
*d2++ = clp[384+((y2 + c4)>>16)]; //up-right
y1 = tab_76309[*py1++];
*d1++ = clp[384+((y1 + c1)>>16)];
*d1++ = clp[384+((y1 - c2 - c3)>>16)];
*d1++ = clp[384+((y1 + c4)>>16)]; //down-right
y2 = tab_76309[*py2++];
*d2++ = clp[384+((y2 + c1)>>16)];
*d2++ = clp[384+((y2 - c2 - c3)>>16)];
*d2++ = clp[384+((y2 + c4)>>16)];
}
d1 += 3*width;
d2 += 3*width;
py1 += width;
py2 += width;
}
}

再给出YUV420SP转换的函数,如下:

void yuv420sp_to_rgb24(YUV_TYPE type, unsigned char* yuvbuffer,unsigned char* rgbbuffer, int width,int height)
{
int y1, y2, u, v;
unsigned char *py1, *py2;
int i, j, c1, c2, c3, c4;
unsigned char *d1, *d2;
unsigned char *src_u;
static int init_yuv420p = 0; src_u = yuvbuffer + width * height; // u py1 = yuvbuffer; // y
py2 = py1 + width;
d1 = rgbbuffer;
d2 = d1 + 3 * width; init_yuv420p_table(); for (j = 0; j < height; j += 2)
{
for (i = 0; i < width; i += 2)
{
if (type == FMT_NV12)
{
u = *src_u++;
v = *src_u++; // v紧跟u,在u的下一个位置
}
if (type == FMT_NV21)
{
v = *src_u++;
u = *src_u++; // u紧跟v,在v的下一个位置
} c1 = crv_tab[v];
c2 = cgu_tab[u];
c3 = cgv_tab[v];
c4 = cbu_tab[u]; //up-left
y1 = tab_76309[*py1++];
*d1++ = clp[384+((y1 + c1)>>16)];
*d1++ = clp[384+((y1 - c2 - c3)>>16)];
*d1++ = clp[384+((y1 + c4)>>16)]; //down-left
y2 = tab_76309[*py2++];
*d2++ = clp[384+((y2 + c1)>>16)];
*d2++ = clp[384+((y2 - c2 - c3)>>16)];
*d2++ = clp[384+((y2 + c4)>>16)]; //up-right
y1 = tab_76309[*py1++];
*d1++ = clp[384+((y1 + c1)>>16)];
*d1++ = clp[384+((y1 - c2 - c3)>>16)];
*d1++ = clp[384+((y1 + c4)>>16)]; //down-right
y2 = tab_76309[*py2++];
*d2++ = clp[384+((y2 + c1)>>16)];
*d2++ = clp[384+((y2 - c2 - c3)>>16)];
*d2++ = clp[384+((y2 + c4)>>16)];
}
d1 += 3*width;
d2 += 3*width;
py1 += width;
py2 += width;
}
}

参考资料:
http://www.fourcc.org/yuv.php
https://wiki.videolan.org/YUV/

李迟 2015.8.5 晚上

本文固定链接: http://www.latelee.org/my-study/yuv-learning-yuv420p-to-rgb24.html

YUV格式学习:YUV420P、YV12、NV12、NV21格式转换成RGB24(转载)的更多相关文章

  1. 音视频编解码: YUV存储格式中的YUV420P,YUV420SP,NV12, NV21理解(转)

    概述  之前介绍了YUV码流的采样格式,下面分析下YUV码流的存储格式,YUV码流的存储格式与采样格式息息相关.总的来讲,YUV存储格式主要分为两种: planar 平面格式 指先连续存储所有像素点的 ...

  2. YUV图解 (YUV444, YUV422, YUV420, YV12, NV12, NV21)

    背景: 最近在研究音视频,了解YUV这样的格式对于音视频开发比较重要. 虽然这篇文章大部分是转载别人的,但是经过了校对以后,重新排版并补充了一部分内容   概览: 之所以提出yuv格式的原因,是为了解 ...

  3. 小白学习Python之路---py文件转换成exe可执行文件

    一.背景 今天闲着无事,写了一个小小的Python脚本程序,然后给同学炫耀的时候,发现每次都得拉着其他人过来看着自己的电脑屏幕,感觉不是很爽,然后我想着网上肯定有关于Python脚本转换成可执行文件的 ...

  4. xml转换为json格式时,如何将指定节点转换成数组 Json.NET

    使用Json.NET转换xml成json时,如果xml只有单个节点,但json要求是数组形式[], JsonConvert.SerializeXmlNode 并不能自动识别 示例如下: RecordA ...

  5. json.net xml转换为json格式时,如何将指定节点转换成数组

    using System.Xml.Linq; using Newtonsoft.Json; Response.ContentType = "application/json"; X ...

  6. OpenCV 学习笔记(13)图像转换成视频

    关键 1参数里的分辨率是图像本身的分辨率,而不是指定生成的视频分辨率.如果要修改分辨率,要么后期软件处理,要么读图的时候resize 2要正常退出,不要强制退出. 3生成的只能是avi格式. #inc ...

  7. Maven 学习笔记——将普通的Java项目转换成Maven项目(3)

    将一个普通的java项目转换成Maven项目并不是一个很大的任务,仅仅只需要下面的几步就能将转换成功.下面我是用一个简单的Selenium测试小demon作为例子来说的. 移调项目中所有关联的Libr ...

  8. OpenCV 学习笔记(9)RGB转换成灰度图像的一个常用公式Gray = R*0.299 + G*0.587 + B*0.114

    https://blog.csdn.net/fly_wt/article/details/86432886 RGB转换成灰度图像的一个常用公式是:Gray = R*0.299 + G*0.587 + ...

  9. OpenGL实现相机视频NV21格式转RGB格式

    笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D实战核心技术详解 ...

随机推荐

  1. Learning English From Android Source Code:2 Ampersand

    这一次想把标点符号的英语表达总结一下,这些单词非常重要但easy被我们忽视.以我的经验,还是多认识几个.以备不时之需. 以下从"标点符号"開始: punctuation [英][ˌ ...

  2. caffe搭建--WINDOWS+VS2013下生成caffe并进行cifar10分类测试

    http://blog.csdn.net/naaaa/article/details/52118437 标签: windowsvs2013caffecifar10 2016-08-04 15:33 1 ...

  3. Effective Java - [2. 创建与销毁对象]

    让对象的创建与销毁在掌控中. Item 1: 使用静态工厂方法而非使用构造函数 public static Boolean valueOf(boolean b) { return b ? Boolea ...

  4. Dash 使用

    花了 160 买了这个软件,至少看一遍它的 user guide,钱不能白花. https://kapeli.com/guide/guide.html 设置全局快捷键 Preference -> ...

  5. 看不懂JDK8的流操作?5分钟带你入门(转)

    在JDK1.8里有两个非常高级的新操作,它们分别是:Lambda 表达式和 Stream 流. Lambda表达式 让我们先说说 Lambda 表达式吧,这个表达式最大的作用就是简化语法,让代码更加易 ...

  6. Sqoop hive导出到mysql[转]

    通过Sqoop将Hive表数据导入到MySQL通常有两种情况. 第一种是将hive上某张表的全部数据导入到mysql对应的表中. 第二种是将hive上某张表中的部分数据导入到mysql对应的表中. 两 ...

  7. Javascript高级程序设计笔记(很重要尤其是对象的设计模式与继承)

    var obj = {'a':'a'}; var fun = function (){} console.log(typeof obj);//object console.log(typeof fun ...

  8. 收集Oracle数据库中的SQL基线信息(一)基础信息收集

    Oracle数据库中的SQL基线信息,当数据库出现性能问题时,在业务无法提供相应业务信息时,通过对比SQL基线信息来查找SQL的变化. 查找数据库一天内运行次数大于5000次的sqlid select ...

  9. 在ubuntu 64位的机器上执行arm-linux-gcc提示 no such file or directory【转】

    本文转载自:http://blog.csdn.net/sno_guo/article/details/17059381 解压好了arm-linuxg-gcc 放到了$PATH路径下, 无论怎么执行都提 ...

  10. 棋盘覆盖问题 (粉书 P230 【递归】** )

    转载自:http://blog.csdn.net/akof1314/article/details/5423608  (赞) 在一个 2^k * 2^k 个方格组成的棋盘中,若恰有一个方格与其它方格不 ...