图像基础
一、基础概念
像素:pixel,这个单词是picture和element两个单词字母组成,像素是图像显示的基本单位。通常说一张图片的分辨率大小是1920*1080,意思是长度是1920个像素点,宽度是1080个像素点,乘积是1920*1080=2073600,也就是说这个图片是200万像素。
PPI:Pixels Per Inch,即每英寸像素。也就是手机屏幕上每英寸面积到底能存放多少个像素点。理论上屏幕PPI越高,屏幕越精细,画质相对更出色。
分辨率:图像的尺寸,长和宽。
1080P:一种视频显示格式,P是逐行扫描,数字1080表示垂直方向有1080条水平扫描线。通常1080p的画面分辨率为1920*1080。
帧率:每秒显示的图片数。
码流(码率):是指视频文件在单位时间内使用的数据流量。同样分辨率下,视频文件的码流越大,压缩比就越小,画面质量就越好。
RGB:从颜色发光的原理推演出来的,自然界的任何颜色都可用红黄绿三色混合而成,亮度等于三色亮度之总和,越混合亮度越高,即加法混合。
YUV:优化彩色视频信号的传输,使其向后兼容老式黑白电视。Y表示明亮度(Luminance或Luma),也就是灰阶值;而UV则表示色度(Chrominance或Chroma),描述影像色彩及饱和度,用于指定像素颜色。YUV是大多数视频编码所需要的的数据格式。
YCbCr:YUV经过缩放和偏移的翻版。Y就是luminance,明亮,灰阶值。C就是Chrominance,色度。Cb就是blue和green的色度。Cr就是red和green的色度。
PTS:Presentation Time Stamp,显示时间戳,用来告诉播放器什么时候显示这一帧数据。是一个持续增长的数字,可以通过一个时间基数除以“帧率(fps)”来获得。
比如 fps=60/1 , timebase=1/60000,timescale=60000, 每一个 PTS 的增长 timescale / fps = 1000,因此每一帧 PTS 的时间如下(假设开始为0):
frame=0, PTS = 0, PTS_TIME = 0
frame=1, PTS = 1000, PTS_TIME = PTS * timebase = 0.016
frame=2, PTS = 2000, PTS_TIME = PTS * timebase = 0.033
Ffmpeg中timebase是有理数AVRational,{1,timescale}
DTS:Decoding Time Stamp,解码时间戳,告诉播放器什么时候解码这一帧数据。
GOP:Group of Pictures,图像组,画面组,一个GOP就是一组连续的画面,用来辅助随机存取。GOP的第一个图像必须是I帧,这样能保证GOP不需要参考其他图像,可以独立解码。GOP的长度是一个I帧到下一个I帧的间隔,即多少帧里出现一次I帧,长度可变。长GOP可以提供高的压缩比,但会造成随机存取的延迟(必须等到下一个I帧)和误差的积累(P帧的误差传播)。
二、图片
2.1 JPEG
JPEG(英文全称:Joint Photographic Experts Group)压缩技术可以说是所有图像压缩技术的基础。它适合静态图像的压缩,直接处理整个画面,压缩倍数为20-80倍,分辨率没有选择的余地。所以要等到整个压缩档案传输完成才开始进行解压缩成影像画面,而这样的方式造成传输一个高解析画面时须耗时数十秒甚至数分钟。它用有损压缩的方式去除图像的冗余数据,但存在着一定的失真。由于其高效的压缩效率和标准化要求,目前已广泛用于彩色传真、静止图像、电话会议、印刷及新闻图片的传送。由于各种浏览器都支持JPEG这种图像格式,因此它也被广泛用于图像预览和制作HTM网页。
Jpeg官网https://jpeg.org/。
Jpeg library由Independent JPEG Group负责,官网http://www.ijg.org/。Libjpeg使用可参考:JPEG图像压缩和解压缩操作,图像解码之一——使用libjpeg解码jpeg图片
Turbojpeg针对ARM和X86优化,宣称速度是libjpeg的2-6倍,https://github.com/libjpeg-turbo/libjpeg-turbo.git。
2.2 BMP
BMP图像内部实际上存储的就是RGB数据,在RGB数据前加上bmp头。转换程序参考:视音频数据处理入门:RGB、YUV像素数据处理 leixiaohua
BMP文件的数据按照从文件头开始的先后顺序分为四个部分:
◆ 位图文件头(bmp file header): 提供文件的格式、大小等信息。
◆ 位图信息头(bitmap information header):提供图像数据的尺寸、位平面数、压缩方式、颜色索引等信息。
◆ 调色板(color palette):可选,如使用索引来表示图像,调色板就是索引与其对应的颜色的映射表。
◆ 位图数据(bitmap data):图像数据区。
如果位图是16位、24位和32位色,则图像文件中不保留调色板,即不存在调色板,图像的颜色直接在位图数据中给出;在windows下,DWORD是内置类型int,WORD是内置类型short,BYTE是内置类型char,LONG是内置类型long。
BMP图片文件数据表如下:
数据段名称 |
大小(byte) |
开始地址 |
结束地址 |
位图文件头(bitmap-file header) |
14 |
0000h |
000Dh |
位图信息头(bitmap-information header) |
40 |
000Eh |
0035h |
调色板(color table) |
由biBitCount决定 |
0036h |
未知 |
图片点阵数据(bitmap data) |
由图片大小和颜色定 |
未知 |
未知 |
BMP文件头结构体定义如下:
#pragma pack(2) // 对结构体进行2字节对齐,整个结构体占用14字节 typedef struct tagBITMAPFILEHEADER { UINT16 bfType; //2Bytes,必须为"BM",即0x424D 才是Windows位图文件 DWORD bfSize; //4Bytes,整个BMP文件的大小 UINT16 bfReserved1; //2Bytes,保留,为0 UINT16 bfReserved2; //2Bytes,保留,为0 DWORD bfOffBits; //4Bytes,文件起始位置到图像像素数据的字节偏移量 } BITMAPFILEHEADER;
BMP信息头结构体定义如下:
// 位图信息头占用40个字节,位图文件头和信息头一共占用54个字节。 typedef struct _tagBMP_INFOHEADER { DWORD biSize; //4Bytes,INFOHEADER结构体大小,存在其他版本INFOHEADER,用作区分 LONG biWidth; //4Bytes,图像宽度(以像素为单位) LONG biHeight; //4Bytes,图像高度,+:图像存储顺序为Bottom2Top,-:Top2Bottom WORD biPlanes; //2Bytes,图像数据平面,BMP存储RGB数据,因此总为1 WORD biBitCount; //2Bytes,图像像素位数 DWORD biCompression; //4Bytes,0:不压缩,1:RLE8,2:RLE4 DWORD biSizeImage; //4Bytes,4字节对齐的图像数据大小 LONG biXPelsPerMeter; //4 Bytes,用象素/米表示的水平分辨率 LONG biYPelsPerMeter; //4 Bytes,用象素/米表示的垂直分辨率 DWORD biClrUsed; //4 Bytes,实际使用的调色板索引数,0:使用所有的调色板索引 DWORD biClrImportant; //4 Bytes,重要的调色板索引数,0:所有的调色板索引都重要 } BMP_INFOHEADER;
BMP调色板结构体定义如下:
typedef struct _tagRGBQUAD { BYTE rgbBlue; //指定蓝色强度 BYTE rgbGreen; //指定绿色强度 BYTE rgbRed; //指定红色强度 BYTE rgbReserved; //保留,设置为0 } RGBQUAD;
BMP图像数据区
位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数:
当biBitCount=1时,8个像素占1个字节;
当biBitCount=4时,2个像素占1个字节;
当biBitCount=8时,1个像素占1个字节;
当biBitCount=24时,1个像素占3个字节;
Windows规定一个扫描行所占的字节数必须是4的倍数(即以long为单位),不足的以0填充,
一个扫描行所占的字节数计算方法:
DataSizePerLine= (biWidth* biBitCount+31)/8;
// 一个扫描行所占的字节数
DataSizePerLine= DataSizePerLine/4*4; // 字节数必须是4的倍数
位图数据的大小(不压缩情况下):
DataSize= DataSizePerLine* biHeight;
三、视频
视频码流分析工具:Elecard:http://www.elecard.com/en/index.html
一般的摄像头有三种数据输出模式:YUYV、MJPEG和H.264。普通摄像头一般只有两种图像数据输出:YUV(原始数据)、MJPEG两种格式。
3.1 YUV(YCbCr)
RGB是一种颜色的表示法,计算机中一般采用24位来存储,每个颜色占8位。YUV也是一种颜色空间,为什么要出现YUV,主要有两个原因,一个是为了让彩色信号兼容黑白电视机,另外一个原因是为了减少传输的带宽。YUV中,Y表示亮度,U(Cb)和V(Cr)表示色度,总之它是将RGB信号进行了一种处理,根据人对亮度更敏感些,增加亮度的信号,减少颜色的信号,以这样“欺骗”人的眼睛的手段来节省空间。YUV到RGB颜色空间转换关系是:
R = Y + 1.042*(V-128);
G = Y - 0.34414*(U-128) - 0.71414*(V-128);
B = Y + 1.772*(U-128);
YUV的格式也很多,不过常见的就是422、420等。YUYV就是422形式,简单来说就是,两个像素点P1、P2本应该有Y1、U1、V1和Y2、U2、V2这六个分量,但是实际只保留Y1、U1、Y2、V2。即YUYV用两个像素的存储空间存储了三个全像素(或RGB)数据,节省了1/3存储或传输空间。
每种YUV格式都有一个分配的FOURCC代码。FOURCC代码是32位无符号整数,是通过串联四个ASCII字符创建的。
YUV有packed format(压缩格式、打包格式)、planar format(平面格式)和semi-planar(半平面)三种。packed format中YUV是混合在一起的,YUV采样量被打包在一起为一个数组。Planar format中每一个Y分量、U分量和V分量都是以独立的平面组织的,也就是说所有的U分量都在Y分量之后出现,而V分量在所有的U分量之后。semi-planar是两个平面,正常的planar是三个平面,即Y平面、U平面和V平面,而semi-planar是两个平面,uv为同一个平面,即一个Y平面,一个UV平面。
一般的录像程序是先从摄像头得到yuv420 planar的数据,然后编码成h264格式帧,最后存储成3gp/mpeg等格式的视频文件。
参考:简书 YUV格式小结
3.2 MJPEG
MJPEG(Motion JPEG)是在JPEG基础发展起来的动态图像压缩技术,它只单独的对某一帧进行压缩,而基本不考虑视频流中不同帧之间的变化。使得可获取清晰度很高的视频图像,而且可灵活设置每路的视频清晰度和压缩帧数。其压缩后的画面还可任意剪接。但它的缺陷也非常明显,其一:丢帧现象严重、实时性差,在保证每路都必须是高清晰的前提下,很难完成实时压缩。其二:压缩效率低,存储占用空间较大。
后来又出现了多层式JPEG(ML-JPEG)压缩技术,它采取渐层式技术,先传输低解析的图档,然后再补送更细节的压缩资料,使画面品质改善。这种方式所需的时间虽然与原先的方式一样。但由于可以先看到画面,所以使用者会觉得这种方式较好。
mipeg格式的图像其像素格式是Ycrcb(其是YUV的一种延伸叫法,其是两者都是相等的),mipeg的图像是在转换为Ycrcb后进行了一些量化、编码的操作,最后生成了一个码表,供解码时使用(其实跟bmp格式的图像存储差不多),其中的量化操作是导致图像质量下降的原因,这也是为什么mipeg格式的图像比YUV图像的质量差的原因。
MJPEG格式输出帧率在1080P时可达30fps,而YUV格式只有4、5fps,这是因为YUYV的数据量较大,影响了摄像头的读取,占用的带宽很大。MJPEG压缩比一般20:1。
相比H.264,MJPEG优点是图像清晰度好,支持第三方工具打开,对图像计算处理要求低,缺点是数据量大,存储空间、网络带宽要求高。
3.3 H.264
H.264高清算法压缩格式,码流更小,便于网络传送以及节省存储空间;相较MJPEG算法,可节省2/3带宽,更利于高清视频传输。H.264压缩比是200:1。
H.264 的功能分为两层:视频编码层(VCL, Video Coding Layer)和网络提取层(NAL,Network Abstraction Layer)。VCL 数据即编码处理的输出,它表示被压缩编码后的视频数据序列。在VCL 数据传输或存储之前,这些编码的VCL 数据,先被映射或封装进NAL 单元中。每个NAL 单元包括一个原始字节序列负荷(RBSP, Raw Byte Sequence Payload)、一组对应于视频编码的NAL 头信息。RBSP 的基本结构是:在原始编码数据的后面填加了结尾比特。一个bit“1”若干比特“0”,以便字节对齐。
H.264标准没有规定编码器的实现或流程,常用的为VideoLan的x264和思科的openh264。
注:H264编码的输入数据需要是YUV420P。
x264_param_default():设置参数集结构体x264_param_t的缺省值。
x264_picture_alloc():为图像结构体x264_picture_t分配内存。
x264_encoder_open():打开编码器。
x264_encoder_encode():编码一帧图像。
x264_encoder_close():关闭编码器。
x264_picture_clean():释放x264_picture_alloc()申请的资源。
存储数据的结构体如下所示。
x264_picture_t:存储压缩编码前的像素数据。
x264_nal_t:存储压缩编码后的码流数据。
参考:V4L2视频采集与H264编码3—X264移植 V4L2视频采集及H264实时压缩
H.264解析
参考:视音频数据处理入门:H.264视频码流解析 leixiaohua
H.264原始码流(又称为“裸流”)是由一个一个的NALU组成的,结构如下图所示。
其中每个NALU之间通过startcode(起始码)进行分隔,起始码分成两种:0x000001(3Byte)或者0x00000001(4Byte)。如果NALU对应的Slice为一帧的开始就用0x00000001,否则就用0x000001。
H.264码流解析的步骤就是首先从码流中搜索0x000001和0x00000001,分离出NALU;然后再分析NALU的各个字段。
4、视频编解码
视频的播放过程可以简单理解为一帧一帧的画面按照时间顺序呈现出来的过程,就像在一个本子的每一页画上画,然后快速翻动的感觉。但是在实际应用中,并不是每一帧都是完整的画面,因为如果每一帧画面都是完整的图片,那么一个视频的体积就会很大,这样对于网络传输或者视频数据存储来说成本太高,所以通常会对视频流中的一部分画面进行压缩(编码)处理。由于压缩处理的方式不同,视频中的画面帧就分为了不同的类别,其中包括:I 帧、P 帧、B 帧。
I 帧(Intra coded frames):I 帧图像采用帧内编码方式,即只利用了单帧图像内的空间相关性,而没有利用时间相关性。I 帧使用帧内压缩,不使用运动补偿,由于 I 帧不依赖其它帧,所以是随机存取的入点,同时是解码的基准帧。I 帧主要用于接收机的初始化和信道的获取,以及节目的切换和插入,I 帧图像的压缩倍数相对较低。I 帧图像是周期性出现在图像序列中的,出现频率可由编码器选择。
P 帧(Predicted frames):P 帧和 B 帧图像采用帧间编码方式,即同时利用了空间和时间上的相关性。P 帧图像只采用前向时间预测,可以提高压缩效率和图像质量。P 帧图像中可以包含帧内编码的部分,即 P 帧中的每一个宏块可以是前向预测,也可以是帧内编码。
B 帧(Bi-directional predicted frames):B 帧图像采用双向时间预测,可以大大提高压缩倍数。值得注意的是,由于 B 帧图像采用了未来帧作为参考,因此 MPEG-2 编码码流中图像帧的传输顺序和显示顺序是不同的。
也就是说,一个 I 帧可以不依赖其他帧就解码出一幅完整的图像,而 P 帧、B 帧不行。P 帧需要依赖视频流中排在它前面的帧才能解码出图像。B 帧则需要依赖视频流中排在它前面或后面的帧才能解码出图像。
一般平均来说,I的压缩率是7(跟JPG差不多),P是20,B可以达到50,可见使用B帧能节省大量空间,节省出来的空间可以用来保存多一些I帧,这样在相同码率下,可以提供更好的画质。
4.1 标准
视频中存在很多冗余信息,比如图像相邻像素之间有较强的相关性,视频序列的相邻图像之间内容相似,人的视觉系统对某些细节不敏感等,对这部分冗余信息进行处理的过程就是视频编码。
H.26x系列由ITU(国际电传视讯联盟主导):
H.261:主要在老的视频会议和视频电话产品中使用。
H.263:主要用在视频会议、视频电话和网络视频上。
H.264:H.264/MPEG-4第十部分,或称AVC(Advanced Video Coding,高级视频编码),是一种视频压缩标准,一种被广泛使用的高精度视频的录制、压缩和发布格式。
H.265:高效率视频编码(High Efficiency Video Coding,简称HEVC)是一种视频压缩标准,H.264/MPEG-4 AVC的继任者。可支持4K分辨率甚至到超高画质电视,最高分辨率可达到8192×4320(8K分辨率),这是目前发展的趋势,尚未有大众化编码软件出现。
MPEG系列(由ISO[国际标准组织机构]下属的MPEG[运动图象专家组]开发):
MPEG-1第二部分:MPEG-1第二部分主要使用在VCD上,有些在线视频也使用这种格式。
MPEG-2第二部分(MPEG-2第二部分等同于H.262,使用在DVD、SVCD和大多数数字视频广播系统中。
MPEG-4第二部分(MPEG-4第二部分标准可以使用在网络传输、广播和媒体存储上。
五、视频封装格式
封装器要匹配编码器,常见封装器与编译器对应关系。
名称 |
推出机构 |
流媒体 |
支持的视频编码 |
支持的音频编码 |
目前使用领域 |
AVI |
Microsoft Inc. |
不支持 |
几乎所有格式 |
几乎所有格式 |
BT下载影视 |
MP4 |
MPEG |
支持 |
MPEG-2, MPEG-4, H.264, H.263等 |
AAC, MPEG-1 Layers I, II, III, AC-3等 |
互联网视频网站 |
TS |
MPEG |
支持 |
MPEG-1, MPEG-2, MPEG-4, H.264 |
MPEG-1 Layers I, II, III, AAC, |
IPTV,数字电视 |
FLV |
Adobe Inc. |
支持 |
Sorenson, VP6, H.264 |
MP3, ADPCM, Linear PCM, AAC等 |
互联网视频网站 |
MKV |
CoreCodec Inc. |
支持 |
几乎所有格式 |
几乎所有格式 |
互联网视频网站 |
RMVB |
Real Networks Inc. |
支持 |
RealVideo 8, 9, 10 |
AAC, Cook Codec, RealAudio Lossless |
BT下载影视 |
5.1 avi
一般常用avilib.c avilib.h封装AVI音视频,参考:使用avilib封装MJPEG数据应用实例
void AVI_set_video(avi_t *AVI, int width, int height, double fps, char *compressor); void AVI_set_audio(avi_t *AVI, int channels, long rate, int bits, int format, long mp3rate); int AVI_write_frame(avi_t *AVI, char *data, long bytes, int keyframe); int AVI_dup_frame(avi_t *AVI); int AVI_write_audio(avi_t *AVI, char *data, long bytes); int AVI_append_audio(avi_t *AVI, char *data, long bytes); long AVI_bytes_remain(avi_t *AVI); int AVI_close(avi_t *AVI); long AVI_bytes_written(avi_t *AVI); avi_t *AVI_open_input_file(char *filename, int getIndex); avi_t *AVI_open_fd(int fd, int getIndex); int avi_parse_input_file(avi_t *AVI, int getIndex); long AVI_audio_mp3rate(avi_t *AVI); long AVI_video_frames(avi_t *AVI); int AVI_video_width(avi_t *AVI); int AVI_video_height(avi_t *AVI); double AVI_frame_rate(avi_t *AVI); char* AVI_video_compressor(avi_t *AVI); int AVI_audio_channels(avi_t *AVI); int AVI_audio_bits(avi_t *AVI); int AVI_audio_format(avi_t *AVI); long AVI_audio_rate(avi_t *AVI); long AVI_audio_bytes(avi_t *AVI); long AVI_audio_chunks(avi_t *AVI); long AVI_max_video_chunk(avi_t *AVI); long AVI_frame_size(avi_t *AVI, long frame); long AVI_audio_size(avi_t *AVI, long frame); int AVI_seek_start(avi_t *AVI); int AVI_set_video_position(avi_t *AVI, long frame); long AVI_get_video_position(avi_t *AVI, long frame); long AVI_read_frame(avi_t *AVI, char *vidbuf, int *keyframe); int AVI_set_audio_position(avi_t *AVI, long byte); int AVI_set_audio_bitrate(avi_t *AVI, long bitrate); long AVI_read_audio(avi_t *AVI, char *audbuf, long bytes); long AVI_audio_codech_offset(avi_t *AVI); long AVI_audio_codecf_offset(avi_t *AVI); long AVI_video_codech_offset(avi_t *AVI); long AVI_video_codecf_offset(avi_t *AVI); int AVI_read_data(avi_t *AVI, char *vidbuf, long max_vidbuf, char *audbuf, long max_audbuf, long *len); void AVI_print_error(char *str); char *AVI_strerror(); char *AVI_syserror(); int AVI_scan(char *name); int AVI_dump(char *name, int mode); char *AVI_codec2str(short cc); int AVI_file_check(char *import_file); void AVI_info(avi_t *avifile); uint64_t AVI_max_size(); int avi_update_header(avi_t *AVI); int AVI_set_audio_track(avi_t *AVI, int track); int AVI_get_audio_track(avi_t *AVI); int AVI_audio_tracks(avi_t *AVI);
参考:
1. 普通摄像头的数据输出格式YUV与mjpeg之间联系、DCT离散余弦变换去噪跟压缩
2. BMP图像数据格式详解
3. Linux 下V4l2摄像头采集图片,实现yuyv转RGB,RGB转BMP,RGB伸缩,jpeglib 库实现压缩RGB到内存中,JPEG经UDP发送功
4. 8位YUV格式的视频渲染 MSDN
5. YUV格式介绍 YUV2RGB fourcc官网
7. H264系列一 YUV、YCbCr与RGB H264系列
8. 试简介视频编码技术?
9. 最简单的视频编码器:基于libx264(编码YUV为H.264)
10. JPEG文件编/解码详解
11. [总结]FFMPEG视音频编解码零基础学习方法 雷霄骅
12. 视音频数据处理入门:RGB、YUV像素数据处理 leixiaohua
13. 视频编码 v4l2 摄像头 图像处理 li_wen01