做视频采集与处理,自然少不了要学会分析YUV数据。因为从采集的角度来说,一般的视频采集芯片输出的码流一般都是YUV数据流的形式,而从视频处 理(例如H.264、MPEG视频编解码)的角度来说,也是在原始YUV码流进行编码和解析,所以,了解如何分析YUV数据流对于做视频领域的人而言,至 关重要。本文就是根据我的学习和了解,简单地介绍如何分析YUV数据流。
YUV,分为三个分量,“Y”表示明亮度(Luminance或Luma),也就是灰度值;而“U”和“V” 表示的则是色度(Chrominance或Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。
与我们熟知的RGB类似,YUV也是一种颜色编码方法,主要用于电视系统以及模拟视频领域,它将亮度信息(Y)与色彩信息(UV)分离,没有UV信息一样 可以显示完整的图像,只不过是黑白的,这样的设计很好地解决了彩色电视机与黑白电视的兼容问题。并且,YUV不像RGB那样要求三个独立的视频信号同时传 输,所以用YUV方式传送占用极少的频宽。
好了,言归正传,谈谈如何分析YUV码流吧。YUV码流有多种不同的格式,要分析YUV码流,就必须搞清楚你面对的到底是哪一种格式,并且必须搞清楚这种格式的YUV采样和分布情况。下面我将介绍几种常用的YUV码流格式,供大家参考。
1. 采样方式
YUV码流的存储格式其实与其采样的方式密切相关,主流的采样方式有三种,YUV4:4:4,YUV4:2:2,YUV4:2:0,关于其详细原理,可以 通过网上其它文章了解,这里我想强调的是如何根据其采样格式来从码流中还原每个像素点的YUV值,因为只有正确地还原了每个像素点的YUV值,才能通过 YUV与RGB的转换公式提取出每个像素点的RGB值,然后显示出来。
用三个图来直观地表示采集的方式吧,以黑点表示采样该像素点的Y分量,以空心圆圈表示采用该像素点的UV分量。
先记住下面这段话,以后提取每个像素的YUV分量会用到。
- YUV 4:4:4采样,每一个Y对应一组UV分量。
- YUV 4:2:2采样,每两个Y共用一组UV分量。
- YUV 4:2:0采样,每四个Y共用一组UV分量。
2. 存储方式
下面我用图的形式给出常见的YUV码流的存储方式,并在存储方式后面附有取样每个像素点的YUV数据的方法,其中,Cb、Cr的含义等同于U、V。
(1) YUVY 格式 (属于YUV422)
YUYV为YUV422采样的存储格式中的一种,相邻的两个Y共用其相邻的两个Cb、Cr,分析,对于像素点Y'00、Y'01 而言,其Cb、Cr的值均为 Cb00、Cr00,其他的像素点的YUV取值依次类推。
(2) UYVY 格式 (属于YUV422)
UYVY格式也是YUV422采样的存储格式中的一种,只不过与YUYV不同的是UV的排列顺序不一样而已,还原其每个像素点的YUV值的方法与上面一样。
(3) YUV422P(属于YUV422)
YUV422P也属于YUV422的一种,它是一种Plane模式,即打包模式,并不是将YUV数据交错存储,而是先存放所有的Y分量,然后存储所有的 U(Cb)分量,最后存储所有的V(Cr)分量,如上图所示。其每一个像素点的YUV值提取方法也是遵循YUV422格式的最基本提取方法,即两个Y共用 一个UV。比如,对于像素点Y'00、Y'01 而言,其Cb、Cr的值均为 Cb00、Cr00。
(4)YV12,YU12格式(属于YUV420)
YU12和YV12属于YUV420格式,也是一种Plane模式,将Y、U、V分量分别打包,依次存储。其每一个像素点的YUV数据提取遵循 YUV420格式的提取方式,即4个Y分量共用一组UV。注意,上图中,Y'00、Y'01、Y'10、Y'11共用Cr00、Cb00,其他依次类推。
(5)NV12、NV21(属于YUV420)
NV12和NV21属于YUV420格式,是一种two-plane模式,即Y和UV分为两个Plane,但是UV(CbCr)为交错存储,而不是分为三个plane。其提取方式与上一种类似,即Y'00、Y'01、Y'10、Y'11共用Cr00、Cb00
3. 总结
几种常见的YUV码流格式就简单地列在上面了,大家在处理YUV码流前,先了解清楚自己的码流到底属于哪一种,然后对应进行处理。
最后,再回答一个疑问,即分析清楚YUV码流格式了,我们可以做什么?最常用的一点就是,提取出所有的Y分量,然后利用vc或者matlab把你采集的图 像的灰度值(Y分量)显示处理,这样你就可以很快地知道你采集的图像是否有问题了。后面我将继续写一些文章讲述如何提取、转换、显示这些YUV原始码流, 有兴趣可以继续关注,欢迎留言讨论。
=====================================================================================================
概述
转载:http://hi.baidu.com/yrworld/blog/item/e6e0a9120a6ca3cbc2fd78e2.html/cmtid/21fa09d8d7a32a3b32fa1cf8
在《入门视频采集与处理(学会分析YUV数据)》这篇文章中,我们已经学会了如何分析采集到的YUV原始码流,那么,下一步就是如何预览显示所采集的码流了,只有经过显示,才能非常直观地看出自己采集的数据是否有问题。
关于如何显示原始的YUV码流,根据我的经验,一般有三种方法:
(1) 利用 matlab ,写相关的程序进行显示。(本文我将提供相关显示程序)
(2) 使用 MFC + Direct Draw 来实现。(稍后整理好了再提供出来)
(3) 使用 第三方工具(YUVviewerPlus.exe),很好的软件。(在附件中提供)
由于使用matlab来进行图像的显示,程序代码简单易懂,直观,维护和修改方便,方便根据不同的图像尺寸和YUV码流存储方式进行相应的修改,故本文重点进行讨论。
对于采集的图像,我们一般需要重点关注Y分量是否正确,直接显示Y分量数据就可以看到灰度图像,根据显示的灰度图像,我们可以很快地判断出图像的轮廓是否正确。大家可以根据上一节的内容提取出码流中某一帧的Y分量内容,利用下面的程序在matlab中进行显示。
测试条件: 【图像的尺寸】:720x576;【文件内容】:只有一帧图像的Y分量;
- function yuvtest(filename)
- % 打开图像文件
- fid = fopen(filename,'r');
- if fid == -1
- error('the file can not open ');
- end
- line = 576; %图像的高
- colom = 720; %图像的宽
- im = zeros(line,colom);
- for i1 = 1:line
- im(i1,:) = fread(fid,colom); %读取数据到矩阵中
- end
- im = im./255; %归一化
- figure,imshow(im); %显示图像
- fclose(fid);
- end
当然,上述程序只能显示出灰度图像,如果希望显示彩色图像,则首先需要把YUV码流转换为RGB数据流,转换公式如下:
- // 转换公式(浮点方式)
- R = Y + 1.4075*(V-128)
- G = Y - 0.3455*(U-128) - 0.7169*(V-128)
- B = Y + 1.779*(U-128)
至于每一个像素点的YUV分量的值该怎么取,请参考本文开头提到的那篇文章,无论是YUV444、YUV422、还是YUV420格式,根据对应的方法提取完YUV并转换为RGB数据后,其文件大小应该是: 图像的高 * 图像的宽 * 3 。由于不同的YUV码流转换为RGB数据的提取方式均不相同,这里我就不提供统一的转换程序了,大家根据公式和提取YUV的方法自己来转换。
在写文件的时候,可以考虑以如下的存储格式:
- R R R R R
- R R R R R
- R R R R R
- G G G G G
- G G G G G
- G G G G G
- B B B B B
- B B B B B
- B B B B B
存好转换后的RGB文件后,可以利用我提供的下面这个matlab程序进行显示,即可看到彩色图像。
测试条件: 【图像的尺寸】:720x576;【文件内容】:由R、G、B三个分量分块组成的图像数据;
ok,到此,怎样使用matlab显示YUV码流的灰度图像和彩色图像就说到这里了,当然,附件中有一个工具“YUVviewerPlus.exe”也特别好用,可以直接选择YUV码流的格式进行显示。不过我依然建议大家自己动手写相关的matlab程序或者MFC程序来分析自己的码流,这样就可以根据实际情况相应修改代码,而不用局限于人家的软件了
- function showrgb(inputFile)
- % 初始化
- width = 720; %图像的宽
- height = 576; %图像的高
- %打开文件
- fid = fopen(inputFile);
- %读取数据
- if fid ~= -1
- img = uint8(zeros(height,width,3));
- img_t = uint8(zeros(height,width));
- for i1=1:3
- for i2=1:height
- img_t(i2,:)=fread(fid,width);
- end
- img(:,:,i1) = img_t;
- end
- figure,imshow(img);
- fclose(fid);
- end