源自:http://hi.baidu.com/prince_hyai/item/d328959f62360bc7b72531d3
注意该代码中直接读取文件头是错误的,struct结构体内存对齐并非在结构体末尾增加空间,而是在变量后面增加字节空间。
比如:
typedef struct {
uint16_t bfType; // 位图文件的类型,必须为BM(1-2字节)
uint32_t bfSize; // 位图文件的大小,以字节为单位(3-6字节)
uint16_t bfReserved1; // 位图文件保留字,必须为0(7-8字节)
uint16_t bfReserved2; // 位图文件保留字,必须为0(9-10字节)
uint32_t bfOffBits; // 位图数据的起始位置,以相对于位图(11-14字节)
} BMPFILEHEADER;
这个结构体会补2个字节,位置在bfType变量后面,而非最后那个变量bfOffBits后面。
因此,分次读取文件才能获取到正确的结果。
#ifndef _BMPLOAD_H_ #define _BMPLOAD_H_ #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> // 纹理图像结构 typedef struct { int width; // 纹理宽度 int height; // 纹理高度 int components; // 每个象素对应的字节数,3:24位图,4:带alpha通道的24位图 unsigned char *data; // 纹理数据 unsigned int unpack_size; // 纹理数据大小 } TEXTUREIMAGE; // BMP文件头(14字节) typedef struct { uint16_t bfType; // 位图文件的类型,必须为BM(1-2字节) uint32_t bfSize; // 位图文件的大小,以字节为单位(3-6字节) uint16_t bfReserved1; // 位图文件保留字,必须为0(7-8字节) uint16_t bfReserved2; // 位图文件保留字,必须为0(9-10字节) uint32_t bfOffBits; // 位图数据的起始位置,以相对于位图(11-14字节) } BMPFILEHEADER; // BMP信息头(40字节) typedef struct { uint32_t biSize; // 本结构所占用字节数(15-18字节) uint32_t biWidth; // 位图的宽度,以像素为单位(19-22字节) uint32_t biHeight; // 位图的高度,以像素为单位(23-26字节) uint16_t biPlanes; // 目标设备的级别,必须为1(27-28字节) uint16_t biBitCount; // 每个像素所需的位数,必须是1(双色),4(16色),8(256色)或24(真彩色)之一(29-30字节) uint32_t biCompression; // 位图压缩类型,必须是 0(不压缩),1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一(31-34字节) uint32_t biSizeImage; // 位图的大小,以字节为单位(35-38字节) uint32_t biXPelsPerMeter;// 位图水平分辨率,每米像素数(39-42字节) uint32_t biYPelsPerMeter;// 位图垂直分辨率,每米像素数(43-46字节) uint32_t biClrUsed; // 位图实际使用的颜色表中的颜色数(47-50字节) uint32_t biClrImportant; // 位图显示过程中重要的颜色数(51-54字节) } BMPINFOHEADER; #endif // 读取BMP文件创建纹理 int LoadBmp(char *filename, TEXTUREIMAGE *textureImg) { int i, j; FILE *file; BMPFILEHEADER bmpFile; BMPINFOHEADER bmpInfo; int pixel_size; unsigned int unpack_size; struct stat finfo; memset(&bmpFile, 0, sizeof bmpFile); memset(&bmpInfo, 0, sizeof bmpInfo); memset(textureImg, 0, sizeof (TEXTUREIMAGE)); if (-1 == stat(filename, &finfo)) { perror("Error to stat"); return -1; } // 打开文件 file = fopen(filename, "rb "); if (file == NULL) { perror("Open file error"); return -1; } // 获取文件头 fread(&bmpFile.bfType, 2, 1, file); fread(&bmpFile.bfSize, 4, 1, file); // 防止文件不完整 if (finfo.st_size != bmpFile.bfSize) { printf("file size:%d bmp info size: %d, file not complete!\n"); return -1; } fread(&bmpFile.bfReserved1, 2, 1, file); fread(&bmpFile.bfReserved2, 2, 1, file); fread(&bmpFile.bfOffBits, 4, 1, file); //printf("bmpFile.bfType:%X\nbmpFile.bfSize:%u\nbmpFile.bfReserved1:%d\nbmpFile.bfReserved2:%d\nbmpFile.bfOffBits:%d\n", bmpFile.bfType, bmpFile.bfSize, bmpFile.bfReserved1, bmpFile.bfReserved2, bmpFile.bfOffBits); fread(&bmpInfo, 40, 1, file); /* printf("bmpInfo.biSize:%d\n", bmpInfo.biSize); printf("bmpInfo.biWidth:%d\n", bmpInfo.biWidth); printf("bmpInfo.biHeight:%d\n", bmpInfo.biHeight); printf("bmpInfo.biPlanes:%d\n", bmpInfo.biPlanes); printf("bmpInfo.biBitCount:%d\n", bmpInfo.biBitCount); printf("bmpInfo.biCompression:%d\n", bmpInfo.biCompression); printf("bmpInfo.biSizeImage:%d\n", bmpInfo.biSizeImage); printf("bmpInfo.biXPelsPerMeter:%d\n", bmpInfo.biXPelsPerMeter); printf("bmpInfo.biYPelsPerMeter:%d\n", bmpInfo.biYPelsPerMeter); printf("bmpInfo.biClrUsed:%d\n", bmpInfo.biClrUsed); printf("bmpInfo.biClrImportant:%d\n", bmpInfo.biClrImportant); */ // 验证文件类型 if (bmpFile.bfType != 0x4D42) { printf("File Type Error\n"); fclose(file); return -1; } if (bmpInfo.biCompression != 0) { printf("file compressed!\n"); return -1; } if (bmpInfo.biBitCount != 24) { printf("only support 24 bit map\n"); fclose(file); return -1; } // 获取图像色彩数 pixel_size = bmpInfo.biBitCount >> 3; // 有效数据大小 unpack_size = bmpInfo.biWidth * bmpInfo.biHeight * pixel_size; textureImg->data = (unsigned char*) malloc(unpack_size); if (textureImg->data == NULL) { fclose(file); return -1; } int bytes_add = bmpInfo.biWidth * pixel_size % 4; // 读取文件数据 for (i = 0; i < bmpInfo.biHeight; i++) { for (j = 0; j < bmpInfo.biWidth; j++) { // 红色分量 fread( textureImg->data + (i * bmpInfo.biWidth + j) * pixel_size + 2, sizeof(unsigned char), 1, file); // 绿色分量 fread( textureImg->data + (i * bmpInfo.biWidth + j) * pixel_size + 1, sizeof(unsigned char), 1, file); // 蓝色分量 fread(textureImg->data + (i * bmpInfo.biWidth + j) * pixel_size + 0, sizeof(unsigned char), 1, file); // Alpha分量 if (pixel_size == 4) { fread(textureImg-> data + (i * bmpInfo.biWidth + j) * pixel_size + 3, sizeof(unsigned char), 1, file); } } if (bytes_add > 0) { // if pixel_size == 4 then bytes_add == 0 fseek(file, bytes_add, SEEK_CUR); } } // 记录图像相关参数 textureImg->width = bmpInfo.biWidth; textureImg->height = bmpInfo.biHeight; textureImg->components = pixel_size; textureImg->unpack_size = unpack_size; fclose(file); return 0; } /** * @brief 检测图片数据的颜色数量 * @param[in] jpg_data 解压后的JPG图片数据,注意用于计算必须为unsigned * @param[in] unpack_size 解压后图片数据长度 * @param[in] width JPG图片宽度 * @param[in] height JPG图片高度 * @param[in] components 图片每像素字节数 * @param[in] max_color 颜色数量超过这个值就认为是正常信的图片 * @return 颜色数量超过max_color就返回0,认为是正常图片,否则返回2,是垃圾信图片 */ static int scan_jpg_color(unsigned char *jpg_data, size_t unpack_size, int width, int height, int components, int max_color) { if (NULL == jpg_data) return 0; register int i, j, pixel, colors = 0; char *bitHash = (char *) malloc(2 * 1024 * 1024 * sizeof (char)); if (NULL == bitHash) { perror("malloc error"); return 0; } memset(bitHash, 0, 2 * 1024 * 1024 * sizeof (char)); // 遍历一遍像素点 for (i=0; i<unpack_size; i+=components) { pixel = 0; for (j=0; j<components; j++) { pixel = (pixel << 8) | jpg_data[i+j]; } bitHash[pixel / 8] |= 1 << (pixel % 8); } for (i=0; i<2*1024*1024*8; i++) { if (bitHash[i / 8] & (1 << (i % 8))) { colors++; if (colors > max_color) { printf("colors more than %d!\n", max_color); free(bitHash); return 0; } } } free(bitHash); printf("color's count:%d\n", colors); return 2; } /** * @brief 根据图片数据和位置坐标,初始化数组samp * @param[in] jpg_data 解压后的JPG图片数据,注意用于计算必须为unsigned * @param[in] width JPG图片宽度 * @param[in] height JPG图片高度 * @param[in] components 图片每像素字节数 * @param[out] samp 待初始化的数组 * @param[in] pos 在图片中的坐标 * @param[in] num 几个坐标点,也代表了数组元素的个数 * @return 返回初始化了数组几个数字 */ static int init_pixel_array(unsigned char *jpg_data, int width, int height, int components, int samp[], int pos[][2], int num) { int pixel, i, j, k = 0; size_t pos_num; for (i=0; i<num; i++) { if (width < pos[i][0] || height < pos[i][1]) continue; // 如果点不在图片内,继续下一个 pos_num = pos[i][1] * width * components + pos[i][0] * components; pixel = 0; for (j=0; j<components; j++) { // RGB的值存入一个整数 pixel = (pixel << 8) | jpg_data[pos_num+j]; } samp[k] = pixel; if (k > 0) { if (samp[k-1] == samp[k]) { k--; } } k++; } return k; } #define JPG_SAMP_PIXELS 5 static int scan_jpg_samps(unsigned char *jpg_data, size_t unpack_size, int width, int height, int components, float max_rate) { if (NULL == jpg_data) return 0; register int i, j, k = 0, m; register unsigned int pixel; register unsigned int max_num = max_rate * (unpack_size / 3); // 最多可以有多少个像素点相同 int samp[JPG_SAMP_PIXELS] = {-1, -1, -1, -1, -1}; int pos[JPG_SAMP_PIXELS][2] = {{0, 0}, {25, 25}, {50, 50}, {(width-1), 0}, {width/2, height/2}}; unsigned int count[JPG_SAMP_PIXELS] = {0}; //size_t pos_num; //printf("max same pixel:%u\n", max_num); k = init_pixel_array(jpg_data, width, height, components, samp, pos, JPG_SAMP_PIXELS); //printf("k=%d samp[0] = %X samp[1] = %X samp[2]=%X samp[3] = %X samp[4] = %X\n", k, samp[0], samp[1], samp[2], samp[3], samp[4]); // 遍历图片的每一个像素点 for (i=0; i<unpack_size; i+=components) { pixel = 0; for (j=0; j<components; j++) { pixel = (pixel << 8) | jpg_data[i+j]; } for (m=0; m<k; m++) { if (pixel == samp[m]) { count[m]++; if (count[m] > max_num) { printf("Spam JPG: [%d] same color's pixel max than %u!\n", m, max_num); return 1; } break; // 如果跟其中一个采样点相同,就不可能跟剩下的几个相同了 } } } int pixel_num = unpack_size / 3; for (m=0; m<k; m++) { printf("Ham JPG: same pixel count[%d] = %u (%f).\n", m, count[m], count[m] / (float)pixel_num); } return 0; } /** * @brief 统计与采样点相同的像素点个数和颜色数量 * @param[in] jpg_data 解压后的JPG图片数据,注意按位计算时必须用无符号类型 * @param[in] unpack_size 解压后图片数据长度 * @param[in] width JPG图片宽度 * @param[in] height JPG图片高度 * @param[in] components 图片每像素字节数 * @param[in] max_rate 像素点与采样点相同数量所占比例超过此值就认为是垃圾信的图片 * @param[in] max_color 颜色数量超过这个值就认为是正常信的图片 * @return 返回0:颜色数量超过max_color且相同颜色点数小于要求的值;返回1:相同颜色点数过多;返回2:颜色数太少 */ static int scan_jpg_samp_color(unsigned char *jpg_data, size_t unpack_size, int width, int height, int components, float max_rate, int max_color) { if (NULL == jpg_data) return 0; register int i, j; register int samp = -1; register unsigned int pixel; register unsigned int count = 0; register int colors = 0; register unsigned int max_num = max_rate * (unpack_size / 3); // 最多可以有多少个像素点相同 int pos_x = width-1, pos_y = 0; size_t pos_num; //printf("max same pixel:%u\n", max_num); if (components > 3) { //printf("components=%d return 0!\n", components); return 0; } pos_num = pos_y * width * components + pos_x * components; pixel = 0; for (j=0; j<components; j++) { // RGB的值存入一个整数 pixel = (pixel << 8) | jpg_data[pos_num+j]; } samp = pixel; // 24位真彩色,2的24次方,每一个位代表一种颜色,需要2^24/8=2MB空间 char *bitHash = (char *) malloc(2 * 1024 * 1024 * sizeof (char)); if (NULL == bitHash) { perror("malloc error"); return 0; } memset(bitHash, 0, 2 * 1024 * 1024 * sizeof (char)); // 遍历图片的每一个像素点 //register size_t mid_pos = unpack_size / 3 / 2 * 3; //register int k; for (i=0; i<unpack_size; i+=components) { pixel = 0; for (j=0; j<components; j++) { pixel = (pixel << 8) | jpg_data[i+j]; } if (pixel == samp) { count++; if (count > max_num) { printf("same color's pixel max than %u, Spam JPG!\n", max_num); free(bitHash); return 1; } } // 将这个数值所在的位置一 bitHash[pixel / 8] |= (1 << (pixel % 8)); /* 这个地方多比较一次,对于正常图片,能省6%=(1.282-1.202)/1.282的时间,但是对于垃圾图片,要多耗费40%的时间 * 上面数据是通过某些图片总结出来的,所以不留算了。 if (i == mid_pos) { colors = 0; for (k=0; k<2*1024*1024*8; k++) { if (bitHash[k / 8] & (1 << (k % 8))) colors++; if (colors > max_color) { printf("Ham JPG: colors more than %d!\n", max_color); free(bitHash); return 0; } } } */ } int pixel_num = unpack_size / 3; printf("same pixel count = %u(%f)\n", count, count / (float)pixel_num); colors = 0; for (i=0; i<2*1024*1024*8; i++) { if (bitHash[i / 8] & (1 << (i % 8))) { colors++; if (colors > max_color) { printf("Ham JPG: colors more than %d!\n", max_color); free(bitHash); return 0; } } } free(bitHash); printf("Spam JPG: color's count:%d\n", colors); return 2; } /** * @brief 统计与采样点相同的像素点个数和颜色数量 * @param[in] jpg_data 加压后的JPG图片数据,注意按位计算时必须用无符号类型 * @param[in] unpack_size 解压后图片数据长度 * @param[in] width JPG图片宽度 * @param[in] height JPG图片高度 * @param[in] components 图片每像素字节数 * @param[in] max_rate 像素点与采样点相同数量所占比例超过此值就认为是垃圾信的图片 * @param[in] max_color 颜色数量超过这个值就认为是正常信的图片 * @return 返回0:颜色数量超过max_color且相同颜色点数小于要求的值;返回1:相同颜色点数过多;返回2:颜色数太少 */ static int scan_jpg_samps_color(unsigned char *jpg_data, size_t unpack_size, int width, int height, int components, float max_rate, int max_color) { if (NULL == jpg_data) return 0; register int i, j, k = 0, m; register unsigned int pixel; register int colors = 0; register unsigned int max_num = max_rate * (unpack_size / 3); // 最多可以有多少个像素点相同 int samp[JPG_SAMP_PIXELS] = {-1, -1, -1, -1, -1}; int pos[JPG_SAMP_PIXELS][2] = {{0, 0}, {25, 25}, {50, 50}, {(width-1), 0}, {width/2, height/2}}; unsigned int count[JPG_SAMP_PIXELS] = {0}; //printf("max same pixel:%u\n", max_num); k = init_pixel_array(jpg_data, width, height, components, samp, pos, JPG_SAMP_PIXELS); //printf("k=%d samp[0] = %X samp[1] = %X samp[2]=%X samp[3] = %X samp[4] = %X\n", k, samp[0], samp[1], samp[2], samp[3], samp[4]); // 24位真彩色,2的24次方,每一个位代表一种颜色,需要2^24/8=2MB空间 char *bitHash = (char *) malloc(2 * 1024 * 1024 * sizeof (char)); if (NULL == bitHash) { perror("malloc error"); return 0; } memset(bitHash, 0, 2 * 1024 * 1024 * sizeof (char)); // 遍历图片的每一个像素点 //register size_t mid_pos = unpack_size / 3 / 2 * 3; //register int k; for (i=0; i<unpack_size; i+=components) { pixel = 0; for (j=0; j<components; j++) { pixel = (pixel << 8) | jpg_data[i+j]; } for (m=0; m<k; m++) { if (pixel == samp[m]) { count[m]++; if (count[m] > max_num) { printf("same color's pixel max than %u, Spam JPG!\n", max_num); free(bitHash); return 1; } } } // 将这个数值所在的位置一 bitHash[pixel / 8] |= (1 << (pixel % 8)); /* 这个地方多比较一次,对于正常图片,能省6%=(1.282-1.202)/1.282的时间,但是对于垃圾图片,要多耗费40%的时间 * 上面数据是通过某些图片总结出来的,所以不留算了。 if (i == mid_pos) { colors = 0; for (k=0; k<2*1024*1024*8; k++) { if (bitHash[k / 8] & (1 << (k % 8))) colors++; if (colors > max_color) { printf("Ham JPG: colors more than %d!\n", max_color); free(bitHash); return 0; } } } */ } int pixel_num = unpack_size / 3; for (m=0; m<k; m++) printf("same pixel count = %u(%f)\n", count[m], count[m] / (float)pixel_num); colors = 0; for (i=0; i<2*1024*1024*8; i++) { if (bitHash[i / 8] & (1 << (i % 8))) { colors++; if (colors > max_color) { printf("Ham JPG: colors more than %d!\n", max_color); free(bitHash); return 0; } } } free(bitHash); printf("Spam JPG: color's count:%d\n", colors); return 2; } int main(int argc, char **argv) { TEXTUREIMAGE image; if (0 == LoadBmp( "test.bmp", &image)) { printf("加载图片成功\n"); printf("==============================================\n"); scan_jpg_color(image.data, image.unpack_size, image.width, image.height, image.components, 40000); printf("==============================================\n"); scan_jpg_samps(image.data, image.unpack_size, image.width, image.height, image.components, 0.1); printf("==============================================\n"); scan_jpg_samp_color(image.data, image.unpack_size, image.width, image.height, image.components, 0.1, 40000); printf("==============================================\n"); scan_jpg_samps_color(image.data, image.unpack_size, image.width, image.height, image.components, 0.1, 40000); printf("==============================================\n"); free(image.data); } else { printf("加载图片失败\n"); } return 0; }
相关推荐
通过查看和运行这个项目,初学者可以更直观地理解和学习C语言读取BMP文件的方法。 总之,掌握BMP文件的结构和C语言的文件操作技巧,能帮助我们实现基本的图像处理功能。通过实践,你可以更好地理解这些概念,并为...
在C语言中读取BMP(Bitmap)文件是一项基本的图像处理任务,它涉及到...以上内容涵盖了C语言读取BMP文件的基本知识点,包括文件结构、解析过程和VS2010项目的创建。实际操作时,需要结合具体需求进行相应的扩展和优化。
这个压缩包“BMP文件的读写C代码及文档.rar”包含了关于如何使用C语言进行BMP文件读写操作的相关资料,包括一个PDF文档“BMP file format.pdf”和一个名为“readwriteBMP.rar”的源代码压缩包。 BMP文件格式的基础...
本程序旨在帮助初学者理解如何利用C语言读取BMP文件,为学习数字图像处理打下基础。 首先,我们要了解BMP文件的基本结构。BMP文件分为文件头、信息头和像素数据三部分。文件头包含文件类型标识、文件大小等信息;...
C语言读取bmp图像是指使用C语言编程来读取bmp格式的图像文件,获取图像的宽度、高度、像素个数等信息。bmp格式是微软视窗平台上的一个简单的图形文件格式,通常用来存储位图图像。 bmp格式的图像文件由四部分组成:...
用C语言读取BITMAP文件,此源码默认读取当前目录下的1.bmp。可以改成处理其他BMP文件。
- **读取BMP文件**:使用C语言读取BMP文件,解析文件头获取图像信息,然后逐行读取图像数据。 - **颜色空间转换**:根据目标颜色深度,将原始像素数据转换为新的颜色表示。例如,从24位转到8位,需要进行颜色量化...
从给定的代码片段来看,这是一段使用C语言编写的程序,主要功能是...总之,这段代码不仅展示了如何使用C语言读取BMP文件,还深入介绍了颜色空间转换的细节,对于理解图像处理中的基本概念和操作具有很高的参考价值。
就是用c语言读取一副bmp图像文件的信息头信息
BMP(Bitmap-File)图形文件是Windows采用的图形文件格式,在Windows环境下运行的所有图象处理软件都支持BMP图象文件格式。Windows系统内部各图像绘制操作都是以BMP为基础的。Windows 3.0以前的BMP图文件格式与显示...
2. **写入BMP图像**:根据读取的数据重新构建一个BMP文件。 #### 五、详细代码解析 ##### 1. 主函数 ```c int main() { // 读取图像 char readName[] = "read.BMP"; if (!readBmp(readName)) { printf("Failed ...
纯C语言读写24BMP文件,32位BMP转24位BMP; 详情参见:https://blog.csdn.net/libizhide/article/details/104144513 下载后请修改错误:main.c中55行pic=(PIC *)malloc(1);改为pic=(PIC *)malloc(sizeof(PIC)*1);
C语言作为基础的编程语言,虽然没有内置的库直接支持BMP文件的读取,但通过理解BMP文件的结构,我们可以编写自定义的代码来解析和读取这种格式的图像。以下是对"bmp.rar"中涉及的C语言读取BMP图像知识点的详细解释:...
C语言作为基础的编程语言,虽然没有内置的图形库来直接处理BMP文件,但通过理解BMP文件的结构,我们可以用C语言编写程序来读取、修改和保存BMP图像。本文将详细介绍如何利用C语言操作BMP文件,以及获取图像的长宽...
C语言读取BMP位图文件 , 用C写的 , 可以读取 BMP 文件的位置 , 方便对图档作修正
在C语言中,我们通常通过打开文件、读取和写入字节流来处理BMP文件。以下是涉及的关键知识点: 1. 文件操作:使用`fopen()`函数打开文件,`fread()`和`fwrite()`用于读写文件内容,`fclose()`关闭文件。 2. BMP...
本篇文章将深入探讨如何读取BMP文件的十六进制代码。 首先,了解BMP文件结构是关键。一个BMP文件通常由以下几个部分组成: 1. 文件头(File Header):包含文件类型标识和文件大小信息。 2. 位图信息头(Bitmap ...
本文将深入探讨如何使用C语言读取BMP(Bitmap)位图文件,并解析代码中涉及的关键概念和技术细节。通过分析给定的代码片段,我们将了解BMP文件格式、C语言结构体的使用以及文件操作的基本方法。 ### BMP文件格式 ...
用C语言实现BMP图像读取,显示,简单处理!通过结构体变量存储文件头信息,定义动态二维数组实现像素信息的存储!