BMP转换为YUV学习小结

BMP转换为YUV(Linux):

1.YUV格式

  • 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分量的字节数
    640x480/4 而U和V所占的字节数相同:

    YUV420P在屏幕像素上的分布表示:
    在这里插入图片描述
    每四个Y使用同一组U和V。共用UV的方式如上图

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

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

  • YUV422示意图
    每两个像素点共用一对UV值,所以,在512x512的图像中,需要:
    512x512 + 512x512/2 + 512x512/2字节来存储。
    512x512 是Y分量的字节数
    512x512/2 而U和V所占的字节数相同
    每两个Y使用同一组U和V。

    两个Y公用的 U = 两个Y对应的U之和/2
    两个Y公用的 V = 两个Y对应的V之和/2

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

我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

  1. 全新的界面设计 ,将会带来全新的写作体验;
  2. 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
  3. 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
  4. 全新的 KaTeX数学公式 语法;
  5. 增加了支持甘特图的mermaid语法[^1] 功能;
  6. 增加了 多屏幕编辑 Markdown文章功能;
  7. 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
  8. 增加了 检查列表 功能。

2.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)

3.RGB转换为YUV420:

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

  • 首先获得全部的RGB像素点(包含处理bmp数据头)
    由于是24位bmp,RGB像素点不需要任何修改,直接读取即可,存放在g_RgbBuf中
/**************************************************************************
 - 函数名称: FnOpenBmp()
 - 功能描述: 1.打开bmp
			 2.处理bmp的头信息,并跳过bmp的头54个字节
 - 输入参数: * BmpPath:输入bmp图像地址
 - 输出参数: 无
 - 返 回 值: 成功返回0
 - 其它说明: 本函数里面有图像读取和图像信息内存分配功能
 - 修改日期    版本号     修改人	     修改内容
 - -----------------------------------------------
 - 
**************************************************************************/
int FnOpenBmp(const char * BmpPath)
{
	int nRet;
    int BmpFd = open(BmpPath , O_RDWR);
    if(BmpFd <= 0)
    {
        perror("");
		printf("错误代码:%d\n",BmpEer);
        return BmpEer;
    }
    
	//开辟信息存放空间
    struct BitmapHeader * HeadInfo = malloc(HeadInfoRAM);
    struct BitmapInfo * BmpInfo    = malloc(BmpInfoRAM );
    if(NULL == HeadInfo  || NULL == BmpInfo )
    {
        printf("错误代码:%d\n",ApplyBmpHeadEer);
        return ApplyBmpHeadEer;
    }
 
    //读取bmp的头信息
    nRet = read(BmpFd , HeadInfo , HeadInfoRAM);
		if(nRet != HeadInfoRAM)
		{	
			printf("错误代码:%d\n",ReadHeadInfoRAMEer);
			return ReadHeadInfoRAMEer;
		}
	
    nRet = read(BmpFd , BmpInfo  , BmpInfoRAM);
		if(nRet != BmpInfoRAM)
		{
			printf("错误代码:%d\n",ReadBmpInfoRAMEer);
			return ReadBmpInfoRAMEer;
		}
	
	//计算有没有满足长宽4倍的条件
	if(0 == BmpInfo->width % 4)
	{
		g_BmpWidth = BmpInfo->width;
	}
	else 
	{
		g_BmpWidth = ((BmpInfo->width * BmpInfo->bit_count+31)/32)*4;
	}
	if(0==BmpInfo->height % 2)
	{
		g_BmpHeight = BmpInfo->height;
	}
	else 
	{
		g_BmpHeight = BmpInfo->height + 1;
	}	
	// g_BmpWidth  = BmpInfo->width  ;
    // g_BmpHeight = BmpInfo->height ;	
    printf("bmp_x = %d bmp_y = %d\n" , g_BmpWidth , g_BmpHeight);

    //读取RGB信息
    g_RgbBuf = malloc(g_BmpWidth * g_BmpHeight * 3);
    if(NULL == g_RgbBuf)
    {
        printf("错误代码:%d\n",RgbBufEer);
        return RgbBufEer;
    }
    unsigned int ret =  read(BmpFd , g_RgbBuf , g_BmpWidth * g_BmpHeight * 3);
    // if(ret != bmp_w * bmp_h * 3)
    // {
        // printf("读取bmp_rgb失败\n");
        // return -1;
    // }
	
 
    free (BmpInfo);
    free (HeadInfo);
    close(BmpFd);
    return 0;
}
  • 根据RGB转换所有像素点的YUV
    bmp的存储形式:
    在这里插入图片描述
/**************************************************************************
* 函数名称: FnConvertYuv()
* 功能描述: 将RGB数据进行运算成YUV数据
* 输入参数: 无
* 输出参数: 无
* 返 回 值: 0
* 其它说明: 无
* 修改日期    版本号     修改人	     修改内容
* -----------------------------------------------
* 
**************************************************************************/
int FnConvertYuv()
{
    unsigned char * TmpY = NULL;
    unsigned char * TmpU = NULL;
    unsigned char * TmpV = NULL;
	
	
	g_YuvY     = malloc(g_BmpWidth * g_BmpHeight);
    g_YuvTmpU  = malloc(g_BmpWidth * g_BmpHeight);
    g_YuvTmpV  = malloc(g_BmpWidth * g_BmpHeight);
    //由于yuv_y等参数需要进行地址加,所以这里使用tmp_y等保存最开始的地址
    TmpY = g_YuvY;
    TmpU = g_YuvTmpU;
    TmpV = g_YuvTmpV;

    if(NULL == g_YuvY || NULL == g_YuvTmpU ||  NULL == g_YuvTmpV)
    {
        printf("错误代码:%d\n",ApplyYuv);
        return ApplyYuv;
    }

    for(int i = g_BmpHeight - 1 ; i >= 0 ; i--)
    {
        for(int j = 0 ; j < g_BmpWidth ; j++)
        {
			//转换原理还得研究?
            *g_YuvY = (unsigned char)
                	 ((float)(g_RgbBuf[g_BmpWidth * i * 3 + j * 3]) * 0.3f +       //r
                      (float)(g_RgbBuf[g_BmpWidth * i * 3 + j * 3 +1]) * 0.59f +	 //g
                      (float)(g_RgbBuf[g_BmpWidth * i + j * 3 + 2]) * 0.11f);		 //b


            *g_YuvTmpU = (unsigned char)
                		 ((float)(g_RgbBuf[g_BmpWidth * i * 3 + j * 3]) / 2 -
                          (float)(g_RgbBuf[g_BmpWidth * i * 3 + j * 3 + 1]) * 0.4187f -
                          (float)(g_RgbBuf[g_BmpWidth * i * 3 + j * 3 + 2]) * 0.0813f + 128);


            *g_YuvTmpV = (unsigned char)
                		 (-(float)(g_RgbBuf[g_BmpWidth * i * 3 + j * 3]) * 0.1684f -
                          (float)(g_RgbBuf[g_BmpWidth * i * 3 + j * 3 + 1]) * 0.3316f +
                          (float)(g_RgbBuf[g_BmpWidth * i * 3 + j * 3 + 2]) / 2 + 128);
            g_YuvY++;
            g_YuvTmpV++;
            g_YuvTmpU++;
        }
     }

    g_YuvY    = TmpY;
    g_YuvTmpU = TmpU;
    g_YuvTmpV = TmpV;
    printf("BMP转YUV成功\n");
    return 0;
}
  • 根据YUV格式再使用上面的采样公式对所有的UV进行对应格式的采样
/**************************************************************************
* 函数名称: FnConvertUv()
* 功能描述: 转换成功的YUV数据进行UV采样
* 输入参数: g_Mode: 0-420格式 2-422格式
* 输出参数: 无
* 返 回 值: 0
* 其它说明: 1.420格式:四个Y公用的 U = 四个Y对应的U之和/4,四个Y公用的 V = 四个Y对应的V之和/4
			2.422格式:两个Y公用的 U = 两个Y对应的U之和/2,两个Y公用的 V = 两个Y对应的V之和/2
* 修改日期    版本号     修改人	     修改内容
* -----------------------------------------------
* 
**************************************************************************/
int FnConvertUv()
{
    unsigned char * TmpU = NULL;
    unsigned char * TmpV = NULL;
	
    if( YUV420 == g_Mode )
    {
        g_YuvU = malloc(g_BmpWidth/2 * g_BmpHeight/2);
        g_YuvV = malloc(g_BmpWidth/2 * g_BmpHeight/2);
        TmpU = g_YuvU;
        TmpV = g_YuvV;
        if( NULL == g_YuvU || NULL == g_YuvV)
        {
            printf("错误代码:%d\n",ApplyUv);
            return ApplyUv;
        }
        //对420UV进行采样
        for(int i = 0 ; i < g_BmpHeight ; i+=2)
        {
            for(int j = 0 ; j < g_BmpWidth ; j+=2)
            {
                *g_YuvU = (g_YuvTmpU[i * g_BmpWidth + j] + 
                        g_YuvTmpU[i * g_BmpWidth + j + 1] +
                        g_YuvTmpU[i * g_BmpWidth + j]+
                        g_YuvTmpU[i * g_BmpWidth + j + 1]) / 4;

                *g_YuvV = (g_YuvTmpV[i * g_BmpWidth + j] + 
                        g_YuvTmpV[i * g_BmpWidth + j + 1] +
                        g_YuvTmpV[i * g_BmpWidth + j]+
                        g_YuvTmpV[i * g_BmpWidth + j + 1]) / 4;

                g_YuvU++;
                g_YuvV++;
            }
        }
    }
    if( YUV422 == g_Mode )
    {
        g_YuvU = malloc(g_BmpWidth/2 * g_BmpHeight);
        g_YuvV = malloc(g_BmpWidth/2 * g_BmpHeight);
        TmpU = g_YuvU;
        TmpV = g_YuvV;
        if( NULL == g_YuvU || NULL == g_YuvV)
        {
            printf("错误代码:%d\n",ApplyUv);
            return ApplyUv;
        }
        //对422UV进行采样
        for(int i = 0 ; i < g_BmpHeight ; i+=1)
        {
            for(int j = 0 ; j < g_BmpWidth ; j+=2)
            {
                
                *g_YuvU = (g_YuvTmpU[i * g_BmpWidth + j] + 
                        g_YuvTmpU[i * g_BmpWidth + j + 1]  ) /2;

                *g_YuvV = (g_YuvTmpV[i * g_BmpWidth + j] + 
                        g_YuvTmpV[i * g_BmpWidth + j + 1]  ) /2;

                g_YuvU++;
                g_YuvV++;
            }
        }
    }

    g_YuvU = TmpU;
    g_YuvV = TmpV;
    return 0;
    printf("YUV数据转换完成\n");
}
  • 创建.yuv文件,并且把YUV写进文件中
/**************************************************************************
* 函数名称: FnMakeYuv()
* 功能描述: 生成YUV格式文件,写入yuv数据
* 输入参数: 无
* 输出参数: 无
* 返 回 值: 0
* 其它说明: 无
* 修改日期    版本号     修改人	     修改内容
* -----------------------------------------------
* 
**************************************************************************/
int FnMakeYuv(const char * BmpPath)
{
    int  nRet ;
	char PathBuf[1024] = {0};
 
	
	// printf("bmp_x4 = %d bmp_y4 = %d\n" , bmp_w , bmp_h);
    if( YUV420 == g_Mode )
    {
        int YuvFd = open("./test.yuv" , O_RDWR | O_TRUNC | O_CREAT);
        if(YuvFd <= 0)
        {
            perror("");
            printf("错误代码:%d\n",YUVEer);
            return YUVEer;
        }
        nRet = write(YuvFd , g_YuvY , (g_BmpWidth * g_BmpHeight));
            if(nRet != (g_BmpWidth * g_BmpHeight))
            {
                printf("错误代码:%d\n",WiteYuvYEer);
                return WiteYuvYEer;
            }
        nRet = write(YuvFd , g_YuvU , (g_BmpWidth/2 * g_BmpHeight/2));
            if(nRet != (g_BmpWidth/2 * g_BmpHeight/2))
            {
                printf("错误代码:%d\n",WiteYuvUEer);
                return WiteYuvUEer;
            }
        nRet = write(YuvFd , g_YuvV , (g_BmpWidth/2 * g_BmpHeight/2));
            if(nRet != (g_BmpWidth/2 * g_BmpHeight/2))
            {
                printf("错误代码:%d\n",WiteYuvVEer);
                return WiteYuvVEer;
            }
        close(YuvFd);  //关闭文件 
    }
    if( YUV422 == g_Mode )
    {
        int YuvFd = open("./test.yuv" , O_RDWR | O_TRUNC | O_CREAT);
        if(YuvFd <= 0)
        {
            perror("");
            printf("错误代码:%d\n",YUVEer);
            return YUVEer;
        }
        nRet = write(YuvFd , g_YuvY , (g_BmpWidth * g_BmpHeight));
            if(nRet != (g_BmpWidth * g_BmpHeight))
            {
                printf("错误代码:%d\n",WiteYuvYEer);
                return WiteYuvYEer;
            }
        nRet = write(YuvFd , g_YuvU , (g_BmpWidth/2 * g_BmpHeight));
            if(nRet != (g_BmpWidth/2 * g_BmpHeight))
            {
                printf("错误代码:%d\n",WiteYuvUEer);
                return WiteYuvUEer;
            }
        nRet = write(YuvFd , g_YuvV , (g_BmpWidth/2 * g_BmpHeight));
            if(nRet != (g_BmpWidth/2 * g_BmpHeight))
            {
                printf("错误代码:%d\n",WiteYuvVEer);
                return WiteYuvVEer;
            }
        close(YuvFd);  //关闭文件 
    }

		
	printf("YUV文件生成完成\n");	
	 
    return 0;
}

4.成果展示

以420格式为例:
在这里插入图片描述

5.完整工程:BMPtoYUV

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值