【數字影象】C++8位和24位BMP點陣圖的平滑、銳化、二值化處理,以及24位真彩圖的灰度化
阿新 • • 發佈:2019-01-07
BMP標頭檔案:
#ifndef BMP_H//前處理器 #define BMP_H typedef unsigned char BYTE; typedef unsigned short WORD; typedef unsigned int DWORD; typedef long LONG; //BMP檔案頭(14位元組) typedef struct tagBITMAPFILEHEADER { //WORD bfType;//點陣圖檔案的型別,必須為BM(在結構體中讀取會發生錯誤,所以在函式中讀取) DWORD bfSize;//點陣圖檔案的大小,以位元組為單位 WORD bfReserved1;//點陣圖檔案保留字,必須為0 WORD bfReserved2;//點陣圖檔案保留字,必須為0 DWORD bfOffBits;//點陣圖資料的起始位置,以相對於點陣圖檔案頭的偏移量表示,以位元組為單位 }BITMAPFILEHEADER; //BMP資訊頭(40位元組) typedef struct tagBITMAPINFOHEADER { DWORD biSize;//本結構所佔用位元組數 LONG biWidth;//點陣圖的寬度,以畫素為單位 LONG biHeight;//點陣圖的高度,以畫素為單位 WORD biPlanes;//目標裝置的級別,必須為1 WORD biBitCount;//每個畫素所需的位數,必須是1(雙色),4(16色),8(256色)16(高彩色)或24(真彩色)之一 DWORD biCompression;//點陣圖壓縮型別,必須是0(不壓縮),1(BI_RLE8壓縮型別)或2(BI_RLE4壓縮型別)之一 DWORD biSizeImage;//點陣圖的大小(其中包含了為了補齊行數是4的倍數而新增的空位元組),以位元組為單位 LONG biXPelsPerMeter;//點陣圖水平解析度,每米畫素數 LONG biYPelsPerMeter;//點陣圖垂直解析度,每米畫素數 DWORD biClrUsed;//點陣圖實際使用的顏色表中的顏色數 DWORD biClrImportant;//點陣圖顯示過程中重要的顏色數 }BITMAPINFOHEADER; //調色盤 //只有8位點陣圖才用調色盤,用畫素值作為索引(0~255),調色盤中RGB值都是一樣的,範圍是0~255 //一個unsigned char的範圍剛好是0~255,所以用BYTE型別儲存8位點陣圖的畫素 typedef struct tagRGBQUAD { BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved;//保留,必須為0 }RGBQUAD; //畫素資訊 //8位BMP圖1個位元組代表一個畫素,所以可以不用結構儲存畫素素組,直接用一個指標即可 typedef struct tagIMAGEDATA { BYTE blue; BYTE green; BYTE red; }IMAGEDATA; #endif
8位BMP標頭檔案:
#ifndef EIGHTBITMAP_H//前處理器 #define EIGHTBITMAP_H #include<iostream> #include"BMP.h" using namespace std; class eightBitMap { private: char imageName[30];//影象名 int width, height;//影象的寬高 BITMAPFILEHEADER bmpHead;//檔案頭 BITMAPINFOHEADER bmpInfo;//資訊頭 BYTE *imagedata = NULL, *newimagedata = NULL;//儲存圖片畫素資訊的二維陣列 RGBQUAD *pallet = new RGBQUAD[256];//調色盤指標 FILE *fpin, *fpout;//檔案指標 //平滑運算元也是通過模板進行處理的,所以可以把平滑處理和銳化處理通過一個函式實現 int Template1[3][3]{ 1,1,1,1,1,1,1,1,1 };//平滑模板 int Template2[3][3]{ 0,-1,0,-1,5,-1,0,-1,0 };//laplace銳化模板,4鄰域(原圖減去輪廓) int Template3[3][3]{ -1,-1,-1,-1,9,-1,-1,-1,-1 };//laplace銳化模板,8鄰域 public: bool readImage();//讀取圖片 bool writeImage();//儲存圖片 bool Operation(int x);//影象平滑和銳化處理 bool Operation(int Template[][3], int coefficient);//影象處理 bool Binarization();//二值化 void showBmpHead(BITMAPFILEHEADER BmpHead);//顯示檔案頭 void showBmpInfo(tagBITMAPINFOHEADER BmpInfo);//顯示資訊頭 }; bool eightBitMap::readImage() { cout << "輸入要讀取的圖片名:"; cin >> imageName; if (!fopen_s(&fpin, imageName, "rb")) { //讀取圖片型別 WORD bfType; fread(&bfType, sizeof(WORD), 1, fpin);//fread()的使用 if (bfType != 0x4d42) { cout << "該圖片不是BMP!" << endl; return false; } //讀取檔案頭和資訊頭 fread(&bmpHead, sizeof(tagBITMAPFILEHEADER), 1, fpin); fread(&bmpInfo, sizeof(tagBITMAPINFOHEADER), 1, fpin); //檢查是否是8位點陣圖 if (bmpInfo.biBitCount != 8) { cout << "該圖片不是8位!" << endl; return false; } //讀取調色盤 fread(pallet, sizeof(RGBQUAD), 256, fpin); //讀取圖片的畫素資訊 width = bmpInfo.biWidth; height = bmpInfo.biHeight; width = (width*sizeof(BYTE) + 3) / 4 * 4;//影象的每一行必須是4的整數倍 imagedata = new BYTE[width*height]; fread(imagedata, sizeof(BYTE)*width, height, fpin); //顯示檔案頭和資訊頭 showBmpHead(bmpHead); showBmpInfo(bmpInfo); //關閉圖片 fclose(fpin); } else { cout << "圖片不存在!" << endl; return false; } return true; } bool eightBitMap::writeImage() { char imageName[30]; cout << "輸入處理後的點陣圖名:"; cin >> imageName; //建立點陣圖檔案 if (fopen_s(&fpout, imageName, "wb")) { cout << "建立檔案失敗!" << endl; return false; } //寫入點陣圖型別 WORD bfBYTE = 0x4d42; fwrite(&bfBYTE, 1, sizeof(WORD), fpout); //寫入檔案頭和資訊頭 fwrite(&bmpHead, 1, sizeof(BITMAPFILEHEADER), fpout); fwrite(&bmpInfo, 1, sizeof(BITMAPINFOHEADER), fpout); //寫入調色盤 fwrite(pallet, sizeof(RGBQUAD), 256, fpout); //寫入影象畫素資料 fwrite(newimagedata, sizeof(BYTE)*width, height, fpout); //關閉圖片 fclose(fpout); //釋放記憶體 delete imagedata; delete newimagedata; delete pallet; return true; } bool eightBitMap::Operation(int x) { if (x == 1) return Operation(Template1, 9); else if (x == 2) return Operation(Template2, 1); else if (x == 3) return Operation(Template3, 1); else { cout << "模板呼叫錯誤!" << endl; return false; } } bool eightBitMap::Operation(int Template[][3], int coefficient) { //分配新畫素素組的空間 newimagedata = new BYTE[width*height]; //進行模板操作 for (int i = 0; i < height; ++i) for (int j = 0; j < width; ++j) { if (i == 0 || j == 0 || i == height - 1 || j == width - 1) *(newimagedata + i*width + j) = *(imagedata + i*width + j); else { int sum = 0; for (int m = i - 1; m < i + 2; ++m) for (int n = j - 1; n < j + 2; ++n) { sum += (*(imagedata + m*width + n))*Template[n - j + 1][m - i + 1] / coefficient; } //8位BMP中一個畫素值,對應調色盤中索引號為該畫素值的項所存放的RGB色彩 //所以畫素值範圍為0~255 //畫素值小於0就取0,大於255就取255 sum = (sum >= 0) ? sum : 0; sum = ((sum + *(imagedata + i*width + j))>255) ? 255 : sum; *(newimagedata + i*width + j) = sum; } } //儲存圖片 if (writeImage()) { cout << "\n處理成功!" << endl; return true; } else { cout << "\n處理失敗!" << endl; return false; } } bool eightBitMap::Binarization() { //分配新畫素素組的空間 newimagedata = new BYTE[width*height]; //二值化操作 for (int i = 0; i < width*height; ++i) if (*(imagedata + i) > 128) *(newimagedata + i) = 255; else *(newimagedata + i) = 0; //儲存圖片 if (writeImage()) { cout << "\n處理成功!" << endl; return true; } else { cout << "\n處理失敗!" << endl; return false; } } void eightBitMap::showBmpHead(BITMAPFILEHEADER BmpHead) { cout << "\n圖片檔案頭:" << endl; cout << " 圖片大小:" << BmpHead.bfSize << endl; cout << " 保留字_1:" << BmpHead.bfReserved1 << endl; cout << " 保留字_2:" << BmpHead.bfReserved2 << endl; cout << " 實際點陣圖片資料的偏移位元組數:" << BmpHead.bfOffBits << endl; } void eightBitMap::showBmpInfo(tagBITMAPINFOHEADER BmpInfo) { cout << "圖片資訊頭:" << endl; cout << " 結構體的長度:" << BmpInfo.biSize << endl; cout << " 點陣圖寬:" << BmpInfo.biWidth << endl; cout << " 點陣圖高:" << BmpInfo.biHeight << endl; cout << " 平面數:" << BmpInfo.biPlanes << endl; cout << " 採用顏色位數:" << BmpInfo.biBitCount << endl; cout << " 壓縮方式:" << BmpInfo.biCompression << endl; cout << " 實際點陣圖資料佔用的位元組數:" << BmpInfo.biSizeImage << endl; cout << " X方向解析度:" << BmpInfo.biXPelsPerMeter << endl; cout << " Y方向解析度:" << BmpInfo.biYPelsPerMeter << endl; cout << " 使用的顏色數:" << BmpInfo.biClrUsed << endl; cout << " 重要顏色數:" << BmpInfo.biClrImportant << endl; } #endif
24位BMP標頭檔案:
#ifndef TWENTYFOURBITMAP_H #define TWENTYDOURBITMAP_H #include <iostream> #include "BMP.h" using namespace std; class twentyfourBitMap { char imageName[30];//影象名 int width, height;//影象的寬高 BITMAPFILEHEADER bmpHead;//檔案頭 BITMAPINFOHEADER bmpInfo;//資訊頭 IMAGEDATA *imagedata = NULL, *newimagedata = NULL;//儲存圖片畫素資訊的二維陣列 FILE *fpin, *fpout;//檔案指標 //平滑運算元也是通過模板進行處理的,所以可以把平滑處理和銳化處理通過一個函式實現 int Template1[3][3]{ 1,1,1,1,1,1,1,1,1 };//平滑模板 int Template2[3][3]{ 0,-1,0,-1,5,-1,0,-1,0 };//laplace銳化模板,4鄰域 int Template3[3][3]{ -1,-1,-1,-1,9,-1,-1,-1,-1 };//laplace銳化模板,8鄰域 public: bool readImage();//讀取圖片 bool writeImage();//儲存圖片 bool Operation(int x);//影象平滑和銳化處理 bool Operation(int Template[][3], int coefficient);//影象處理 bool makeGray();//將彩色圖轉換為灰度圖 bool Binarization();//二值化 void showBmpHead(BITMAPFILEHEADER BmpHead);//顯示檔案頭 void showBmpInfo(tagBITMAPINFOHEADER BmpInfo);//顯示資訊頭 }; bool twentyfourBitMap::readImage() { cout << "輸入要讀取的圖片名:"; cin >> imageName; if (!fopen_s(&fpin, imageName, "rb")) { //讀取圖片型別 WORD bfType; fread(&bfType, sizeof(WORD), 1, fpin);//fread()的使用 if (bfType != 0x4d42) { cout << "該圖片不是BMP!" << endl; return false; } //讀取檔案頭和資訊頭 fread(&bmpHead, sizeof(tagBITMAPFILEHEADER), 1, fpin); fread(&bmpInfo, sizeof(tagBITMAPINFOHEADER), 1, fpin); //檢查是否是24位點陣圖 if (bmpInfo.biBitCount != 24) { cout << "該圖片不是24位!" << endl; return false; } //讀取圖片的畫素資訊 width = bmpInfo.biWidth; height = bmpInfo.biHeight; width = (width * sizeof(BYTE) + 3) / 4 * 4;//影象的每一行必須是4的整數倍 imagedata = new IMAGEDATA[width*height]; fread(imagedata, sizeof(IMAGEDATA)*width, height, fpin); //顯示檔案頭和資訊頭 showBmpHead(bmpHead); showBmpInfo(bmpInfo); //關閉圖片 fclose(fpin); } else { cout << "圖片不存在!" << endl; return false; } return true; } bool twentyfourBitMap::writeImage() { char imageName[30]; cout << "輸入處理後的點陣圖名:"; cin >> imageName; //建立點陣圖檔案 if (fopen_s(&fpout, imageName, "wb")) { cout << "建立檔案失敗!" << endl; return false; } //寫入點陣圖型別 WORD bfBYTE = 0x4d42; fwrite(&bfBYTE, 1, sizeof(WORD), fpout); //寫入檔案頭和資訊頭 fwrite(&bmpHead, 1, sizeof(BITMAPFILEHEADER), fpout); fwrite(&bmpInfo, 1, sizeof(BITMAPINFOHEADER), fpout); //寫入影象畫素資料 fwrite(newimagedata, sizeof(IMAGEDATA)*width, height, fpout); //關閉圖片 fclose(fpout); //釋放記憶體 delete imagedata; delete newimagedata; return true; } bool twentyfourBitMap::Operation(int x) { if (x == 1) return Operation(Template1, 9); else if (x == 2) return Operation(Template2, 1); else if (x == 3) return Operation(Template3, 1); else { cout << "模板呼叫錯誤!" << endl; return false; } } bool twentyfourBitMap::Operation(int Template[][3], int coefficient) { //分配新畫素素組的空間 newimagedata = new IMAGEDATA[width*height]; //進行模板操作 for (int i = 0; i < height; ++i) for (int j = 0; j < width; ++j) { if (i == 0 || j == 0 || i == height - 1 || j == width - 1) *(newimagedata + i*width + j) = *(imagedata + i*width + j); else { int sum = 0; for (int m = i - 1; m < i + 2; ++m) for (int n = j - 1; n < j + 2; ++n) { sum += ((*(imagedata + m*width + n)).blue)*Template[n - j + 1][m - i + 1] / coefficient; } //8位BMP中一個畫素值,對應調色盤中索引號為該畫素值的項所存放的RGB色彩 //所以畫素值範圍為0~255 //畫素值小於0就取0,大於255就取255 sum = (sum >= 0) ? sum : 0; sum = (sum + ((*(imagedata + i*width + j)).blue)>255) ? 255 : sum; //把新RGB值存入新陣列 (*(newimagedata + i*width + j)).blue = sum; (*(newimagedata + i*width + j)).green = sum; (*(newimagedata + i*width + j)).red = sum; } } //儲存圖片 if (writeImage()) { cout << "\n處理成功!" << endl; return true; } else { cout << "\n處理失敗!" << endl; return false; } } bool twentyfourBitMap::makeGray() { //分配新畫素素組的空間 newimagedata = new IMAGEDATA[width*height]; //進行灰度化操作 int sum; for (int i = 0; i < width*height; ++i) { sum = (*(imagedata + i)).blue + (*(imagedata + i)).green + (*(imagedata + i)).red; (*(newimagedata + i)).blue = (*(newimagedata + i)).green = (*(newimagedata + i)).red = sum / 3; } //儲存圖片 if (writeImage()) { cout << "\n處理成功!" << endl; return true; } else { cout << "\n處理失敗!" << endl; return false; } } bool twentyfourBitMap::Binarization() { //分配新畫素素組的空間 newimagedata = new IMAGEDATA[width*height]; //二值化操作 for (int i = 0; i < width*height; ++i) if ((*(imagedata + i)).blue > 128) (*(newimagedata + i)).blue = (*(newimagedata + i)).green = (*(newimagedata + i)).red = 255; else (*(newimagedata + i)).blue = (*(newimagedata + i)).green = (*(newimagedata + i)).red = 0; //儲存圖片 if (writeImage()) { cout << "\n處理成功!" << endl; return true; } else { cout << "\n處理失敗!" << endl; return false; } } void twentyfourBitMap::showBmpHead(BITMAPFILEHEADER BmpHead) { cout << "\n圖片檔案頭:" << endl; cout << " 圖片大小:" << BmpHead.bfSize << endl; cout << " 保留字_1:" << BmpHead.bfReserved1 << endl; cout << " 保留字_2:" << BmpHead.bfReserved2 << endl; cout << " 實際點陣圖片資料的偏移位元組數:" << BmpHead.bfOffBits << endl; } void twentyfourBitMap::showBmpInfo(tagBITMAPINFOHEADER BmpInfo) { cout << "圖片資訊頭:" << endl; cout << " 結構體的長度:" << BmpInfo.biSize << endl; cout << " 點陣圖寬:" << BmpInfo.biWidth << endl; cout << " 點陣圖高:" << BmpInfo.biHeight << endl; cout << " 平面數:" << BmpInfo.biPlanes << endl; cout << " 採用顏色位數:" << BmpInfo.biBitCount << endl; cout << " 壓縮方式:" << BmpInfo.biCompression << endl; cout << " 實際點陣圖資料佔用的位元組數:" << BmpInfo.biSizeImage << endl; cout << " X方向解析度:" << BmpInfo.biXPelsPerMeter << endl; cout << " Y方向解析度:" << BmpInfo.biYPelsPerMeter << endl; cout << " 使用的顏色數:" << BmpInfo.biClrUsed << endl; cout << " 重要顏色數:" << BmpInfo.biClrImportant << endl; } #endif
原始碼:
int main() {
int depth = 0;
cout << "輸入要處理的點陣圖深度:";
cin >> depth;
if (depth == 8) {
eightBitMap Image;
if (Image.readImage()) {
int answer;
cout << "\n1.平滑處理"
<< "\n2.4鄰域銳化"
<< "\n3.8鄰域銳化"
<< "\n4.二值化"
<< "\n選擇處理方式:";
cin >> answer;
switch (answer) {
case 1:
Image.Operation(1);
break;
case 2:
Image.Operation(2);
break;
case 3:
Image.Operation(3);
break;
case 4:
Image.Binarization();
break;
default:
cout << "錯誤選擇!" << endl;
}
}
else cout << "點陣圖讀取錯誤!" << endl;
}
else if (depth == 24) {
twentyfourBitMap Image;
if (Image.readImage()) {
int answer;
cout << "\n1.平滑處理"
<< "\n2.4鄰域銳化"
<< "\n3.8鄰域銳化"
<< "\n4.彩色點陣圖灰度化"
<< "\n5.二值化"
<< "\n選擇處理方式:";
cin >> answer;
switch (answer) {
case 1:
Image.Operation(1);
break;
case 2:
Image.Operation(2);
break;
case 3:
Image.Operation(3);
break;
case 4:
Image.makeGray();
break;
case 5:
Image.Binarization();
break;
default:
cout << "錯誤選擇!" << endl;
}
}
else cout << "點陣圖讀取錯誤!" << endl;
}
else cout << "錯誤深度輸入!" << endl;
cin.get();
cin.get();
}
除錯及測試(由於8位點陣圖的處理和24位點陣圖的處理在結果上差別不大,所以只用24點陣圖做測試)
1.平滑處理
平滑處理前:平滑處理後:
對比可以看出,平滑處理使影象更加模糊。
2.銳化處理(平滑後再銳化效果更好,所以使用平滑後的點陣圖銳化。8鄰域銳化比4鄰域銳化效果好,所以只使用8鄰域銳化)
8鄰域銳化前: 8鄰域銳化後:
對比可以看出,銳化後,影象的邊緣更加明顯
3.二值化
二值化處理前: 二值化處理後:
對比可以看出,二值化使影象只有黑白兩色,沒有灰色的過渡
4.彩色點陣圖灰度化
灰度化處理前: 灰度化處理後:
對比可以看出,灰度化使彩色影象轉換成灰度影象