bmp转换为YUV420p指南

您所在的位置:网站首页 4krgb和yuv420 bmp转换为YUV420p指南

bmp转换为YUV420p指南

#bmp转换为YUV420p指南| 来源: 网络整理| 查看: 265

bmp转换为YUV420p指南(Linux): 一、如何获得一张YUV图片

(1)在网络上下载一张随便的jpg图片或者bmp图片

使用画图软件打开,并且设置成640*480大小的

在这里插入图片描述

(2)在Linux下安装ffmpeg

apt install ffmpeg

(3)使用ffmpeg转换jpg为YUV格式

ffmpeg -i 1.jpg -s 640x480 -pix_fmt yuv420p test.yuv

二、如何查看YUV图片

利用ffmpeg自带的图片查看器ffplay:

ffplay -video_size 640x480 test.yuv

三、YUV420格式

YUV两大类格式:

(1)平面格式(planar formats)

将Y、U、V的三个分量分别存放在不同的矩阵中。先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V。

如:YUV420P在数组中的存储格式

在这里插入图片描述

(2)紧缩格式(packed formats)

YUV数据是交替存储在数组中的,类似与BMP的RGB存储

如:YUV422 在数组中的存储格式 在这里插入图片描述

YUV420示意图

每四个像素点共用一对UV值,所以,在640x480的图像中,需要640x480 + 640x480/4 + 640x480/4字节来存储。

640x480是Y分量的字节数

而U和V所占的字节数相同:640x480/4

YUV420P在屏幕像素上的分布表示: 在这里插入图片描述

每四个Y使用同一组U和V。共用UV的方式如上图

四个Y公用的 U = 四个Y对应的U之和/4

四个Y公用的 V = 四个Y对应的V之和/4

上面两条公式就是YUV420P的采样公式

四、RGB与YUV互相转化公式

一个像素的RGB对应的YUV:

Y=0.2990R + 0.5870G + 0.1140B U=0.500R - 0.419G - 0.081B + 128 V=-0.1684R - 0.3316G + 0.500B + 128

(注意:网上找的大部分公式可能来源于同一个地方,所以U和V的公式反过来了,这导致我在转换的时候出现色彩不正常的情况,并且研究很久才知道U和V的公式反过来了(坑爹啊!!))

一个YUV像素点对应的RGB:

R = Y + 1.403 * (V - 128)

G = Y - 0.343 * (U - 128) - 0.714 * (V - 128)

B = Y + 1.770 * (V - 128)

对于420p格式的YUV图片:RGB转换YUV以后,得到的U和V还需要进行一次采样转化,转化方式为每四个点公用一个U和V,公用的方式如上面的YUV420示意图。

五、RGB转换为YUV420:

在这里我们以bmp(24位位图)图片格式为例:

在这里插入图片描述

1.首先获得全部的RGB像素点(包含处理bmp数据头)

有关bmp数据头部感兴趣的自己上百度找

不感兴趣的直接用C库函数 leesk函数跳过头部54个字节

由于是24位bmp,RGB像素点不需要任何修改,直接读取即可,存放在rgb_buf中

![bmp示意图](C:\Users\A\Desktop\工作\md图片\bmp示意图.png)int fn_Open_Bmp(const char * bmp_path) { int bmp_fd = open(bmp_path , O_RDWR); if(bmp_fd printf("申请bmp头信息空间失败\n"); return -1; } //读取bmp的头信息 read(bmp_fd , head_info , 14); read(bmp_fd , bmp_info , 40); bmp_w = bmp_info->width; bmp_h = bmp_info->height; printf("bmp_x = %d bmp_y = %d\n" , bmp_w , bmp_h); //读取RGB信息 rgb_buf = malloc(bmp_w * bmp_h * 3); if(rgb_buf == NULL) { printf("申请bmp_rgb空间失败\n"); return -1; } unsigned int ret = read(bmp_fd , rgb_buf , bmp_w * bmp_h * 3); if(ret != bmp_w * bmp_h * 3) { printf("读取bmp_rgb失败\n"); return -1; } free(bmp_info); free(head_info); close(bmp_fd); return 0; }

2.根据RGB转换所有像素点的YUV

bmp的存储形式:

在这里插入图片描述

int fn_Convert_Yuv() { unsigned char * tmp_y = NULL; unsigned char * tmp_u = NULL; unsigned char * tmp_v = NULL; //申请空间 yuv_y = malloc(bmp_w * bmp_h); //yuv_tmp_u和yuv_tmp_v保存所有的U,V数据,已备后面进行采样 yuv_tmp_u = malloc(bmp_w * bmp_h); yuv_tmp_v = malloc(bmp_w * bmp_h); //由于yuv_y等参数需要进行地址加,所以这里使用tmp_y等保存最开始的地址 tmp_y = yuv_y; tmp_u = yuv_tmp_u; tmp_v = yuv_tmp_v; if(yuv_y == NULL || yuv_tmp_u == NULL || yuv_tmp_v == NULL) { printf("申请yuv空间失败\n"); return -1; } //由于bmp图片存放的格式是倒叙,所以int i = bmp_h - 1是在bmp图片从下往上,从左往右转换YUV //遍历所有的像素点进行转换 for(int i = bmp_h - 1 ; i >= 0 ; i--) { for(int j = 0 ; j unsigned char * tmp_u = NULL; unsigned char * tmp_v = NULL; yuv_u = malloc(bmp_w/2 * bmp_h/2); yuv_v = malloc(bmp_w/2 * bmp_h/2); tmp_u = yuv_u; tmp_v = yuv_v; if(yuv_u == NULL || yuv_v == NULL) { printf("申请uv空间失败\n"); return; } //对根据yuv_tmp_u和yuv_tmp_v,对UV进行采样,转换的数据保存在yuv_u和yuv_v中 for(int i = 0 ; i *yuv_u = ((yuv_tmp_u[i * bmp_w + j] + yuv_tmp_u[i * bmp_w + j + 1] + yuv_tmp_u[(i) * bmp_w + j]+ yuv_tmp_u[(i) * bmp_w + j + 1])) / 4; *yuv_v = ((yuv_tmp_v[i * bmp_w + j] + yuv_tmp_v[i * bmp_w + j + 1] + yuv_tmp_v[(i) * bmp_w + j]+ yuv_tmp_v[(i) * bmp_w + j + 1])) / 4; yuv_v++; yuv_u++; } } yuv_u = tmp_u; yuv_v = tmp_v; //对y、u、v 信号进行抗噪处理(可以不要) for (int i=0;i if(yuv_u[i]240) yuv_v[i] = 240; } printf("转uv成功\n"); return; }

4.创建.yuv文件,并且把YUV写进文件中

void fn_Make_Yuv(const char * bmp_path) { char path_buf[1024] = {0}; int yuv_fd = open("./test.yuv" , O_RDWR | O_TRUNC | O_CREAT); if(yuv_fd //1.打开bmp if(fn_Open_Bmp(bmp_path) != 0) { return -1; } else { printf("打开bmp成功\n"); } //2.转换yuv fn_Convert_Yuv(); //3.对uv进行采样 fn_Convert_Uv(); //4.创建yuv文件并写入像素点 fn_Make_Yuv(bmp_path); printf("yuv_y len = %ld\n" , strlen(yuv_y)); printf("yuv_u len = %ld\n" , strlen(yuv_u)); printf("yuv_v len = %ld\n" , strlen(yuv_v)); system("ffplay -video_size 640x480 -pixel_format yuv420p test.yuv"); free(yuv_y); free(yuv_u); free(yuv_v); free(yuv_tmp_u); free(yuv_tmp_v); return 0; } /* 打开bmp 处理bmp的头信息 并跳过bmp的头54个字节 */ int fn_Open_Bmp(const char * bmp_path) { int bmp_fd = open(bmp_path , O_RDWR); if(bmp_fd printf("申请bmp头信息空间失败\n"); return -1; } //读取bmp的头信息 read(bmp_fd , head_info , 14); read(bmp_fd , bmp_info , 40); bmp_w = bmp_info->width; bmp_h = bmp_info->height; printf("bmp_x = %d bmp_y = %d\n" , bmp_w , bmp_h); //读取RGB信息 rgb_buf = malloc(bmp_w * bmp_h * 3); if(rgb_buf == NULL) { printf("申请bmp_rgb空间失败\n"); return -1; } unsigned int ret = read(bmp_fd , rgb_buf , bmp_w * bmp_h * 3); if(ret != bmp_w * bmp_h * 3) { printf("读取bmp_rgb失败\n"); return -1; } free(bmp_info); free(head_info); close(bmp_fd); return 0; } int fn_Convert_Yuv() { unsigned char * tmp_y = NULL; unsigned char * tmp_u = NULL; unsigned char * tmp_v = NULL; yuv_y = malloc(bmp_w * bmp_h); yuv_tmp_u = malloc(bmp_w * bmp_h); yuv_tmp_v = malloc(bmp_w * bmp_h); //由于yuv_y等参数需要进行地址加,所以这里使用tmp_y等保存最开始的地址 tmp_y = yuv_y; tmp_u = yuv_tmp_u; tmp_v = yuv_tmp_v; if(yuv_y == NULL || yuv_tmp_u == NULL || yuv_tmp_v == NULL) { printf("申请yuv空间失败\n"); return -1; } for(int i = bmp_h - 1 ; i >= 0 ; i--) { for(int j = 0 ; j unsigned char * tmp_u = NULL; unsigned char * tmp_v = NULL; yuv_u = malloc(bmp_w/2 * bmp_h/2); yuv_v = malloc(bmp_w/2 * bmp_h/2); tmp_u = yuv_u; tmp_v = yuv_v; if(yuv_u == NULL || yuv_v == NULL) { printf("申请uv空间失败\n"); return; } //对UV进行采样 for(int i = 0 ; i *yuv_u = ((yuv_tmp_u[i * bmp_w + j] + yuv_tmp_u[i * bmp_w + j + 1] + yuv_tmp_u[(i) * bmp_w + j]+ yuv_tmp_u[(i) * bmp_w + j + 1])) / 4; *yuv_v = ((yuv_tmp_v[i * bmp_w + j] + yuv_tmp_v[i * bmp_w + j + 1] + yuv_tmp_v[(i) * bmp_w + j]+ yuv_tmp_v[(i) * bmp_w + j + 1])) / 4; yuv_v++; yuv_u++; } } yuv_u = tmp_u; yuv_v = tmp_v; //对y、u、v 信号进行抗噪处理 for (int i=0;i if(yuv_u[i]240) yuv_v[i] = 240; } printf("转uv成功\n"); } void fn_Make_Yuv(const char * bmp_path) { char path_buf[1024] = {0}; int yuv_fd = open("./test.yuv" , O_RDWR | O_TRUNC | O_CREAT); if(yuv_fd char tmp_path[1024] = {0}; char * tmp_p = malloc(1024); char * p = NULL; strcpy(tmp_path , path); p = strtok(tmp_path , "/"); strcpy(tmp_p , p); while(1) { p = strtok(NULL , "/"); if(p == NULL) { break; } else { strcpy(tmp_p , p); } } return tmp_p; }

BMP_TO_YUV.h:

#ifndef BMP_TO_YUV_H #define BMP_TO_YUV_H #include #include #include #include #include #include #include int fn_Bmp_To_Yuv(const char * bmp_path); int fn_Open_Bmp(const char * bmp_path); int fn_Convert_Yuv(); void fn_Convert_Uv(); void fn_Make_Yuv(); char * get_name(const char * path); int bmp_w; int bmp_h; unsigned char * rgb_buf; unsigned char * yuv_y; unsigned char * yuv_tmp_u; unsigned char * yuv_tmp_v; unsigned char * yuv_u; unsigned char * yuv_v; unsigned char * y_out; unsigned char * u_out; unsigned char * v_out; //bmp头文件信息结构体 struct bitmap_header//文件头 -->14个字节 { unsigned short type; //文件类型,必须为BM unsigned int size; // 位图文件大小 unsigned short reserved1; //预留位 unsigned short reserved2; //预留位 unsigned int offbits; // bmp图像文件头数据偏移量 }__attribute__((packed));//--》忽略该结构体地址对齐 struct bitmap_info//像素头 --》40个字节 { unsigned int size; // 本结构大小 unsigned int width; //像素点宽度 unsigned int height; //像素点高度 unsigned short planes;//目标设备的级别,必须为1 unsigned short bit_count; // 色深每个像素点所占的位数24bit unsigned int compression; //是否压缩,0表示不压缩 unsigned int size_img; // bmp数据大小,必须是4的整数倍 unsigned int X_pel;//位图水平分辨率 unsigned int Y_pel;//位图垂直分辨率 unsigned int clrused;//位图实际使用的颜色表中的颜色数 unsigned int clrImportant;//位图显示过程中重要的颜色数 }__attribute__((packed)); #endif


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3