1. 程式人生 > >【數字影象】C++8位和24位BMP點陣圖的平滑、銳化、二值化處理,以及24位真彩圖的灰度化

【數字影象】C++8位和24位BMP點陣圖的平滑、銳化、二值化處理,以及24位真彩圖的灰度化

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.彩色點陣圖灰度化


灰度化處理前:                                                                      灰度化處理後:

                              

對比可以看出,灰度化使彩色影象轉換成灰度影象