YUV420P转BMP指南

YUV420P转BMP指南

一、YUV像素点转换为RGB公式

R = Y + 1.403 * (U - 128)

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

B = Y + 1.770 * (V - 128)

同样值得注意的是:网上的公式依然是反过来的(网上对应V的地方是U,U的地方是V)

二、从YUV420P格式到BMP转换算法

在这里插入图片描述

​ 由上图可得,yuv420格式是每四个Y公用一对UV,所以U和V的数量都为Y的四分之一

​ 而:下图则是YUV420在图像文件中的存储形式,首先存储所有的Y像素点,接着是U,最后是V

在这里插入图片描述

转换过程可以理解为首先从yuv文件中取得所有的y,u,v分别放在对应的数组yuv_y,yuv_u , yuv_v中。由于Y像素点的总数与实际像素是对应的,但是需要确定这些Y使用的是哪组的u,v。根据转换公式获得的rgb像素点按顺序存储到bmp文件中即可。

转换图示:

在这里插入图片描述

由于y的个数跟像素点个数相同,所以这里不必管y。

用yuv_y的行下标/2 * (yuv宽 / 2) + yuv_y的列下标/2,即可获得某一个y使用的是那一组的u和v

由于bmp图片是倒序存放,所以算法还应该变为:

用(yuv高 - yuv_y的行下标 - 1)/ 2 * (yuv宽 / 2) + yuv_y的列下标 / 2

三、代码

(1)main函数:注意主函数传参需要传入与bmp相同的宽和高

#include "YUV_TO_BMP.h"

int main(int argc , char **argv)
{
    if(argc < 3)
    {
        printf("请输入图片路径\n");
        return 0;
    }
    fn_Yuv_To_Bmp(argv[1] , atoi(argv[2]) , atoi(argv[3]));
    
    return 0;
}

(2)YUV_TO_BMP.c

#include "YUV_TO_BMP.h"

int fn_Yuv_To_Bmp(const char * yuv_path , int width , int hight)
{
    yuv_width = width;
    yuv_hight = hight;
    //1.打开yuv文件,并获取Y,U,V分别放在不同的数组中
    int ret = fn_Open_Yuv(yuv_path);
    if(ret < 0)
    {
        printf("获取yuv像素点失败\n");
        return -1;
    }
    else
    {
        printf("打开yuv成功\n");
    }

    //2.转yuv为rgb
    ret = fn_Convert_Yuv();
    if(ret < 0)
    {
        printf("申请rgb空间失败\n");
        return -1;
    }
    else
    {
        printf("rgb像素点转换成功\n");
    }

    //3.创建bmp文件,处理文件头,并把rgb放进bmp里面
    ret = fn_Make_Bmp();
    if(ret < 0)
    {
        printf("创建bmp文件失败\n");
        return -1;
    }
    else
    {
        printf("YUV转BMP成功\n");
    }

    return 0;
}

int fn_Make_Bmp()
{
    int bmp_fd = open("./test.bmp" , O_RDWR | O_TRUNC | O_CREAT , 0777);
    if(bmp_fd <= 0)
    {
        perror("");
        return -1;
    }
    //处理bmp数据头
    struct bitmap_header head_info;
    memset(&head_info , 0 , 14);
    head_info.type = BM;
    head_info.size = yuv_hight * yuv_width * 3 + 54;
    head_info.offbits = 54;

    struct bitmap_info bmp_info;
    memset(&bmp_info , 0 , 40);
    bmp_info.size = 40;
    bmp_info.width = yuv_width;
    bmp_info.height = yuv_hight;
    bmp_info.planes = 1;
    bmp_info.bit_count = 24;
    bmp_info.size_img = yuv_hight * yuv_width * 3;
    bmp_info.X_pel = yuv_width;
    bmp_info.Y_pel = yuv_hight;

    //把数据头,像素点写入bmp文件中
    write(bmp_fd , &head_info , 14);
    write(bmp_fd , &bmp_info , 40);
    int ret = write(bmp_fd , rgb_buf , yuv_hight * yuv_width * 3);
    printf("ret = %d\n" , ret);

    return 0;
}

int fn_Convert_Yuv()
{
    rgb_buf = malloc(yuv_width * yuv_hight * 3);
    if(rgb_buf == NULL)
    {
        return -1;
    }

    int i = 0;
    int j = 0;
    for(i = 0 ; i < yuv_hight ; i++)
    {
        for(j = 0 ; j < yuv_width ; j++)
        {
            //r
            rgb_buf[i * yuv_width * 3 + j * 3] = (unsigned char)
                                     ((float)yuv_y[(yuv_hight - i - 1) * yuv_width + j] + 
 1.403 * ((float)yuv_u[(((yuv_hight - i - 1) / 2) * (yuv_width / 2)  + (j / 2))] - 128));
            
            if(rgb_buf[i * yuv_width * 3 + j * 3] < 0)
            {
                rgb_buf[i * yuv_width * 3 + j * 3] = 0;
            }
            else if(rgb_buf[i * yuv_width * 3 + j * 3] > 255)
            {
                rgb_buf[i * yuv_width * 3 + j * 3] = 255;
            }
            
            //g
            rgb_buf[i * yuv_width * 3 + j * 3 + 1] = (unsigned char)
                                     ((float)yuv_y[(yuv_hight - i - 1) * yuv_width + j] - 
0.3455 * ((float)yuv_v[(((yuv_hight - i - 1) / 2) * (yuv_width / 2)  + (j / 2))] - 128) -
0.7169 * ((float)yuv_u[(((yuv_hight - i - 1) / 2) * (yuv_width / 2)  + (j / 2))] - 128));
            
            if(rgb_buf[i * yuv_width * 3 + j * 3 + 1] < 0)
            {
                rgb_buf[i * yuv_width * 3 + j * 3 + 1] = 0;
            }
            else if(rgb_buf[i * yuv_width * 3 + j * 3 + 1] > 255)
            {
                rgb_buf[i * yuv_width * 3 + j * 3 + 1] = 255;
            }

            //b
            rgb_buf[i * yuv_width * 3 + j * 3 + 2] = (unsigned char)
                                     ((float)yuv_y[(yuv_hight - i - 1) * yuv_width + j] + 
 1.779 * ((float)yuv_v[(((yuv_hight - i - 1) / 2) * (yuv_width / 2)  + (j / 2))] - 128));
            
            if(rgb_buf[i * yuv_width * 3 + j * 3 + 2] < 0)
            {
                rgb_buf[i * yuv_width * 3 + j * 3 + 2] = 0;
            }
            else if(rgb_buf[i * yuv_width * 3 + j * 3 + 2] > 255)
            {
                rgb_buf[i * yuv_width * 3 + j * 3 + 2] = 255;
            }

        }
    }

    return 0;
}

int fn_Open_Yuv(const char * yuv_path)
{
    int yuv_fd = open(yuv_path , O_RDWR);
    if(yuv_fd <= 0)
    {
        perror("");
        return -1;
    }

    yuv_y = malloc(yuv_width * yuv_hight);
    yuv_u = malloc(yuv_width / 2 * yuv_hight / 2);
    yuv_v = malloc(yuv_width / 2 * yuv_hight / 2);
    if(yuv_y == NULL || yuv_u == NULL || yuv_v == NULL)
    {
        printf("申请yuv空间失败\n");
        return -1;
    }

    //读取yuv数据
    int ret = 0;
    ret = read(yuv_fd , yuv_y , yuv_width * yuv_hight);
    if(ret != yuv_width * yuv_hight)
    {
        printf("读取y数据失败\n");
        return -1;
    }
    ret = read(yuv_fd , yuv_u , yuv_width / 2 * yuv_hight / 2);
    if(ret != yuv_width / 2 * yuv_hight / 2)
    {
        printf("读取u数据失败\n");
        return -1;
    }
    ret = read(yuv_fd , yuv_v , yuv_width / 2 * yuv_hight / 2);
    if(ret != yuv_width / 2 * yuv_hight / 2)
    {
        printf("读取v数据失败\n");
        return -1;
    }

    close(yuv_fd);

    return 0;
}

(3)YUV_TO_BMP.h(头文件中包含了bmp文件头结构体)

#ifndef YUV_TO_BMP_H
#define YUV_TO_BMP_H

#include <stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#define BM 19778

unsigned char * yuv_y;
unsigned char * yuv_u;
unsigned char * yuv_v;
unsigned char * rgb_buf;
int yuv_width; 
int yuv_hight;

int fn_Yuv_To_Bmp(const char * yuv_path , int width , int hight);
int fn_Open_Yuv(const char * yuv_path);
int fn_Convert_Yuv();
int fn_Make_Bmp();

//bmp头文件信息结构体
struct bitmap_header//文件头 -->14个字节
{
	unsigned short	type; //文件类型,必须为BM
	unsigned int  size; // 位图文件大小
	unsigned short reserved1; //预留位
	unsigned short reserved2; //预留位
	unsigned int offbits; // bmp图像文件头数据偏移量(填54)
}__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;//位图实际使用的颜色表中的颜色数(24位位图 = 0)
	unsigned int clrImportant;//位图显示过程中重要的颜色数(24位位图 = 0)
}__attribute__((packed));

#endif

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值