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 <= 0){perror("");return -1;}//为头信息申请空间struct bitmap_header * head_info = malloc(14);struct bitmap_info * bmp_info = malloc(40);if(head_info == NULL || bmp_info == NULL){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 < bmp_w ; j++){*yuv_y = (unsigned char)((float)(rgb_buf[bmp_w * i * 3 + j * 3]) * 0.3f +(float)(rgb_buf[bmp_w * i * 3 + j * 3 +1]) * 0.59f +(float)(rgb_buf[bmp_w * i + j * 3 + 2]) * 0.11f);*yuv_tmp_u = (unsigned char)((float)(rgb_buf[bmp_w * i * 3 + j * 3]) / 2 -(float)(rgb_buf[bmp_w * i * 3 + j * 3 + 1]) * 0.4187f -(float)(rgb_buf[bmp_w * i * 3 + j * 3 + 2]) * 0.0813f + 128);*yuv_tmp_v = (unsigned char)(-(float)(rgb_buf[bmp_w * i * 3 + j * 3]) * 0.1684f -(float)(rgb_buf[bmp_w * i * 3 + j * 3 + 1]) * 0.3316f +(float)(rgb_buf[bmp_w * i * 3 + j * 3 + 2]) / 2 + 128);yuv_y++;yuv_tmp_v++;yuv_tmp_u++;}} //指针偏移为开始位置yuv_y = tmp_y;yuv_tmp_u = tmp_u;yuv_tmp_v = tmp_v;printf("转yuv成功\n");return 0;
}

3.根据YUV420格式再使用上面的采样公式对所有的UV进行YUV420格式的采样

采样公式:

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

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

void fn_Convert_Uv()
{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 < bmp_h ; i+=2){for(int j = 0 ; j < bmp_w ; j+=2){*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<bmp_w * bmp_h ;i++){if(yuv_y[i]<16)yuv_y[i] = 16;if(yuv_y[i]>235)yuv_y[i] = 235;}for(int i=0;i<bmp_w * bmp_h/4;i++){if(yuv_u[i]<16)yuv_u[i] = 16;if(yuv_v[i]<16)yuv_v[i] = 16;if(yuv_u[i]>240)yuv_u[i] = 240;if(yuv_v[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 <= 0){perror("");return;}write(yuv_fd , yuv_y , bmp_w * bmp_h);write(yuv_fd , yuv_u , bmp_w/2 * bmp_h/2);write(yuv_fd , yuv_v , bmp_w/2 * bmp_h/2);
}

六、完整代码

把这些代码都放在同一个文件夹里面,然后编译

把需要转换的bmp图片作为主函数传参就行(别的格式不行)

main.c:

#include "BMP_TO_YUV.h"//test main
int main(int argc , char ** argv)
{if(argc < 1){printf("请输入图片路径\n");}fn_Bmp_To_Yuv(argv[1]);
}

BMP_TO_YUV.c:

#include "BMP_TO_YUV.h"int fn_Bmp_To_Yuv(const char * bmp_path)
{//1.打开bmpif(fn_Open_Bmp(bmp_path) != 0){return -1;}else{printf("打开bmp成功\n");}//2.转换yuvfn_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 <= 0){perror("");return -1;}struct bitmap_header * head_info = malloc(14);struct bitmap_info * bmp_info = malloc(40);if(head_info == NULL || bmp_info == NULL){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 < bmp_w ; j++){*yuv_y = (unsigned char)((float)(rgb_buf[bmp_w * i * 3 + j * 3]) * 0.3f +(float)(rgb_buf[bmp_w * i * 3 + j * 3 +1]) * 0.59f +(float)(rgb_buf[bmp_w * i + j * 3 + 2]) * 0.11f);*yuv_tmp_u = (unsigned char)((float)(rgb_buf[bmp_w * i * 3 + j * 3]) / 2 -(float)(rgb_buf[bmp_w * i * 3 + j * 3 + 1]) * 0.4187f -(float)(rgb_buf[bmp_w * i * 3 + j * 3 + 2]) * 0.0813f + 128);*yuv_tmp_v = (unsigned char)(-(float)(rgb_buf[bmp_w * i * 3 + j * 3]) * 0.1684f -(float)(rgb_buf[bmp_w * i * 3 + j * 3 + 1]) * 0.3316f +(float)(rgb_buf[bmp_w * i * 3 + j * 3 + 2]) / 2 + 128);yuv_y++;yuv_tmp_v++;yuv_tmp_u++;}}yuv_y = tmp_y;yuv_tmp_u = tmp_u;yuv_tmp_v = tmp_v;printf("RGB转yuv成功\n");
}
void fn_Convert_Uv()
{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 < bmp_h ; i+=2){for(int j = 0 ; j < bmp_w ; j+=2){*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<bmp_w * bmp_h ;i++){if(yuv_y[i]<16)yuv_y[i] = 16;if(yuv_y[i]>235)yuv_y[i] = 235;}for(int i=0;i<bmp_w * bmp_h/4;i++){if(yuv_u[i]<16)yuv_u[i] = 16;if(yuv_v[i]<16)yuv_v[i] = 16;if(yuv_u[i]>240)yuv_u[i] = 240;if(yuv_v[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 <= 0){perror("");return;}write(yuv_fd , yuv_y , bmp_w * bmp_h);write(yuv_fd , yuv_u , bmp_w/2 * bmp_h/2);write(yuv_fd , yuv_v , bmp_w/2 * bmp_h/2);
}char * get_name(const char * path)
{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 <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>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; //文件类型,必须为BMunsigned 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;//目标设备的级别,必须为1unsigned short bit_count; // 色深每个像素点所占的位数24bitunsigned 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

bmp转换为YUV420p指南相关推荐

  1. FFmpeg开发实战(五):bmp转换为jpeg格式图像

    文章目录 1. bmp结构 2. bgr24转yuv420p 3. yuv420转jpeg 4. 下载 本文介绍了将bmp格式图像转换为jpeg格式图像的方法,附有详细的代码和图像示例. 1. bmp ...

  2. java bmp转jpg,在java中将bmp转换为jpg

    How do you convert bmp to jpg in Java? I know how to use the ImageIO way but is there a much faster ...

  3. 在C#中以编程方式将PNG或JPG图像转换为PSD指南出炉!

    PNG和JPG格式是众所周知的包含单层视觉信息的栅格图像文件格式.而Photoshop文档(PSD)文件包含几层来显示图片.您可以在.NET应用程序中使用C#以编程方式轻松地将PNG或JPG图像转换为 ...

  4. python bmp转jpg_python bmp转换为jpg 并删除原图

    {"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],&q ...

  5. PDF,png,jpg,bmp转换为word

    (1)原图 (2)转换后的效果 有疑问与需求,请联系qq:728297725

  6. 使用C++实现多张BMP图片转换为YUV动画----附加淡入淡出转场(逐渐变明变暗),及垂直滑像转场(逐行渐变)

    使用C++实现多张BMP图片转换为YUV动画----附加淡入淡出转场(逐渐变明变暗),及垂直滑像转场(逐行渐变) 一.BMP图像简介 1.BMP图像是什么? 2.BMP图像文件结构 1)图象文件头 2 ...

  7. BMP 转 YUV (BMP2YUV)

    本文介绍BMP 转 YUV.其实这是以前"数据压缩"实验课上的内容,前几天有人问我相关的问题,突然发现自己有一段时间没有接触BMP也有些生疏了,因此翻出资料总结一下. BMP文件格 ...

  8. YUV444、YUV422、YUV420、YUV420P、YUV420SP、YV12、YU12、NV12、NV21

    前言 各种YUV格式多如牛毛啊,刚开始学起来确实很费劲,网上搜索的文章讲的并不是很明白. 各种不同的YUV格式其实只是采样方式和存储方式不同,就这两点,不同的采样方式是为了实现节省内存,不同的存储方式 ...

  9. apache 网站转nginx_nginx部署避坑指南+高级配置详解

    目录 一:nginx可以替代tomcat吗? 二:nginx配置的各项参数指标 三:浏览器request header中provisional headers are shown 四:nginx服务访 ...

  10. 如何批量转.jpg/.bmp图片

    将.bmp转换为.jpg: 1.在图片目录下新建一个TXT,写入ren *.bmp *.jpg 2.将txt后缀改成.bat 3.双击该bat文件即可 将.jpg转换为.bmp: 1.在图片目录下新建 ...

最新文章

  1. 小米半年来最大调整:成立技术委员会,雷军称技术事关生死存亡
  2. redis事务不具有回滚机制,那么它是如何进行事务控制的
  3. KMP算法之 好理解的模板
  4. jQuery实现下拉列表移动 效果
  5. 如何看透借款人的隐形负债?
  6. binder,hwbinder,vndbinder之间的关系
  7. springboot如何使用log4j记录日志
  8. 从酷睿双核到Tiger Lake-H,英特尔如何帮游戏笔记本完成蜕变
  9. MyEclipse设置代码自动补全,及取消空格和‘=’补全
  10. 合并两个有序链表 java_合并两个有序链表
  11. silverlight(二)样式
  12. 开机时出现:reboot and select proper boot 、关于IDE与AHCI
  13. jacoco+maven 初次使用覆盖率工具
  14. 2021Java笔试题总结!三星java经典手机游戏
  15. SPSS——总体均数的估计及假设检验(t 检验)
  16. 成为软件架构师需要什么?
  17. 移动硬盘插服务器上坏了,移动硬盘接口坏了怎么办解决教程
  18. html电子印章,挑战电子印章系统 HTML版
  19. IntellijIdea2018 Liscense server激活码
  20. Python画樱花树

热门文章

  1. 我连鼠标光标都是爱你的形状——MATLAB自定义光标及png转化为光标数组
  2. 2021年系统架构设计师考试大纲
  3. 利用eclipse自定义模板创建日志打印模板
  4. 35万推特僵尸账号发现始末:这只是开始?
  5. 【小飞兔整站下载】整站下载器哪个好用_整站下载工具哪个好
  6. 【Web:Bootstrap框架】简单实现理解
  7. 信创操作系统--统信UOS桌面版(软件管理:应用商店、包管理器)
  8. VAssistX 10.9 和 VS2010破解(win10环境)
  9. Activiti实现流程定义的控制与修改
  10. Ubuntu20.04下搜狗输入法安装配置(超精简)