【数据压缩】实验二 BMP2YUV

一.实验原理

1.BMP文件

BMP(全称Bitmap)是Windows操作系统中的标准图像文件格式, BMP文件的图像深度可选lbit、4bit、8bit、16bit及24bit。BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。由于BMP文件格式是Windows环境中交换与图有关的数据的一种标准,因此在Windows环境中运行的图形图像软件都支持BMP图像格式。

2.BMP文件的构成

 典型的BMP图像文件由四部分组成:


(1)位图头文件数据结构,它包含BMP图像文件的类型、显示内容等信息;

(2)位图信息数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息;

(3)调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板;

(4)位图数据,这部分的内容根据BMP位图使用的位数不同而不同,在24位图中直接使用RGB,而其他的小于24位的使用调色板中颜色索引值。

3.二进制编辑器打开BMP文件

前两个字节42 4D表示图像类型为BMP ,文件头FILEHEADER为14字节依次包含文件大小,偏移量等信息,信息头INFOHEADER为40字节,依次包含宽度,高度,深度,图像大小等信息。之后便根据深度的不同,存储的数据发生不同。

(1)24bit

由于24bitBMP图像是真彩色,故无调色板。INFOHEADER后直接开始存实际的位图数据。

(2)8bit


由于8bitBMP图像非真彩色,故需要调色板。

调色板所占空间字节数=sizeof(RGBQUAD)∗2^BitCount (如8bitBMP调色板所占空间字节数=4*2^8=2^10=1024)

所以INFOHEADER后的1024个字节属于改8bit BMP文件的调色板部分。

 

二.BMP2YUV文件转换流程分析

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

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



3.调用RGB2YUV的函数实现RGB到YUV数据的转换

4.写YUV文件

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

 

三.代码分析

1.关键代码分析:

(1)像素为16位的时候,位与移位取像素数据转换为8bit/彩色分量,写RGB缓冲区,用图解的方法更加直观。


代码实现:

 if (info_h.biBitCount == 16)
	{
		for (Loop = 0; Loop < height * width ; Loop+=2)//loop每次循环中应该要逐次增加2个字节(16位)。
		{
			*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;
		}
	}


(2) 图像的扫描方式是按从左到右、从下到上的顺序。所以用flip来控制上下翻转,flip=1时正序,为0时上下翻转。

代码实现:

if (!flip) {
		for (j = 0; j < y_dim; j++)
		{
			y = y_buffer + (y_dim - j - 1) * x_dim;
			u = u_buffer + (y_dim - j - 1) * x_dim;
			v = v_buffer + (y_dim - j - 1) * x_dim;

			for (i = 0; i < x_dim; i++) {
				g = b + 1;
				r = b + 2;
				*y = (unsigned char)(RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
				*u = (unsigned char)(-RGBYUV01684[*r] - RGBYUV03316[*g] + (*b) / 2 + 128);
				*v = (unsigned char)((*r) / 2 - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);
				b += 3;
				y++;
				u++;
				v++;
			}
		}
	}


(3)biSizeImage指的是实际图像数据的大小,以字节为单位。规定每一行扫描的字节数必须是4字节的整数倍,与DWORD对齐。因此在计算中宽必须是4的整数倍,如果不是整数倍,则取大于宽的离4的整数倍最近的数值,这样每行的像素可以整数次读取完成。

代码实现:

if (((info_h.biWidth*info_h.biBitCount)%4) == 0)
		width = info_h.biWidth/ 8 * info_h.biBitCount;
	else
		width = (info_h.biWidth*info_h.biBitCount + 31) / 32 * 4;


 2.其他代码(具体分析已以注释方式呈现)

(1)main.cpp

#include <stdio.h>
#include <windows.h>
#include "bmp2yuv.h"

void main(int argc, char *argv[])
{
	FILE *bmpFile = NULL, *yuvFile = NULL;
	BITMAPFILEHEADER File_header;
	BITMAPINFOHEADER Info_header;
	char* bmpFileName=NULL;
	char* yuvFileName=NULL;
	unsigned char * rgbBuf = NULL;
	unsigned char * yBuff = NULL;
	unsigned char * uBuff = NULL;
	unsigned char * vBuff = NULL;
	int flip = 0;//flip=1时正序,为0时上下翻转
	int frame_count = 250;//每图50帧,1bit,4bit,8bit,16bit,24bit各一张,共250帧
	int frame_width, frame_height;
	//打开yuv文件
	yuvFileName=argv[6];
	if ((yuvFile = fopen(yuvFileName, "wb")) == NULL)
		{
			printf("yuv file failed!");
			exit(0);
		}
	else
	{
		printf("The output yuv file is %s\n",yuvFileName);
	}

	for (int n = 1; n < 6; n++)
	{
		//	循环打开bmp文件
		if ((bmpFile = fopen(argv[n], "rb")) == NULL)
		{
			printf("bmp file open failed!");
			exit(0);
		}
		else
	    {
			printf("The input bmp file is %s\n",bmpFileName);
	    }
		
		//	读文件头信息
		if (fread(&File_header, sizeof(BITMAPFILEHEADER), 1, bmpFile) != 1)
		{
			printf("read file header error!");
			exit(0);
		}
		//判断是否为bmp文件
		if (File_header.bfType != 0x4D42)
		{
			printf("Not bmp file!");
			exit(0);
		}
		//读信息头信息
		if (fread(&Info_header, sizeof(BITMAPINFOHEADER), 1, bmpFile) != 1)
		{
			printf("read info header error!");
			exit(0);
		}
		
	
		frame_width = Info_header.biWidth;
		frame_height=Info_header.biHeight;
		printf("This is a %d bits image!\n", Info_header.biBitCount);
		printf("\nbmp size: \t%d X %d\n", Info_header.biWidth, Info_header.biHeight);

		//开辟缓冲区
		rgbBuf = (unsigned char *)malloc(frame_height*frame_width * 3);
		memset(rgbBuf, 0, frame_height*frame_width * 3);//初始化函数,用于清0
		yBuff = (unsigned char *)malloc(frame_height*frame_width);
		uBuff = (unsigned char *)malloc((frame_height*frame_width) / 4);
		vBuff = (unsigned char *)malloc((frame_height*frame_width) / 4);
		
	    ReadRGB(File_header, Info_header, bmpFile, rgbBuf);

		if (RGB2YUV(frame_width, frame_height, rgbBuf, yBuff, uBuff, vBuff, flip))
		{
			printf("rgb2yuv error");
			exit(1);
		}
		for (int i = 0; i < frame_width*frame_height; i++)
		{
			if (yBuff[i] < 16) yBuff[i] = 16;
			if (yBuff[i] > 235) yBuff[i] = 235;
		}

		for (int i = 0; i < frame_width*frame_height / 4; i++)
		{
			if (uBuff[i] < 16) uBuff[i] = 16;
			if (uBuff[i] > 240) uBuff[i] = 240;
			if (vBuff[i] < 16) vBuff[i] = 16;
			if (vBuff[i] > 240) vBuff[i] = 240;
		}
		for (int f = 0; f < 50; f++)
		{
			fwrite(yBuff, 1, frame_width * frame_height, yuvFile);
			fwrite(uBuff, 1, (frame_width * frame_height) / 4, yuvFile);
			fwrite(vBuff, 1, (frame_width * frame_height) / 4, yuvFile);
		}
    }


	free(rgbBuf);
	free(yBuff);
	free(uBuff);
	free(vBuff);
	fclose(bmpFile);
	fclose(yuvFile);
	getchar();

}

(2)bmp2yuv.cpp

#include <stdio.h>
#include <windows.h>
#include <math.h>


float RGBYUV02990[256], RGBYUV05870[256], RGBYUV01140[256];
float RGBYUV01684[256], RGBYUV03316[256];
float RGBYUV04187[256], RGBYUV00813[256];

bool MakePalette(FILE * pFile, BITMAPFILEHEADER &file_h, BITMAPINFOHEADER & info_h, RGBQUAD *pRGB_out)
{
	/*如果图像开始位置与信息头结束的位置中间,还有2的info_h.biBitCount次方(颜色数)个RGBUAQ空间,则存在调色板*/
	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;
}

//bmp转rgb
void ReadRGB( BITMAPFILEHEADER & file_h, BITMAPINFOHEADER & info_h, FILE * bmpFile,unsigned char * rgbDataOut)
{
	unsigned long Loop, i, j;
	unsigned char mask=0;
	unsigned char  *Data;
	unsigned long width, height;

	/*计算实际的宽高*/
	//由于需与DWORD对齐,所以每行需满足为4字节的整数倍,若不是则需补成其整数倍
	if (((info_h.biWidth*info_h.biBitCount)%4) == 0)
		width = info_h.biWidth/ 8 * info_h.biBitCount;
	else
		width = (info_h.biWidth*info_h.biBitCount + 31) / 32 * 4;
	//判断高是否为偶数,若不是,补齐
	if ((info_h.biHeight % 2) == 0)
		height = info_h.biHeight;
	else
		height = info_h.biHeight + 1;
	
	Data = (unsigned char *)malloc(height*width);
	//写入数据到Data中
	fseek(bmpFile, file_h.bfOffBits, 0);
	if (fread(Data, height*width, 1, bmpFile) != 1)
	{
		printf("read file error!");
		exit(0);
	}
	
	RGBQUAD *pRGB = (RGBQUAD *)malloc(sizeof(RGBQUAD)*(unsigned int)pow(2,(float)info_h.biBitCount));//把unsigned char 改为unsigned int,否则溢出
												
	if (!MakePalette(bmpFile, file_h, info_h, pRGB))
		printf("No palette!");

	/*判断深度,根据不同深度做出不同操作*/
	//深度为24bit时,不需要调用调色板
	if (info_h.biBitCount == 24)
	{
		memcpy(rgbDataOut, Data, height*width);//将Data中的数据直接拷到rgbDataOut
		free(Data);
		return;
	}
	
	//深度为16bit时,位与移位取像素数据转换为8bit/彩色分量写RGB缓冲区
    if (info_h.biBitCount == 16)
	{
		for (Loop = 0; Loop < height * width ; Loop+=2)//loop每次循环中应该要逐次增加2个字节(16位)。
		{
			*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;
		}
	}
	//深度小于等于8时,构造调色板
	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++;
		}
	}
	free(Data);
	free(pRGB);

}



int RGB2YUV(int x_dim, int y_dim, void *bmp, void *y_out, void *u_out, void *v_out, int flip)
{
	static int init_done = 0;

	long i, j, size;
	unsigned char *r, *g, *b;
	unsigned char *y, *u, *v;
	unsigned char *pu1, *pu2, *pv1, *pv2, *psu, *psv;
	unsigned char *y_buffer, *u_buffer, *v_buffer;
	unsigned char *sub_u_buf, *sub_v_buf;
	void InitLookupTable();

	if (init_done == 0)
	{
		InitLookupTable();
		init_done = 1;
	}


	if ((x_dim % 2) || (y_dim % 2)) return 1;
	size = x_dim * y_dim;

	// allocate memory
	y_buffer = (unsigned char *)y_out;
	sub_u_buf = (unsigned char *)u_out;
	sub_v_buf = (unsigned char *)v_out;
	u_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
	v_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
	if (!(u_buffer && v_buffer))
	{
		if (u_buffer) free(u_buffer);
		if (v_buffer) free(v_buffer);
		return 2;
	}

	b = (unsigned char *)bmp;
	y = y_buffer;
	u = u_buffer;
	v = v_buffer;

	// convert RGB to YUV
	if (!flip) {
		for (j = 0; j < y_dim; j++)
		{
			y = y_buffer + (y_dim - j - 1) * x_dim;
			u = u_buffer + (y_dim - j - 1) * x_dim;
			v = v_buffer + (y_dim - j - 1) * x_dim;

			for (i = 0; i < x_dim; i++) {
				g = b + 1;
				r = b + 2;
				*y = (unsigned char)(RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
				*u = (unsigned char)(-RGBYUV01684[*r] - RGBYUV03316[*g] + (*b) / 2 + 128);
				*v = (unsigned char)((*r) / 2 - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);
				b += 3;
				y++;
				u++;
				v++;
			}
		}
	}
	else {
		for (i = 0; i < size; i++)
		{
			g = b + 1;
			r = b + 2;
			*y = (unsigned char)(RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
			*u = (unsigned char)(-RGBYUV01684[*r] - RGBYUV03316[*g] + (*b) / 2 + 128);
			*v = (unsigned char)((*r) / 2 - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);
			b += 3;
			y++;
			u++;
			v++;
		}
	}

	// subsample UV
	for (j = 0; j < y_dim / 2; j++)
	{
		psu = sub_u_buf + j * x_dim / 2;
		psv = sub_v_buf + j * x_dim / 2;
		pu1 = u_buffer + 2 * j * x_dim;
		pu2 = u_buffer + (2 * j + 1) * x_dim;
		pv1 = v_buffer + 2 * j * x_dim;
		pv2 = v_buffer + (2 * j + 1) * x_dim;
		for (i = 0; i < x_dim / 2; i++)
		{
			*psu = (*pu1 + *(pu1 + 1) + *pu2 + *(pu2 + 1)) / 4;
			*psv = (*pv1 + *(pv1 + 1) + *pv2 + *(pv2 + 1)) / 4;
			psu++;
			psv++;
			pu1 += 2;
			pu2 += 2;
			pv1 += 2;
			pv2 += 2;
		}
	}

	free(u_buffer);
	free(v_buffer);

	return 0;
}


void InitLookupTable()
{
	for (int i = 0; i<256; i++)
	{
		RGBYUV02990[i] = (float)0.2990 * i;
		RGBYUV05870[i] = (float)0.5870 * i;
		RGBYUV01140[i] = (float)0.1140 * i;
		RGBYUV01684[i] = (float)0.1684 * i;
		RGBYUV03316[i] = (float)0.3316 * i;
		RGBYUV04187[i] = (float)0.4187 * i;
		RGBYUV00813[i] = (float)0.0813 * i;
	}
}



四.实验结果

1. 1bit


2. 4bit


3. 8bit


4. 16bit


5. 24bit




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值