Created
March 12, 2013 09:09
-
-
Save shonenada/5141363 to your computer and use it in GitHub Desktop.
BMP 图像处理,包括去色,反色,左右翻转,上下翻转。
This file contains 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 transport_x(BmpFile* file, int channel){ | |
int step = GetStep(file, channel); | |
int height = file->infoHead.height; | |
int i, j, k; | |
int temp; | |
for(i=0;i<height;i++){ | |
for(j=0;j<step/2;j++){ | |
temp = file->pData[i*step + j]; | |
file->pData[i*step + j] = file->pData[i*step + step - j - 1]; | |
file->pData[i*step + step - j - 1] = temp; | |
} | |
for(j=0;j<step/channel;j++){ | |
for(k=0;k<channel/2;k++){ | |
temp = file->pData[i*step + channel*j + k]; | |
file->pData[i*step + channel*j] = file->pData[i*step + channel*j + channel-k-1]; | |
file->pData[i*step + channel*j + channel-k-1] = temp; | |
} | |
} | |
} | |
} | |
//垂直翻转 | |
void transport_y(BmpFile* file, int channel){ | |
int i, j; | |
int temp; | |
int step = GetStep(file, channel); | |
int height = file->infoHead.height; | |
for(i=0;i<height/2;i++){ | |
for(j=0;j<step;j++){ | |
temp = file->pData[i*step + j]; | |
file->pData[i*step + j] = file->pData[(height-i-1)*step + j]; | |
file->pData[(height-i-1)*step + j] = temp; | |
} | |
} | |
} | |
// void transport_x(BmpFile* file, int channel){ | |
int step = GetStep(file, channel); | |
int height = file->infoHead.height; | |
int i, j, k; | |
int temp; | |
for(i=0;i<height;i++){ | |
for(j=0;j<step/2;j++){ | |
temp = file->pData[i*step + j]; | |
file->pData[i*step + j] = file->pData[i*step + step - j - 1]; | |
file->pData[i*step + step - j - 1] = temp; | |
} | |
for(j=0;j<step/channel;j++){ | |
for(k=0;k<channel/2;k++){ | |
temp = file->pData[i*step + channel*j + k]; | |
file->pData[i*step + channel*j] = file->pData[i*step + channel*j + channel-k-1]; | |
file->pData[i*step + channel*j + channel-k-1] = temp; | |
} | |
} | |
} | |
} | |
void transport_y(BmpFile* file, int channel){ | |
int i, j; | |
int temp; | |
int step = GetStep(file, channel); | |
int height = file->infoHead.height; | |
for(i=0;i<height/2;i++){ | |
for(j=0;j<step;j++){ | |
temp = file->pData[i*step + j]; | |
file->pData[i*step + j] = file->pData[(height-i-1)*step + j]; | |
file->pData[(height-i-1)*step + j] = temp; | |
} | |
} | |
} | |
// 反色 | |
void inverse(BmpFile* file, int channel){ | |
int i,j; | |
int step = GetStep(file, channel); | |
int height = file->infoHead.height; | |
for(i=0;i<height;i++){ | |
for(j=0;j<step;j++){ | |
file->pData[i*step + j] = 255 - file->pData[i*step + j]; | |
} | |
} | |
} | |
// 去色并转变为灰度图片 | |
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; | |
} | |
// 仅仅通过 R/G/B 数值改变进行去色 | |
void ChangeColor(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* newData = new unsigned char[height * step / 3]; | |
int index=0; | |
unsigned char R, G, B; | |
double Gray; | |
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; | |
file->pData[i*step + j + 2] = (unsigned char) Gray; | |
file->pData[i*step + j + 1] = (unsigned char) Gray; | |
file->pData[i*step + j + 0] = (unsigned char) Gray; | |
} | |
} | |
} | |
int main(int argc, char* argv[]) | |
{ | |
//读入图像 | |
BmpFile * pGrayBmpFile = LoadBMPImage("lena.gray.bmp"); | |
BmpFile * pRGBBmpFile = LoadBMPImage("lena.rgb.bmp"); | |
//如果失败 | |
if(!pGrayBmpFile || !pRGBBmpFile) | |
return -1; | |
/** | |
transport_x(pGrayBmpFile, 1); | |
transport_x(pRGBBmpFile, 3); | |
SaveBMPImage("new_x.gray.bmp", pGrayBmpFile); | |
SaveBMPImage("new_x.rgb.bmp", pRGBBmpFile); | |
transport_y(pGrayBmpFile, 1); | |
transport_y(pRGBBmpFile, 3); | |
SaveBMPImage("new_y.gray.bmp", pGrayBmpFile); | |
SaveBMPImage("new_y.rgb.bmp", pRGBBmpFile); | |
inverse(pGrayBmpFile, 1); | |
SaveBMPImage("inverse.gray.bmp", pGrayBmpFile); | |
inverse(pRGBBmpFile, 3); | |
SaveBMPImage("inverse.rgb.bmp", pRGBBmpFile); | |
RemoveColor(pRGBBmpFile); | |
SaveBMPImage("RemoveColor.rgb.bmp", pRGBBmpFile); | |
ChangeColor(pRGBBmpFile); | |
SaveBMPImage("ChangeColor.rgb.bmp", pRGBBmpFile); | |
*/ | |
//请在命令行打印出已经读入的图像的宽度和高度 | |
//打印 "lena.gray.bmp 信息头中的宽度。 | |
cout << "Gray Image's Width: " << pGrayBmpFile->infoHead.width << endl; | |
//打印 "lena.gray.bmp 信息头中的高度。 | |
cout << "Gray Image's Height: " << pGrayBmpFile->infoHead.height << endl; | |
//打印 "lena.rgb.bmp 信息头中的宽度。 | |
cout << "RGB Image's Width: " << pRGBBmpFile->infoHead.width << endl; | |
//打印 "lena.rgb.bmp 信息头中的高度。 | |
cout << "RGB Image's Height: " << pRGBBmpFile->infoHead.height << endl; | |
cout << endl; | |
//指定位置 | |
int x = 37; //<-改为你的学号的最后两位 | |
int y = 37; //<-改为你的学号的最后两位 | |
//分别读出两幅图像里指定位置的像素值,赋值给下面四个变量 | |
unsigned char GRAY, R, G, B; | |
// 获取 lena.gray.bmp 文件在保存矩阵的宽度,赋值为 GrayStep | |
int GrayStep = GetStep(pGrayBmpFile, 1); | |
// 获取 lena.rgb.bmp 文件在保存矩阵的高度,赋值为 RGBStep | |
int RGBStep = GetStep(pRGBBmpFile, 3); | |
// pData 用指针以二维矩阵形式保存,故 (x,y) 点的位置应该为 [(y * width) + x]。 | |
GRAY = pGrayBmpFile->pData[y * GrayStep + x]; | |
// RGB文件每个像素需 3 个存储单位来保存R、G、B的值 | |
// BMP 保存时 三色顺序为 BGR | |
R = pRGBBmpFile->pData[y * RGBStep + (x * 3) + 2]; | |
G = pRGBBmpFile->pData[y * RGBStep + (x * 3) + 1]; | |
B = pRGBBmpFile->pData[y * RGBStep + (x * 3)]; | |
//在命令行打印从两幅图像读出的像素值 | |
cout << "Gray: " << (int)GRAY << endl; | |
cout << "R: " << (int)R << endl; | |
cout << "G: " << (int)G << endl; | |
cout << "B: " << (int)B << endl; | |
cout << endl; | |
//按照实验报告模板要求,修改像素值,并使用SaveBMPImage函数保存到文件 | |
// 保存位置 | |
const char* new_gray_filename = "mygray.bmp"; | |
const char* new_rgb_filename = "myrgb.bmp"; | |
// 修改指定的值 | |
int gray_modify_position = x * GrayStep + y; | |
pGrayBmpFile->pData[gray_modify_position] = 0; | |
SaveBMPImage(new_gray_filename, pGrayBmpFile); | |
int rgb_modify_position[3] = {y * RGBStep + (x * 3) + 2, | |
y * RGBStep + (x * 3) + 1, | |
y * RGBStep + (x * 3)}; | |
pRGBBmpFile->pData[rgb_modify_position[0]] = 0; | |
pRGBBmpFile->pData[rgb_modify_position[1]] = 0; | |
pRGBBmpFile->pData[rgb_modify_position[2]] = 255; | |
SaveBMPImage(new_rgb_filename, pRGBBmpFile); | |
//释放内存 | |
free(pGrayBmpFile->pData); | |
free(pRGBBmpFile->pData); | |
free(pGrayBmpFile); | |
free(pRGBBmpFile); | |
system("pause"); | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment