关于BMP文件
- BMP文件包括以下几部分:
文件头、信息头、调色板、图像数据 - 文件头一般14个字节
- 信息头一般40个字节
- 彩色图像一般无调色板,灰度图像调色板一般为
unsigned char clrPal[1024] = {0,0,0,0, 1,1,1,0, …, 255,255,255,0}
文件头
Field Name | Size in Bytes | Description |
---|---|---|
bfType | 2 | The characters “BM” |
bfSize | 4 | The size of the file in bytes (bfOffBits+biSizeImage) |
bfReserved1 | 2 | Unused - must be zero (0) |
bfReserved2 | 2 | Unused - must be zero (0) |
bfOffBits | 4 | Offset to start of Pixel Data (灰度图像 54+1024, 彩色图像 54) |
图片信息头
Field Name | Size in Bytes | Description |
---|---|---|
biSize | 4 | Header Size - Must be at least 40 (40) |
biWidth | 4 | Image width in pixels |
biHeight | 4 | Image height in pixels |
biPlanes | 2 | Must be 1 (1) |
biBitCount | 2 | Bits per pixel - 1, 4, 8(灰度图像), 16, 24(彩色图像), or 32 |
biCompression | 4 | Compression type (0 = uncompressed) |
biSizeImage | 4 | Image Size ( 灰度图像 ⌈biWidth/4⌉×4×biHeight ) |
biXPelsPerMeter | 4 | Preferred resolution in pixels per meter (0) |
biYPelsPerMeter | 4 | Preferred resolution in pixels per meter (0) |
biClrUsed | 4 | Number Color Map entries that are actually used (灰度256, 彩色0) |
biClrImportant | 4 | Number of significant colors (0) |
图像数据的表示
灰度图像
- 表示为二维数组,数组元素称为像素,像素值为0~255的整数(0最暗, 255最亮)
- 数组行数等于图像高度,数组列数等于图像宽度
- 实际采用一维数组存储,逐行存储各像素,先存最后行;每行元素个数必为4的倍数,否则补充1~3个元素使满足此条件
彩色图像 - 等价于三个灰度图像,对应蓝绿红三原色
- 表示为“二维数组” ,数组元素(即像素)为三个0~255的整数
- 数组行数等于图像高度,数组列数等于图像宽度
- 实际采用一维数组存储,逐行存储各像素(每个像素表示为3个0~ 255的整数);每行元素个数必为4的倍数,否则补充1~3个元素使满足此条件。
文件示例
最简色彩平衡算法
- 像素 d a t a [ i ∗ s t r i d e + j ] data[i*stride+j] data[i∗stride+j]平衡后为 d a t a X [ i ∗ s t r i d e + j ] dataX[i*stride+j] dataX[i∗stride+j],算法如下:
- 对图像data的所有像素进行升序排序,得序列x
- 将序列头部%s的像素修改为0,尾部%t的像素修改为255,其中s和t一般相等,测试时建议取值为2,4,8,16
- 令其它(100-s-t)%的像素中,最小值为minV,最大值为maxV,那么对于其中的像素data[i*stride+j],修改后的值为:
d a t a X [ i ∗ s t r i d e + j ] = 255 × d a t a [ i ∗ s t r i d e + j ] − m i n V m a x V − m i n V dataX[i*stride+j]=255×\frac {data[ i*stride+j ] -minV} {maxV-minV} dataX[i∗stride+j]=255×maxV−minVdata[i∗stride+j]−minV
- 上面处理的是灰度图像,彩色图像则三原色分别如上处理。
最简色彩平衡之加速算法
上页算法的核心是计算minV和maxV,但涉及排序太耗时。
- 加速算法如下:
- 对于图像的所有可能的像素值,即0,1,2,…,255,统计每个值出现的次数,保存在数组P(double P[256];)
- 计算每个像素值出现的频率,仍保存在数组P,像素值k出现的频率为P[k]=P[k]/count,其中count为图像包含的像素总数
- 满足下面条件的,最小的k,即为minV:
P [ 0 ] + P [ 1 ] + P [ 2 ] + ⋯ + P [ k − 1 ] + P [ k ] ≥ s % P[0]+P[1]+P[2]+⋯+P[k−1]+P[k]≥s\% P[0]+P[1]+P[2]+⋯+P[k−1]+P[k]≥s% - 满足下面条件的,最大的k,即为maxV:
P [ k ] + P [ k + 1 ] + ⋯ + P [ 253 ] + P [ 254 ] + P [ 255 ] ≥ t % P[k]+P[k+1]+⋯+P[253]+P[254]+P[255]≥t\% P[k]+P[k+1]+⋯+P[253]+P[254]+P[255]≥t%
效果对比
黑白
彩色
实现代码1
balance.h
# ifndef BMP_H
# define BMP_H
/*
BMP格式
这种格式内的数据分为三到四个部分,依次是:
文件信息头 (14字节)存储着文件类型,文件大小等信息
图片信息头 (40字节)存储着图像的尺寸,颜色索引,位平面数等信息
调色板 (由颜色索引数决定)【可以没有此信息】
位图数据 (由图像尺寸决定)每一个像素的信息在这里存储
一般的bmp图像都是24位,也就是真彩。每8位为一字节,24位也就是使用三字节来存储每一个像素的信息,三个字节对应存放r,g,b三原色的数据,
每个字节的存贮范围都是0-255。那么以此类推,32位图即每像素存储r,g,b,a(Alpha通道,存储透明度)四种数据。8位图就是只有灰度这一种信息,
还有二值图,它只有两种颜色,黑或者白。
*/
// 文件信息头结构体
typedef struct tagBITMAPFILEHEADER
{
// unsigned short bfType; // 19778,必须是BM字符串,对应的十六进制为0x4d42,十进制为19778,否则不是bmp格式文件
unsigned int bfSize; // 文件大小 以字节为单位(2-5字节)
unsigned short bfReserved1; // 保留,必须设置为0 (6-7字节)
unsigned short bfReserved2; // 保留,必须设置为0 (8-9字节)
unsigned int bfOffBits; // 从文件头到像素数据的偏移 (10-13字节)
} BITMAPFILEHEADER;
//图像信息头结构体
typedef struct tagBITMAPINFOHEADER
{
unsigned int biSize; // 此结构体的大小 (14-17字节)
long biWidth; // 图像的宽 (18-21字节)
long biHeight; // 图像的高 (22-25字节)
unsigned short biPlanes; // 表示bmp图片的平面属,显然显示器只有一个平面,所以恒等于1 (26-27字节)
unsigned short biBitCount; // 一像素所占的位数,一般为24 (28-29字节)
unsigned int biCompression; // 说明图象数据压缩的类型,0为不压缩。 (30-33字节)
unsigned int biSizeImage; // 像素数据所占大小, 这个值应该等于上面文件头结构中bfSize-bfOffBits (34-37字节)
long biXPelsPerMeter; // 说明水平分辨率,用象素/米表示。一般为0 (38-41字节)
long biYPelsPerMeter; // 说明垂直分辨率,用象素/米表示。一般为0 (42-45字节)
unsigned int biClrUsed; // 说明位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有调色板项)。 (46-49字节)
unsigned int biClrImportant; // 说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。(50-53字节)
} BITMAPINFOHEADER;
//24位图像素信息结构体,即调色板
typedef struct _PixelInfo {
unsigned char rgbBlue; //该颜色的蓝色分量 (值范围为0-255)
unsigned char rgbGreen; //该颜色的绿色分量 (值范围为0-255)
unsigned char rgbRed; //该颜色的红色分量 (值范围为0-255)
unsigned char rgbReserved;// 保留,必须为0
} PixelInfo;
#endif
背白照片实现:balance.c
#include<stdio.h>
#include <malloc.h>
#include "balance.h"
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
void showBmpHead(BITMAPFILEHEADER pBmpHead)
{ //定义显示信息的函数,传入文件头结构体
printf("BMP file size: %dkb\n", fileHeader.bfSize/1024);
printf("reserved1 0: %d\n", fileHeader.bfReserved1);
printf("reserved2 0: %d\n", fileHeader.bfReserved2);
printf("off bit: %d\n", fileHeader.bfOffBits);
}
void showBmpInfoHead(BITMAPINFOHEADER pBmpinfoHead)
{//定义显示信息的函数,传入的是信息头结构体
printf("info head:\n" );
printf("info size:%d\n" ,infoHeader.biSize);
printf("biWidth:%d\n" ,infoHeader.biWidth);
printf("biHeight:%d\n" ,infoHeader.biHeight);
printf("biPlanes:%d\n" ,infoHeader.biPlanes);
printf("biBitCount:%d\n" ,infoHeader.biBitCount);
printf("biCompression:%d\n" ,infoHeader.biCompression);
printf("biSizeImage:%d\n" ,infoHeader.biSizeImage);
printf("biXPelsPerMeter:%d\n" ,infoHeader.biXPelsPerMeter);
printf("biYPelsPerMeter:%d\n" ,infoHeader.biYPelsPerMeter);
printf("biClrUsed:%d\n" ,infoHeader.biClrUsed);
printf("biClrImportant:%d\n" ,infoHeader.biClrImportant);
}
int main()
{
FILE* fp;
fp = fopen("picture/bwp1.bmp", "rb");//读取同目录下的bmp文件。
if(fp == NULL)
{
printf("open succussful!\n");
return -1;
}
//如果不先读取bifType,根据C语言结构体Sizeof运算规则——整体大于部分之和,从而导致读文件错位
unsigned short fileType;
PixelInfo clrPal[256];
fread(&fileType,1,sizeof (unsigned short), fp);
if (fileType = 0x4d42)
{
printf("file type ok!" );
printf("\nfile type is: %d\n", fileType);
fread(&fileHeader, 1, sizeof(BITMAPFILEHEADER), fp);
showBmpHead(fileHeader);
fread(&infoHeader, 1, sizeof(BITMAPINFOHEADER), fp);
showBmpInfoHead(infoHeader);
fread(&clrPal, 256, sizeof(PixelInfo), fp);
unsigned char data[infoHeader.biSizeImage];
unsigned char new_data[infoHeader.biSizeImage];
fread(&data, 1, sizeof(data), fp);
fclose(fp);
int i, j;
double P[256];
//初始化
for(i = 0; i<256;i++)
{
P[i] = 0;
}
int stride = infoHeader.biSizeImage/infoHeader.biHeight;
int count = 0;
for( i = 0; i<infoHeader.biHeight; i++){
for ( j = 0;j < infoHeader.biWidth; j++){
P[data[i*stride+j]] += 1;
count += 1;
}
}
// 获取频率
for(i = 0; i<256;i++)
P[i] /= count;
// 设置参数
int s = 10;
double sp = s*0.01;
int t = 10;
double tp = t*0.01;
double sum = 0;
int minV, maxV;
for (i=0;i<256;i++)
{
sum += P[i];
if(sum>=sp){
minV = i;
break;
}
}
sum = 0;
for (i=255;i>-1;i--)
{
sum += P[i];
if(sum>=tp){
maxV = i;
break;
}
}
for( i = 0; i<infoHeader.biHeight; i++){
for ( j = 0;j < infoHeader.biWidth; j++){
if(data[i*stride+j]<minV)
new_data[i*stride+j] = 0;
else if (data[i*stride+j]<=maxV)
new_data[i*stride+j] = (int)(255 * ((double)(data[i*stride+j] - minV)/(maxV- minV)));
else
new_data[i*stride+j] = 255;
}
}
FILE *wfp= fopen("picture/bwp3.bmp", "wb");
printf("%d\n", sizeof(fileType));
fwrite(&fileType, sizeof(fileType) , 1, wfp);
printf("%d\n", sizeof(fileHeader));
fwrite(&fileHeader, sizeof(fileHeader) , 1, wfp);
printf("%d\n", sizeof(infoHeader));
fwrite(&infoHeader, sizeof(infoHeader) , 1, wfp);
printf("%d\n", sizeof(clrPal));
fwrite(&clrPal, sizeof(clrPal) , 1, wfp);
printf("%d\n", sizeof(new_data));
fwrite(&new_data, sizeof(new_data) , 1, wfp);
fclose(wfp);
}
}
彩色照片实现:balance_cp.c
#include<stdio.h>
#include <malloc.h>
#include "balance.h"
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
void showBmpHead(BITMAPFILEHEADER pBmpHead)
{ //定义显示信息的函数,传入文件头结构体
printf("BMP file size: %dkb\n", fileHeader.bfSize/1024);
printf("reserved1 0: %d\n", fileHeader.bfReserved1);
printf("reserved2 0: %d\n", fileHeader.bfReserved2);
printf("off bit: %d\n", fileHeader.bfOffBits);
}
void showBmpInfoHead(BITMAPINFOHEADER pBmpinfoHead)
{//定义显示信息的函数,传入的是信息头结构体
printf("info head:\n" );
printf("info size:%d\n" ,infoHeader.biSize);
printf("biWidth:%d\n" ,infoHeader.biWidth);
printf("biHeight:%d\n" ,infoHeader.biHeight);
printf("biPlanes:%d\n" ,infoHeader.biPlanes);
printf("biBitCount:%d\n" ,infoHeader.biBitCount);
printf("biCompression:%d\n" ,infoHeader.biCompression);
printf("biSizeImage:%d\n" ,infoHeader.biSizeImage);
printf("biXPelsPerMeter:%d\n" ,infoHeader.biXPelsPerMeter);
printf("biYPelsPerMeter:%d\n" ,infoHeader.biYPelsPerMeter);
printf("biClrUsed:%d\n" ,infoHeader.biClrUsed);
printf("biClrImportant:%d\n" ,infoHeader.biClrImportant);
}
int main()
{
FILE* fp;
fp = fopen("picture/cp1.bmp", "rb");//读取同目录下的bmp文件。
if(fp == NULL)
{
printf("open succussful!\n");
return -1;
}
//如果不先读取bifType,根据C语言结构体Sizeof运算规则——整体大于部分之和,从而导致读文件错位
unsigned short fileType;
fread(&fileType,1,sizeof (unsigned short), fp);
if (fileType = 0x4d42)
{
printf("file type ok!" );
printf("\nfile type is: %d\n", fileType);
fread(&fileHeader, 1, sizeof(BITMAPFILEHEADER), fp);
showBmpHead(fileHeader);
fread(&infoHeader, 1, sizeof(BITMAPINFOHEADER), fp);
showBmpInfoHead(infoHeader);
unsigned char data[infoHeader.biSizeImage];
unsigned char new_data[infoHeader.biSizeImage];
fread(&data, 1, sizeof(data), fp);
fclose(fp);
int i, j;
double P[3][256];
//初始化
for(i = 0; i<3;i++)
for(j = 0; j<256;j++)
{
P[i][j] = 0;
}
int stride = infoHeader.biSizeImage/infoHeader.biHeight;
int count = 0;
for( i = 0; i<infoHeader.biHeight; i++){
for ( j = 0;j < infoHeader.biWidth; j++){
P[0][data[i*stride+3*j]] += 1;
P[1][data[i*stride+3*j+1]] += 1;
P[2][data[i*stride+3*j+2]] += 1;
count += 1;
}
}
// 获取频率
for(i = 0; i<256;i++)
{
P[0][i] /= count;
P[1][i] /= count;
P[2][i] /= count;
}
// 设置参数
int s = 10;
double sp = s*0.01;
int t = 10;
double tp = t*0.01;
double sum = 0;
int minV[3] = {0, 0, 0};
int maxV[3] = {0, 0, 0};
for (j = 0; j < 3; j++)
{
sum = 0;
for (i=0;i<256;i++)
{
sum += P[j][i];
if(sum>=sp){
minV[j] = i;
break;
}
}
}
for (j = 0; j < 3; j++)
{
sum = 0;
for (i=255;i>-1;i--)
{
sum += P[j][i];
if(sum>=tp){
maxV[j] = i;
break;
}
}
}
int k;
for( i = 0; i<infoHeader.biHeight; i++){
for ( j = 0;j < infoHeader.biWidth; j++){
for (k = 0; k < 3; k++)
{
if(data[i*stride+3*j+k] < minV[k])
new_data[i*stride+3*j+k] = 0;
else if (data[i*stride+3*j+k]<= maxV[k])
new_data[i*stride+3*j+k] = (int)(255 * ((double)(data[i*stride+3*j+k] - minV[k])/(maxV[k]- minV[k])));
else
new_data[i*stride+3*j+k] = 255;
}
}
}
FILE *wfp= fopen("picture/cp2.bmp", "wb");
printf("%d\n", sizeof(fileType));
fwrite(&fileType, sizeof(fileType) , 1, wfp);
printf("%d\n", sizeof(fileHeader));
fwrite(&fileHeader, sizeof(fileHeader) , 1, wfp);
printf("%d\n", sizeof(infoHeader));
fwrite(&infoHeader, sizeof(infoHeader) , 1, wfp);
printf("%d\n", sizeof(new_data));
fwrite(&new_data, sizeof(new_data) , 1, wfp);
fclose(wfp);
}
}