C語言讀取和存儲bmp格式圖片
開發(fā)過程中有時候需要解析bmp數(shù)據(jù),下面先簡單介紹bmp數(shù)據(jù)組成,后面附上C語言讀取和存儲bmp格式圖片代碼。
典型的位圖文件格式通常包含下面幾個數(shù)據(jù)塊:
1、BMP文件頭:保存位圖文件的總體信息。
2、位圖信息頭:保存位圖圖像的詳細信息。位圖信息:保存位圖圖像的詳細信息。
3、調(diào)色板:保存所用顏色的定義。調(diào)色板:保存所用顏色的定義。
4、位圖數(shù)據(jù):保存一個又一個像素的實際圖像。位圖數(shù)據(jù):保存一個又一個像素的實際圖像。
1. BMP文件頭(14字節(jié))
BMP文件頭數(shù)據(jù)結(jié)構(gòu)含有BMP文件的類型、文件大小和位圖起始位置等信息。
這部分是識別信息,典型的應用程序會首先普通讀取這部分數(shù)據(jù)以確保的確是位圖文件并且沒有損壞。
位圖頭結(jié)構(gòu)體定義如下:
typedef struct
{
uint16_t type; //位圖文件的類型,必須為BM(1-2字節(jié))
uint32_t size; //位圖文件的大小,以字節(jié)為單位(3-6字節(jié),低位在前)
uint16_t reserved1; //位圖文件保留字,必須為0(7-8字節(jié))
uint16_t reserved2; //位圖文件保留字,必須為0(9-10字節(jié))
uint32_t off_bits; //位圖數(shù)據(jù)位置的地址偏移,即起始位置,以相對于位圖(11-14字節(jié),低位在前)
}__attribute__ ((packed)) bmp_file_header_t;
2. 位圖信息頭(40字節(jié))
這部分告訴應用程序圖像的詳細信息,在屏幕上顯示圖像將會使用這些信息,它從文件的第15個字節(jié)開始。
位圖信息頭結(jié)構(gòu)體定義如下:
typedef struct
{
uint32_t size;
int32_t width;
int32_t height;
uint16_t planes;
uint16_t bit_count;
uint32_t compression;
uint32_t size_image;
uint32_t x_pels_permeter;
uint32_t y_pels_permeter;
uint32_t clr_used;
uint32_t clr_important;
} bmp_info_header_t;
結(jié)構(gòu)體變量解析如下:
- uint32_t size; 15-18字節(jié):定義以下用來描述影像的區(qū)塊(BitmapInfoHeader)的大小,即本結(jié)構(gòu)所占用字節(jié)數(shù),它的值是:40
- int32_t width; 19-22字節(jié):位圖寬度,以像素為單位。
- int32_t height; 23-26字節(jié):位圖高度,以像素為單位。
- uint16_t planes; 27-28字節(jié):保存所用彩色位面的個數(shù)。不經(jīng)常使用。
- uint16_t bit_count; 29-30字節(jié):保存每個像素的位數(shù),它是圖像的顏色深度。常用值是1(雙色灰階)、4(16色灰階)、8(256色灰階)和24(彩色)。
- uint32_t compression; 31-34字節(jié):定義所用的壓縮算法。允許的值是0、1、2、3、4、5。
- 0 - 沒有壓縮(也用BI_RGB表示)
1 - 行程長度編碼 8位/像素(也用BI_RLE8表示)
2 - 行程長度編碼4位/像素(也用BI_RLE4表示)
3 - Bit field(也用BI_BITFIELDS表示)
4 - JPEG圖像(也用BI_JPEG表示)
5 - PNG圖像(也用BI_PNG表示)
- uint32_t size_image; 35-38字節(jié):位圖的大小(其中包含了為了補齊行數(shù)是4的倍數(shù)而添加的空字節(jié)),以字節(jié)為單位。這是原始位圖數(shù)據(jù)的大小,不要與文件大小混淆。
- uint32_t x_pels_permeter; 39-42字節(jié):位圖水平分辨率,每米像素數(shù)。
- uint32_t y_pels_permeter; 43-46字節(jié):位圖垂直分辨率,每米像素數(shù)。
- uint32_t clr_used; 47-50字節(jié):位圖實際使用的顏色表中的顏色數(shù)。
- uint32_t clr_important; 51-54字節(jié):位圖顯示過程中重要的顏色數(shù),當每個顏色都重要時這個值與顏色數(shù)目(clr_used)相等。
3. 調(diào)色板
BMP調(diào)色板結(jié)構(gòu)體定義如下:
typedef struct _tagRGBQUAD
{
BYTE rgbBlue; //指定藍色強度
BYTE rgbGreen; //指定綠色強度
BYTE rgbRed; //指定紅色強度
BYTE rgbReserved; //保留,設置為0
} RGBQUAD;
1,4,8位圖像才會使用調(diào)色板數(shù)據(jù),16,24,32位圖像不需要調(diào)色板數(shù)據(jù),即調(diào)色板最多只需要256項(索引0 - 255)。
顏色表的大小根據(jù)所使用的顏色模式而定:2色圖像為8字節(jié);16色圖像位64字節(jié);256色圖像為1024字節(jié)。其中,每4字節(jié)表示一種顏色,并以B(藍色)、G(綠色)、R(紅色)、alpha(32位位圖的透明度值,一般不需要)。即首先4字節(jié)表示顏色號1的顏色,接下來表示顏色號2的顏色,依此類推。
顏色表中RGBQUAD結(jié)構(gòu)數(shù)據(jù)的個數(shù)有biBitCount來確定,當biBitCount=1,4,8時,分別有2,16,256個表項:
- 當biBitCount=1時,為2色圖像,BMP位圖中有2個數(shù)據(jù)結(jié)構(gòu)RGBQUAD,一個調(diào)色板占用4字節(jié)數(shù)據(jù),所以2色圖像的調(diào)色板長度為2*4為8字節(jié)。
- 當biBitCount=4時,為16色圖像,BMP位圖中有16個數(shù)據(jù)結(jié)構(gòu)RGBQUAD,一個調(diào)色板占用4字節(jié)數(shù)據(jù),所以16像的調(diào)色板長度為16*4為64字節(jié)。
- 當biBitCount=8時,為256色圖像,BMP位圖中有256個數(shù)據(jù)結(jié)構(gòu)RGBQUAD,一個調(diào)色板占用4字節(jié)數(shù)據(jù),所以256色圖像的調(diào)色板長度為256*4為1024字節(jié)。
- 當biBitCount=16,24或32時,沒有顏色表。
4. 位圖數(shù)據(jù)
位圖數(shù)據(jù)記錄了位圖的每一個像素值。
像素是從下到上、從左到右保存的。
每個像素使用一個或者多個字節(jié)表示。
如果一個圖像水平線的字節(jié)數(shù)不是4的倍數(shù),這行就使用空字節(jié)補齊,通常是ASCII碼0。
例如:
1、一張5 * 6的圖片,有30個pixels,因為列數(shù)6不是4的倍數(shù),所以會顯示成:
xxxxxx00 xxxxxx00 xxxxxx00 xxxxxx00 xxxxxx00
其中,x代表調(diào)色盤的編號,0代表補齊的空字節(jié)
2、一張4 * 4的圖片,有16個pixels,因為列數(shù)剛好是4的倍數(shù),所以會顯示成:
xxxx xxxx xxxx xxxx
C語言讀取和存儲bmp示例代碼
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
typedef struct
{
uint16_t type;
uint32_t size;
uint16_t reserved1;
uint16_t reserved2;
uint32_t off_bits;
}__attribute__ ((packed)) bmp_file_header_t;
typedef struct
{
uint32_t size;
int32_t width;
int32_t height;
uint16_t planes;
uint16_t bit_count;
uint32_t compression;
uint32_t size_image;
uint32_t x_pels_permeter;
uint32_t y_pels_permeter;
uint32_t clr_used;
uint32_t clr_important;
} bmp_info_header_t;
static bmp_file_header_t s_bmp_file_header = { 0x4d42, 0, 0, 0, 0 };
static bmp_info_header_t s_bmp_info_header = { 0, 0, 0, 1, 8, 0, 0, 0, 0, 0, 0 };
static uint8_t s_bmpdata[200 * 200] = { 0 };
static uint32_t s_bmp_col = 0;
static uint32_t s_bmp_row = 0;
char in_file_path[256] = "in.bmp";
char out_file_path[256] = "out.bmp";
int32_t bmp_file_to_image(const char *file_path, uint8_t *image, uint32_t *col, uint32_t *row)
{
FILE *file = NULL;
uint32_t line_width = 0;
uint32_t width = 0;
uint32_t height = 0;
int32_t err = 0;
uint8_t buf[200 * 200] = { 0 };
char temp[2048] = { 0 };
int i = 0;
do {
if (NULL == file_path || NULL == image)
{
err = -1;
break;
}
printf("[%s] file_path = %s\n", __func__, file_path);
file = fopen(file_path, "rb");
if (NULL == file)
{
err = -1;
break;
}
fread(&s_bmp_file_header, sizeof(s_bmp_file_header), 1, file);
fread(&s_bmp_info_header, sizeof(s_bmp_info_header), 1, file);
fread(temp, 4*256, 1, file);
width = s_bmp_info_header.width;
height = s_bmp_info_header.height;
*col = width;
*row = height;
line_width = (width + 3) / 4 * 4;
printf("[%s] line_width = %d, width = %d, height = %d\n", __func__, line_width, width, height);
for (i = height - 1; i >= 0; i--)
{
if (line_width == width)
{
fread(buf + i * width, width, 1, file);
}
else if (line_width > width)
{
fread(buf + i * width, width, 1, file);
fread(temp, line_width-width, 1, file);
}
}
memcpy(image, buf, width * height);
} while (0);
if (file != NULL)
{
fclose(file);
}
return err;
}
int32_t dump_image_to_bmp_file(const char *file_path, uint8_t *image, uint32_t width, uint32_t height)
{
FILE *file = NULL;
int32_t err = 0;
do {
if (NULL == file_path || NULL == image)
{
err = -1;
break;
}
uint32_t line_width = (width + 3) / 4 * 4;
s_bmp_file_header.off_bits = sizeof(bmp_file_header_t) + sizeof(bmp_info_header_t)
+ 4 * 256;
s_bmp_file_header.size = s_bmp_file_header.off_bits + line_width * height;
s_bmp_info_header.size = sizeof(bmp_info_header_t);
s_bmp_info_header.width = width;
s_bmp_info_header.height = height;
s_bmp_info_header.size_image = line_width * height;
printf("[%s] line_width = %d, width = %d, height = %d\n", __func__, line_width, width, height);
file = fopen(file_path, "wb");
if (NULL == file)
{
err = -1;
break;
}
fwrite(&s_bmp_file_header.type, 1, sizeof(s_bmp_file_header.type), file);
fwrite(&s_bmp_file_header.size, 1, sizeof(s_bmp_file_header.size), file);
fwrite(&s_bmp_file_header.reserved1, 1, sizeof(s_bmp_file_header.reserved1), file);
fwrite(&s_bmp_file_header.reserved2, 1, sizeof(s_bmp_file_header.reserved2), file);
fwrite(&s_bmp_file_header.off_bits, 1, sizeof(s_bmp_file_header.off_bits), file);
fwrite(&s_bmp_info_header, 1, sizeof(bmp_info_header_t), file);
uint8_t alpha = 0;
int32_t i;
for (i = 0; i < 256; i++)
{
fwrite(&i, 1, sizeof(uint8_t), file);
fwrite(&i, 1, sizeof(uint8_t), file);
fwrite(&i, 1, sizeof(uint8_t), file);
fwrite(&alpha, 1, sizeof(uint8_t), file);
}
for (i = height - 1; i >= 0; i--)
{
fwrite(image + i * width, 1, width, file);
if (line_width > width)
{
uint8_t line_align[4] = { 0 };
fwrite(line_align, 1, line_width - width, file);
}
}
fflush(file);
} while (0);
if (file != NULL)
{
fclose(file);
}
return err;
}
int main()
{
int32_t err = 0;
err = bmp_file_to_image(in_file_path, s_bmpdata, &s_bmp_col, &s_bmp_row);
if (err != 0)
{
return -1;
}
printf("[%s] s_bmp_col = %d, s_bmp_row = %d\n", __func__, s_bmp_col, s_bmp_row);
dump_image_to_bmp_file(out_file_path, s_bmpdata, s_bmp_col, s_bmp_row);
return 0;
}
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
QT調(diào)用vs2019生成的c++動態(tài)庫的方法實現(xiàn)
本文主要介紹了QT調(diào)用vs2019生成的c++動態(tài)庫的方法實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2024-06-06
C語言 數(shù)據(jù)結(jié)構(gòu)與算法之字符串詳解
這篇文章將帶大家深入了解C語言數(shù)據(jù)結(jié)構(gòu)與算法中的字符串,文中主要是介紹了字符串的定義、字符串的比較以及一些串的抽象數(shù)據(jù)類型,感興趣的可以學習一下2022-01-01
C++?LeetCode1827題解最少操作使數(shù)組遞增
這篇文章主要為大家介紹了C++?LeetCode1827題解最少操作使數(shù)組遞增示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12
kernel劫持modprobe?path內(nèi)容詳解
這篇文章主要為大家介紹了kernel劫持modprobe?path的內(nèi)容詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-05-05
數(shù)據(jù)結(jié)構(gòu)之矩陣行列和相等的實例
這篇文章主要介紹了數(shù)據(jù)結(jié)構(gòu)之矩陣行列和相等的實例的相關資料,希望通過本文能幫助到大家,讓大家掌握這部分內(nèi)容,需要的朋友可以參考下2017-10-10

