c語言解析bmp圖片

来源:http://www.cnblogs.com/recallfuture/archive/2017/08/05/7288346.html
-Advertisement-
Play Games

心血來潮想瞭解下常用圖片的格式解析,翻看了一些資料後,發現最簡單的是bmp格式,所以先拿它開刀。 BMP格式 這種格式內的數據分為 三到四個 部分,依次是: 1. 文件信息頭 (14位元組)存儲著文件類型,文件大小等信息 2. 圖片信息頭 (40位元組)存儲著圖像的尺寸,顏色索引,位平面數等信息 3. ...


心血來潮想瞭解下常用圖片的格式解析,翻看了一些資料後,發現最簡單的是bmp格式,所以先拿它開刀。

BMP格式

這種格式內的數據分為三到四個部分,依次是:

  1. 文件信息頭 (14位元組)存儲著文件類型,文件大小等信息
  2. 圖片信息頭 (40位元組)存儲著圖像的尺寸,顏色索引,位平面數等信息
  3. 調色板 (由顏色索引數決定)【可以沒有此信息
  4. 點陣圖數據 (由圖像尺寸決定)每一個像素的信息在這裡存儲

一般的bmp圖像都是24位,也就是真彩。每8位為一位元組,24位也就是使用三位元組來存儲每一個像素的信息,三個位元組對應存放r,g,b三原色的數據,每個位元組的存貯範圍都是0-255。
那麼以此類推,32點陣圖即每像素存儲r,g,b,a(Alpha通道,存儲透明度)四種數據。8點陣圖就是只有灰度這一種信息,還有二值圖,它只有兩種顏色,黑或者白。


文件信息頭格式

typedef struct tagBITMAPFILEHEADER {
    unsigned short bfType;      // 19778,必須是BM字元串,對應的十六進位為0x4d42,十進位為19778,否則不是bmp格式文件
    unsigned int bfSize;        // 文件大小
    unsigned short bfReserved1; // 保留,必須設置為0
    unsigned short bfReserved2; // 保留,必須設置為0
    unsigned int bfOffBits;     // 從文件頭到像素數據的偏移
} BITMAPFILEHEADER;

圖片信息頭格式

typedef struct tagBITMAPINFOHEADER {
    unsigned int biSize;        // 此結構體的大小
    int biWidth;                // 圖像的寬
    int biHeight;               // 圖像的高
    unsigned short biPlanes;    // 表示bmp圖片的平面屬,顯然顯示器只有一個平面,所以恆等於1
    unsigned short biBitCount;  // 一像素所占的位數,一般為24
    unsigned int biCompression; // 說明圖象數據壓縮的類型,0為不壓縮。
    unsigned int biSizeImage;   // 像素數據所占大小, 這個值應該等於上面文件頭結構中bfSize-bfOffBits
    int biXPelsPerMeter;        // 說明水平解析度,用象素/米表示。一般為0
    int biYPelsPerMeter;        // 說明垂直解析度,用象素/米表示。一般為0
    unsigned int biClrUsed;     // 說明點陣圖實際使用的彩色表中的顏色索引數(設為0的話,則說明使用所有調色板項)。 
    unsigned int biClrImportant;// 說明對圖象顯示有重要影響的顏色索引的數目,如果是0,表示都重要。
} BITMAPINFOHEADER;

調色板信息

這裡需要根據文件信息頭的bfOffBits是否等於54(由前面的固定14+40位元組得出)來判斷是否存在此調色板信息,如果是,則不存在;大於的話即存在。
可以根據需求提取其中的信息,或者直接移動到點陣圖數據區讀取像素信息。
這個地方可以表示為一個二維數組unsigned char palette[N][M], 其中N表示總的顏色索引數,M表示每像素占的位元組數。例如一個24點陣圖,每像素由3個位元組構成,M即為3,每個位元組可表示0-255共256種顏色,所以N為256 。
數組中存放的是索引信息,也就是一張映射表,標識顏色索引號與其代表的顏色的對應關係

點陣圖數據

這裡就存放著所有的像素信息了,每像素為一位元組,讀取出來後通過查詢調色板獲得顏色信息。

如果圖像是24位或是32位數據的點陣圖的話,點陣圖數據區就不是索引而是實際的像素值了。下麵說明一下,此時點陣圖數據區的每個像素的RGB顏色陣列排布:

24位RGB按照BGR的順序來存儲每個像素的各顏色通道的值,一個像素的所有顏色分量值都存完後才存下一個下一個像素,不進行交織存儲。

32位數據按照BGRA的順序存儲,其餘與24位點陣圖的方式一樣。

註意:由於點陣圖信息頭中的圖像高度是正數,所以點陣圖數據在文件中的排列順序是從左下角到右上角,以行為主序排列的。
也就是說,最先讀取到的是位於從上往下數最後一行最左端的像素,然後是同行向右一列的像素,讀取完一整行後,繼續讀取倒數第二行,然後繼續向上直到讀完所有數據。


對齊規則

我們知道Windows預設的掃描的最小單位是4位元組,如果數據對齊滿足這個值的話對於數據的獲取速度等都是有很大的增益的。因此,BMP圖像順應了這個要求,要求每行的數據的長度必須是4的倍數,如果不夠需要進行比特填充(以0填充),這樣可以達到按行的快速存取。這時,點陣圖數據區的大小就未必是 圖片寬×每像素位元組數×圖片高 能表示的了,因為每行可能還需要進行比特填充。

填充後的每行的位元組數為:
int iLineByteCnt = (((m_iImageWidth * m_iBitsPerPixel) + 31) >> 5) << 2;
其中m_iBitsPerPixel為每像素的比特數。

這樣,點陣圖數據區的大小為:
m_iImageDataSize = iLineByteCnt * m_iImageHeight;

我們在掃描完一行數據後,也可能接下來的數據並不是下一行的數據,可能需要跳過一段填充數據:
skip = 4 - ((m_iImageWidth * m_iBitsPerPixel)>>3) & 3;

實現代碼

以24點陣圖為例

/* BmpFormat.h
 * 存放上述幾個結構體
 */
 
 // 文件頭結構體
typedef struct tagBITMAPFILEHEADER {
    unsigned short bfType;      // 19778,必須是BM字元串,對應的十六進位為0x4d42,十進位為19778,否則不是bmp格式文件
    unsigned int bfSize;        // 文件大小
    unsigned short bfReserved1; // 保留,必須設置為0
    unsigned short bfReserved2; // 保留,必須設置為0
    unsigned int bfOffBits;     // 從文件頭到像素數據的偏移
} BITMAPFILEHEADER;

//圖像信息頭結構體
typedef struct tagBITMAPINFOHEADER {
    unsigned int biSize;        // 此結構體的大小
    int biWidth;                // 圖像的寬
    int biHeight;               // 圖像的高
    unsigned short biPlanes;    // 表示bmp圖片的平面屬,顯然顯示器只有一個平面,所以恆等於1
    unsigned short biBitCount;  // 一像素所占的位數,一般為24
    unsigned int biCompression; // 說明圖象數據壓縮的類型,0為不壓縮。
    unsigned int biSizeImage;   // 像素數據所占大小, 這個值應該等於上面文件頭結構中bfSize-bfOffBits
    int biXPelsPerMeter;        // 說明水平解析度,用象素/米表示。一般為0
    int biYPelsPerMeter;        // 說明垂直解析度,用象素/米表示。一般為0
    unsigned int biClrUsed;     // 說明點陣圖實際使用的彩色表中的顏色索引數(設為0的話,則說明使用所有調色板項)。 
    unsigned int biClrImportant;// 說明對圖象顯示有重要影響的顏色索引的數目,如果是0,表示都重要。
} BITMAPINFOHEADER;

//24點陣圖像素信息結構體
typedef struct _PixelInfo {
    unsigned char b;
    unsigned char g;
    unsigned char r;
} PixelInfo;
/* TestBmp.c
 * 
 */

#include <stdio.h>
#include <malloc.h>
#include "BmpFormat.h"

int main(){
    FILE* fp;
    fp = fopen("image.bmp", "rb");//讀取同目錄下的image.bmp文件。
    if(fp == NULL){
        printf("打開'image.bmp'失敗!\n");
        return -1;
    }
    
    BITMAPFILEHEADER fileHeader;
    BITMAPINFOHEADER infoHeader;
    
    //從文件里讀取信息
    fread(&fileHeader, sizeof(fileHeader), 1, fp);
    fread(&infoHeader, sizeof(infoHeader), 1, fp);
    
    //解析頭信息
    if(fileHeader.bfType != 19778){
        printf("'image.bmp'不是一個bmp格式的文件。\n");
        return -1;
    }
    
    //輸出文件信息
    printf("大小:%dkb\n", fileHeader.bfSize);
    printf("尺寸:寬%d 高%d\n", infoHeader.biWidth, infoHeader.biHeight);
    printf("位數:%d\n", infoHeader.biBitCount);
    printf("偏移量:%d\n", fileHeader.bfOffBits);
    
    if(infoHeader.biBitCount != 24 || fileHeader.bfOffBits != 54){
        printf("\n暫時只支持24點陣圖片。\n");
        return -1;
    }
    
    //計算補齊量(這裡用更容易理解的除法和求餘,效果和位運算相同)
    int offset = (infoHeader.biBitCount*infoHeader.biWidth/8)%4;  
    if (offset != 0){
        offset = 4 - offset;  
    }
    
    //動態創建一塊記憶體以存儲像素信息
    int size = infoHeader.biHeight * (infoHeader.biWidth + offset);
    PixelInfo* pixel = (PixelInfo*)malloc(size);
    
    //迴圈讀取
    for (i=0; i<infoHeader.biHeight; i++){
        for (j=0; j<infoHeader.biWidth; j++){
            fread(pixel, sizeof(PixelInfo), 1, fp);
        }
        if (offset != 0){  
            for (j=0; j<offset; j++){  
                fread(pixel, sizeof(unsigned char), 1, fp);  
            }
        }
    }
    
    //可以在這裡對像素進行處理
    //比如輸出左下角的像素信息
    printf("\n左下角的像素的RGB為:%d\t%d\t%d\n", pixel[0].r, pixel[0].g, pixel[0].b);
    
    //釋放記憶體
    free(pixel);
    
    return 0;
}

參考文章

常見圖片格式詳解 - Xiangism
BMP文件格式詳解(BMP file format)


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 二級功能變數名稱之間共用Cookie,很重要的一點就是配置,如下: domain設置為.ahdqxx.com,如果你的功能變數名稱是www.ahdqxx.com,mall.ahdqxx.com,那麼請設置你的domain為.ahdqxx.com path設置為/ <authentication mode="Form ...
  • 問題通常我們在設置子控制項的一些與外觀、佈局有關的屬性時,比如Size、Location、Anchor或Dock等,會激發子控制項的 Layout事件,並可能會引起視窗重繪。當子控制項較多時,如果頻繁設置上述屬性(例如在窗體的初始化代碼中),多個子控制項的Layout事件會引起視窗重繪效率問題,比如閃爍。特 ...
  • 貼一下自己序列化的代碼: 上面的寫法持續序列化不會有記憶體溢出的性能問題,之前一直被告知直接引用公司某位老鳥封裝好的dll來序列化,後來發現了老是出現記憶體溢出,貼一下它的錯誤寫法,僅供吸取教訓: 哎,老鳥趕時間的時候寫代碼都這麼隨意嗎?看到被註釋掉的try catch我猜測他曾經也覺得這裡有問題,不過 ...
  • 其實我也不知道如何定義這個標題,詞乏,姑且先這樣定義吧。 看了本文章的朋友,如果有更好標題,請告訴我,謝謝。 有個項目使用SDK時遇到這樣一個情況。 該SDK有個BtPrinterManager類,擁有兩個方法:ServerPrint和ClientPrint,這兩個方法有一部分參數是一樣的,一部分參 ...
  • 新建Model1.edmx文件 頁面引用資料庫Model1 如下創建AD資料庫的model文件、 找到以下菜單 找到所需要引用資料庫類名稱。 在頁面後臺引用 ...
  • 大家可能在項目中,有的時候,由於顯示的內容過長,所以,需要顯示一部分內容,然後後面用省略號,把滑鼠放上去,會顯示出來全部的內容。 作為一個LowB程式員的我,第一反應是SubString截取,然後替換,然後ToolTip顯示原有的內容。 我相信很大一部分的初級程式員第一想法也是這個,然而,這種方法不 ...
  • using System.IO; System.Windows.Forms.SaveFileDialog objSave = new System.Windows.Forms.SaveFileDialog();objSave.Filter = "(*.txt)|*.txt|" + "(*.*)|*. ...
  • Java連接MySQL資料庫增刪改查通用方法 運行環境:eclipse+MySQL 以前我們Java連接MySQL資料庫都是一個資料庫寫一個類,類下麵寫好多方法,要是多個資料庫,就要寫多個類多個方法,導致代碼編寫太過於繁瑣,所以為了改變這樣的繁瑣,我將連接資料庫的方法進行了一系列的封裝,使用戶傳入一 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...