#include <iostream>
#include <fstream>
#include <cmath>
#pragma pack(1) //取消对齐,如果不取消的话 sizeof(BITMAPFILEHEADER)就是16,而不是14
using namespace std;
typedef short int UINT16;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef long LONG;
typedef struct tagBITMAPFILEHEADER{
WORD bfType;//文件类型,必须是0x424D,即字符“BM” 这个属性必须注释掉,不然由于内存对齐的影响后面会出错
DWORD bfSize;//文件大小
WORD bfReserved1;//保留字
WORD bfReserved2;//保留字
DWORD bfOffBits;//从文件头到实际位图数据的偏移字节数
}BITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER{
DWORD biSize;//信息头大小
LONG biWidth;//图像宽度
LONG biHeight;//图像高度
WORD biPlanes;//位平面数,必须为1
WORD biBitCount;//每像素位数
DWORD biCompression; //压缩类型
DWORD biSizeImage; //压缩图像大小字节数
LONG biXPelsPerMeter; //水平分辨率
LONG biYPelsPerMeter; //垂直分辨率
DWORD biClrUsed; //位图实际用到的色彩数
DWORD biClrImportant; //本位图中重要的色彩数
}BITMAPINFOHEADER; //位图信息头定义
typedef struct tagRGBQUAD{
BYTE rgbBlue; //该颜色的蓝色分量
BYTE rgbGreen; //该颜色的绿色分量
BYTE rgbRed; //该颜色的红色分量
BYTE rgbReserved; //保留值
}RGBQUAD;//调色板定义
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
}BITMAPINFO, *LPBITMAPINFO, *PBITMAPINFO;
typedef struct tagIMAGEDATA//像素信息
{
BYTE blue;
BYTE green;
BYTE red;
}IMAGEDATA;
void bmpTransform(const BITMAPINFOHEADER &bmpInfo,unsigned char* data)
{
int BPP = bmpInfo.biBitCount;//BPP(Bits Per Pixel)每像素的比特数
int Width = bmpInfo.biWidth;//每行的像素数
int Height = bmpInfo.biHeight;
int RowSize = 4*(BPP*Width/32);//每行的字节数
int channels = BPP/8;//每个像素字节数,也就是几个颜色通道
for(int start = 0; start < channels; start++){
int C[255] = {0};
double P[255] = {0},Q[255] = {0};//灰度图
for(int i = 0; i < Height; i++){
for(int j = start; j < RowSize; j += channels){//idx为递增量
unsigned char k = data[RowSize*i+j];//我这里改成unsigned int都会出错,不知道为什么别人的没错
C[k]++;
}
}
//P
for(int i = 0; i < 255; i++) P[i] = (double)C[i]/(Width*Height);
//Q
Q[0] = P[0];
for(int i = 1; i < 255; i++) Q[i] = Q[i-1] + P[i];
//dataX
for(int i = 0; i < Height; i++){
for(int j = start; j < RowSize; j += channels){//idx为递增量
unsigned char k = data[RowSize*i+j];
data[RowSize*i+j] = round(255*Q[k]);
}
}
}
}
int main(int argc, char* argv[])
{
int a = sizeof(BITMAPFILEHEADER);
BITMAPFILEHEADER bmpFile;
BITMAPINFOHEADER bmpInfo;
RGBQUAD* colorTable;
string inFileName;
string outFileName;
switch(argc){
case 1:
cout << "File name of the image to be processed:";
cin >> inFileName;
outFileName = string(inFileName).insert(inFileName.find("."),"_eq");//insert会改变原string,这里先拷贝一份后进行insert
break;
case 2:
inFileName = argv[1];//argv[0] 指向程序运行的全路径名
outFileName = string(inFileName).insert(inFileName.find("."),"_eq");
break;
case 3:
inFileName = argv[1];
outFileName = argv[2];
break;
default: break;
}
fstream is;
is.open(inFileName,std::fstream::in | std::fstream::binary);
if(!is){ puts("文件打开失败"); return 0;}
//is.seekp(2);//跳过"BM"
is.read((char*)&bmpFile,sizeof(BITMAPFILEHEADER));
is.read((char*)&bmpInfo,sizeof(BITMAPINFOHEADER));
int BPP = bmpInfo.biBitCount;//BPP(Bits Per Pixel)为每像素的比特数
int Width = bmpInfo.biWidth;//每行的像素数
int Height = bmpInfo.biHeight;
int RowSize = 4*(BPP*Width/32);//每行的字节数
//这个与对齐规则有关,windows要求每行数据长度必须是4的倍数
//int skip = 4-(Width*BPP/8)&3;//即是=4-(Width*BPP/8)%4;//可能需要跳过的填充数据=4-每行的字节数被4整除的余数
//技巧,对2的n次方取余的话可以用与操作代替
//int allRowSize = RowSize + skip;//数据+填充 实际每行大小
//int DataSize = allRowSize*Height*BPP/8;//所有数据字节数=每行的字节数*行数(高度)
//所有数据字节数=每行的字节数*行数(高度)=RowSize*Width 所有像素数=Height*Width
//读取调色板
if(BPP == 8){
colorTable = new RGBQUAD[256];
is.read((char*)colorTable,sizeof(RGBQUAD)*256);
}
//读取像素数据
unsigned char* data = (unsigned char*)malloc(RowSize*Height);;//如果使用char*的话,会出现一些错误,比如int a = data[i];时,会赋值错误,不太清楚char赋值给int时的转换方法
is.seekp(bmpFile.bfOffBits);
is.read((char*)data,RowSize*Height);//拷贝进内存
if(BPP == 8){
bmpTransform(bmpInfo,data);
}else if(BPP == 24){
bmpTransform(bmpInfo,data);
}
/*
//c语言方式
FILE* fop = fopen("test2.bmp", "wb");
if (fop == 0)
return 0;
fwrite("BM",2,1,fop);
fwrite(&bmpFile, sizeof(BITMAPFILEHEADER), 1, fop);
fwrite(&bmpInfo, sizeof(BITMAPINFOHEADER), 1, fop);
fwrite(colorTable, sizeof(RGBQUAD), 256, fop);
fwrite(data,RowSize*Height, 1, fop);
fclose(fop);*/
ofstream os;
os.open(outFileName,std::fstream::binary);
//os.write("BM",2*sizeof(char));
os.write((char*)&bmpFile,sizeof(BITMAPFILEHEADER));
os.write((char*)&bmpInfo,sizeof(BITMAPINFOHEADER));
if(BPP == 8){ os.write((char*)colorTable,sizeof(RGBQUAD)*(1<<BPP)); free(colorTable);}
os.write((char*)data,RowSize*Height);
is.close();
os.close();
return 0;
}