Skip to content

Instantly share code, notes, and snippets.

@shonenada
Created April 5, 2013 14:42
Show Gist options
  • Save shonenada/5319803 to your computer and use it in GitHub Desktop.
Save shonenada/5319803 to your computer and use it in GitHub Desktop.
#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