【データ圧縮4】BMP2YUV

実験要件

1. Vs で複数の BMP ファイルを自分で生成します。少なくとも 5 つの異なるシーン写真、クラスを含むロゴ、学籍番号の下 4 桁、名前が必要です。基本要件は 24 ビット BMP です。 2. 複数のファイルを書き込みます。最初の
ステップで生成される BMP ファイルを YUV ファイルに変換します コマンド ラインで各画面のフレーム数を設定する必要があります 最終的な YUV ファイルには少なくとも 200 フレームが含まれます 関数の定義、バッファ割り当て、リバースに重点を置きます読み取りと書き込み、および構造化操作。
3. プログラム全体をデバッグし、YUV ファイルを再生して、実験結果を確認します。

実験の基礎

BMP ファイル形式

概要

BMP (ビットマップ): Windows オペレーティング システムの標準画像ファイル形式であるビットマップ形式。画像のピクセル領域データを変換せずに保存できます。通常、デバイス依存ビットマップ (DDB) とデバイス非依存ビットマップ (DIB) の 2 つのカテゴリに分類できます。ビットマップ記憶形式を使用し、ほとんどのアプリケーションでは他の圧縮を使用しないため、他のファイル形式よりも多くのスペースを消費します。BMP ファイルの画像深度は 1 ビット、4 ビット、8 ビット、16 ビット、24 ビットです。

BMP ファイルにデータが保存される場合、画像は左から右、下から上にスキャンされます。(下位ビットが最初)

ファイル構成

成分 意味 バイト
ビットマップファイルヘッダー(BITMAPFILEHEADER) BMP画像ファイルの種類や表示内容などが含まれます。 14
ビットマップ情報ヘッダー (BITMAPINFOHEADER) BMP画像の圧縮方式や色などの情報が含まれます。 40
パレット グレースケールまたはインデックス付きイメージの場合のみ、トゥルーカラー イメージ (24 ビット BMP) にはパレットがありません 4
実データ(ImageData) トゥルー カラー イメージ: ビットマップ データは実際の RGB 値です。グレースケール イメージまたはインデックス付きイメージ: ビットマップ データはパレット内のピクセル カラーのインデックス値です。 4*n

(1) ビットマップファイルのヘッダ構成

BMP画像ファイルの種類や表示内容などの情報が含まれます
ここに画像の説明を挿入

(2) ビットマップ情報ヘッダの構成

BMP画像の幅、高さ、圧縮方法、定義された色などの情報が含まれます。
ここに画像の説明を挿入

(3)パレット

オプション。トゥルー カラー マップ (24 ビット BMP) など、一部のビットマップにはパレットが必要ですが、一部のビットマップにはパレットが必要ありません。
ここに画像の説明を挿入
ここに画像の説明を挿入

(4) 実績データ

BMP ビットマップで使用されるビット数に応じて、RGB は 24 ビット イメージで直接使用されますが、24 ビット未満のその他のものはパレットのカラー インデックス値を使用します。

2 色のビットマップの場合、1 ビットでピクセルの色を表すことができるため (通常、0 は黒、1 は白を意味します)、1 バイトで 8 つのピクセルを表すことができます。16 色の​​ビットマップの場合、4 ビットでピクセルの色を表します。
, したがって、1 バイトで 2 ピクセルを表すことができ、
256 色のビットマップの場合は 1 バイトで 1 ピクセルを表すことができ、
トゥルー カラー イメージの場合は 3 バイトで 1 ピクセルを表すことができます。

RGBファイル形式とYUVファイル形式

RGB画像ストレージ

4:4:4 フォーマット、BGRBGRBGR に保存…

YUV画像ストレージ

4:2:0フォーマット保存の場合、色差信号U、Vのサンプリング点数はYの水平方向、垂直方向の1/2となるため、Y成分のサイズは縦横、横方向のサイズとなります。 U および V コンポーネントは高さ幅* 1 /4です

保存するときは、最初に Y 成分を保存し、次に U 成分と V 成分を保存します。

RGB2YUV

ここに画像の説明を挿入

BMP2YUV実験プロセス

1. プログラムの初期化 (入力ファイルと出力ファイルを開く、変数とバッファーを定義するなど)
2. BMP ファイルを読み取り、RGB データを抽出または生成し、バッファーに書き込みます (カラー ビット深度が異なると処理方法が異なります)
3. RGB2YUV 関数を呼び出しますRGB から YUV データへの変換を実現します。
4. YUV ファイルを書き込みます
。 5. ファイルを閉じ、バッファを解放します。

BMP2YUV 実験コード

rgb2yuv.h

int RGB2YUV (int x_dim, int y_dim, void *bmp, void *y_out, void *u_out, void *v_out, int flip);
void InitLookupTable();

bmp2yuv.h

#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();

main.cpp

#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<40;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;
}

rgb2yuv.cpp:

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

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



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;

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

	// check to see if x_dim and y_dim are divisible by 2
	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()
{
    
    
	int i;

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

bmp2yuv.cpp:

#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);
	}
}

BMP2YUVの実験結果

0T6343307-8

おすすめ

転載: blog.csdn.net/ppinecone/article/details/124624296