Skip to content

Instantly share code, notes, and snippets.

@shonenada
Created March 12, 2013 09:09
Show Gist options
  • Save shonenada/5141363 to your computer and use it in GitHub Desktop.
Save shonenada/5141363 to your computer and use it in GitHub Desktop.
BMP 图像处理,包括去色,反色,左右翻转,上下翻转。
#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