天天看点

补充:BMP序列转YUV

实验目的:

1.理解bmp文件的基本组成;

2.实现bmp序列向yuv文件的转换(基本要求为24bit的bmp文件,进阶要求为支持小于24bit的bmp文件),并用YUV Viewer播放验证。

BMP文件格式:

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

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

位图文件头:

typedef struct tagBITMAPFILEHEADER {WORD bfType;

LONG biXPelsPerMeter;

LONG biYPelsPerMeter;

DWORD biClrImportant; /说明对图像显示有重要影响的颜色索引的数目,如果是 0,表示都重要。/

} BITMAPINFOHEADER;

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

本实验的BMP文件为24位的真彩色图,不需要调色板。

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

R、G、B所在位置,直接把此段数据当做普通RGB文件进行处理即可。

使用windows.h中的BITMAPFILEHEADER 和BITMAPINFOHEADER 即可直接读出位图文件头和位图信息头,并使用其.biWidth和.biHeight直接读出图像的宽和高,并对剩余数据进行普通的RGBtoYUV转换,分别对多个BMP文件进行以上操作并将得到的数据放入同一YUV文件中即可获得YUV视频,通过循环写入的次数控制每幅图的帧长。

main函数

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

int main(char argc,char *argv[])
{
	int height,width;
	BITMAPFILEHEADER File_header;
	BITMAPINFOHEADER Info_header;
	char* bmpfile_name[10];
	char* yuvfile_name=NULL;
	FILE* bmp;
	FILE* yuv;
	unsigned char *rgb_buffer,*y_buffer,*u_buffer,*v_buffer;
	bool flip=true;

	yuvfile_name=argv[11];
	yuv=fopen(yuvfile_name,"wb");
		if(yuv==NULL)
		{
			printf("cannot find yuv file!\n");
			exit(1);
		}
		else
		{
			printf("The input yuv file is %s\n",yuvfile_name);
		}

	/*一共10张bmp图片*/
	/*argv[1]到argv[10]用来表示这10张bmp图片*/
	for(int num=0;num<10;num++)
	{
		bmpfile_name[num]=argv[num+1];
	}
	
	height=atoi(argv[12]);
	width=atoi(argv[13]);

	for(int i=0;i<10;i++)
	{
		bmp=fopen(bmpfile_name[i],"rb");
	
		if(bmp==NULL)
		{
			printf("cannot find bmp file!\n");
			exit(1);
		}
		else
		{
			printf("The input bmp file is %s\n",bmpfile_name[i]);
		}
	
		

		if((fread(&File_header,sizeof(BITMAPFILEHEADER),1,bmp))!=1)
		{
			printf("Read File header error!");
			exit(0);
		}

		/*判断是否为bmp文件*/
		if(File_header.bfType!=0x4D42)
		{
			printf("Not bmp file!");
			exit(0);
		}
		else
		{
			printf("This is bmp file!\n");
		}

		if((fread(&Info_header,sizeof(BITMAPINFOHEADER),1,bmp))!=1)
		{
			printf("Read Info header error!");
			exit(0);
		}

		/*判断行数是否为4的倍数*/
		if(Info_header.biWidth%4==0)
		{
			width=Info_header.biWidth;
		}
		else
		{
			width=(Info_header.biWidth*Info_header.biBitCount+31)/32*(32/8); 
			//每行的像素数*每个像素的位数=每行像素的总位数,必须为32bit的整数倍(4字节)
			//做法:(x+31)/32*(32/8)	
		}
	
		/*判断列数是否为偶数*/
		if(Info_header.biHeight%2==0)
		{
			height=Info_header.biHeight;
		}
		else
		{
			height=Info_header.biHeight+1;
		}

		int size=width*height;
		rgb_buffer=(unsigned char*)malloc(size*3);
		y_buffer=(unsigned char*)malloc(size);
		u_buffer=(unsigned char*)malloc(size/4);
		v_buffer=(unsigned char*)malloc(size/4);

		/*从bmp得到rgb数据*/
		read_bmp_2_get_rgb(bmp,rgb_buffer,File_header,Info_header);
		
		
		/*rgb转yuv*/
		if(RGB2YUV (width, height, rgb_buffer, y_buffer, u_buffer, v_buffer, flip))
		{
			printf("rgb2yuv failed!\n");
		}
		else
		{
			printf("rgb2yuv successfully!\n");
		}

		int j=0;
		for(j=0;j<size;j++)
		{
			if(y_buffer[j]<16) y_buffer[j]=16;
			if(y_buffer[j]>235) y_buffer[j]=235;
		}

		for(j=0;j<size/4;j++)
		{
			if(u_buffer[j]<16) y_buffer[j]=16;
			if(u_buffer[j]>240) y_buffer[j]=240;
		}

		for(j=0;j<size/4;j++)
		{
			if(v_buffer[j]<16) v_buffer[j]=16;
			if(v_buffer[j]>240) v_buffer[j]=240;
		}
		/*每张bmp图片播放30帧*/
		for(int k=0;k<30;k++)
		{
			fwrite(y_buffer,1,size,yuv);
			fwrite(u_buffer,1,size/4,yuv);
			fwrite(v_buffer,1,size/4,yuv);
		}
		free(rgb_buffer);
		free(y_buffer);
		free(u_buffer);
		free(v_buffer);	
	}
	
		fclose(yuv);
	return 0;
}

头文件bmp2yuv.h

```cpp
#include"stdio.h"
#include"windows.h"
void read_bmp_2_get_rgb(FILE* bmp,unsigned char* rgb_out,BITMAPFILEHEADER &file_header,BITMAPINFOHEADER &info_header);
int RGB2YUV (int x_dim, int y_dim, void *bmp, void *y_out, void *u_out, void *v_out, int flip);
void InitLookupTable();



           

read_bmp_2_get_yuv函数

在#include"stdlib.h"
#include"stdio.h"
#include"windows.h"
#include"malloc.h"

void read_bmp_2_get_rgb(FILE* bmp,unsigned char* rgb_out,BITMAPFILEHEADER &file_header,BITMAPINFOHEADER &info_header)
{
	int width,height,w,h;
	unsigned char *inverse_buffer,*buffer;
	int x=0,y=0;

	//行数为4的倍数
	if(info_header.biWidth%4==0)
	{
		w=info_header.biWidth;
	}
	else
	{
		w=(info_header.biWidth*info_header.biBitCount+31)/32*(32/8);
	}

	//列数为偶数
	if(info_header.biHeight%2==0)
	{
		h=info_header.biHeight;
	}
	else
	{
		h=info_header.biHeight+1;
	}

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

	inverse_buffer=(unsigned char*)malloc(height*width);
	buffer=(unsigned char*)malloc(height*width);

	//将bmp指针定位到有效数据的起始处
	fseek(bmp,file_header.bfOffBits,0);
	//int fseek(FILE *stream, long offset, int fromwhere);
	//函数设置文件指针stream的位置。
	//如果执行成功,stream将指向以fromwhere为基准,偏移offset(指针偏移量)个字节的位置,函数返回0。
	fread(inverse_buffer,height*width,1,bmp);

	for(y=0;y<height;y++)
	{
		for(x=0;x<width;x++)
		{
		buffer[y*width+x]=inverse_buffer[width*(height-y-1)+x];
		}
	}

	if(info_header.biBitCount==24)
	{
		memcpy(rgb_out,buffer,height*width);
		free(inverse_buffer);
		free(buffer);
	}
}
这里插入代码片


           

rgb2yuv函数

在#include"stdlib.h"
#include"stdio.h"
#include"windows.h"
#include"malloc.h"

void read_bmp_2_get_rgb(FILE* bmp,unsigned char* rgb_out,BITMAPFILEHEADER &file_header,BITMAPINFOHEADER &info_header)
{
	int width,height,w,h;
	unsigned char *inverse_buffer,*buffer;
	int x=0,y=0;

	//行数为4的倍数
	if(info_header.biWidth%4==0)
	{
		w=info_header.biWidth;
	}
	else
	{
		w=(info_header.biWidth*info_header.biBitCount+31)/32*(32/8);
	}

	//列数为偶数
	if(info_header.biHeight%2==0)
	{
		h=info_header.biHeight;
	}
	else
	{
		h=info_header.biHeight+1;
	}

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

	inverse_buffer=(unsigned char*)malloc(height*width);
	buffer=(unsigned char*)malloc(height*width);

	//将bmp指针定位到有效数据的起始处
	fseek(bmp,file_header.bfOffBits,0);
	//int fseek(FILE *stream, long offset, int fromwhere);
	//函数设置文件指针stream的位置。
	//如果执行成功,stream将指向以fromwhere为基准,偏移offset(指针偏移量)个字节的位置,函数返回0。
	fread(inverse_buffer,height*width,1,bmp);

	for(y=0;y<height;y++)
	{
		for(x=0;x<width;x++)
		{
		buffer[y*width+x]=inverse_buffer[width*(height-y-1)+x];
		}
	}

	if(info_header.biBitCount==24)
	{
		memcpy(rgb_out,buffer,height*width);
		free(inverse_buffer);
		free(buffer);
	}
}
这里插入代码片


           

实验结果

输出文件

用yuv viewer打开:

补充:BMP序列转YUV
补充:BMP序列转YUV
补充:BMP序列转YUV

继续阅读