關於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圖像。