【数据压缩】bmp2yuv



1、实验原理 

        (1).BMP文件格式回顾            

位图文件头BITMAPFILEHEADER

位图信息头BITMAPINFOHEADER

调色板Palette

实际的位图数据ImageData


         (2)分别是BITMAPFILEHEADER和BITMAPINFOHEADER结构体内的情况

        (3)调色板实际上是一个数组,它所包含的元素与位图所具有的颜色数相同,决定于biClrUsedbiBitCount字段。数组中每个元素的类型是一个RGBQUAD结构。真彩色无调色板部分。

     

        #include<windows.h>文件头中都有包括,可以不用再写,不然就会报错为重复定义。


       (4)bmp位图的图像深度

       一般分为6种,分别是1、2、4、8、16、24,有时还有32位的。2色图像为8字节;16色图像位64字节;256色图像为1024字节。其中,每4字节表示一种颜色,并以B(蓝色)、G(绿色)、R(红色)、alpha(32位位图的透明度值,一般不需要)。对于真彩色图,图象数据就是实际的R、G、B值。对于2色位图,用1位就可以表示该象素的颜色(一般0表示黑,1表示白),所以一个字节可以表示8个象素。对于16色位图,用4位可以表示一个象素的颜色,所以一个字节可以表示2个象素。对于256色位图,一个字节刚好可以表示1个象素。


        (5)bmp写入rgb再写入yuv

        需要注意的是BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序,所以当我们写入rgb的时候,其实不用先倒叙写,可以在rgb写成yuv时加入flip=false,就可以倒叙写入,yuv就是正确数据了。


        (6)yuv文件从一帧变为一个视频

        这里主要是写入yuvFile的时候的写入方式改变,写成ab+可以将(注释里有),然后利用循环,一帧yuv写成有至少200帧的yuv视频。


2、实验过程

    (1)BMP2YUV文件转换流程分析

1. 程序初始化(打开两个文件、定义变量和缓冲区等)

2.  读取BMP文件,抽取或生成RGB数据写入缓冲区

3.  调用RGB2YUV的函数实现RGBYUV数据的转换

4. YUV文件

5.  程序收尾工作(关闭文件,释放缓冲区)


    (2)读取bmp文件

    (3)像素位数在前有提到

    (4)储存方式

     16位2个字节中RGB的保存顺序如左,第一个字节前5位保存红色R,后3位保存绿色G的高3位G1;第二个字节前3位保存绿色G的低3位G2,后5位保存蓝色B。存储时使用小端存储,就变成了这样如右。

     24位RGB各占1字节,因此每次只要顺序读取即可。小端方式保存,保存顺序是B,G,R。读完之后前进3字节读取下一颜色值。

                            


3、实验主要程序分析

1、bmp2rgb2yuv.h

  与之前的没太大差别,就是申明用了的函数,注意可以加入#include<windows.h>,之前我没加报了错

2、main.cpp

 (1)写一个循环,有20帧bmp图写入yuv序列中

for (int k = 1; k < 21; k++)
	{

 (2)因为需要反复打开yuv文件,第二个参数传"ab+"即yuvFileName若不存在,则新建一个;若存在,则在当前文件尾追加
if ((yuvFile = fopen(argv[21], "ab+")) == NULL)
		{
			printf("yuv file failed!");
			exit(0);
		}



(3)读bmp文件
if (fread(&File_header, sizeof(BITMAPFILEHEADER), 1, bmpFile) != 1)
		{
			printf("read file header error!");
			exit(0);
		}
		if (File_header.bfType != 0x4D42)
		{
			printf("Not bmp file!");
			exit(0);
		}
		else
		{
			printf("this is a %c\n", File_header.bfType);//debug
		}
		if (fread(&Info_header, sizeof(BITMAPINFOHEADER), 1, bmpFile) != 1)
		{
			printf("read info header error!");
			exit(0);
		}
		else
		{
		}
		//	end read header


(4)这里就是规定我们的yuv文件的大小
int width ;
		int height ;
		if ((Info_header.biWidth % 4) == 0)
			width = Info_header.biWidth;
		else
			width = (Info_header.biWidth*Info_header.biBitCount + 31) / 32 * 4;
		//加31再除以32后下取整,就保证了计算结果是离这个数最近的而且是比它大的32的倍数,
		//也就保证了是4字节的整数倍。乘以4和行数,得到4字节整数倍的图像大小。 
		if ((Info_header.biHeight % 2) == 0)
			height = Info_header.biHeight;
		else
			height = Info_header.biHeight + 1;



(5)给我们写的rgbBuf、yBuf、uBuf、vBuf开完空间后我们就直接进入函数进行转换
BMP2RGB2YUV( File_header, Info_header, bmpFile,rgbBuf);//调用bmp2rgbyuv.cpp中的bmp2rgb()函数
		//将文件名、头文件、信息头文件、储存rgb数据的空间传入函数

		RGB2YUV(width, height, rgbBuf, yBuf, uBuf, vBuf, flip);



(6)转换成yuv之后还有防止溢出的一些if语句了与rgb2yuv那里一致就不展示了。但是这里我们需要将一帧yuv重复多次显示,当然就是写个循环啦。

for (int m = 0; m < 10; m++)
		{
			fwrite(yBuf, 1, width * height, yuvFile);
			fwrite(uBuf, 1, (width * height) / 4, yuvFile);
			fwrite(vBuf, 1, (width * height) / 4, yuvFile);
		}




(7)之后就是close文件们和free指针空间啦。
3、bmp2rgb2yuv.cpp
(1)我们的函数叫做
void BMP2RGB2YUV(BITMAPFILEHEADER & file_h, BITMAPINFOHEADER & info_h, FILE * pFile, unsigned char * rgbDataOut)


(2)我们规定了这些变量
unsigned long Loop, i, j, width, height, w, h;
	unsigned char mask, *Index_Data, *Data;



(3)给变量们赋值
if ((info_h.biWidth % 4) == 0)
		w = info_h.biWidth;
	else
		w = (info_h.biWidth*info_h.biBitCount + 31) / 32 * 4;
	if ((info_h.biHeight % 2) == 0)
		h = info_h.biHeight;
	else
		h = info_h.biHeight + 1;

	width = w / 8 * info_h.biBitCount;
	height = h;

	Index_Data = (unsigned char *)malloc(height*width);
	Data = (unsigned char *)malloc(height*width);


(4)得到了实际存储的字节数后就可以开辟数据缓冲区了,使用文件头的字节偏移属性bfOffBits直接把文件指针定位到像素值数据的起始,开始读取数据,一次读取一个字节。

fseek(pFile, file_h.bfOffBits, 0);
	if (fread(Index_Data, height*width, 1, pFile) != 1)
	{
		printf("read file error!");
		exit(0);
	}

(5)分别根据bmp文件的位数进行不同的操作
switch(info_h.biBitCount)//根据bmp位数来选择操作


case24:
          memcpy(rgbDataOut, Data, height*width);
          free(Data);
          return;


case 16:
		for (Loop = 0; Loop < height * width; Loop += 2)
			//纠错:"loop++"改为"Loop+=2";loop每次循环中应该要逐次增加2,因为loop表示一个字节,而它应该每次16位,即2个字节。
		{
			*rgbDataOut = (Data[Loop] & 0x1F) << 3;
			*(rgbDataOut + 1) = ((Data[Loop] & 0xE0) >> 2) + ((Data[Loop + 1] & 0x03) << 6);
			*(rgbDataOut + 2) = (Data[Loop + 1] & 0x7C) << 1;
			rgbDataOut += 3;
		}


RGBQUAD *pRGB = (RGBQUAD *)malloc(sizeof(RGBQUAD)*(unsigned int)pow(2, (float)info_h.biBitCount));
	//把unsigned char 改为unsigned int
	if (!MakePalette(pFile, file_h, info_h, pRGB))
		printf("No palette!");


mask = 0x80;
	for (Loop = 0; Loop<height*width; Loop++)
	{
		switch (info_h.biBitCount)
		{
		case 1:
			mask = 0x80;
			break;
		case 2:
			mask = 0xC0;
			break;
		case 4:
			mask = 0xF0;
			break;
		case 8:
			mask = 0xFF;
		}

		int shiftCnt = 1;

		while (mask)
		{
			unsigned char index = mask == 0xFF ? Data[Loop] : ((Data[Loop] & mask) >> (8 - shiftCnt * info_h.biBitCount));

			*rgbDataOut = pRGB[index].rgbBlue;
			*(rgbDataOut + 1) = pRGB[index].rgbGreen;
			*(rgbDataOut + 2) = pRGB[index].rgbRed;

			if (info_h.biBitCount == 8)
				mask = 0;
			else
				mask >>= info_h.biBitCount;
			rgbDataOut += 3;
			shiftCnt++;
		}
	}


(6)创建调色板的函数

bool MakePalette(FILE * pFile, BITMAPFILEHEADER &file_h, BITMAPINFOHEADER & info_h, RGBQUAD *pRGB_out)
{
	if ((file_h.bfOffBits - sizeof(BITMAPFILEHEADER) - info_h.biSize) == sizeof(RGBQUAD)*pow(2, (float)info_h.biBitCount))
	{
		fseek(pFile, sizeof(BITMAPFILEHEADER) + info_h.biSize, 0);
		fread(pRGB_out, sizeof(RGBQUAD), (unsigned int)pow(2, (float)info_h.biBitCount), pFile);
		return true;
	}
	else
		return false;
}


4、rgb2yuv.cpp

老师给的文件,这里就略了。

4、实验结果

   (1)先用画图软件另存找到的512*512.jpg文件,分别称为14824位图,然后用ps生成16位图。我找了四张不一样的jpg, 大小都是512*512的,这样方便看yuv。分别给每张图的位图。没有数字的jpg为原图,1~5分别是1、4、8、16、24位图。

   (2)转换成yuv视频截图

       我们随机截图看一下

这是一张一位图

这是两张4位图

这是两张8位图

这是两张16位图

这是一张24位图

5、实验体会

     这次实验主要还是转换文件格式的操作,但是介绍了一种我们不熟悉的文件格式,一开始这种陌生感带来的紧张致使项目里的.cpp文件报错无数,主要还是文件头的意义没有理解。其实到现在还有一点疑惑,当我输入的是720*576的bmp文件时,转换出来的1位图转为yuv时会出错,而其他文件没有问题。而且这次实验需要我们把yuv写成视频,要有至少200帧,所以还要在原来的程序基础上加一个循环,写入一个yuv文件里,这也是一个新点。






评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值