c读取bmp文件

源自: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; 
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值