24位BMP圖像RGB與YUV轉換

關於BMP圖像格式的部分這裏就不講了,網上有很多資料可以查閱,詳細可以看下面的代碼。

在讀取了BMP圖像以後,開始把RGB轉化爲YUV(有關YUV可以查閱wiki)。

本次使用的YUV標準是8-bit BT.601 YUV,也就是YUV 4:4:4。


從RGB轉化爲YUV格式有很多種方法,我採用的是整型法,公式如下:

Y = ( (  66 * R + 129 * G +  25 * B + 128) >> 8) +  16
U = ( ( -38 * R -  74 * G + 112 * B + 128) >> 8) + 128
V = ( ( 112 * R -  94 * G -  18 * B + 128) >> 8) + 128


若設定C = Y - 16,D = U - 128,E = V - 128,則YUV轉化爲RGB的公式如下:

R = clip(( 298 * C + 409 * E + 128) >> 8)
G = clip(( 298 * C - 100 * D - 208 * E + 128) >> 8)
B = clip(( 298 * C + 516 * D + 128) >> 8)


其中clip是截斷函數,因爲我們必須把計算出來的結果控制在0~255範圍內。截斷函數可以簡單的把超過255部分設爲255,低於0的部分設爲0,也可以用線性變換來截斷。這裏我就用簡單的截斷方法了。接下來放一下完整代碼:

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <vector>
using namespace std;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;

//位圖文件頭定義;
//其中不包含文件類型信息(由於結構體的內存結構決定,
//要是加了的話將不能正確讀取文件信息)
typedef struct  tagBITMAPFILEHEADER{
//	WORD bfType;//文件類型,必須是0x424D,即字符“BM”
	DWORD bfSize;//文件大小
	WORD bfReserved1;//保留字
	WORD bfReserved2;//保留字
	DWORD bfOffBits;//從文件頭到實際位圖數據的偏移字節數
}BITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER{
	DWORD biSize;//信息頭大小
	DWORD biWidth;//圖像寬度
	DWORD biHeight;//圖像高度
	WORD biPlanes;//位平面數,必須爲1
	WORD biBitCount;//每像素位數
	DWORD  biCompression; //壓縮類型
	DWORD  biSizeImage; //壓縮圖像大小字節數
	DWORD  biXPelsPerMeter; //水平分辨率
	DWORD  biYPelsPerMeter; //垂直分辨率
	DWORD  biClrUsed; //位圖實際用到的色彩數
	DWORD  biClrImportant; //本位圖中重要的色彩數
}BITMAPINFOHEADER; //位圖信息頭定義

typedef struct tagRGBQUAD{
	BYTE rgbBlue; //該顏色的藍色分量
	BYTE rgbGreen; //該顏色的綠色分量
	BYTE rgbRed; //該顏色的紅色分量
	BYTE rgbReserved; //保留值
}RGBQUAD;//調色板定義

//像素信息
typedef struct tagIMAGEDATA
{
	BYTE blue;
	BYTE green;
	BYTE red;
}DATA;

typedef struct tagYUV{
	BYTE Y;
	BYTE U;
	BYTE V;
}YUV;

//變量定義
BITMAPFILEHEADER strHead;
BITMAPINFOHEADER strInfo;
RGBQUAD strPla[256];//256色調色板

BYTE clip(int n)
{
	if(n>255)
		n=255;
	if(n<0)
		n=0;
	return n;
}
int main(){
	int width,height;//圖片的寬度和高度
	FILE *fpi;
	fpi=fopen("input.bmp","rb");
	if(fpi != NULL){
		//先讀取文件類型
		WORD bfType;
		fread(&bfType,1,sizeof(WORD),fpi);
		if(0x4d42!=bfType) {
			cout<<"It's not a bmp!"<<endl;
			return 0;
		}
		//讀取bmp文件的文件頭和信息頭
		fread(&strHead,1,sizeof(tagBITMAPFILEHEADER),fpi);
		fread(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fpi);
		// for(unsigned int i=0;i<strInfo.biClrUsed;i++)  
  //       { //讀取調色板 
  //       	fread(&strPla[i].rgbBlue,1,sizeof(BYTE),fpi);  
  //       	fread(&strPla[i].rgbGreen,1,sizeof(BYTE),fpi);  
  //       	fread(&strPla[i].rgbRed,1,sizeof(BYTE),fpi);  
  //       	fread(&strPla[i].rgbReserved,1,sizeof(BYTE),fpi);
  //       } 
        fseek(fpi,0,1078);
        int h=strInfo.biHeight,w=strInfo.biWidth,size=strInfo.biSizeImage/3;
        DATA *imgdata=new DATA[size];
		fread(imgdata,1,sizeof(DATA)*size,fpi);//讀取bmp數據信息
		YUV *trans = new YUV[size];
		fclose(fpi);

		//把rgb轉變爲yuv
		for(int i=0;i<size;i++)
		{
			trans[i].Y = ( (  66 * imgdata[i].red + 129 * imgdata[i].green +  25 * imgdata[i].blue + 128) >> 8) +  16;
			trans[i].U = ( ( -38 * imgdata[i].red -  74 * imgdata[i].green + 112 * imgdata[i].blue + 128) >> 8) + 128;
			trans[i].V = ( ( 112 * imgdata[i].red -  94 * imgdata[i].green -  18 * imgdata[i].blue + 128) >> 8) + 128;
		}

		//把彩色轉爲灰色
		DATA *gray = new DATA[size];
		for(int i=0;i<size;i++){
			gray[i].blue=trans[i].Y;
			gray[i].green=trans[i].Y;
			gray[i].red=trans[i].Y;
		}
		FILE *fpo1;
		fpo1=fopen("gray.bmp","wb");
		if(fpo1==NULL)
			cout<<"wrong"<<endl;
		fwrite(&bfType,1,sizeof(WORD),fpo1);
		fwrite(&strHead,1,sizeof(tagBITMAPFILEHEADER),fpo1);
		fwrite(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fpo1);
		for(unsigned int i=0;i<strInfo.biClrUsed;i++)  
		{  
			fwrite(&strPla[i].rgbBlue,1,sizeof(BYTE),fpo1);  
			fwrite(&strPla[i].rgbGreen,1,sizeof(BYTE),fpo1);  
			fwrite(&strPla[i].rgbRed,1,sizeof(BYTE),fpo1);  
			fwrite(&strPla[i].rgbReserved,1,sizeof(BYTE),fpo1);  
		} 
		fwrite(gray,1,sizeof(DATA)*size,fpo1);
		fclose(fpo1);
		delete[] gray;
		//灰度圖寫入完成

		//改變yuv的y值
		for(int i=0;i<size;i++){
			trans[i].Y+=10;
			if(trans[i].Y>255)
				trans[i].Y=255;
		}

		//將yuv轉爲rgb並導出文件“yuv2rgb.bmp”
		FILE *fpo2;
		fpo2=fopen("yuv2rgb.bmp","wb");
		DATA *newrgb = new DATA[size];
		if(fpo2==NULL)
			cout<<"wrong"<<endl;
		fwrite(&bfType,1,sizeof(WORD),fpo2);
		fwrite(&strHead,1,sizeof(tagBITMAPFILEHEADER),fpo2);
		fwrite(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fpo2);
		for(unsigned int i=0;i<strInfo.biClrUsed;i++)  
		{  
			fwrite(&strPla[i].rgbBlue,1,sizeof(BYTE),fpo1);  
			fwrite(&strPla[i].rgbGreen,1,sizeof(BYTE),fpo1);  
			fwrite(&strPla[i].rgbRed,1,sizeof(BYTE),fpo1);  
			fwrite(&strPla[i].rgbReserved,1,sizeof(BYTE),fpo1);  
		} 
		for(int i=0;i<size;i++){
			int C = trans[i].Y - 16;
			int D = trans[i].U - 128;
			int E = trans[i].V - 128;
			newrgb[i].red = clip(( 298 * C           + 409 * E + 128) >> 8);
			newrgb[i].green = clip(( 298 * C - 100 * D - 208 * E + 128) >> 8);
			newrgb[i].blue = clip(( 298 * C + 516 * D           + 128) >> 8);

		}
		fwrite(newrgb,1,sizeof(DATA)*size,fpo2);
		fclose(fpo2);
		delete[] imgdata;
		delete[] trans;
		delete[] newrgb;
	}
	else
	{
		cout<<"Can't open the file!"<<endl;
		return 0;
	}
	return 0;
}

這段代碼可處理無調色板的24位bmp圖像,如果是有調色板的只需要去掉中間的註釋,如果是其它位的bmp需要進行一些細節上的修改。

這段代碼讀入input.bmp圖像,將RGB轉化爲YUV後,把RGB都設爲計算所得的Y值輸出就是灰度圖了,會輸出一個gray.bmp。

然後再把YUV轉化爲RGB,輸出一個yuv2rgb.bmp圖像。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章