C++ YUV420文件读取与显示,绘制矩形框,绘制线段(绘制直线),绘制多边形(常用YUV数据格式——YUV420P中的YU12与YUV420SP中的NV21)

Posted Dontla

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ YUV420文件读取与显示,绘制矩形框,绘制线段(绘制直线),绘制多边形(常用YUV数据格式——YUV420P中的YU12与YUV420SP中的NV21)相关的知识,希望对你有一定的参考价值。

文章目录

YUV查看器及测试图片

参考文章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 文件读取显示缩放裁剪等操作教程

YUV 文件读取显示缩放裁剪等操作教程

CAD绘制矩形框命令

如何使用 python 和 openCV 从 .yuv 视频文件 (YUV420) 中提取帧?

详解 YUV 格式(I420/YUV420/NV12/NV12/YUV422)

视频存储格式YUV420 NV12 NV21 i420 YV12