Created
April 5, 2013 14:42
-
-
Save shonenada/5319803 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <iostream> | |
using namespace std; | |
typedef struct | |
{ | |
char type1; | |
char type2; | |
}BmpFileHead; | |
typedef struct | |
{ | |
unsigned int imageSize; | |
unsigned int blank; | |
unsigned int startPosition; | |
unsigned int length; | |
unsigned int width; | |
unsigned int height; | |
unsigned short colorPlane; | |
unsigned short bitColor; | |
unsigned int zipFormat; | |
unsigned int realSize; | |
unsigned int xPels; | |
unsigned int yPels; | |
unsigned int colorUse; | |
unsigned int colorImportant; | |
}BmpInfoHead; | |
typedef struct{ | |
unsigned char G; | |
unsigned char B; | |
unsigned char R; | |
unsigned char A; | |
}RGBA; | |
typedef struct | |
{ | |
BmpFileHead fileHead; | |
BmpInfoHead infoHead; | |
unsigned char * pData; | |
}BmpFile; | |
BmpFile* LoadBMPImage( const char* filename) | |
{ | |
int channels; | |
FILE * p; | |
BmpFile *pBmpFile = NULL; | |
int step, modbytes, idx; | |
if( !filename || strlen(filename) == 0 ) | |
{ | |
fprintf(stderr, "null filename\n"); | |
return NULL; | |
} | |
//打开文件 | |
p = fopen(filename, "rb"); | |
if(!p) | |
{ | |
fprintf(stderr, "Can not open file %s\n", filename); | |
return NULL; | |
} | |
pBmpFile = (BmpFile*)malloc( sizeof(BmpFile)); | |
//fread(&bmpfilehead, sizeof(BmpFileHead), 1, p); | |
//从文件中读BmpFileHead,在一些系统中sizeof(BmpFileHead)=4 | |
//所以不能使用上面那行代码,而要使用下面这行 | |
fread(&(pBmpFile->fileHead), 2, 1, p); | |
//从文件中读取BmpInfoHead | |
fread(&(pBmpFile->infoHead), sizeof(BmpInfoHead), 1, p); | |
//如果文件的前两个字节不是'BM',则不是BMP文件 | |
if( (pBmpFile->fileHead.type1 != 'B' && pBmpFile->fileHead.type1 != 'b') || | |
(pBmpFile->fileHead.type2 != 'M' && pBmpFile->fileHead.type2 != 'm') ) | |
{ | |
fprintf(stderr, "The file is not a BMP file" ); | |
fclose(p); | |
free(pBmpFile); | |
return NULL; | |
} | |
//对读入的数据进行检查 | |
//对于Gray图像, bitColor=8; | |
//对于RGB图像, bitColor=24 | |
channels = pBmpFile->infoHead.bitColor / 8; | |
if( channels != 1 && channels != 3) | |
{ //只支持1个或3个通道的图像 | |
fprintf(stderr, "Only 1 or 3 channels images can be loaded." ); | |
fclose(p); | |
free(pBmpFile); | |
return NULL; | |
} | |
//检查宽度和高度是否合理 | |
if(pBmpFile->infoHead.width <= 0 || pBmpFile->infoHead.height <=0) | |
{ | |
fprintf(stderr, "Bad image size"); | |
fclose(p); | |
free(pBmpFile); | |
return NULL; | |
} | |
//每行像素占的字节数为4的倍数,计算出这个数值step | |
modbytes = (pBmpFile->infoHead.width * channels)%4; | |
step = modbytes ? ((pBmpFile->infoHead.width*channels)-modbytes+4) : (pBmpFile->infoHead.width*channels); | |
pBmpFile->pData = (unsigned char*)malloc(step * pBmpFile->infoHead.height); | |
//跳到文件中的数据区开始位置 | |
fseek(p, pBmpFile->infoHead.startPosition, SEEK_SET); | |
//将所有图像数据读入,由于BMP以底端为第一行存储, | |
//而图像一般以上端为第一行存储 | |
for(idx=pBmpFile->infoHead.height-1; idx >= 0 ; idx--) | |
{ | |
int n = fread(pBmpFile->pData + step*idx, step, 1, p); | |
if(n!=1) | |
{ | |
fprintf(stderr, "read image data error.\n"); | |
break; | |
} | |
} | |
fclose(p); | |
return pBmpFile; | |
} | |
int SaveBMPImage( const char* filename, const BmpFile * pBmpFile ) | |
{ | |
FILE * p; | |
int channels; | |
int modbytes, step; | |
if( !filename || strlen(filename) == 0 ) | |
{ | |
fprintf(stderr, "null filename\n"); | |
return -1; | |
} | |
if(!pBmpFile) | |
{ | |
fprintf(stderr, "null BMP file data\n"); | |
return -1; | |
} | |
//读取通道数目 | |
channels = pBmpFile->infoHead.bitColor / 8; | |
//每行像素占的字节数为4的倍数,计算出这个数值step | |
modbytes = (pBmpFile->infoHead.width * channels)%4; | |
step = modbytes ? ((pBmpFile->infoHead.width*channels)-modbytes+4) : (pBmpFile->infoHead.width*channels); | |
p = fopen(filename, "wb"); | |
if(!p) | |
{ | |
fprintf(stderr, "Can not create file" ); | |
return -1; | |
} | |
//写入数据头信息 | |
fwrite(&(pBmpFile->fileHead), sizeof(BmpFileHead), 1, p); | |
fwrite(&(pBmpFile->infoHead), sizeof(BmpInfoHead), 1, p); | |
//写入数据信息 | |
if(channels==1) | |
{ | |
RGBA rgba; | |
for(int idx =0; idx<256; idx++) | |
{ | |
rgba.R=idx; | |
rgba.G=idx; | |
rgba.B=idx; | |
rgba.A = 0; | |
fwrite(&rgba, sizeof(RGBA), 1, p); | |
} | |
} | |
//写入数据 | |
for(int idx = pBmpFile->infoHead.height-1; idx >=0 ; idx--) | |
fwrite(pBmpFile->pData + step*idx, step, 1, p); | |
fclose(p); | |
return 0; | |
} | |
//GetStep 函数返回 BmpFile->pData 表示的矩阵的宽度 | |
int GetStep(BmpFile* file, int channel){ | |
int modbytes = (file->infoHead.width * channel) %4 ; | |
int step = modbytes ? ((file->infoHead.width * channel) - modbytes + 4) : (file->infoHead.width * channel); | |
return step; | |
} | |
// 去色并转变为灰度图片 | |
void RemoveColor(BmpFile* file){ | |
int channels = file->infoHead.bitColor / 8; | |
if(channels != 3){ | |
cout << "Bmp file's channel must be 3" << endl; | |
return ; | |
} | |
int i, j; | |
int height = file->infoHead.height; | |
int step = GetStep(file, 3); | |
unsigned char R, G, B; | |
double Gray; | |
unsigned char* newData = new unsigned char[height * step / 3]; | |
int index=0; | |
for(i=0;i<height;i++){ | |
for(j=0;j<step;j+=3){ | |
R = file->pData[i*step + j + 2]; | |
G = file->pData[i*step + j + 1]; | |
B = file->pData[i*step + j]; | |
Gray = 0.299 * R + 0.587 * G + 0.114 * B; | |
newData[index++] = (unsigned char)Gray; | |
} | |
} | |
file->infoHead.bitColor = 8; | |
file->pData = newData; | |
} | |
void binary_Bmp(BmpFile* file){ | |
int n = file->infoHead.height * GetStep(file, 1); // 总数 | |
int n1, n2; // 大于阈值的像素点数量、小于阈的像素点数量 | |
int s1, s2; // 大于阈值的像素总和、小于阈值的像素总和 | |
double w1, w2; // 权值 | |
double u1, u2; // 平均值 | |
double maxBetween = 0.0; | |
double between; | |
int threshold; | |
for(int t=0; t<256; ++t) | |
{ | |
n1 = 0; | |
n2 = 0; | |
s1 = 0; | |
s2 = 0; | |
for(int i=0; i<n; ++i) | |
{ | |
if((int)file->pData[i] < t) | |
{ | |
++n1; | |
s1 = s1 + (int)file->pData[i]; | |
} | |
else | |
{ | |
++n2; | |
s2 = s2 + (int)file->pData[i]; | |
} | |
} | |
w1 = (double)n1 / (double)n; | |
w2 = (double)n2 / (double)n; | |
u1 = (double)s1 / (double)n1; | |
u2 = (double)s2 / (double)n2; | |
between = w1 * w2 * (u1 - u2) * (u1 - u2); | |
if( n1 && n2 && maxBetween < between) | |
{ | |
maxBetween = between; | |
threshold = t; | |
} | |
} | |
for(int i=0; i<n; ++i) | |
{ | |
file->pData[i] = file->pData[i] > threshold ? (char)255 : (char)0; | |
} | |
} | |
int compare(BmpFile* one, BmpFile* two) | |
{ | |
int n1 = one->infoHead.height * GetStep(one, 1); | |
int n2 = two->infoHead.height * GetStep(two, 1); | |
if(n1 != n2) | |
{ | |
return -1; | |
//error | |
} | |
int r=0; | |
for(int i=0; i<n1; ++i) | |
{ | |
r += (int)one->pData[i] ^ (int)two->pData[i]; | |
} | |
return r; | |
} | |
int main() | |
{ | |
BmpFile* origin_bmp = LoadBMPImage("origin-rgb.bmp"); | |
BmpFile* com_bmp_1 = LoadBMPImage("origin-rgb-1.bmp"); | |
BmpFile* com_bmp_2 = LoadBMPImage("origin-rgb-2.bmp"); | |
RemoveColor(origin_bmp); | |
binary_Bmp(origin_bmp); | |
RemoveColor(com_bmp_1); | |
binary_Bmp(com_bmp_1); | |
RemoveColor(com_bmp_2); | |
binary_Bmp(com_bmp_2); | |
int c1 = compare(origin_bmp, com_bmp_1); | |
int c2 = compare(origin_bmp, com_bmp_2); | |
if(c1 < c2){ | |
cout << "similar: Pic1" << endl; | |
} | |
if(c1 == c2){ | |
cout << "Equal" << endl; | |
} | |
if(c1 > c2){ | |
cout << "similar: Pic2" << endl; | |
} | |
cout << "c1=" << c1 << " c2=" << c2 << endl; | |
//SaveBMPImage("D:\\Projects\\cpp\\test\\Debug\\to_bin_bmp.bmp", origin_bmp); | |
return 1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment