C语言实现对BMP图像信息的读取、灰白处理和白平衡处理

想了想发上来自己以后也好看看。
先直接上代码吧,有时间可能会写一下学习的过程
注:白平衡使用灰白世界算法

首先是源文件:

#define     _CRT_SECURE_NO_WARNINGS
#include "IMGVision.h"

_inline void showBmpHead(tBITMAPFILEHEADER pBmpHead);
_inline void showBmpInfoHead(tBITMAPINFOHEADER pBmpInfo);
_inline uint8_t IMG_BMP_FILEINFOGET(const char* ORI_IMG);


FILE* IMG;
unsigned short fileType;

tBITMAPFILEHEADER BITMAPFILEHEADER;
tBITMAPINFOHEADER BITMAPINFOHEADER;
_tPixelInfo PixelInfo;

struct rgbPerPixel
{
	unsigned char r;
	unsigned char g;
	unsigned char b;
};

uint8_t IMG_Whitebalance(const char* ORI_IMG,const char* NEW_IMG)
{
	FILE* IMG_K;
	FILE* IMG_G;
	rgbPerPixel* Pc = (rgbPerPixel*)malloc(sizeof(rgbPerPixel));
	unsigned long r_SUM=0, g_SUM=0, b_SUM=0;
	unsigned char r_Aver, g_Aver, b_Aver;
	unsigned int Grey_Aver;
	double Kr, Kg, Kb;
	unsigned long IMGPixelcount;
	IMG = fopen(ORI_IMG, "rb");
	if (IMG == NULL)
	{
		printf("图片打开失败,检查图片格式或是否有损\r\n");
		return -1;
	}
	fread(&fileType, 1, sizeof(unsigned short), IMG);
	if (fileType == 0x4d42)
	{
		fread(&BITMAPFILEHEADER, 1, sizeof(tBITMAPFILEHEADER), IMG);
		fread(&BITMAPINFOHEADER, 1, sizeof(tBITMAPINFOHEADER), IMG);
	}
	else
	{
		printf("BMP图片类型不正确,请检查BMP图片的数据格式\r\n");
		fclose(IMG);
		return -2;
	}

	for (long i = (BITMAPINFOHEADER.biWidth * BITMAPINFOHEADER.biHeight); i > 0; i--)
	{
		fread(Pc, 1, sizeof(rgbPerPixel), IMG);
		r_SUM = r_SUM + Pc->r;
		g_SUM = g_SUM + Pc->g;
		b_SUM = b_SUM + Pc->b;
		//printf("第%d个像素的累加值%d,%d,%d\r\n", i, r_SUM, g_SUM, b_SUM);
		//printf("第%d个像素RGB为 (%d,%d,%d)\r\n", i, Pc->r, Pc->g, Pc->b);
	}
	fclose(IMG);
	IMGPixelcount = BITMAPINFOHEADER.biWidth * BITMAPINFOHEADER.biHeight;
		r_Aver = r_SUM / IMGPixelcount;
		g_Aver = g_SUM / IMGPixelcount;
		b_Aver = b_SUM / IMGPixelcount;

		printf("R通道平均值为%0x\tG通道平均值为%0x\tB通道平均值为%0x\r\n",r_Aver,g_Aver,b_Aver);
	Grey_Aver = (r_Aver + g_Aver + b_Aver) / 3;
	if (Grey_Aver > 255)Grey_Aver = 255;

	Kr = (double)(Grey_Aver *1.0/ r_Aver);
	Kg = (double)(Grey_Aver *1.0/ g_Aver);
	Kb = (double)(Grey_Aver *1.0/ b_Aver);

	printf("三通道灰度平均值为%d\r\nR通道增益为%.5f\r\nG通道增益为%.5f\r\nB通道增益为%.5f\r\n",Grey_Aver,Kr,Kg,Kb);

	IMG_G = fopen(NEW_IMG, "wb");
	IMG_K = fopen(ORI_IMG, "rb");

	if (IMG_K == NULL)
	{
		printf("图片打开失败,检查图片格式或是否有损\r\n");
		return -1;
	}
	fread(&fileType, 1, sizeof(unsigned short), IMG_K);
	if (fileType == 0x4d42)
	{
		fread(&BITMAPFILEHEADER, 1, sizeof(tBITMAPFILEHEADER), IMG_K);
		fread(&BITMAPINFOHEADER, 1, sizeof(tBITMAPINFOHEADER), IMG_K);
	}
	else
	{
		printf("BMP图片类型不正确,请检查BMP图片的数据格式\r\n");
		fclose(IMG_K);
		return -2;
	}


	if (IMG_G == NULL)
	{
		printf("文件建立失败...\r\n");
		return -3;
	}
	fwrite(&fileType, 2, 1, IMG_G);
	fwrite(&BITMAPFILEHEADER, sizeof(tBITMAPFILEHEADER), 1, IMG_G);
	fwrite(&BITMAPINFOHEADER, sizeof(tBITMAPINFOHEADER), 1, IMG_G);
	for (long i = (BITMAPINFOHEADER.biWidth * BITMAPINFOHEADER.biHeight); i > 0; i--)
	{
		fread(Pc, 1, sizeof(rgbPerPixel), IMG_K);
		Pc->r = ((Pc->r) * Kr * 1.0); if ((Pc->r) > 255)Pc->r = 255;
		Pc->g = ((Pc->g) * Kg * 1.0); if ((Pc->g) > 255)Pc->g = 255;
		Pc->b = ((Pc->b) * Kb * 1.0); if ((Pc->b) > 255)Pc->b = 255;
		fwrite(Pc, 1, 3, IMG_G);
		//printf("第%d个像素RGB为 (%d,%d,%d)\r\n", i, Pc->r, Pc->g, Pc->b);
	}


	free(Pc);
	fclose(IMG_K);
	fclose(IMG_G);
	printf("\t图像白平衡处理完成,图片默认保存于自定义目录下\r\n\r\n");
	return 0;
}

uint8_t IMG_GreyChange(const char* ORI_IMG,const char* NEW_IMG)
{
	uint8_t _IS_OPEN = IMG_BMP_FILEINFOGET(ORI_IMG);

	FILE* IMG_G;
	unsigned char Grey;
	
	if (_IS_OPEN == 0)
	{
		printf("BMP图片信息读取成功\r\n");
		rgbPerPixel* Pc = (rgbPerPixel*)malloc(sizeof(rgbPerPixel));
		IMG_G = fopen(NEW_IMG, "wb");
		if (IMG_G == NULL)
		{
			printf("文件建立失败...\r\n");
			return -2;
		}
		fwrite(&fileType, 2, 1, IMG_G);
		fwrite(&BITMAPFILEHEADER, sizeof(tBITMAPFILEHEADER), 1, IMG_G);
		fwrite(&BITMAPINFOHEADER, sizeof(tBITMAPINFOHEADER), 1, IMG_G);
		for (long i = (BITMAPINFOHEADER.biWidth * BITMAPINFOHEADER.biHeight); i > 0; i--)
		{
			fread(Pc, 1, sizeof(rgbPerPixel), IMG);
			Grey = (Pc->r * 30 + Pc->g * 59 + Pc->b * 11 + 50) / 100;
			Pc->r = Pc->g = Pc->b = Grey;
			fwrite(Pc, 1, 3, IMG_G);
			//printf("第%d个像素RGB为 (%d,%d,%d)\r\n", i, Pc->r, Pc->g, Pc->b);
		}
		free(Pc);
		fclose(IMG);
		fclose(IMG_G);
		printf("\t图像灰值处理完成,图片保存于自定义目录下\r\n\r\n");
		return 0;
	}
	else if(_IS_OPEN == -1)
	{
		printf("图片打开失败,检查图片格式或是否有损\r\n");
		printf("BMP图片读取失败...\r\n");
		return -1;
	}
	else
	{
		printf("BMP图片类型有误,请检查BMP文件数据格式\r\n");
		return-2;
	}
}

uint8_t BMP_INFOPRINT(const char* BMP)
{
	IMG = fopen(BMP, "rb");
	if (IMG == NULL)
	{
		printf("检查文件是否存在或者是否损坏\r\n");
		return -1;
	}

	fread(&fileType, 1, sizeof(unsigned short), IMG);
	if (fileType == 0x4d42)
	{
		printf("BMP文件信息如下:\r\n");
		printf("文件标识符:%d\r\n", fileType);
		fread(&BITMAPFILEHEADER, 1, sizeof(tBITMAPFILEHEADER), IMG);
		showBmpHead(BITMAPFILEHEADER);
		fread(&BITMAPINFOHEADER, 1, sizeof(tBITMAPINFOHEADER), IMG);
		showBmpInfoHead(BITMAPINFOHEADER);
		fclose(IMG);
		return 0;
	}
	else
	{
		printf("BMP图片类型不正确,请检查BMP图片的数据格式\r\n");
		fclose(IMG);
		return -2;
	}
}


_inline void showBmpHead(tBITMAPFILEHEADER pBmpHead)
{
	printf("BMP文件大小:%dkb\n", BITMAPFILEHEADER.bfSize / 1024);
	printf("保留字必须为0:%d\n", BITMAPFILEHEADER.bfReserved1);
	printf("保留字必须为0:%d\n", BITMAPFILEHEADER.bfReserved2);
	printf("位图数据偏移字节量:%d\n", BITMAPFILEHEADER.bfOffBits);
}

_inline void showBmpInfoHead(tBITMAPINFOHEADER pBmpInfo)
{
	printf("文件类型:BMP\n");
	printf("信息头大小:%d\n", BITMAPINFOHEADER.biSize);
	printf("位图宽度:%d\n", BITMAPINFOHEADER.biWidth);
	printf("位图高度:%d\n", BITMAPINFOHEADER.biHeight);
	printf("图像位面数:%d\n", BITMAPINFOHEADER.biPlanes);
	printf("图像单个像素大小:%d\n", BITMAPINFOHEADER.biBitCount);
	printf("图像压缩方式:%d\n", BITMAPINFOHEADER.biCompression);
	printf("图像大小:%d\n", BITMAPINFOHEADER.biSizeImage);
	printf("水平方向分辨率:%d\n", BITMAPINFOHEADER.biXPelsPerMeter);
	printf("垂直方向分辨率:%d\n", BITMAPINFOHEADER.biYPelsPerMeter);
	printf("使用的颜色索引数:%d\n", BITMAPINFOHEADER.biClrUsed);
	printf("重要颜色索引数:%d\n", BITMAPINFOHEADER.biClrImportant);
}
_inline uint8_t IMG_BMP_FILEINFOGET(const char* ORI_IMG)
{

	IMG = fopen(ORI_IMG, "rb");
	if (IMG == NULL)
	{
		return -1;
	}

	fread(&fileType, 1, sizeof(unsigned short), IMG);
	if (fileType == 0x4d42)
	{
		fread(&BITMAPFILEHEADER, 1, sizeof(tBITMAPFILEHEADER), IMG);
		fread(&BITMAPINFOHEADER, 1, sizeof(tBITMAPINFOHEADER), IMG);
		return 0;
	}
	else
	{
		fclose(IMG);
		return -2;
	}
}

然后是头文件“IMGVision.h”:

#ifndef _IMG_VISION_H_
#define _IMG_VISION_H_

#include <stdio.h>
#include <stdint.h>
#include <malloc.h>

//14 Bytes 文件头信息块
typedef struct tBITMAPFILEHEADER
{
	//unsigned short		bfType;			//BMP : 0X4D42 WINDOWS读 42 4D  DEC= 19778
	unsigned int	bfSize;			//图片大小
	unsigned short		bfReserved1;	//保留字为0
	unsigned short		bfReserved2;	//保留字为0
	unsigned int	bfOffBits;		//文件头到像素数据偏移量
};
//40 Bytes 图像描述信息块
typedef struct tBITMAPINFOHEADER
{
	unsigned int	biSize;			//此结构体大小
	long	biWidth;
	long	biHeight;
	unsigned short		biPlanes;		//平面显示属,一般显示器只有一个平面所以为1
	unsigned short		biBitCount;		//一个像素所占的位数,一般为24,带A通道的有32
	unsigned int	biCompression;	//图像数据压缩的类,0为不压缩
	unsigned int	biSizeImage;	//像素数据所占的大小 =bfSize-bfOffBits
	long	biXPelsPerMeter;//水平分辨率
	long	biYPelsPerMeter;//垂直分辨率
	unsigned int	biClrUsed;		//位图实际使用的彩表的彩色索引数,为0证明全部使用
	unsigned int	biClrImportant;	//说明对图象显示有重要影响的颜色索引数,如果是0,表示无差别
};

//调色板(由颜色索引数决定)-可以省略此信息()
typedef struct _tPixelInfo
{
	unsigned char		rgbBlue;			//该颜色的蓝色分量(0~255)
	unsigned char		rgbGreen;			//该颜色的绿色分量(0~255)
	unsigned char		rgbRed;				//该颜色的红色分量(0~255)
	unsigned char		rgbReserved;		//保留字
};
//图像像素区数据 2色图像 - 1点1位
//				 16色图像- 1点4位(半字节)
//				 256色图像-1点8位 (整字节)
//				 真彩图像- 1点24位 (3字节)
//在一个字节中,不存在两个像素点的数据补位,一个字节如果在存好一个像素的数据之后有冗余,后补00
//不同显示模式下的输出一幅图像的补位情况不同


uint8_t BMP_INFOPRINT(const char* BMP);
uint8_t IMG_GreyChange(const char* ORI_IMG, const char* NEW_IMG);
uint8_t IMG_Whitebalance(const char* ORI_IMG,const char* NEW_IMG);






#endif // !_IMG_VISION_H_

调用以上两个文件即可,为了直观体会,这里多加一个写好的输出测试:

#define     _CRT_SECURE_NO_WARNINGS
#include "IMGVision.h"
#include "Windows.h"

int main()
{
	unsigned int mode;
	char IMG[200] = "L:/ImgFactor/Temp/HelloWorld/HelloWorld/source/IMGSource/IEI.bmp";
	char NEWIMG[200] = "NEWIEI_G.bmp";
	char IMG_QYZ[200] = "L:/ImgFactor/Temp/HelloWorld/HelloWorld/source/IMGSource/IMGT.bmp";
	char NEWIMG_QYZ[30] = "NEWIMGT_G.bmp";
	printf("使用此程序进行BMP图像处理:输入mode\r\n\t\t1->BMP图片信息提取\r\n\t\t2->BMP灰白处理\r\n\t\t3->BMP图片白平衡处理(请注意,白平衡不适用于极端情况)\r\n输入mode->> ");
	scanf_s("%d", &mode);
	getchar();
	system("cls");
	if(mode == 1)
	{
		printf("下方粘贴目标图片地址:\r\n");
		scanf("%s", IMG);
		printf("目标图片地址为:%s\r\n点击任意按键继续\r\n\r\n",IMG);
		getchar();
		system("cls");
		BMP_INFOPRINT((const char*)IMG);
	}
	else if (mode == 2)
	{
		printf("下方粘贴目标图片地址:\r\n");
		scanf("%s", IMG);
		printf("目标图片地址为:%s\r\n下方输入图片保存地址:(若手动输入则回车继续)\r\n",IMG);
		scanf("%s", NEWIMG);
		printf("图片保存地址为:%s\r\n点击任意按键继续\r\n\r\n", NEWIMG);
		system("cls");
		IMG_GreyChange((const char*)IMG, (const char*)NEWIMG);
	}
	else if (mode == 3)
	{
		printf("下方粘贴目标图片地址:\r\n");
		scanf("%s", IMG);
		printf("目标图片地址为:%s\r\n下方输入图片保存地址:(若手动输入则回车继续)\r\n", IMG);
		scanf("%s", NEWIMG);
		printf("图片保存地址为:%s\r\n点击任意按键继续\r\n\r\n", NEWIMG);
		system("cls");
		IMG_Whitebalance((const char*)IMG, (const char*)NEWIMG);
	}
	else if (mode == 0)
	{
		system("cls");
		printf("进行自动处理演示,使用默认图片和默认处理方式\t任意按键继续\r\n");getchar();
		printf("BMP图片信息提取->>>>>>>>>>>\r\n");
		BMP_INFOPRINT((const char*)IMG_QYZ);
		printf("\r\n任意按键继续\r\n"); getchar();
		printf("BMP图片灰白处理->>>>>>>>>>>\r\n");
		IMG_GreyChange((const char*)IMG_QYZ, (const char*)NEWIMG_QYZ);
		printf("\r\n任意按键继续\r\n"); getchar();
		printf("BMP图片白平衡处理->>>>>>>>>>>\r\n");
		IMG_Whitebalance((const char*)IMG, (const char*)NEWIMG);
		printf("\r\n演示结束,前往程序所在路径查看NEWIMG_QYZ.bmp以及NEWIEI_QYZ.bmp; 任意按键结束\r\n"); getchar();
	}
	else
	{
		printf("方案不存在");
		system("pause");
		return -1;
	}
	
	printf("Hello World !");


	return 0;
}



以上三个文件打包就是一个完整的处理程序
处理效果如下:
原图像原图像
灰白处理后的图像
灰白处理图像
原图像
原图像
白平衡处理后的图像
白平衡处理后的图像
白平衡不适用于处理极端情况下的图片,所以处理后会出现这种现象。
具体原理可以百度白平衡不适用的情况。

----------------------------END OF ARTICLE-------------------------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值