最近數字圖像處理課要求用C++處理BMP圖像,我很無語,有大好的matlab不用。。。。ios
可是,利用C++去寫的話確實會對原理和codeing水平有些幫助,因此認真寫了。。編程
實驗環境:windows10+Clion+MinGW64windows
參考資料:https://blog.csdn.net/qq_36752072/article/details/78151770數據結構
本工程所使用的頭文件:this
#include<iostream> #include <cmath> #include <windows.h> #include <stdio.h>
要進行BMP圖像的處理,那咱們首先就要了解BMP圖片的格式,其實主要分爲四個部分:spa
一、位圖文件頭數據(BITMAPFILEHEADER):這個數據結構包含了BMP圖像文件的類型、大小等信息;.net
typedef struct targetBITMAPFILEHEADER{ WORD bfType; //文件類型,對於位圖來講,這一部分爲0x4d42 DWORD bfSize; //文件大小(包含這14字節) WORD bfReserved1; //保留字,不考慮 WORD bfReserved2; //保留字,同上 DWORD bfOffBits; //實際位圖數據的偏移字節數,即前三個部分長度之和 }BITMAPFILEHEADER;
二、位圖信息頭數據(BITMAPINFOHEADER):這個數據結構則是包含了BMP圖像數據的寬、高、壓縮方法、定義顏色、佔用空間等等信息;指針
typedef struct targetBITMAPINFOHEADER{ DWORD biSize; //指定此結構體的長度,爲40 LONG biWidth; //位圖寬 LONG biHeight; //位圖高 WORD biPlanes; //平面數,爲1 WORD biBitCount; //採用顏色位數,能夠是1,2,4,8,16,24,新的能夠是32 DWORD biCompression; //壓縮方式,能夠是0,1,2,其中0表示不壓縮 DWORD biSizeImage; //實際位圖數據佔用的字節數 LONG biXPelsPerMeter; //X方向分辨率 LONG biYPelsPerMeter; //Y方向分辨率 DWORD biClrUsed; //使用的顏色數,若是爲0,則表示默認值(2^顏色位數) DWORD biClrImportant; //重要顏色數,若是爲0,則表示全部顏色都是重要的 }BITMAPINFOHEADER;
三、調色板(RGBQUAD):其中,這一部分的數據結構是可選擇的,有些爲徒須要調色板,有些位圖則不須要(好比24位的真彩圖就不須要);code
//爲何須要調色板呢? //理由是:能夠用調色板對顏色進行映射,從而壓縮儲存空間。 //正常狀況下,24bit的位圖每個像素都有RGB三個通道,一共須要24bit //可是,一幅圖裏可能用不到那麼多顏色,好比256級灰度圖像。 //此時,只須要用8bit,就能夠表示2^8種經過調色板定義的顏色。 typedef struct tagRGBQUAD{ BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved; //不用管設爲0便可 }RGBQUAD;
四、位圖數據:這部分的內容根據BMP位圖使用的位數不一樣而不一樣,在24位真彩圖中,直接使用RGB,而其餘的小於24位的則使用調色板中顏色的索引值。blog
typedef struct tagIMAGEDATA { BYTE blue; BYTE green; BYTE red; }IMAGEDATA;
接下來咱們須要瞭解的是,如何進行圖像旋轉。首先,最重要的是,咱們要知道旋轉以後,整個圖片的大小實際上是改變了的(以圖片中心旋轉的話)。圖片來自《數字圖像處理編程入門》。
能夠明顯地看出,從原來的點到如今的點是一種線性變換,那麼咱們就能夠用矩陣來表示這種運算,而後求逆運算就能夠由(x1,y1)求出原來的座標(x0,y0),這至關於把旋轉後圖上的像素,映射到原圖的像素上,而後把原圖該映射點的數據複製給新圖該點,這樣就完成了圖片的旋轉。
對於沒法映射到原圖的點,咱們能夠把它們的RGB值設爲0,這樣就會顯示成黑色。
可是像素畢竟不是點,旋轉以後會有偏差,,若是旋轉以後的像素點並非很如人意的落在像素點上,而是落在臨近的四個像素點構成的正方形區域內(並且這種狀況應該是很常見的一種),咱們使用雙線性插值法來估計該點像素值。
/** *注意,因爲包含了頭文件<windows.h>,因此文件頭和信息頭還有調色板的定義都用不到 */
void rotateBMP(string path, string resPath, int angle) { //獲取路徑名 const char *picture_name=path.c_str(); const char *res_name=resPath.c_str(); //定義文件指針 FILE *file, *targetFile; //定義文件頭和信息頭 BITMAPFILEHEADER bmpFile, writeBmpFile; BITMAPINFOHEADER bmpInfo, writeBmpInfo; //角度轉弧度 auto thelta=(double)(angle*PI/180); auto cosa=(float)cos((double)thelta); auto sina=(float)sin((double)thelta); file = fopen(picture_name, "rb"); targetFile=fopen(res_name,"wb"); fread(&bmpFile, sizeof(BITMAPFILEHEADER), 1, file); fseek(file, sizeof(BITMAPFILEHEADER), 0);//跳過位圖文件頭結構 fread(&bmpInfo, sizeof(BITMAPINFOHEADER), 1, file); // //fseek(file,sizeof(BITMAPINFOHEADER),0);//跳過信息頭 this->ShowBMPHead(bmpFile); this->ShowBMPInfoHead(bmpInfo); /** step 1 : 圖片處理第一步,首先是完成將文件頭,信息頭等的數據遷移 **/ writeBmpFile = bmpFile; writeBmpInfo = bmpInfo; int width = bmpInfo.biWidth; int height = bmpInfo.biHeight; cout<<width<<","<<height<<endl; //原圖的四個角座標 auto SrcX1=(float)(-0.5*width); auto SrcY1=(float)(0.5*height); auto SrcX2=(float)(0.5*width); auto SrcY2=(float)(0.5*height); auto SrcX3=(float)(-0.5*width); auto SrcY3=(float)(-0.5*height); auto SrcX4=(float)(0.5*width); auto SrcY4=(float)(-0.5*height); //新圖的四個角座標 float DstX1=cosa*SrcX1+sina*SrcY1; float DstY1=-sina*SrcX1+cosa*SrcY1; float DstX2=cosa*SrcX2+sina*SrcY2; float DstY2=-sina*SrcX2+cosa*SrcY2; float DstX3=cosa*SrcX3+sina*SrcY3; float DstY3=-sina*SrcX3+cosa*SrcY3; float DstX4=cosa*SrcX4+sina*SrcY4; float DstY4=-sina*SrcX4+cosa*SrcY4; //計算新圖的寬度和高度 auto newWidth=(int)(max(fabs(DstX4-DstX1),fabs(DstX3-DstX2))+0.5); auto newHeight=(int)(max(fabs(DstY4-DstY1),fabs(DstY3-DstY2))+0.5); writeBmpInfo.biWidth = newWidth; writeBmpInfo.biHeight = newHeight; // 在計算實際佔用的空間的時候咱們須要將寬度爲4byte的倍數 int writewidth = WIDTHBYTES(newWidth * writeBmpInfo.biBitCount); writeBmpInfo.biSizeImage = writewidth * writeBmpInfo.biHeight; writeBmpFile.bfSize = 54 + writeBmpInfo.biSizeImage; //把修改過的文件頭和信息頭寫入目標文件 fwrite(&writeBmpFile, 1, sizeof(BITMAPFILEHEADER), targetFile); fwrite(&writeBmpInfo, 1, sizeof(BITMAPINFOHEADER), targetFile); //申請空間 if(bmpInfo.biBitCount==24)//若是是24位的BMP圖 { int l_width=WIDTHBYTES(width * bmpInfo.biBitCount); BYTE *preData = (BYTE *)malloc(height * l_width); memset(preData, 0, height * l_width); BYTE *aftData = (BYTE *)malloc(newHeight * writewidth); memset(aftData, 0, newHeight * writewidth); //原來的旋轉中心 int rotateX = width / 2; int rotateY = height / 2; //新圖的中心 int write_rotateX = newWidth / 2; int write_rotateY = newHeight / 2; int OriginalImg = l_width * height; int LaterImg = writewidth * newHeight; fread(preData, 1, OriginalImg, file); for (int i = 0; i < newHeight; ++i) { for (int j = 0; j < newWidth; ++j) { int index = i * writewidth + j * 3; // 利用公式計算這個原來的點的地方 double y0 = (j - write_rotateX) * sina + (i - write_rotateY) * cosa + rotateY; double x0 = (j - write_rotateX) * cosa - (i - write_rotateY) * sina + rotateX; if((x0>=0)&&(x0<width)&&(y0>=0)&&(y0<=height)) { /** * 咱們在這裏使用雙線性插值法來完成對應 */ int y0_True = y0; int x0_True = x0; double distance_to_a_X = x0 - x0_True; double distance_to_a_Y = y0 - y0_True; int original_point_A = y0_True * l_width + x0_True * 3; int original_point_B = y0_True * l_width + (x0_True + 1) * 3; int original_point_C = (y0_True + 1) * l_width + x0_True * 3; int original_point_D = (y0_True + 1) * l_width + (x0_True + 1) * 3; if (x0_True == width - 1) { original_point_A = original_point_B; original_point_C = original_point_D; } if (y0_True == height - 1) { original_point_C = original_point_A; original_point_D = original_point_B; } //至關於blue aftData[index] = (1 - distance_to_a_X) * (1 - distance_to_a_Y) * preData[original_point_A] + (1 - distance_to_a_X) * distance_to_a_Y * preData[original_point_B] + distance_to_a_X * (1 - distance_to_a_Y) * preData[original_point_C] + distance_to_a_X * distance_to_a_Y * preData[original_point_D]; //至關於green aftData[index + 1] = (1 - distance_to_a_X) * (1 - distance_to_a_Y) * preData[original_point_A + 1] + (1 - distance_to_a_X) * distance_to_a_Y * preData[original_point_B + 1] + distance_to_a_X * (1 - distance_to_a_Y) * preData[original_point_C + 1] + distance_to_a_X * distance_to_a_Y * preData[original_point_D + 1]; //至關於red aftData[index + 2] = (1 - distance_to_a_X) * (1 - distance_to_a_Y) * preData[original_point_A + 2] + (1 - distance_to_a_X) * distance_to_a_Y * preData[original_point_B + 2] + distance_to_a_X * (1 - distance_to_a_Y) * preData[original_point_C + 2] + distance_to_a_X * distance_to_a_Y * preData[original_point_D + 2]; } } } fwrite(aftData,1,LaterImg,targetFile); fclose(file); fclose(targetFile); delete [] preData; delete [] aftData; } else if(bmpInfo.biBitCount==8)//若是是8位的BMP圖 { RGBQUAD strPla[256];//複製調色板 for (unsigned int nCounti = 0; nCounti < 256; nCounti++) { fread((char *)&(strPla[nCounti].rgbBlue), 1, sizeof(BYTE), file); fread((char *)&(strPla[nCounti].rgbGreen), 1, sizeof(BYTE), file); fread((char *)&(strPla[nCounti].rgbRed), 1, sizeof(BYTE), file); fread((char *)&(strPla[nCounti].rgbReserved), 1, sizeof(BYTE), file); } //寫入調色板 for (int nCounti = 0; nCounti < 256; nCounti++) { fwrite((char *)&(strPla[nCounti].rgbBlue), 1, sizeof(BYTE), targetFile); fwrite((char *)&(strPla[nCounti].rgbGreen), 1, sizeof(BYTE), targetFile); fwrite((char *)&(strPla[nCounti].rgbRed), 1, sizeof(BYTE), targetFile); fwrite((char *)&(strPla[nCounti].rgbReserved), 1, sizeof(BYTE), targetFile); } int l_width=WIDTHBYTES(width * bmpInfo.biBitCount); BYTE *preData = (BYTE *)malloc(height * l_width); memset(preData, 0, height * l_width); BYTE *aftData = (BYTE *)malloc(newHeight * writewidth); memset(aftData, 0, newHeight * writewidth); //讀取原圖像素數據 fread(preData, sizeof(GRAYDATA)*width, height, file); //初始化新圖的像素點 for (int i = 0; i < newHeight; ++i) { for (int j = 0; j < newWidth; ++j) { *(aftData+i*newWidth+j)=0; } } int rotateX = width / 2; int rotateY = height / 2; //新圖的中心 int write_rotateX = newWidth / 2; int write_rotateY = newHeight / 2; int OriginalImg = l_width * height; int LaterImg = writewidth * newHeight; fread(preData, 1, OriginalImg, file); for (int i = 0; i < newHeight; ++i) { for (int j = 0; j < newWidth; ++j) { int index = i * writewidth + j ; // 利用公式計算這個原來的點的地方 double y0 = (j - write_rotateX) * sina + (i - write_rotateY) * cosa + rotateY; double x0 = (j - write_rotateX) * cosa - (i - write_rotateY) * sina + rotateX; if((x0>=0)&&(x0<width)&&(y0>=0)&&(y0<=height)) { /** * 咱們在這裏使用雙線性插值法來完成對應 */ int y0_True = y0; int x0_True = x0; double distance_to_a_X = x0 - x0_True; double distance_to_a_Y = y0 - y0_True; int original_point_A = y0_True * l_width + x0_True ; int original_point_B = y0_True * l_width + (x0_True + 1) ; int original_point_C = (y0_True + 1) * l_width + x0_True ; int original_point_D = (y0_True + 1) * l_width + (x0_True + 1) ; if (x0_True == width - 1) { original_point_A = original_point_B; original_point_C = original_point_D; } if (y0_True == height - 1) { original_point_C = original_point_A; original_point_D = original_point_B; } //至關於blue aftData[index] = (1 - distance_to_a_X) * (1 - distance_to_a_Y) * preData[original_point_A] + (1 - distance_to_a_X) * distance_to_a_Y * preData[original_point_B] + distance_to_a_X * (1 - distance_to_a_Y) * preData[original_point_C] + distance_to_a_X * distance_to_a_Y * preData[original_point_D]; } } } fwrite(aftData,1,LaterImg,targetFile); fclose(file); fclose(targetFile); delete [] preData; delete [] aftData; } else { cout<<"錯誤的輸入!!!!!!!!!!!!!"<<endl; } }