C++ YUV420文件读取与显示,绘制矩形框,绘制线段(绘制直线),绘制多边形(常用YUV数据格式——YUV420P中的YU12与YUV420SP中的NV21)
Posted Dontla
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ YUV420文件读取与显示,绘制矩形框,绘制线段(绘制直线),绘制多边形(常用YUV数据格式——YUV420P中的YU12与YUV420SP中的NV21)相关的知识,希望对你有一定的参考价值。
文章目录
- YUV420有两种数据排列格式(YUV420P和YUV420SP)★★★★★(必须要清楚你要读取的YUV文件是哪种格式的!)
- 示例1:对YU12图像的读取,绘制【矩形框】,再保存为YU12格式
- 示例2:对YU12图像的读取,绘制【线段】,再保存为YU12格式(有bug,不能绘制斜向上的。。【已修复】)(传入参数两个点,可能从左下角到右上角,从右下角到左上角,,,各种顺序、边界情况,测试没有问题,不存在越界报错,但循环判断次数可能过多,有待优化)有bug,斜向上直线斜率过高画不全(已修复)
- 示例3:对YU12图像的读取,绘制【单个多边形区域】,再保存为YU12格式
- 示例4:对YU12图像的读取,绘制【多个多边形区域】,再保存为YU12格式
参考文章1:C++使用fread读取yuv一帧并写入新的yuv
YUV Formats分成两个格式:
紧缩格式(packed formats):将Y、U、V值储存成Macro Pixels阵列,和RGB的存放方式类似。
平面格式(planar formats):将Y、U、V的三个分量分别存放在不同的矩阵中。
参考文章:YUV详解数据和格式
Y”表示明亮度(Luminance或Luma),也就是灰阶值;而“U”和“V”表示的则是色度(Chrominance或Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。
参考文章:YUV模型中Y表示什么U V用来表示什么
参考文章:YUV帧结构(紧缩格式packed【打包格式】和平面格式planar)(A:B:C表示法)
YUV420有两种数据排列格式(YUV420P和YUV420SP)★★★★★(必须要清楚你要读取的YUV文件是哪种格式的!)
我这也写得不太好,具体可参考这篇大佬文章:图解YU12、I420、YV12、NV12、NV21、YUV420P、YUV420SP、YUV422P、YUV444P的区别
YUV420P:
- i420(YU12): YYYYYYYY UU VV(大部分软件默认的加载方式,至少YUView是的)
- YV12: YYYYYYYY VV UU
YUV420SP:
- NV12: YYYYYYYY UVUV
- NV21: YYYYYYYY VUVU
共用关系:4个Y共用一个U一个V
YU12格式(i420)(YV12就是U和V分量存储顺序调过来)(Cr对应U,Cb对应V)
共用关系:
NV21格式(android平台下使用相机默认图像格式,我们海康摄像头也是的)
共用关系:
示例1:对YU12图像的读取,绘制【矩形框】,再保存为YU12格式
以我们下的这个YUV文件为例,它是YU12的,YUV查看工具(windows)和YUV测试文件
对YU12图像的读取,绘制矩形框,再保存为YU12格式
main.cpp
#pragma warning(disable : 4996)
#include<iostream>
#include <stdio.h>
#include <stdlib.h>
#include "math.h"
#include "memory.h"
using namespace std;
#define DEMO_CHECK_RET_EX(c) if(c)printf("DEMO_ASSERT_RET FAILED!\\n"); return;
void demo_add_rect_region_to_yuv(unsigned char* pFrm, int pic_w, int pic_h, int rect_x, int rect_y, int rect_w, int rect_h, int R, int G, int B); //左上角(rect_x, rect_y),宽rect_w,高rect_h
int main()
FILE* in, * out;
//char data[100000];
int len = 640 * 480 * 3 / 2; //每帧大小为 h*w*1.5
unsigned char* buff = (unsigned char*)malloc(len);
if (!buff)
cout << "malloc buff faild" << endl;
return -1;
string file_in = "640×480.yuv";
string file_out = "output.yuv";
in = fopen(file_in.c_str(), "rb");
if (in == nullptr)
cout << "open file in " << file_in << "failed" << endl;
return 0;
out = fopen(file_out.c_str(), "wb+");
if (out == nullptr)
cout << "open file " << file_out << "failed" << endl;
return 0;
//若要完成多帧,可使用循环读取每一帧 并使用seek函数指定起始位置
cout << "读取文件" << endl;
fread(buff, sizeof(char), len, in);
cout << "读取文件完成" << endl;
cout << "绘制文件" << endl;
int img_width = 640;
int img_height = 480;
demo_add_rect_region_to_yuv(
buff,
img_width,
img_height,
0.3 * img_width,
0.2 * img_height,
(0.7 - 0.3) * img_width,
(0.8 - 0.2) * img_height,
18, 255, 232);
cout << "绘制文件完成" << endl;
cout << "写入文件" << endl;
fwrite(buff, sizeof(char), len, out);
cout << "写入文件完成" << endl;
fclose(in);
fclose(out);
return 0;
void demo_add_rect_region_to_yuv(unsigned char* pFrm, int pic_w, int pic_h, int rect_x, int rect_y, int rect_w, int rect_h, int R, int G, int B) //左上角(rect_x, rect_y),宽rect_w,高rect_h
//FILE *file = NULL;
int i;
//const char* save_file_name = "yvu_with_rect.yuv";
unsigned char* pLuma = (unsigned char*)pFrm; //Y
unsigned char* pChroma = (unsigned char*)pFrm + pic_w* pic_h; //UV
//unsigned char* pChroma = (unsigned char*)pFrm; //UV
DEMO_CHECK_RET_EX(NULL == pLuma);
DEMO_CHECK_RET_EX(NULL == pChroma);
//file = fopen(save_file_name,"wb");
//DEMO_CHECK_RET_EX(NULL == file);
//printf("demo_add_rect_to_yuv start!\\n pic_w:[%d], pic_h[%d]\\n, rect:[x:%d y:%d, w:%d, h:%d]\\n", pic_w,pic_h,rect_x,rect_y,rect_w,rect_h);
/* Set up the rectangle border size */
const int border = 5;
/* RGB convert YUV */
int Y, U, V;
Y = 0.299 * R + 0.587 * G + 0.114 * B;
U = -0.1687 * R + 0.3313 * G + 0.5 * B + 128;
V = 0.5 * R - 0.4187 * G - 0.0813 * B + 128;
/* Locking the scope of rectangle border range */
int j, k;
for (j = rect_y; j < rect_y + rect_h; j++) //第几行
for (k = rect_x; k < rect_x + rect_w; k++) //第几列
if (k < (rect_x + border) || k >(rect_x + rect_w - border) || //这里决定要绘制的区域
j < (rect_y + border) || j >(rect_y + rect_h - border))
/* Components of YUV's storage address index */
int y_index = j * pic_w + k; //YU12方式
int u_index = j / 2 * pic_w / 2 + k / 2;
int v_index = u_index + (pic_w * pic_h)/4;
/* set up YUV's conponents value of rectangle border */
pLuma[y_index] = Y;
pChroma[u_index] = U;
pChroma[v_index] = V;
#if 0
for (i = 0; i < pFrm->height; i++)
fwrite(pLuma + i * pFrm->width, 1, pFrm->width, file);
for (i = 0; i < pFrm->height / 2; i++)
fwrite(pChroma + i * pFrm->width, 1, pFrm->width, file);
fclose(file);
#endif
//printf("demo_add_rect_to_yuv end!\\n\\n" );
运行结果:
读取文件
读取文件完成
绘制文件
绘制文件完成
写入文件
写入文件完成
用YUView查看生成的文件:
示例2:对YU12图像的读取,绘制【线段】,再保存为YU12格式(有bug,不能绘制斜向上的。。【已修复】)(传入参数两个点,可能从左下角到右上角,从右下角到左上角,,,各种顺序、边界情况,测试没有问题,不存在越界报错,但循环判断次数可能过多,有待优化)有bug,斜向上直线斜率过高画不全(已修复)
以我们下的这个YUV文件为例,它是YU12的,YUV查看工具(windows)和YUV测试文件
#pragma warning(disable : 4996)
#include<iostream>
#include <stdio.h>
#include <stdlib.h>
#include "math.h"
#include "memory.h"
using namespace std;
#define DEMO_CHECK_RET_EX(c) if(c)printf("DEMO_ASSERT_RET FAILED!\\n"); return;
#define KY_MIN(x,y) (x < y ? x : y)
#define KY_MAX(x,y) (x > y ? x : y)
#define OPDEVSDK_POS_MAX_TARGET_VERTEX_NUM (10) /*!< 目标区域最多顶点数量 */
/**
* @brief 单坐标点描述
*/
typedef struct _OPDEVSDK_POS_POINT_ST_
float x; /*!< X轴归一化坐标,范围[0,1] */
float y; /*!< Y轴归一化坐标,范围[0,1] */
OPDEVSDK_POS_POINT_ST;
typedef struct _OPDEVSDK_POS_REGION_ST_
unsigned int pointNum; /*!< 区域对应坐标点数量 */ //ar:做什么用的?
OPDEVSDK_POS_POINT_ST point[OPDEVSDK_POS_MAX_TARGET_VERTEX_NUM]; /*!< 区域所有坐标点描述 */ //ar:10个顶点?
OPDEVSDK_POS_REGION_ST;
void demo_add_line_segment_to_yuv(unsigned char* pFrm, int pic_w, int pic_h, int rect_x, int rect_y, int rect_w, int rect_h, int R, int G, int B); //左上角(rect_x, rect_y),宽rect_w,高rect_h
int main()
FILE* in, * out;
//char data[100000];
int len = 640 * 480 * 3 / 2; //每帧大小为 h*w*1.5
unsigned char* buff = (unsigned char*)malloc(len);
if (!buff)
cout << "malloc buff faild" << endl;
return -1;
string file_in = "640×480.yuv";
string file_out = "output.yuv";
in = fopen(file_in.c_str(), "rb");
if (in == nullptr)
cout << "open file in " << file_in << "failed" << endl;
return 0;
out = fopen(file_out.c_str(), "wb+");
if (out == nullptr)
cout << "open file " << file_out << "failed" << endl;
return 0;
cout << "读取文件" << endl;
fread(buff, sizeof(char), len, in);
cout << "读取文件完成" << endl;
cout << "绘制文件" << endl;
int img_width = 640;
int img_height = 480;
demo_add_line_segment_to_yuv(
buff,
img_width,
img_height,
0.9 * img_width,
0.0 * img_height,
1.0 * img_width,
0.9 * img_height,
18, 255, 232);
cout << "绘制文件完成" << endl;
cout << "写入文件" << endl;
fwrite(buff, sizeof(char), len, out);
cout << "写入文件完成" << endl;
fclose(in);
fclose(out);
free(buff);
return 0;
void demo_add_line_segment_to_yuv(unsigned char* pFrm, int pic_w, int pic_h, int x1, int y1, int x2, int y2, int R, int G, int B) //左上角(rect_x, rect_y),宽rect_w,高rect_h
int rect_x = KY_MIN(x1, x2);
int rect_y = KY_MIN(y1, y2);
int rect_w = KY_MAX(x1, x2) - rect_x;
int rect_h = KY_MAX(y1, y2) - rect_y;
//const char* save_file_name = "yvu_with_rect.yuv";
unsigned char* pLuma = (unsigned char*)pFrm; //Y
unsigned char* pChroma = (unsigned char*)pFrm + pic_w * pic_h; //UV
//unsigned char* pChroma = (unsigned char*)pFrm; //UV
DEMO_CHECK_RET_EX(NULL == pLuma);
DEMO_CHECK_RET_EX(NULL == pChroma);
//file = fopen(save_file_name,"wb");
//DEMO_CHECK_RET_EX(NULL == file);
//printf("demo_add_rect_to_yuv start!\\n pic_w:[%d], pic_h[%d]\\n, rect:[x:%d y:%d, w:%d, h:%d]\\n", pic_w,pic_h,rect_x,rect_y,rect_w,rect_h);
/* Set up the rectangle border size */
const int border = 5;
/* RGB convert YUV */
int Y, U, V;
Y = 0.299 * R + 0.587 * G + 0.114 * B;
U = -0.1687 * R + 0.3313 * G + 0.5 * B + 128;
V = 0.5 * R - 0.4187 * G - 0.0813 * B + 128;
/* Locking the scope of rectangle border range */
int j, k;
if (rect_w == 0) //线段竖直
for (j = KY_MAX(0, rect_y); j < KY_MIN(pic_h, rect_y + rect_h); j++)
for (k = KY_MAX(0, rect_x - border / 2); k < KY_MIN(pic_w, rect_x + border / 2); k++)
/* Components of YUV's storage address index */
int y_index = j * pic_w + k; //YU12方式
int u_index = j / 2 * pic_w / 2 + k / 2;
int v_index = u_index + (pic_w * pic_h) / 4;
/* set up YUV's conponents value of rectangle border */
pLuma[y_index] = Y;
pChroma[u_index] = U;
pChroma[v_index] = V;
else if (rect_h == 0) //线段水平
for (k = KY_MAX(0, rect_x); k < KY_MIN(pic_w, rect_x + rect_w); k++)
for (j = KY_MAX(0, rect_y - border / 2); j < KY_MIN(pic_h, rect_y + border / 2); j++)
/* Components of YUV's storage address index */
int y_index = j * pic_w + k; //YU12方式
int u_index = j / 2 * pic_w / 2 + k / 2;
int v_index = u_index + (pic_w * pic_h) / 4;
/* set up YUV's conponents value of rectangle border */
pLuma[y_index] = Y;
pChroma[u_index] = U;
pChroma[v_index] = V;
else //线段既不竖直也不水平
int x_min = KY_MIN(x1, x2);
int x_max = KY_MAX(x1, x2);
double border_x_shadow = (double)border / 2 * fabs(y1 - y2) / sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2));
int x_start = KY_MAXYUV 文件读取显示缩放裁剪等操作教程
如何使用 python 和 openCV 从 .yuv 视频文件 (YUV420) 中提取帧?