【视频/图像数据格式】基本视频/图像数据格式

基本视频/图像数据格式

  • 1.概述
  • 2.视频图像数据格式
    • 2.1 yuv420p
    • 2.2 yuv422p
    • 2.3 yuv444p
    • 2.4 RGB格式
    • 2.5 BMP格式
  • 3.格式转换
    • 3.1 RGB24转换为YUV420P
  • 4.视频图像评价指标
    • 4.1 MSE
    • 4.2 PSNR

参考:

  1. 雷霄骅博士博客:
  • http://t.csdnimg.cn/kl2jL
  • http://t.csdnimg.cn/pMLLE
  • http://t.csdnimg.cn/FjtOK
  • http://t.csdnimg.cn/K95yN
  1. ffmpeg-7.0中/test/utils.c文件

1.概述

视频/图像数据格式作为视频/图像处理的对象,是整个视频图像处理体系的最基本单元,有必要熟悉其存储格式。主要视频图像数据格式有yuv420p,yuv422p,yuv444p,bmp和rgb24等,其中视频编码器的主要输入数据格式就是yuv420p,因为这种格式存储数据量很小,易于存储。

视频图像处理体系大致可以分为几层:

  1. 协议层(http、rtmp、file…)
  2. 封装层(mkv,mp4,flv,mpegts,avi…)
  3. 编解码层(h264,h265,mpeg2…)
  4. 像素层(yuv420p,yuv422p,yuv444p,rgb24…)

这里仅关注像素层(视频图像的主要组成部分)的数据格式。

2.视频图像数据格式

2.1 yuv420p

最常见的视频编码数据格式,yuv420p格式中,p的含义是planar,即平面存储。存储时分为三个分量Y、U和V,即在内存当中存储时先存储Y,后存储U,最后V。如果是420p格式,U和V分量的数据大小均为Y分量的1/4,这是因为U和V分量的width和height均为Y分量的1/2。
在内存中,YUV420P的存储方式为:Y0 Y1 Y2 … U0 U1 U2 … V0 V1 V2 …,并且Y的长度分别为U和V分量的4倍。如果不是p格式,可能存储的方式是interleave,即交叉式存储,这里不讨论。YUV420p格式的数据,其读取和写入数据的方式为

int video_yuv420_split(char *url, int w, int h, int num){FILE *fp = fopen(url, "rb+");FILE *fp_y = fopen("output_420_y.y", "wb+");FILE *fp_u = fopen("output_420_u.y", "wb+");FILE *fp_v = fopen("output_420_v.y", "wb+");// 3/2 = 1(Y) + 1/4(U) + 1/4(V)unsigned char *pic = (unsigned char *) mallo c(w * h * 3/2);for(int i = 0; i < num; i++){fread(pic, 1, w * h * 3/2, fp); 				// Read YUV data from .yuv filefwrite(pic, 1, w * h, fp_y);					// Write Y component into fp_yfwrite(pic + w * h, 1, w * h / 4, fp_u);		// Write U component into fp_ufwrite(pic + w * h * 5 / 4, 1, w * h / 4, fp_v);// Write V component into fp_v}free(pic);fclose(fp);fclose(fp_y);fclose(fp_u);fclose(fp_v);return 0;
}

2.2 yuv422p

与yuv420p类似,区别在于U和V分量的比例不同。yuv422p格式当中,仍然是先存储Y分量,后存储U分量,最后是V分量。但是U和V分量的大小分别是Y分量的一半,例如 Y0 Y1 Y2 Y3 U0 U1 V0 V1。读写方式和yuv420p的区别在于

	unsigned char *pic = (unsigned char *) malloc (w * h * 2);for(int i = 0; i < num; i++){fread(pic, 1, w * h * 2, fp); 					// Read YUV data from .yuv filefwrite(pic, 1, w * h, fp_y);					// Write Y component into fp_yfwrite(pic + w * h, 1, w * h / 2, fp_u);		// Write U component into fp_ufwrite(pic + w * h * 3 / 2, 1, w * h / 2, fp_v);// Write V component into fp_v}

2.3 yuv444p

yuv444p格式中,YUV三个分量的大小相同,例如 Y0 Y1 Y2 Y3 U0 U1 U2 U3 V0 V1 V2 V3。读写方式为

	unsigned char *pic = (unsigned char *) malloc (w * h * 3);for(int i = 0; i < num; i++){fread(pic, 1, w * h * 3, fp); 				// Read YUV data from .yuv filefwrite(pic, 1, w * h, fp_y);				// Write Y component into fp_yfwrite(pic + w * h, 1, w * h, fp_u);		// Write U component into fp_ufwrite(pic + w * h * 2, 1, w * h, fp_v);	// Write V component into fp_v}

2.4 RGB格式

对于后缀为.rgb格式的文件,其存储数据的方式与yuv不同。在YUV格式当中,YUV三个通道是分别进行存储,而RGB格式是三个通道交替进行存储,例如 r0 g0 b0 r1 g1 b1 r2 g2 b2…,因此其读写数据的方式也不同。

	unsigned char *pic = (unsigned char *) malloc (w * h * 3);for(int i = 0; i < num; i++){fread(pic, 1, w * h * 3, fp);			// read .rgb filefor(int j = 0; j < w * h * 3; j = j + 3){fwrite(pic + j, 1, 1, fp_y);		// write r componentfwrite(pic + j + 1, 1, 1, fp_u);	// write g componentfwrite(pic + j + 2 , 1, 1, fp_v);	// write b component}

常见的8种颜色RGB数值为

颜色RGB
(255, 255, 255)
(255, 255, 0)
(0, 255, 255)
绿( 0, 255, 0)
品红(255, 0, 255)
(255, 0, 0)
(0, 0, 255)
(0, 0, 0)

另外,灰色为128

2.5 BMP格式

BMP格式是对RGB进行封装得到的格式,能够使用普通的图片浏览器打开。对RGB格式进行封装得到BMP格式的方式如下:

/**1. Convert RGB24 file to BMP file2. @param rgb24path    Location of input RGB file.3. @param width        Width of input RGB file.4. @param height       Height of input RGB file.5. @param url_out      Location of Output BMP file.*/
int video_rgb24_to_bmp(const char *rgb24path,int width,int height,const char *bmppath){typedef struct {  long imageSize;long blank;long startPosition;}BmpHead;typedef struct{long  Length;long  width;long  height;unsigned short  colorPlane;unsigned short  bitColor;long  zipFormat;long  realSize;long  xPels;long  yPels;long  colorUse;long  colorImportant;}InfoHead;int i = 0;int j = 0;BmpHead m_BMPHeader = { 0 };InfoHead  m_BMPInfoHeader = { 0 };char bfType[2] = {'B', 'M'};int header_size = sizeof(bfType) + sizeof(BmpHead) + sizeof(InfoHead);unsigned char *rgb24_buffer = NULL;FILE* fp_rgb24 = NULL;FILE* fp_bmp = NULL;if((fp_rgb24 = fopen(rgb24path, "rb")) == NULL){printf("Error: Cannot open input RGB24 file.\n");return -1;}if((fp_bmp = fopen(bmppath, "wb")) == NULL){printf("Error: Cannot open output BMP file.\n");return -1;}rgb24_buffer = (unsigned char *)malloc(width * height * 3);fread(rgb24_buffer, 1, width * height * 3, fp_rgb24);m_BMPHeader.imageSize = 3 * width * height + header_size;m_BMPHeader.startPosition = header_size;m_BMPInfoHeader.Length = sizeof(InfoHead); m_BMPInfoHeader.width = width;//BMP storage pixel data in opposite direction of Y-axis (from bottom to top).m_BMPInfoHeader.height =- height;m_BMPInfoHeader.colorPlane = 1;m_BMPInfoHeader.bitColor = 24;m_BMPInfoHeader.realSize = 3 * width * height;fwrite(bfType, 1, sizeof(bfType), fp_bmp);fwrite(&m_BMPHeader, 1, sizeof(m_BMPHeader), fp_bmp);fwrite(&m_BMPInfoHeader, 1, sizeof(m_BMPInfoHeader), fp_bmp);//BMP save R1|G1|B1,R2|G2|B2 as B1|G1|R1,B2|G2|R2//It saves pixel data in Little Endian//So we change 'R' and 'B'for(j = 0; j < height; j++){for(i = 0; i < width; i++){// 将R分量和B分量的位置进行交换char temp = rgb24_buffer[(j * width + i) * 3 + 2];rgb24_buffer[(j * width + i) * 3 + 2] = rgb24_buffer[(j * width + i) * 3 + 0];rgb24_buffer[(j * width + i) * 3 + 0] = temp;}}fwrite(rgb24_buffer, 3 * width * height, 1, fp_bmp);fclose(fp_rgb24);fclose(fp_bmp);free(rgb24_buffer);printf("Finish generate %s!\n", bmppath);return 0;
}

在这里,代码执行的任务包括:

  1. 存储写上BMP的头部信息
  2. 将RGB格式的文件修改为BGR。这是因为BMP存储时使用的是小端存储(Little Endian),存储时的顺序为B、G、R

BMP文件是由BITMAPFILEHEADER、BITMAPINFOHEADER、RGB像素数据共3个部分构成,如下所示。其中,BITMAPFILEHEADER对应上述的BmpHead,BITMAPINFOHEADER对应上述的InfoHead。

typedef  struct  tagBITMAPFILEHEADER
{ unsigned short int  bfType;       //位图文件的类型,必须为BM unsigned long       bfSize;       //文件大小,以字节为单位unsigned short int  bfReserverd1; //位图文件保留字,必须为0 unsigned short int  bfReserverd2; //位图文件保留字,必须为0 unsigned long       bfbfOffBits;  //位图文件头到数据的偏移量,以字节为单位
}BITMAPFILEHEADER; 
typedef  struct  tagBITMAPINFOHEADER 
{ long biSize;                    //该结构大小,字节为单位long  biWidth;                  //图形宽度以象素为单位long  biHeight;                 //图形高度以象素为单位short int  biPlanes;            //目标设备的级别,必须为1 short int  biBitcount;          //颜色深度,每个象素所需要的位数short int  biCompression;       //位图的压缩类型long  biSizeImage;              //位图的大小,以字节为单位long  biXPelsPermeter;       	//位图水平分辨率,每米像素数long  biYPelsPermeter;       	//位图垂直分辨率,每米像素数long  biClrUsed;            	//位图实际使用的颜色表中的颜色数long  biClrImportant;       	//位图显示过程中重要的颜色数
}BITMAPINFOHEADER;

3.格式转换

3.1 RGB24转换为YUV420P

RGB24转换YUV420p的公式为:

Y = 0.299 * R + 0.587 * G + 0.114 * B
U =-0.147 * R - 0.289 * G + 0.463 * B
V = 0.615 * R - 0.515 * G - 0.100 * B

代码参考ffmpeg-7.0当中的/test/utils.c,这个文档相比雷霄骅博士的写法有所不同,或许更好理解。

#define SCALEBITS 8
#define ONE_HALF  (1 << (SCALEBITS - 1))
#define FIX(x)    ((int) ((x) * (1 << SCALEBITS) + 0.5))	// 乘以255倍,猜测目的应该是提升精度
#define err_if(expr) do {                                              \if (expr) {                                                        \fprintf(stderr, "%s\n", strerror(errno));                      \exit(1);                                                       \}                                                                  \
} while (0)static void rgb24_to_yuv420p(unsigned char *lum, unsigned char *cb, // lum是Y分量的地址,cb是U分量的地址unsigned char *cr, const unsigned char *src,	// cr是V分量的地址int width, int height)
{int wrap, wrap3, x, y;int r, g, b, r1, g1, b1;const unsigned char *p;wrap  = width;		// yuv指针偏移量,用于定位图像每一行的宽度wrap3 = width * 3;	// rgb指针偏移量,用于定位图像每一行的宽度p     = src;		// src为rgb图像的指针地址// 这里每2x2个像素进行处理,是因为U和V分量的长和宽分别只占据Y分量的1/2for (y = 0; y < height; y += 2) { for (x = 0; x < width; x += 2) {r       = p[0];g       = p[1];b       = p[2];r1      = r;g1      = g;b1      = b;lum[0]  = (FIX(0.29900) * r + FIX(0.58700) * g +FIX(0.11400) * b + ONE_HALF) >> SCALEBITS;r       = p[3];g       = p[4];b       = p[5];r1     += r;g1     += g;b1     += b;lum[1]  = (FIX(0.29900) * r + FIX(0.58700) * g +FIX(0.11400) * b + ONE_HALF) >> SCALEBITS;p      += wrap3;	// 移动到当前2x2小块的左下小块lum    += wrap;		// 移动到当前2x2小块的左下小块r       = p[0];g       = p[1];b       = p[2];r1     += r;g1     += g;b1     += b;lum[0]  = (FIX(0.29900) * r + FIX(0.58700) * g +FIX(0.11400) * b + ONE_HALF) >> SCALEBITS;r       = p[3];g       = p[4];b       = p[5];r1     += r;g1     += g;b1     += b;lum[1]  = (FIX(0.29900) * r + FIX(0.58700) * g +FIX(0.11400) * b + ONE_HALF) >> SCALEBITS;// 每2x2个像素有一个Cb和Cr分量,将其写入到cb和cr数组当中cb[0]   = ((- FIX(0.16874) * r1 - FIX(0.33126) * g1 +FIX(0.50000) * b1 + 4 * ONE_HALF - 1) >> (SCALEBITS + 2)) + 128;cr[0]   = ((FIX(0.50000) * r1 - FIX(0.41869) * g1 -FIX(0.08131) * b1 + 4 * ONE_HALF - 1) >> (SCALEBITS + 2)) + 128;cb++;cr++;p   += -wrap3 + 2 * 3;	// 乘以3是因为rgb是顺序存储的,移动到下一个2x2小块左上小块的r分量lum += -wrap  + 2;		// 回到上一行的起始位置,加2则指向下一个2x2小块的左上小块}p   += wrap3;lum += wrap;}
}static void pgmyuv_save(const char *filename, int w, int h,const unsigned char *rgb_tab)
{FILE *f;int i, h2, w2;unsigned char *cb, *cr;unsigned char *lum_tab, *cb_tab, *cr_tab;lum_tab = malloc(w * h);cb_tab  = malloc(w * h / 4);cr_tab  = malloc(w * h / 4);rgb24_to_yuv420p(lum_tab, cb_tab, cr_tab, rgb_tab, w, h);if (filename) {f = fopen(filename, "wb");fprintf(f, "P5\n%d %d\n%d\n", w, h * 3 / 2, 255);} else {f = stdout;}err_if(fwrite(lum_tab, 1, w * h, f) != w * h); // 写入Y分量h2 = h / 2;w2 = w / 2;cb = cb_tab;cr = cr_tab;if (filename) {for (i = 0; i < h2; i++) {err_if(fwrite(cb, 1, w2, f) != w2);	// 写入U分量err_if(fwrite(cr, 1, w2, f) != w2);	// 写入V分量cb += w2;cr += w2;}fclose(f);} else {for (i = 0; i < h2; i++) {err_if(fwrite(cb, 1, w2, f) != w2);cb += w2;}for (i = 0; i < h2; i++) {err_if(fwrite(cr, 1, w2, f) != w2);cr += w2;}}free(lum_tab);free(cb_tab);free(cr_tab);
}

4.视频图像评价指标

4.1 MSE

MSE全称为Mean Square Error,表示均方误差,其计算方式为

int width = WIDTH;
int height = HEIGHT;
double mse = 0.0;
for(int j = 0; j < width * height; j++){mse += pow((double)(src[j] - dst[j]), 2);
}
mse = mse / (width * height);

4.2 PSNR

PSNR的计算是在MSE计算的基础之上获得的,计算方式为

double psnr = 0.0;
psnr = 10 * log10(255.0 * 255.0 / mse);

PSNR描述了两幅图片的差异程度,单位是dB,dB越大,表示两幅图像越接近,否则差异越大。在视频编码标准中,PSNR是衡量编码工具的重要指标,通常与Bitrate结合起来,来评判编码算法的优劣。

CSDN:https://blog.csdn.net/weixin_42877471
Github:https://github.com/DoFulangChen

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/8873.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

蓝桥杯EDA客观题

目录 前言 一、PCB类知识点和题目分析 1.电阻 2.电容 3.封装类 4.单位转换类 5.电路板结构类 6.PCB绘制规则 7.立创软件 8.PCB硬件 线性电源和开关电源 二、数电知识点和题目分析 1.门电路 2.逻辑代数 3.组合逻辑电路 4.触发器 5.时序逻辑电路 6.其他 三、模…

vue3+ts之el-tooltip换行显示内容

<el-tooltip placement"top-end"><div slot"content" class"tips"><el-button type"primary" click"exportData">导出</el-button></div><template #content><span class"cont…

【项目实战】使用Yolov8 + tesseract 实现身份证信息解析(OCR) + 输入可为图片或者pdf + 完整代码 + 整体方案 + 全网首发

本项目可用于实验,毕业设计参考等。整体效果如下所示: 说明:图片来源于网络,如有侵权,请联系作者删除。 目录 一 数据集制作

C语言--带环链表问题

继续学习 一、判断链表是否带环 141. 环形链表 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;用快慢指针&#xff0c;快指针走两步&#xff0c;慢指针走一步&#xff0c;当慢指针走一半快指针进到环里 当慢指针进环&#xff0c;快指针已经在环中转了一会儿了 | |…

前端TCP三次握手和四次挥手

三次握手过程 客户端发送一个同步(SYN)包给服务器&#xff0c;携带一个随机生成的序列号x&#xff0c;表示请求建立连接。服务器收到SYN包后&#xff0c;发送一个带有自己的序列号y和确认号x1的SYN-ACK包给客户端&#xff0c;表示接受连接请求。客户端收到服务器的SYN-ACK包后…

关于Java selenium使用前浏览器驱动的下载和环境变量的配置

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

vue+ant-design+formBuiler表单构建器——技能提升——form design——亲测有效

最近看到后端同事在弄一个后台管理系统&#xff0c;额&#xff0c;前端真的是夹缝中生存啊&#xff0c;AI抢饭碗&#xff0c;后端也想干前端的活儿。。。 他用到了表单构建器&#xff0c;具体效果如下: 网上有很多适用于ElementUi和ant-design的form design插件&#xff0c;下…

武汉星起航:精准市场定位引领跨境电商新潮流,创造辉煌业绩

在跨境电商领域&#xff0c;市场定位的准确性直接关系到企业的成败。武汉星起航电子商务有限公司&#xff0c;凭借其自运营团队的深厚经验和精准洞察力&#xff0c;成功在亚马逊平台开设多家自营店铺&#xff0c;并取得了显著成绩。这一成绩的取得&#xff0c;离不开公司对市场…

SAP实施- 现状调研问卷-如何引导客户提供现状信息

CO篇- 为了避免用户天马行空回答问题&#xff0c;一般SAP实施现状调研阶段都会基于问卷来调研现状 细 类问题 1组织管理现行的管理考核体系中&#xff0c;有没有部门考核机制&#xff1f;评价部门业绩的关键性指标有哪些&#xff1f;公司组织架构及财务会计核算的成本中心是…

等保测评执行指南:Linux系统安全检查命令集锦

在进行等保测评时&#xff0c;会用到多种Linux命令来检查和配置系统的安全设置。以下是一些常用的命令及其用途&#xff1a; 1. **用户和权限相关** - useradd&#xff1a;添加新用户。 - usermod&#xff1a;修改用户属性。 - userdel&#xff1a;删除用户。 - g…

Mysql InnoDB引擎生产环境配置 - 待完善

一. 前言 在生产环境中mysql如何配置, 有什么约定项… 数据库在生产环境运行的时候&#xff0c;你必须根据机器的内存设置合理的buffer pool的大小&#xff0c;然后设置buffer pool的数量&#xff0c;这样的话&#xff0c;可以尽可能的保证你的数据库的高性能和高并发能力。 …

设计模式——行为型模式——策略模式(含实际业务使用示例、可拷贝直接运行)

目录 策略模式 定义 组成和UML图 代码示例 实际业务场景下策略模式的使用 策略模式优缺点 使用场景 JDK中使用策略模式示例 参考文档 策略模式 定义 策略模式定义了一系列算法&#xff0c;并将每个算法封装起来&#xff0c;使它们可以相互替换&#xff0c;且算法的变化…

C#返回多个值的方法

在C#中&#xff0c;返回多个值有多种方法&#xff0c;以下是常用的几种方式&#xff1a; 使用元组&#xff08;Tuples或ValueTuple&#xff09;: 自C# 7.0起&#xff0c;可以使用元组轻松地从方法返回多个值。元组是一种轻量级的数据结构&#xff0c;可以存储不同类型的数据。例…

外贸客户初次合作不付定金你怎么看

小伙伴有没有遇到这种情况&#xff0c;有一个非常大的订单&#xff0c;但是客户又不愿意付定金怎么办&#xff1f;你接还是不接。 那咱们这个小伙伴呢&#xff0c;就是说&#xff0c;这个客户&#xff0c;他是一个中间商&#xff0c;然后中间商的话呢&#xff0c;他这个订单量…

文件的编码格式都在文件中有标注吗

文件的编码格式并不总是在文件中直接标注的&#xff0c;这取决于文件的类型和用途。但是&#xff0c;有几种情况下文件的编码格式可能会被明确标识或可以推断出来&#xff1a; 文本文件&#xff1a; BOM&#xff08;Byte Order Mark&#xff09;&#xff1a;某些文本文件&#…

Redis技术解析

引言 在Java高级开发的道路上&#xff0c;对Redis的掌握是必不可少的一环。Redis&#xff0c;作为一款开源的&#xff0c;内存中的数据结构存储系统&#xff0c;它可以用作数据库、缓存和消息中间件。本文将深入探讨Redis的核心技术&#xff0c;并结合Java开发环境&#xff0c…

FMEA助力医疗设备研发制造:领跑未来,实现弯道超车!

医疗设备作为保障人类健康的重要工具&#xff0c;其研发与制造水平直接关系到医疗技术的进步。然而&#xff0c;在激烈的市场竞争中&#xff0c;如何能够让自家医疗设备研发制造实现弯道超车&#xff0c;成为行业佼佼者&#xff1f;答案就在于——FMEA&#xff08;失效模式与影…

go方法定义

方法定义 Golang 方法总是绑定对象实例&#xff0c;并隐式将实例作为第一实参 (receiver)。 只能为当前包内命名类型定义方法。 参数 receiver 可任意命名。如方法中未曾使用 &#xff0c;可省略参数名。 参数 receiver 类型可以是 T 或 *T。基类型 T 不能是接口或指针。 不…

试用NXP官方的UDS bootloader

文章目录 1.前言2.资料获取2.1 MCU例程 2.2 开发环境2.3 上位机2.4 硬件 3.工程修改3.1 boot工程修改 3.2 app工程修改4.测试情况5.例程分享 1.前言 最近很多客户在开发S32K系列MCU时咨询是否可以提供基于UDS协议的bootloader。本文以S32K144为例&#xff0c;介绍如何使用NXP官…

qt基础类型转换

uchar*与QByteArray类型 uchar转为QByteArray QByteArray array; unsigned char buf ; arrayQByteArray::fromRawData((char)buf,sizeof(buf)); QByteArray转为uchar //一次性转换 buf reinterpret_cast<unsigned char*>(array.data()); //单个数据转换 (unsigned cha…