想了想发上来自己以后也好看看。
先直接上代码吧,有时间可能会写一下学习的过程
注:白平衡使用灰白世界算法
首先是源文件:
#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-------------------------------------