<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    圖像編程學習筆記1――bmp文件結構處理與顯示

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接

    文本內容轉載自《數字圖像處理編程入門》,代碼為自己實現

    1.1圖和調色板的概念

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

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

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

    RFID設備管理軟件                                                                     RFID設備管理軟件

    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 控制臺程序

     

    [cpp] view plaincopy  
      1. /** 
      2. * 程序名: WorkBmp.cpp 
      3. * 功  能: 讀取和顯示24位BMP圖像,并把圖像數據輸入到ImageData.txt中 
      4. * 24位bmp可以通過畫圖程序中的另存為的文件類型中可以選擇 
      5. * bmp文件放到工程目錄下 
      6. */  
      7. #include <iostream.h>  
      8. #include <stdio.h>  
      9. #include <windows.h>  
      10. #include <fstream.h>  
      11. int biWidth;  //圖像寬  
      12. int biHeight;  //圖像高  
      13. int biBitCount; //圖像類型,每像素位數  
      14. //RGBQUAD *pColorTable;  //顏色表指針  
      15. unsigned char *pBmpBuf;  //存儲圖像數據  
      16. int lineByte;         //圖像數據每行字節數  
      17. /** 
      18. * 函數名: readBmp 
      19. * 參  數: bmpName -- bmp文件名 
      20. * 功  能: 讀入bmp文件,并獲取相應的信息 
      21. */  
      22. bool readBmp(char *bmpName)  
      23. {  
      24.     FILE *fp;  
      25.     if( (fp = fopen(bmpName,"rb")) == NULL)  //以二進制的方式打開文件  
      26.     {  
      27.         cout<<"The file "<<bmpName<<"was not opened"<<endl;  
      28.         return FALSE;  
      29.     }  
      30.     if(fseek(fp,sizeof(BITMAPFILEHEADER),SEEK_CUR))  //跳過BITMAPFILEHEADE  
      31.     {  
      32.         cout<<"跳轉失敗"<<endl;  
      33.         return FALSE;  
      34.     }  
      35.     BITMAPINFOHEADER infoHead;  
      36.     fread(&infoHead,sizeof(BITMAPINFOHEADER),1,fp);   //從fp中讀取BITMAPINFOHEADER信息到infoHead中,同時fp的指針移動  
      37.     biWidth = infoHead.biWidth;  
      38.     biHeight = infoHead.biHeight;  
      39.     biBitCount = infoHead.biBitCount;  
      40.     lineByte = (biWidth*biBitCount/8+3)/4*4;   //lineByte必須為4的倍數  
      41.     //24位bmp沒有顏色表,所以就直接到了實際的位圖數據的起始位置  
      42.     pBmpBuf = new unsigned char[lineByte * biHeight];  
      43.     fread(pBmpBuf,sizeof(char),lineByte * biHeight,fp);  
      44.     fclose(fp);   //關閉文件  
      45.     return TRUE;  
      46.   
      47. }  
      48. /** 
      49. * 函數名: saveBmp 
      50. * 參  數: bmpName -- bmp文件名 
      51. * 功  能: 將bmp位圖文件的相關信息,寫入新創建的文件中 
      52. */  
      53. bool saveBmp(char *bmpName)  
      54. {  
      55.     FILE *fp;  
      56.     if( (fp = fopen(bmpName,"wb") )== NULL)   //以二進制寫入方式打開  
      57.     {  
      58.         cout<<"打開失敗!"<<endl;  
      59.         return FALSE;  
      60.     }  
      61.     //設置BITMAPFILEHEADER參數  
      62.     BITMAPFILEHEADER fileHead;  
      63.     fileHead.bfType = 0x4D42;     
      64.     fileHead.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + lineByte * biHeight;  
      65.     fileHead.bfReserved1 = 0;  
      66.     fileHead.bfReserved2 = 0;  
      67.     fileHead.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);  
      68.     fwrite(&fileHead,sizeof(BITMAPFILEHEADER),1,fp);  
      69.     //設置BITMAPINFOHEADER參數  
      70.     BITMAPINFOHEADER infoHead;  
      71.     infoHead.biSize = 40;  
      72.     infoHead.biWidth = biWidth;  
      73.     infoHead.biHeight = biHeight;  
      74.     infoHead.biPlanes = 1;  
      75.     infoHead.biBitCount = biBitCount;  
      76.     infoHead.biCompression = BI_RGB;  
      77.     infoHead.biSizeImage = lineByte * biHeight;  
      78.     infoHead.biXPelsPerMeter = 0;  
      79.     infoHead.biYPelsPerMeter = 0;  
      80.     infoHead.biClrUsed = 0;  
      81.     infoHead.biClrImportant = 0;  
      82.     //寫入  
      83.     fwrite(&infoHead,sizeof(BITMAPINFOHEADER),1,fp);  
      84.     fwrite(pBmpBuf,sizeof(char),lineByte * biHeight,fp);  
      85.     fclose(fp);    //關閉文件  
      86.     return TRUE;  
      87.   
      88.   
      89. }  
      90. /** 
      91. * 函數名: work 
      92. * 功  能: 處理位圖信息,并將位圖數據保存到ImageData文件中 
      93. */  
      94. void work()  
      95. {  
      96.     char readFileName[] = "nv.BMP";   //定義要讀入的文件名  
      97.     if(FALSE == readBmp(readFileName))  
      98.         cout<<"readfile error!"<<endl;  
      99.     //輸出圖像的信息  
      100.     cout<<"Width = "<<biWidth<<" Height = "<<biHeight<<" biBitCount="<<biBitCount<<endl;  
      101.     ofstream outfile("ImageData.txt",ios::in | ios::trunc);  
      102.     if(!outfile)  
      103.     {  
      104.         cout<<"open error"<<endl;  
      105.         return ;  
      106.     }  
      107.     int count = 0;  
      108.     //圖像數據信息是從左下角按行開始存儲的  
      109.     for(int i = 0; i < biHeight; i++ )  
      110.     {  
      111.         for(int j = 0; j < biWidth; j++ )  
      112.         {  
      113.             for(int k = 0; k < 3; k++ )  
      114.             {  
      115.                 int temp = *(pBmpBuf + i * lineByte + j + k);  
      116.                 count++;  
      117.                 outfile<<temp<<" ";  
      118.                 if(count % 8 == 0)  
      119.                 {  
      120.                     outfile<<endl;  
      121.                 }  
      122.             }  
      123.         }  
      124.     }  
      125.     cout<<"總的像素數:"<<count / 3<<endl;  
      126.   
      127.     char writeBmpName[] = "nvcpy.BMP";  
      128.     saveBmp(writeBmpName);  
      129.     delete []pBmpBuf;  //釋放內存  
      130. }  
      131.   
      132. int main()  
      133. {  
      134.     work();  
      135.     return 0;  
      136. }  
      137.                                            from:http://blog.csdn.net/sun1956/article/details/8648460
    RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全