Linux C++读取RGB24格式的bitmap文件(24-bit Bitmap)

参考:

http://stackoverflow.com/questions/5751749/how-can-i-read-bmp-pixel-values-into-an-array

http://msdn.microsoft.com/en-us/library/dd183374%28v=VS.85%29.aspx

http://msdn.microsoft.com/en-us/library/dd183376%28v=VS.85%29.aspx


本文代码和测试图片下载


【测试图片】

宽50像素,高100像素,每个像素的颜色:Red=30, Green=50, Blue=80,在Windows mspaint.exe中保存为 24-bit Bitmap (.bmp)文件



【代码】

  • bmp_header_def.h
#ifndef _BMP_HEADER_DEF_H_
#define _BMP_HEADER_DEF_H_

typedef int LONG;
typedef unsigned short WORD;
typedef unsigned int DWORD;

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

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, *PBITMAPINFOHEADER;

#endif

  • bmp_reader.h

#ifndef _BMP_READER_H_
#define _BMP_READER_H_

#include "bmp_header_def.h"

class CBmpReader {
public:
	CBmpReader();
	~CBmpReader();
	
public:
	int Load(const char * bmp_file);
	void Unload();

	unsigned int GetWidth() const;
	unsigned int GetHeight() const;
	
	PBITMAPFILEHEADER GetFileHeader() const;
	PBITMAPINFOHEADER GetInfoHeader() const;
	
	char * GetRawData() const;
	
	unsigned int GetRedAt(int row, int col) const;
	unsigned int GetGreenAt(int row, int col) const;
	unsigned int GetBlueAt(int row, int col) const;
	
private:
	PBITMAPFILEHEADER m_pFileHeader;
	PBITMAPINFOHEADER m_pInfoHeader;
	char * m_buffer;
	char * m_pRawData;
	unsigned int m_nWidth;
	unsigned int m_nHeight;
	unsigned int m_nLineBytes;
};

#endif

  • bmp_reader.cpp
#pragma pack(1)

#include "bmp_reader.h"
#include <stdio.h>

CBmpReader::CBmpReader() {
	m_pFileHeader = NULL;
	m_pInfoHeader = NULL;
	m_buffer = NULL;
	m_pRawData = NULL;
	m_nWidth = 0;
	m_nHeight = 0;
	m_nLineBytes = 0;
}

CBmpReader::~CBmpReader() {
	Unload();
}

#define FOUR_BYTES_ALIGN(width_in_bit)  ((((width_in_bit)+31)>>5)<<2)

int CBmpReader::Load(const char * bmp_file) {
	FILE * pf = fopen(bmp_file, "rb");
	if (NULL == pf) {
		return 0;
	}
	
	fseek(pf, 0, SEEK_END);
	long length = ftell(pf);
	rewind(pf);
	m_buffer = new char[length];
	fread(m_buffer, length, 1, pf);

	m_pFileHeader = (PBITMAPFILEHEADER)(m_buffer);
	m_pInfoHeader = (PBITMAPINFOHEADER)(m_buffer + sizeof(BITMAPFILEHEADER));
	m_pRawData = m_buffer + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
	m_nWidth = m_pInfoHeader->biWidth;
	m_nHeight = m_pInfoHeader->biHeight;
	m_nLineBytes = FOUR_BYTES_ALIGN(m_nWidth * 24);

	fclose(pf);
	
	return 0;
}

void CBmpReader::Unload() {
	if (m_buffer) {
		m_pFileHeader = NULL;
		m_pInfoHeader = NULL;
		delete [] m_buffer;
		m_buffer = NULL;
		m_pRawData = NULL;
		m_nWidth = 0;
		m_nHeight = 0;
		m_nLineBytes = 0;
	}
}

unsigned int CBmpReader::GetWidth() const {
	return m_nWidth;
}

unsigned int CBmpReader::GetHeight() const {
	return m_nHeight;
}

PBITMAPFILEHEADER CBmpReader::GetFileHeader() const {
	return m_pFileHeader;
}

PBITMAPINFOHEADER CBmpReader::GetInfoHeader() const {
	return m_pInfoHeader;
}

char * CBmpReader::GetRawData() const {
	return m_pRawData;
}

#define GET_COLOR_AT(row, col, offset) \
	if (NULL == m_pRawData) { \
		return 0; \
	} \
	if ((row) > m_nHeight - 1 || (col) > m_nWidth - 1) { \
		return 0; \
	} \
	return m_pRawData[m_nLineBytes * (row) + 3 * (col) + (offset)];

unsigned int CBmpReader::GetRedAt(int row, int col) const {
	GET_COLOR_AT(row, col, 2);
}

unsigned int CBmpReader::GetGreenAt(int row, int col) const {
	GET_COLOR_AT(row, col, 1);
}

unsigned int CBmpReader::GetBlueAt(int row, int col) const {
	GET_COLOR_AT(row, col, 0);
}
  • main.cpp

#include <iostream>
#include "bmp_reader.h"

using namespace std;

#define OUTPUT_TITLE(title) \
	cout << "[" << #title << "]" << endl;

#define OUTPUT_ITEM_CONTENT(item, name) \
	cout << "\t" << #name << ": " << item->name << endl;

void OutputBitMapFileHeader(PBITMAPFILEHEADER pFileHeader) {
	OUTPUT_TITLE("Bitmap File Header");
	
	if (NULL == pFileHeader) {
		return;
	}

	OUTPUT_ITEM_CONTENT(pFileHeader, bfType);
	OUTPUT_ITEM_CONTENT(pFileHeader, bfSize);
	OUTPUT_ITEM_CONTENT(pFileHeader, bfReserved1);
	OUTPUT_ITEM_CONTENT(pFileHeader, bfReserved2);
	OUTPUT_ITEM_CONTENT(pFileHeader, bfOffBits);
}

void OutputBitMapInfoHeader(PBITMAPINFOHEADER pInfoHeader) {
	OUTPUT_TITLE("Bitmap Info Header");
	
	if (NULL == pInfoHeader) {
		return;
	}

	OUTPUT_ITEM_CONTENT(pInfoHeader, biSize);
	OUTPUT_ITEM_CONTENT(pInfoHeader, biWidth);
	OUTPUT_ITEM_CONTENT(pInfoHeader, biHeight);
	OUTPUT_ITEM_CONTENT(pInfoHeader, biPlanes);
	OUTPUT_ITEM_CONTENT(pInfoHeader, biBitCount);
	OUTPUT_ITEM_CONTENT(pInfoHeader, biCompression);
	OUTPUT_ITEM_CONTENT(pInfoHeader, biSizeImage);
	OUTPUT_ITEM_CONTENT(pInfoHeader, biXPelsPerMeter);
	OUTPUT_ITEM_CONTENT(pInfoHeader, biYPelsPerMeter);
	OUTPUT_ITEM_CONTENT(pInfoHeader, biClrUsed);
	OUTPUT_ITEM_CONTENT(pInfoHeader, biClrImportant);
}

void OutputColorAt(const CBmpReader &reader, int row, int col) {
	cout << "BGR @ [" << row << ", " << col << "]: "  
		<< reader.GetBlueAt(row, col) << ", "
		<< reader.GetGreenAt(row, col) << ", "
		<< reader.GetRedAt(row, col) << endl;
}

int main(int argc, char * argv[]) {
	CBmpReader reader;
	reader.Load("./data.bmp");
	
	int width = reader.GetWidth();
	int height = reader.GetHeight();
	cout << "width: " << width << " height: " << height << endl;

	PBITMAPFILEHEADER pFileHeader = reader.GetFileHeader();
	OutputBitMapFileHeader(pFileHeader);

	PBITMAPINFOHEADER pInfoHeader = reader.GetInfoHeader();
	OutputBitMapInfoHeader(pInfoHeader);
	
	OutputColorAt(reader, 0, 0);
	OutputColorAt(reader, 0, 1);
	OutputColorAt(reader, 0, 2);
	OutputColorAt(reader, 1, 0);
	OutputColorAt(reader, 1, 1);
	OutputColorAt(reader, 1, 2);
	OutputColorAt(reader, 0, width - 1);
	OutputColorAt(reader, height - 1, 0);
	OutputColorAt(reader, height - 1, width - 1);

	return 0;
}

  • Makefile

all:
	g++ -g bmp_reader.cpp main.cpp -o t

clean:
	rm *.o t -f

【执行结果】

width: 50 height: 100
["Bitmap File Header"]
        bfType: 19778
        bfSize: 0
        bfReserved1: 0
        bfReserved2: 54
        bfOffBits: 2621440
["Bitmap Info Header"]
        biSize: 40
        biWidth: 50
        biHeight: 100
        biPlanes: 1
        biBitCount: 24
        biCompression: 0
        biSizeImage: 15200
        biXPelsPerMeter: 0
        biYPelsPerMeter: 0
        biClrUsed: 0
        biClrImportant: 0
BGR @ [0, 0]: 80, 50, 30
BGR @ [0, 1]: 80, 50, 30
BGR @ [0, 2]: 80, 50, 30
BGR @ [1, 0]: 80, 50, 30
BGR @ [1, 1]: 80, 50, 30
BGR @ [1, 2]: 80, 50, 30
BGR @ [0, 49]: 80, 50, 30
BGR @ [99, 0]: 80, 50, 30
BGR @ [99, 49]: 80, 50, 30

【注意】

  1. RGB24格式的bitmap图像文件格式大致是:bitmap file header + bitmap info header + raw data,其中raw data是每行对齐到4字节的,图像的(0,0)点位于图像的左下角,图像的每个像素占3个字节,依次是Blue, Green和Red,而不是通常大家习惯说的RGB顺序
  2. bmp_reader.cpp 开头的 #pragma pack(1) 不能取掉
  3. bmp_reader.cpp 在CBmpReader::Load() 中的 m_nLineBytes = FOUR_BYTES_ALIGN(m_nWidth * 24) 不能换成 m_nLineBytes = m_nWidth * 24,因为要对齐到4字节,否则在main函数中获取各个像素的RGB值的时候会错乱。关于对齐到4字节,参考《图像数据每行对齐到4字节》


你可能感兴趣的:(Linux C++读取RGB24格式的bitmap文件(24-bit Bitmap))