1. 程式人生 > >影象程式設計學習筆記1——bmp檔案結構處理與顯示

影象程式設計學習筆記1——bmp檔案結構處理與顯示

文字內容轉載自《數字影象處理程式設計入門》,程式碼為自己實現

1.1圖和調色盤的概念

如今Windows(3.x以及95,98,NT)系列已經成為絕大多數使用者使用的作業系統,它比DOS成功的一個重要因素是它視覺化的漂亮介面。那麼Windows是如何顯示圖象的呢?這就要談到點陣圖(bitmap)。

我們知道,普通的顯示器螢幕是由許許多多點構成的,我們稱之為象素。顯示時採用掃描的方法:電子槍每次從左到右掃描一行,為每個象素著色,然後從上到下這樣掃描若干行,就掃過了一屏。為了防止閃爍,每秒要重複上述過程幾十次。例如我們常說的螢幕解析度為640×480,重新整理頻率為70Hz,意思是說每行要掃描640個象素,一共有480行,每秒重複掃描螢幕70次。

我們稱這種顯示器為位映象裝置。所謂位映象,就是指一個二維的象素矩陣,而點陣圖就是採用位映象方法顯示和儲存的圖象。舉個例子,圖1.1是一幅普通的黑白點陣圖,圖1.2是被放大後的圖,圖中每個方格代表了一個象素。我們可以看到:整個骷髏就是由這樣一些黑點和白點組成的。

                                                                    

1.1    骷髏                                                                            圖1.2     放大後的骷髏點陣圖


那麼,彩色圖是怎麼回事呢?

我們先來說說三元色RGB概念。

我們知道,自然界中的所有顏色都可以由紅、綠、藍(R,G,B)組合而成。有的顏色含有紅色成分多一些,如深紅;有的含有紅色成分少一些,如淺紅。針對含有紅色成分的多少,可以分成0到255共256個等級,0級表示不含紅色成分;255級表示含有100%的紅色成分。同樣,綠色和藍色也被分成256級。這種分級概念稱為量化。

這樣,根據紅、綠、藍各種不同的組合我們就能表示出256×256×256,約1600萬種顏色。這麼多顏色對於我們人眼來說已經足夠豐富了。

表1.1     常見顏色的RGB組合值

顏色

R

G

B

255

0

0

0

255

0

0

0

255

255

255

0

255

0

255

0

255

255

255

255

255

0

0

0

128

128

128

你大概已經明白了,當一幅圖中每個象素賦予不同的RGB值時,能呈現出五彩繽紛的顏色了,這樣就形成了彩色圖。的確是這樣的,但實際上的做法還有些差別。

讓我們來看看下面的例子。

有一個長寬各為200個象素,顏色數為16色的彩色圖,每一個象素都用R、G、B三個分量表示。因為每個分量有256個級別,要用8位(bit),即一個位元組(byte)來表示,所以每個象素需要用3個位元組。整個圖象要用200×200×3,約120k位元組,可不是一個小數目呀!如果我們用下面的方法,就能省的多。

因為是一個16色圖,也就是說這幅圖中最多隻有16種顏色,我們可以用一個表:表中的每一行記錄一種顏色的R、G、B值。這樣當我們表示一個象素的顏色時,只需要指出該顏色是在第幾行,即該顏色在表中的索引值。舉個例子,如果表的第0行為255,0,0(紅色),那麼當某個象素為紅色時,只需要標明0即可。

讓我們再來計算一下:16種狀態可以用4位(bit)表示,所以一個象素要用半個位元組。整個圖象要用200×200×0.5,約20k位元組,再加上表佔用的位元組為3×16=48位元組.整個佔用的位元組數約為前面的1/6,省很多吧?

這張R、G、B的表,就是我們常說的調色盤(Palette),另一種叫法是顏色查詢表LUT(Look UpTable),似乎更確切一些。Windows點陣圖中便用到了調色盤技術。其實不光是Windows點陣圖,許多圖象檔案格式如pcx、tif、gif等都用到了。所以很好地掌握調色盤的概念是十分有用的。

有一種圖,它的顏色數高達256×256×256種,也就是說包含我們上述提到的R、G、B顏色表示方法中所有的顏色,這種圖叫做真彩色圖(true color)。真彩色圖並不是說一幅圖包含了所有的顏色,而是說它具有顯示所有顏色的能力,即最多可以包含所有的顏色。表示真彩色圖時,每個象素直接用R、G、B三個分量位元組表示,而不採用調色盤技術。原因很明顯:如果用調色盤,表示一個象素也要用24位,這是因為每種顏色的索引要用24位(因為總共有224種顏色,即調色盤有224行),和直接用R,G,B三個分量表示用的位元組數一樣,不但沒有任何便宜,還要加上一個256×256×256×3個位元組的大調色盤。所以真彩色圖直接用R、G、B三個分量表示,它又叫做24位色圖。

1.2 bmp檔案格式

介紹完點陣圖和調色盤的概念,下面就讓我們來看一看Windows的點陣圖檔案(.bmp檔案)的格式是什麼樣子的。

bmp檔案大體上分成四個部分,如圖1.3所示。

點陣圖檔案頭BITMAPFILEHEADER

點陣圖資訊頭BITMAPINFOHEADER

調色盤Palette

實際的點陣圖資料ImageDate

圖1.3     Windows點陣圖檔案結構示意圖

第一部分為點陣圖檔案頭BITMAPFILEHEADER,是一個結構,其定義如下:

typedefstruct tagBITMAPFILEHEADER {

WORD          bfType;

DWORD bfSize;

WORD          bfReserved1;

WORD          bfReserved2;

DWORDbfOffBits;

}BITMAPFILEHEADER;

這個結構的長度是固定的,為14個位元組(WORD為無符號16位整數,DWORD為無符號32位整數),各個域的說明如下:

bfType

指定檔案型別,必須是0x424D,即字串“BM”,也就是說所有.bmp檔案的頭兩個位元組都是“BM”。

bfSize

指定檔案大小,包括這14個位元組。

bfReserved1,bfReserved2     

為保留字,不用考慮

bfOffBits

為從檔案頭到實際的點陣圖資料的偏移位元組數,即圖1.3中前三個部分的長度之和。

第二部分為點陣圖資訊頭BITMAPINFOHEADER,也是一個結構,其定義如下:

typedefstruct 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

指定目標裝置的水平解析度,單位是每米的象素個數,關於解析度的概念,我們將在第4章詳細介紹。

biYPelsPerMeter

指定目標裝置的垂直解析度,單位同上。

biClrUsed

指定本圖象實際用到的顏色數,如果該值為零,則用到的顏色數為2biBitCount

biClrImportant

指定本圖象中重要的顏色數,如果該值為零,則認為所有的顏色都是重要的。

第三部分為調色盤Palette,當然,這裡是對那些需要調色盤的點陣圖檔案而言的。有些點陣圖,如真彩色圖,前面已經講過,是不需要調色盤的,BITMAPINFOHEADER後直接是點陣圖資料。

調色盤實際上是一個數組,共有biClrUsed個元素(如果該值為零,則有2biBitCount個元素)。陣列中每個元素的型別是一個RGBQUAD結構,佔4個位元組,其定義如下:

typedefstruct tagRGBQUAD {

BYTE   rgbBlue; //該顏色的藍色分量

BYTE   rgbGreen; //該顏色的綠色分量

BYTE   rgbRed; //該顏色的紅色分量

BYTE   rgbReserved; //保留值

} RGBQUAD;

第四部分就是實際的圖象資料了。對於用到調色盤的點陣圖,圖象資料就是該象素顏在調色盤中的索引值。對於真彩色圖,圖象資料就是實際的R、G、B值。下面針對2色、16色、256色點陣圖和真彩色點陣圖分別介紹。

對於2色點陣圖,用1位就可以表示該象素的顏色(一般0表示黑,1表示白),所以一個位元組可以表示8個象素。

對於16色點陣圖,用4位可以表示一個象素的顏色,所以一個位元組可以表示2個象素。

對於256色點陣圖,一個位元組剛好可以表示1個象素。

對於真彩色圖,三個位元組才能表示1個象素,哇,好費空間呀!沒辦法,誰叫你想讓圖的顏色顯得更亮麗呢,有得必有失嘛。

要注意兩點:

(1)    每一行的位元組數必須是4的整倍數,如果不是,則需要補齊。這在前面介紹biSizeImage時已經提到了。

(2)    一般來說,.bMP檔案的資料從下到上,從左到右的。也就是說,從檔案中最先讀到的是圖象最下面一行的左邊第一個象素,然後是左邊第二個象素……接下來是倒數第二行左邊第一個象素,左邊第二個象素……依次類推,最後得到的是最上面一行的最右一個象素。

開發工具:vc++6.0,Win32 控制檯程式

/**
* 程式名: WorkBmp.cpp
* 功  能: 讀取和顯示24位BMP影象,並把影象資料輸入到ImageData.txt中
* 24位bmp可以通過畫圖程式中的另存為的檔案型別中可以選擇
* bmp檔案放到工程目錄下
*/
#include <iostream.h>
#include <stdio.h>
#include <windows.h>
#include <fstream.h>
int biWidth;  //影象寬
int biHeight;  //影象高
int biBitCount; //影象型別,每畫素位數
//RGBQUAD *pColorTable;  //顏色表指標
unsigned char *pBmpBuf;  //儲存影象資料
int lineByte;         //影象資料每行位元組數
/**
* 函式名: readBmp
* 參  數: bmpName -- bmp檔名
* 功  能: 讀入bmp檔案,並獲取相應的資訊
*/
bool readBmp(char *bmpName)
{
	FILE *fp;
	if( (fp = fopen(bmpName,"rb")) == NULL)  //以二進位制的方式開啟檔案
	{
		cout<<"The file "<<bmpName<<"was not opened"<<endl;
		return FALSE;
	}
	if(fseek(fp,sizeof(BITMAPFILEHEADER),SEEK_CUR))  //跳過BITMAPFILEHEADE
	{
		cout<<"跳轉失敗"<<endl;
		return FALSE;
	}
	BITMAPINFOHEADER infoHead;
	fread(&infoHead,sizeof(BITMAPINFOHEADER),1,fp);   //從fp中讀取BITMAPINFOHEADER資訊到infoHead中,同時fp的指標移動
	biWidth = infoHead.biWidth;
	biHeight = infoHead.biHeight;
	biBitCount = infoHead.biBitCount;
	lineByte = (biWidth*biBitCount/8+3)/4*4;   //lineByte必須為4的倍數
	//24位bmp沒有顏色表,所以就直接到了實際的點陣圖資料的起始位置
	pBmpBuf = new unsigned char[lineByte * biHeight];
	fread(pBmpBuf,sizeof(char),lineByte * biHeight,fp);
	fclose(fp);   //關閉檔案
	return TRUE;

}
/**
* 函式名: saveBmp
* 參  數: bmpName -- bmp檔名
* 功  能: 將bmp點陣圖檔案的相關資訊,寫入新建立的檔案中
*/
bool saveBmp(char *bmpName)
{
	FILE *fp;
	if( (fp = fopen(bmpName,"wb") )== NULL)   //以二進位制寫入方式開啟
	{
		cout<<"開啟失敗!"<<endl;
		return FALSE;
	}
	//設定BITMAPFILEHEADER引數
	BITMAPFILEHEADER fileHead;
	fileHead.bfType = 0x4D42;   
	fileHead.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + lineByte * biHeight;
	fileHead.bfReserved1 = 0;
	fileHead.bfReserved2 = 0;
	fileHead.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
	fwrite(&fileHead,sizeof(BITMAPFILEHEADER),1,fp);
	//設定BITMAPINFOHEADER引數
	BITMAPINFOHEADER infoHead;
	infoHead.biSize = 40;
	infoHead.biWidth = biWidth;
	infoHead.biHeight = biHeight;
	infoHead.biPlanes = 1;
	infoHead.biBitCount = biBitCount;
	infoHead.biCompression = BI_RGB;
	infoHead.biSizeImage = lineByte * biHeight;
	infoHead.biXPelsPerMeter = 0;
	infoHead.biYPelsPerMeter = 0;
	infoHead.biClrUsed = 0;
	infoHead.biClrImportant = 0;
	//寫入
	fwrite(&infoHead,sizeof(BITMAPINFOHEADER),1,fp);
	fwrite(pBmpBuf,sizeof(char),lineByte * biHeight,fp);
	fclose(fp);    //關閉檔案
	return TRUE;


}
/**
* 函式名: work
* 功  能: 處理點陣圖資訊,並將點陣圖資料儲存到ImageData檔案中
*/
void work()
{
	char readFileName[] = "nv.BMP";   //定義要讀入的檔名
	if(FALSE == readBmp(readFileName))
		cout<<"readfile error!"<<endl;
	//輸出影象的資訊
	cout<<"Width = "<<biWidth<<" Height = "<<biHeight<<" biBitCount="<<biBitCount<<endl;
	ofstream outfile("ImageData.txt",ios::in | ios::trunc);
	if(!outfile)
	{
		cout<<"open error"<<endl;
		return ;
	}
	int count = 0;
	//影象資料資訊是從左下角按行開始儲存的
	for(int i = 0; i < biHeight; i++ )
	{
		for(int j = 0; j < biWidth; j++ )
		{
			for(int k = 0; k < 3; k++ )
			{
				int temp = *(pBmpBuf + i * lineByte + j + k);
				count++;
				outfile<<temp<<" ";
				if(count % 8 == 0)
				{
					outfile<<endl;
				}
			}
		}
	}
	cout<<"總的畫素數:"<<count / 3<<endl;

	char writeBmpName[] = "nvcpy.BMP";
	saveBmp(writeBmpName);
	delete []pBmpBuf;  //釋放記憶體
}

int main()
{
	work();
	return 0;
}


相關推薦

影象程式設計學習筆記1——bmp檔案結構處理顯示

文字內容轉載自《數字影象處理程式設計入門》,程式碼為自己實現 1.1圖和調色盤的概念 如今Windows(3.x以及95,98,NT)系列已經成為絕大多數使用者使用的作業系統,它比DOS成功的一個重要因素是它視覺化的漂亮介面。那麼Windows是如何顯示圖象的呢?這就要談到

Python學習筆記之一 文字檔案的建立顯示

這個程式主要可以實現建立一個文字檔案 makeTextFile.py #!/usr/bin/env python # -*- coding: utf-8 -*- 'makeTextFile.py--建立一個文字檔案' import os #輸入檔名 while True :

ARM體系結構程式設計學習筆記1

第一章 ARM概述與其基本程式設計模型 一、ARM資料型別 1、字(Word):在ARM體系結構中,字的長度為32位。 2、半字(Half-Word):在ARM體系結構中,半字的長度為16位。 3、位元組(Byte):在ARM體系結構中,位元組的長度為8位。 二、ARM處理器儲存

pandas模塊學習筆記1--數據結構

名稱 pandas taf 不同 函數 標記 數據標簽 命名 port pandas是基於Numpy構建的。 pandas的兩個主要數據結構:Series和DataFrame。 Series和DataFrame用的次數非常多,將其導入本地命名空間會更方便: from pa

馬士兵併發程式設計學習筆記1

(一) public class Demo01 { private int count = 10; private Object object = new Object(); @Test

Java影象介面學習筆記1

前些日子剛學完Java的圖形介面,想寫個小計算器練練手。結果出現了一個問題,就是每次執行時介面都是空的,當放大或者縮小以後介面才會出來。。。查了資料後發現原來是setVisible的位置問題。setVisible(true)要放到控制元件前面的話會先刷新出頁面,結果就是後面的控制元件顯示不出來。因此

cmake學習筆記1--單檔案編譯(霜之小刀)

cmake學習筆記1–單檔案編譯(霜之小刀) 歡迎轉載和引用,若有問題請聯絡 若有疑問,請聯絡 Email : [email protected] QQ:2279557541 本文參考了http://blog.csdn.net/piao

Panda學習筆記1——配置檔案

web.xml 配置contextConfigLocation 在web.xml中定義contextConfigLocation引數,Spring會使用這個引數去載入所有逗號分隔的xml檔案,如果沒有這個引數,Spring預設載入web-inf/applicat

python學習筆記1合併檔案

初始兩個txt檔案: 程式碼如下:   #開啟檔案 ftele1=open(r'TeleAddressBook.txt','rb')#出現中文,避免亂碼使用‘rb’模式 ftele2=open(r'EmailAddressBook.txt','rb') ftele1

jvm學習筆記(三)類檔案結構、java方法數65535上限的原因

瞭解class類檔案結構,對於學習smali也是有必要的!一、class類檔案的結構(8位位元組,一個位元組佔8位,以位元組為基礎單位的二進位制流)儲存結構:class檔案是一組以位元組(8位)為基礎單位的二進位制流,各資料嚴格按照順序緊湊排列在class檔案中,中間沒有任何

Python語言程式設計-學習筆記1:簡介及目錄

最近在慕課網上報了一門課,目的是入門Python;課程內容比較緊湊,開了許久了,最近才學習; 作為一門時下火熱的程式語言,僅為興趣,學習學習! 課程簡介: Python語言程式設計 北京理工大學 Python(3.x版本) 嵩天、黃天羽、禮欣 輔助學習網址: 課

Java虛擬機器學習筆記(5)——類檔案結構

          上一篇介紹了JVM物件的記憶體分配和回收策略。這篇接著介紹Java的類檔案結構,這篇的內容可能會比較多,我儘量循序漸進的講。要學習class的檔案結構,先要大體對class檔案結構有哪些內容有一個整體把握。現在,看下面一張表。           上面

C# 好程式碼學習筆記(1):檔案操作、讀取檔案、Debug/Trace 類、Conditional條件編譯、CLS

[TOC] 目錄: 1,檔案操作 2,Debug、Trace類 3,條件編譯 4,MethodImpl 特性 5,CLSComplianAttribute 6,必要時自定義類型別名 最近在閱讀 .NET Core Runtime 的原始碼,參考大佬的程式碼,學習編寫技巧和提高程式碼水平。學

rust學習筆記中級篇2–結構基礎型別成員函式的實現(霜之小刀)

rust學習筆記中級篇2–結構體與基礎型別成員函式的實現(霜之小刀) 歡迎轉載和引用 若有問題請聯絡請聯絡 Email : [email protected] QQ:2279557541 結構體的成員函式的實現 先看個最簡單的示例。

python筆記1——關於檔案的開啟讀寫

一、檔案的開啟與關閉1.open,close函式 #-*- coding:utf-8 -*- # 1、w 寫模式,它是不能讀的,如果用w模式開啟一個已經存在的檔案,會清空以前的檔案內容,重新寫 # w+ 是讀寫內容,只要沾上w,肯定會清空原來的檔案 # 2、r 讀模式,只能讀,不能寫,而且檔案

【Docker】Docker學習筆記(1)-Windows下的配置安裝

第一次接觸Docker是15年夏天,那時候在七牛學習Go語言,周圍的大牛們突然提到Docker容器這個東西,而且非常火爆~直到一年多後才想起來要學習一下Docker到底是個什麼東西(我真是怠惰啊),廢話不多說,開始正文。 配置Hyper-V環境並安裝Doc

CS224n學習筆記1——深度自然語言處理

一、什麼是自然語言處理呢? 自然語言處理是電腦科學家提出的名字,本質上與計算機語言學是同義的,它跨越了計算機學、語言學以及人工智慧學科。 自然語言處理是人工智慧的一個分支,在計算機研究領域中,也有其他的分支,例如計算機視覺、機器人技術、知識表達和推理等。   目標:讓計算機能夠理解人類語言來

iOS學習筆記1-多執行緒GCDblock

學習IOS也有兩三個月了,今天來總結下學習GCD的知識點,希望大家多多指教: 1.GCD簡介以及block GCD:Grand Central Dispatch或者GCD,是一套low level API,提供了一種新的方法來進行併發程式編寫。從基

opencv3程式設計入門學習筆記1-----基本影象容器Mat

1、Mat的結構 Mat本質上是由兩個資料部分組成的類: (包含資訊有矩陣的大小,用於儲存的方法,矩陣儲存的地址等)的矩陣頭和一個指標,指向包含了畫素值的矩陣(可根據選擇用於儲存的方法採用任何維度儲存資料)。矩陣頭部的大小是恆定的。然而,矩陣本身的大小因影象的不同而不同,通

Linux系統程式設計學習筆記1)-檔案的I/O操作

概述 在Linux系統下,通常以一個非負整數來代指一個開啟的檔案,這些檔案可以包括終端,socket,裝置,普通檔案等等。規定的三個標準的檔案描述符為0,1,2下面分別介紹(在互動式shell中,這些檔案描述符通常指向shell執行所在的終端): 檔案描述