圖像處理(RGB分離)

圖像處理技術(RGB分離)

在這裏插入圖片描述
最近學習了圖像處理技術,第一個小工程做的事將一張圖片的rgb分離,存爲三張圖片,就像PS中的RGB通道的三張圖片一樣。

我們先準備兩張24位真彩色圖片,一張寬度像素爲4的倍數,一張則不是。

我們來看下它的文件頭和信息頭都儲存了什麼信息:

位圖文件頭BITMAPFILEHEADER
這是一個結構,其定義如下:

typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;

這個結構的長度是固定的,爲14個字節(WORD爲無符號16位整數,DWORD爲無符號32位整數),各個域的說明如下:
bfType
指定文件類型,必須是0x424D,即字符串“BM”,也就是說所有.bmp文件的頭兩個字節都是“BM”。
bfSize
指定文件大小,包括這14個字節。
bfReserved1,bfReserved2
爲保留字,不用考慮
bfOffBits
爲從文件頭到實際的位圖數據的偏移字節數

位圖信息頭BITMAPINFOHEADER
這也是一個結構,其定義如下:

typedef struct tagBITMAPINFOHEADER{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;

這個結構的長度是固定的,爲40個字節(LONG爲32位整數),各個域的說明如下:
biSize
指定這個結構的長度,爲40。
biWidth
指定圖象的寬度,單位是像素。
biHeight
指定圖象的高度,單位是像素。
biPlanes
必須是1,不用考慮。
biBitCount
指定表示顏色時要用到的位數,常用的值爲1(黑白二色圖), 4(16色圖), 8(256色), 24(真彩色圖)(新的.bmp格式支持32位色,這裏就不做討論了)。
biCompression
指定位圖是否壓縮,有效的值爲BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS(都是一些Windows定義好的常量)。要說明的是,Windows位圖可以採用RLE4,和RLE8的壓縮格式,但用的不多。我們今後所討論的只有第一種不壓縮的情況,即biCompression爲BI_RGB的情況。
biSizeImage
指定實際的位圖數據佔用的字節數,其實也可以從以下的公式中計算出來:
biSizeImage=biWidth’ × biHeight
要注意的是:上述公式中的biWidth’必須是4的整倍數(所以不是biWidth,而是biWidth’,表示大於或等於biWidth的,最接近4的整倍數。舉個例子,如果biWidth=240,則biWidth’=240;如果biWidth=241,biWidth’=244)。
如果biCompression爲BI_RGB,則該項可能爲零
biXPelsPerMeter
指定目標設備的水平分辨率,單位是每米的象素個數,關於分辨率的概念。
biYPelsPerMeter
指定目標設備的垂直分辨率,單位同上。
biClrUsed
指定本圖象實際用到的顏色數,如果該值爲零,則用到的顏色數爲2biBitCount。
biClrImportant
指定本圖象中重要的顏色數,如果該值爲零,則認爲所有的顏色都是重要的。

一開始我想將原圖的每個像素點取出來,然後將RGB分量取出來分別賦值,這樣處理對於寬度爲4的倍數的圖片是沒有問題的。但是到了不是4倍數的圖片就出問題了。

後來瞭解到圖片的讀取和儲存都是按字節來進行,而且是必須要是4 的倍數,那我們的解決方案就明確了,把所有字節讀出來後再賦值。

那麼我們怎麼樣讀出所有的字節來呢?

if (bmpWidth % 4 != 0) {
		bmpWidth = (bmpWidth * infoHeader.biBitCount / 8 + 3) / 4 * 4;
	}
	else {
		bmpWidth = bmpWidth * infoHeader.biBitCount / 8;
	}

利用這個公式轉換寬度,再乘以高度就是所有字節的數量了。

下面附上代碼:

#pragma once
#include<iostream>
#include<fstream>
#include<Windows.h>

using namespace std;



bool readBmp(char *bmpName) {
	FILE *fb = fopen(bmpName, "rb");
	FILE* pfoutr = fopen("r.bmp", "wb");
	FILE* pfoutg = fopen("g.bmp", "wb");
	FILE* pfoutb = fopen("b.bmp", "wb");
	if (fb == 0) {
		return 0;
	}
	BITMAPFILEHEADER fileHeader;
	BITMAPINFOHEADER infoHeader;
	int bmpWidth =0;//圖像的寬
	int bmpHeight=0;//圖像的高
	int bmpOffset = 0;

	fread(&fileHeader, sizeof(BITMAPFILEHEADER), 1, fb);
	fread(&infoHeader, sizeof(BITMAPINFOHEADER), 1, fb);

	bmpHeight = infoHeader.biHeight;
	bmpWidth = infoHeader.biWidth;
	bmpOffset = fileHeader.bfOffBits;
	
	if (bmpWidth % 4 != 0) {
		bmpWidth = (bmpWidth * infoHeader.biBitCount / 8 + 3) / 4 * 4;
	}
	else {
		bmpWidth = bmpWidth * infoHeader.biBitCount / 8;
	}
	
	if (infoHeader.biBitCount >= 1) {
		
		int size1 = bmpHeight * bmpWidth;
		BYTE *img = new BYTE[size1];
		BYTE *img1 = new BYTE[size1];
		BYTE *img2 = new BYTE[size1];
		BYTE *img3 = new BYTE[size1];
		
		fseek(fb, bmpOffset, 0);
		fread(img, sizeof(BYTE), size1, fb);

		for (int i = 0;i < bmpHeight ;i++) {
			for (int j = 0;j < bmpWidth;j++) {
					switch (j % 3) {
					case 0:
						img1[i*bmpWidth + j] = 0;
						img2[i*bmpWidth + j] = 0;
						img3[i*bmpWidth + j] = img[i*bmpWidth + j];
						break;
					case 1:
						img1[i*bmpWidth + j] = 0;
						img2[i*bmpWidth + j] = img[i*bmpWidth + j];
						img3[i*bmpWidth + j] = 0;
						break;
					case 2:
						img1[i*bmpWidth + j] = img[i*bmpWidth + j];
						img2[i*bmpWidth + j] = 0;
						img3[i*bmpWidth + j] = 0;
						break;
					}
				
				
			}
		}
		fwrite(&fileHeader, sizeof(fileHeader), 1, pfoutr);
		fwrite(&infoHeader, sizeof(infoHeader), 1, pfoutr);
		fwrite(img1, sizeof(BYTE), size1, pfoutr);
		fwrite(&fileHeader, sizeof(fileHeader), 1, pfoutg);
		fwrite(&infoHeader, sizeof(infoHeader), 1, pfoutg);
		fwrite(img2, sizeof(BYTE), size1, pfoutg);
		fwrite(&fileHeader, sizeof(fileHeader), 1, pfoutb);
		fwrite(&infoHeader, sizeof(infoHeader), 1, pfoutb);
		fwrite(img3, sizeof(BYTE), size1, pfoutb);
	}


	
	fclose(fb);
	fclose(pfoutr);
	fclose(pfoutg);
	fclose(pfoutb);
	return 1;
}

int main() {
	char bmpName[] = "sea.bmp";
	readBmp(bmpName);
	
}


這代碼我就是測試用,寫代碼時要注意規範,提取函數哈!

原圖:
在這裏插入圖片描述

R通道:在這裏插入圖片描述

G通道:
在這裏插入圖片描述

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