BMP转换YUV实验报告

BMP转YUV实验报告

 学号:201510413025                姓名: 宋靳锞                       班级:  15广电工3班

本次实验结果如下:

24位图

BMP转换YUV实验报告_第1张图片

16位图

BMP转换YUV实验报告_第2张图片

8位

BMP转换YUV实验报告_第3张图片

4位:

BMP转换YUV实验报告_第4张图片

1位:

BMP转换YUV实验报告_第5张图片

300*299的图片

右边的黑色就是补得黑框

BMP转换YUV实验报告_第6张图片

一、BMP文件的基础知识

位图由4部分组成

位图文件头

BITMAPFILEHEADER

包含 BMP 图像文件的类型、显示内容等信息

位图信息头

BITMAPINFOHEADER

 BMP 图像的宽、高、压缩方法,以及定义颜色等信息
调色板PALETTE
可选,真彩色图(24位的 BMP)就不需要调色板
位图数据IMAGEDATA
 24 位图中直接使用 RGB,而其他的小于 24 位的使用调色板中颜色索引值
BMP的各个部分都有结构体定义

位图文件头

typedef struct tagBITMAPFILEHEADER { 
WORD bfType; /* 说明文件的类型 */ 
DWORD bfSize; /* 说明文件的大小,用字节为单位 */ 
WORD bfReserved1; /* 保留,设置为 0 */ 
WORD bfReserved2; /* 保留,设置为 0 */ 
DWORD bfOffBits; /* 说明从 BITMAPFILEHEADER 结构开始到实际的图像数 
据之间的字节偏移量 */ 
} BITMAPFILEHEADER; 

信息头文件

typedef struct tagBITMAPINFOHEADER { 
        DWORD    biSize;       /* 说明结构体所需字节数 */
        LONG        biWidth;   /* 以像素为单位说明图像的宽度 */
        LONG        biHeight;  /* 以像素为单位说明图像的高速 */
        WORD       biPlanes;   /* 说明位面数,必须为1 */
        WORD       biBitCount;  /* 说明位数/像素,1、2、4、8、24 */
        DWORD    biCompression;  /* 说明图像是否压缩及压缩类型 				BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS */
        DWORD    biSizeImage;    /*  以字节为单位说明图像大小 ,必须是4         的整数倍*/
        LONG        biXPelsPerMeter;    /*  目标设备的水平分辨率,像素/米 */
        LONG        biYPelsPerMeter;    /*目标设备的垂直分辨率,像素/米 */
        DWORD    biClrUsed;    /* 说明图像实际用到的颜色数,如果为0
                                                       则颜色数为2的biBitCount次方 */
        DWORD    biClrImportant;  /*说明对图像显示有重要影响的颜色         
                                   索引的数目,如果是0,表示都重要。*/
}  BITMAPINFOHEADER;

调色板

typedef struct tagRGBQUAD { 
       BYTE    rgbBlue;           /*指定蓝色分量*/
       BYTE    rgbGreen;        /*指定绿色分量*/
       BYTE    rgbRed;            /*指定红色分量*/
       BYTE    rgbReserved;   /*保留,指定为0*/
}  RGBQUAD;

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

二、主要代码

主函数里面有6个命令行参数,5个不同场景的bmp文件,和输出的yuv文件

        bmpFileName[0] = argv[1];
bmpFileName[1] = argv[2];
bmpFileName[2] = argv[3];
bmpFileName[3] = argv[4];
bmpFileName[4] = argv[5];
yuvFileName = argv[6];

要包含200帧的画面,每个画面循环写40次

for (i = 0; i < 40; i++)
			{
				fwrite(yBuf, 1, frameWidth * frameHeight, yuvFile);
				fwrite(uBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);
				fwrite(vBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);

			}
主函数里面的流程:

1.定义好yuv,bmp的文件指针

2.打开bmp文件的文件头和信息头

3.开辟rgb、y/u/v的动态存储空间

4.调用readrgb,rgb2yuv的函数

5写yuv文件里面

代码如下:

#include 
#include 
#include 
#include "rgb2yuv.h" 
#include 


void ReadRGB(FILE *pbmp, unsigned char *rgbbuf, BITMAPFILEHEADER &file_h, BITMAPINFOHEADER & info_h);
#define u_int8_t	unsigned __int8
#define u_int		unsigned __int32
#define u_int32_t	unsigned __int32

/*
* rgb2yuv
* required arg1 should be the input RAW RGB24 file
* required arg2 should be the output RAW YUV12 file
*/
int main(int argc, char** argv)
{
	/* variables controlable from command line */
	u_int frameWidth = 256;			/* --width= */
	u_int frameHeight = 256;
	bool flip = FALSE;				/* --flip */
	unsigned int i;
	
	/* internal variables */
	char* bmpFileName[5];
	char* yuvFileName = NULL;
	FILE* bmpFile =NULL;
	FILE* yuvFile = NULL;
	u_int8_t* rgbBuf = NULL;
	u_int8_t* yBuf = NULL;
	u_int8_t* uBuf = NULL;
	u_int8_t* vBuf = NULL;
	u_int32_t videoFramesWritten = 0;

	/* begin process command line */
	/* point to the specified file names */
	bmpFileName[0] = argv[1];
	bmpFileName[1] = argv[2];
	bmpFileName[2] = argv[3];
	bmpFileName[3] = argv[4];
	bmpFileName[4] = argv[5];
	yuvFileName = argv[6];
	

	/* open the RAW file */
	yuvFile = fopen(yuvFileName, "wb");
	if (yuvFile == NULL)
	{
		printf("cannot find yuv file\n");
		exit(1);
	}
	else
	{
		printf("The output yuv file is %s\n", yuvFileName);
	}
	/* open the BMP file */
	for (int m = 0; m < 5; m++)
	{
		bmpFile = fopen(bmpFileName[m], "rb");
		if (bmpFile == NULL)
		{
			printf("cannot find bmp file\n");
			exit(1);
		}
		else
		{
			printf("The input bmp file is %s\n", bmpFileName[m]);
		}

		BITMAPINFOHEADER info_header;
		BITMAPFILEHEADER file_header;

		//	read file & info header
		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 %s\n", file_header.bfType);
		}*/
		if (fread(&info_header, sizeof(BITMAPINFOHEADER), 1, bmpFile) != 1)
		{
			printf("read info header error!");
			exit(0);
		}//end read

		if (info_header.biWidth*info_header.biBitCount % 32 == 0)
		{
			frameWidth = info_header.biWidth;
		}
		else
		{
			frameWidth = (info_header.biWidth*info_header.biBitCount +31) / 32 * (32 / info_header.biBitCount);
		}
	
		//规定每一扫描行的字节数必须是 4 的整倍数(32位的整数倍),也就是DWORD 对齐的
		//一行总的数据量(按位来说),是宽度*位深
		//(x/32)得整数,再加上32就是比x大的最近的32的倍数,除以8就是像素的字节数
		if (info_header.biHeight % 2 == 0)
		{
			frameHeight = info_header.biHeight;
		}
		else
		{
			frameHeight = info_header.biHeight + 1;
		}
		//宽(按字节)必须是4的的整数倍,高(按字节)必须是2的整数倍
		//24位.宽*高*3,16位.宽*高*2,8位.宽*高,4位.宽*高/2,2位.宽*高/4,1位.宽*高/8
		//要保证一行四字节扫描,所以宽为4字节的整数倍
		//要保证1位的时候数据为字节的整数倍
		
		
		/* get an input buffer for a frame */
		rgbBuf = (u_int8_t*)malloc(frameWidth*frameHeight*3);

		/* get the output buffers for a frame */
		yBuf = (u_int8_t*)malloc(frameWidth * frameHeight);
		uBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);
		vBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);

		if (rgbBuf == NULL || yBuf == NULL || uBuf == NULL || vBuf == NULL)
		{
			printf("no enought memory\n");
			exit(1);
		}

			ReadRGB(bmpFile,rgbBuf,file_header,info_header);
			if (RGB2YUV(frameWidth, frameHeight, rgbBuf, yBuf, uBuf, vBuf, flip))
			{
				printf("error");
				return 0;
			}

			for (i = 0; i < frameWidth*frameHeight; i++)
			{
				if (yBuf[i] < 16) yBuf[i] = 16;
				if (yBuf[i] > 235) yBuf[i] = 235;
			}

			for (i = 0; i < frameWidth*frameHeight / 4; i++)
			{
				if (uBuf[i] < 16) uBuf[i] = 16;
				if (uBuf[i] > 240) uBuf[i] = 240;

				if (vBuf[i] < 16) vBuf[i] = 16;
				if (vBuf[i] > 240) vBuf[i] = 240;
			}
			for (i = 0; i < 40; i++)
			{
				fwrite(yBuf, 1, frameWidth * frameHeight, yuvFile);
				fwrite(uBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);
				fwrite(vBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);

			}
			printf("\r...%d", ++videoFramesWritten);
	}

		printf("\n%u %ux%u video frames written\n",
			videoFramesWritten, frameWidth, frameHeight);



	/* cleanup */

	fclose(bmpFile);
	fclose(yuvFile);
	free(rgbBuf);
	free(yBuf);
	free(uBuf);
	free(vBuf);
	return(0);
}

READRGB里面的流程:

1.先把图片的数据信息存到开辟的动态空间里面

2.再根据不同的位深,把data里面的数据写到rgbbu里面

程序如下:

#include "stdlib.h"
#include "rgb2yuv.h"
#include 
#include 
#include 
#include 
//调色板
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, info_h.biBitCount))
	{
		fseek(pFile, sizeof(BITMAPFILEHEADER) + info_h.biSize, 0);
		fread(pRGB_out, sizeof(RGBQUAD), (unsigned int)pow(2, info_h.biBitCount), pFile);
		return true;
	}
	else
		return false;
}
//读取RGB文件
void ReadRGB(FILE *pbmp, unsigned char *rgbbuf, BITMAPFILEHEADER &file_h, BITMAPINFOHEADER & info_h)
{
	unsigned long h, w, pix_h, pix_w;
	if (info_h.biWidth *info_h.biBitCount% 32 == 0)
	{
		pix_w = info_h.biWidth;
	}
	else
	{
		pix_w = (info_h.biWidth*info_h.biBitCount+31 ) / 32 * (32 / info_h.biBitCount);
	}
	//规定每一扫描行的字节数必须是 4 的整倍数(32位的整数倍),也就是DWORD 对齐的
	//一行总的数据量(按位来说),是宽度*位深
	//(x/32)得整数,再加上32就是比x大的最近的32的倍数,除以8就是像素的字节数
	if (info_h.biHeight % 2 == 0)
	{
		pix_h = info_h.biHeight;
	}
	else
	{
		pix_h = info_h.biHeight + 1;
	}
	//宽(按字节)必须是4的的整数倍,高(按字节)必须是2的整数倍
	//24位.宽*高*3,16位.宽*高*2,8位.宽*高,4位.宽*高/2,2位.宽*高/4,1位.宽*高/8
	//要保证一行四字节扫描,所以宽为4字节的整数倍
	//要保证1位的时候数据为字节的整数倍
	w = pix_w * info_h.biBitCount/8;
	h = pix_h;//为了方便定义的宽高
	unsigned char *data,*be_data;
	//be_data = (unsigned char*)malloc(h*w);//buffer大小应该与bmp中有效数据大小相同
	data=(unsigned char*)malloc(w*h);//开辟一个缓冲区,把bmp的图像数据存在缓冲区里面
	fseek(pbmp,file_h.bfOffBits,0);//指针指向图像的数据处
	fread(data,w*h,1,pbmp);//把图像数据存在缓冲区里面
	RGBQUAD *pRGB = (RGBQUAD *)malloc(sizeof(RGBQUAD)*(unsigned int)pow(2, info_h.biBitCount));
	if (info_h.biBitCount == 24)
	{
		//24位的话就把数据copy到rgbbuf里面
		//memcpy是c和c++使用的内存拷贝函数,
		//void *memcpy(void *dest, const void *src, size_t n);
		memcpy(rgbbuf, data, w*h);
	}
	else if (info_h.biBitCount == 16)
	{
		if (file_h.bfOffBits == 70)
		{
			for (unsigned long Loop = 0; Loop < h * w; Loop += 2)
			{
				*rgbbuf = (data[Loop] & 0x1F) << 3;
				//B:用0001 1111取出低字节的右五位,再放到目标字节的高5位(通过右移3位),得到五位的B 
				*(rgbbuf + 1) = ((data[Loop] & 0xE0) >> 3) + ((data[Loop + 1] & 0x07) << 5);
				// G:11100000取出低字节的左三位,00000011取出高字节的右三位,合并后,放到再放到目标字节的高5位,得到6位的G
				*(rgbbuf + 2) = (data[Loop + 1] & 0xF8);
				///R:1111 1000取出高字节的中间五位,再放到目标字节的高5位,得到5位的R  
				rgbbuf += 3;
			}
		}
		else
		{
			for (unsigned long Loop = 0; Loop < h * w; Loop += 2)
			{
				*rgbbuf = (data[Loop] & 0x1F) << 3;
				*(rgbbuf + 1) = ((data[Loop] & 0xE0) >> 2) + ((data[Loop + 1] & 0x03) << 6);
				*(rgbbuf + 2) = (data[Loop + 1] & 0x7C) << 1;
				rgbbuf += 3;
			}

		}
	}
	//判断若为其他位深数,要通过调色板来解决
	else
	{
		if (!MakePalette(pbmp, file_h, info_h, pRGB))
			printf("Nopalette!");
		unsigned char mask = 0;
		for (unsigned long Loop = 0; Loop < w*h; Loop++)
		{
			switch (info_h.biBitCount)
			{
			case 1: mask = 0x80; break;
			case 2: mask = 0xC0; break;
			case 4: mask = 0xF0; break;
			case 8: mask = 0xFF; break;
			default:printf("biBitCount error!"); break;
			}
			int shiftCnt = 1;
			while (mask)//一个字节里面的解析
			{
				unsigned char index = mask == 0xFF ? data[Loop] : ((data[Loop] & mask) >> (8 - shiftCnt * info_h.biBitCount));
				*rgbbuf = pRGB[index].rgbBlue;
				*(rgbbuf + 1) = pRGB[index].rgbGreen;
				*(rgbbuf + 2) = pRGB[index].rgbRed;
				if (info_h.biBitCount == 8)	mask = 0;
				else 	mask >>= info_h.biBitCount;
				rgbbuf += 3;
				shiftCnt++;
			}
		}
	}
	if (data)
		free(data);
	if (pRGB)
		free(pRGB);

}
三、本次实验的注意点
1.8位图的时候
RGBQUAD *pRGB = (RGBQUAD *)malloc(sizeof(RGBQUAD)*(unsigned char)pow(2, info_h.biBitCount));
错误如下:
BMP转换YUV实验报告_第7张图片
删除的地方,是不对的,调色板在1~8位的时候调用,8位时2^8=256,但是unsigned char最大是255,改成unsigned int型的最大为2^32,就不会溢出了

2.注意点

在原来写的rgb2yuv里面有这项宽高为偶数的规定如下

//if ((x_dim % 2) || (y_dim % 2)) return 1;
所以这次有高必须为2的整数倍的要求,否则当高为奇数时,会返回error
if (RGB2YUV(frameWidth, frameHeight, rgbBuf, yBuf, uBuf, vBuf, flip))
			{
				printf("error");
				return 0;
			}
当我们把rgb2yuv里面的要求注释掉时,就不会产生这样的错误了,也是可以成功实现的,如下就是一个300*299的图,是可以运行出来的。
BMP转换YUV实验报告_第8张图片



你可能感兴趣的:(BMP转换YUV实验报告)