天天看點

補充: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

繼續閱讀