考虑到现有的数字图像处理都是基于Windows平台,都或多或少使用了Win32 API函数,不能移植到Linux或者嵌入式系统中。为了使程序可移植,采用标准C语言建立了数字图像处理的基本框架,如下图所示:
程序参考了网上一些博客的内容,并进行了改变,建立了符合自己习惯的数据结构。主要实现了bmp格式图像的打开、保存、创建、图像颜色空间转换等功能,暂时还没有添加具体的处理函数。我想,既然有了程序的框架,添加算法只是编写一个个函数的问题。
本程序具体实现的功能如下:
* 打开和保存bmp文件,这里使用自定义数据结构Bitmap,相关函数定义如下:
bmp.h:
int CreateBitmap(Bitmap* bmp, int width, int height, int bitCount);
void ReleaseBitmap(Bitmap* bmp);
int CheckPath(char *path);
int ReadBitmap(char* path, Bitmap* bmp);
int SaveBitmap(char* path, Bitmap* bmp);
* 图像格式转换
basicprocess.h:
int RGB2Gray(Bitmap* src, Bitmap* dst);
int Gray2RGB(Bitmap* src, Bitmap* dst);
int Gray2BW(Bitmap* src, Bitmap* dst, int threshold);
void hsv2rgb(float H, float S, float V, float *R, float *G, float *B);
void rgb2hsv(float R, float G, float B, float *H, float *S, float* V);
程序源码如下,欢迎大家批评指正。
/*2
****************************************Copyright (c)**************************************************3
** Feisky4
** http://www.cnblogs.com/feisky/5
**6
**------------------------------------- File Info ------------------------------------------------------7
** File name: bmp.h8
** Last modified Date: 2009-9-259
** Last Version: 1.010
** Descriptions: 位图文件结构及基本函数定义 打开和保存bmp文件11
**12
** Created by: Feisky13
** Created date: 2009-07-2514
** Version: 1.015
** Descriptions: Preliminary version.16
**17
**------------------------------------------------------------------------------------------------------18
*/19
#ifndef BMP_H_INCLUDED20
#define BMP_H_INCLUDED21

22
#include <ctype.h>23
#include <stdio.h>24
#include <stdlib.h>25
#include <malloc.h>26
#include <string.h>27

28
/**29
* 位图文件结构及基本函数定义 打开和保存bmp文件30
*/31
typedef unsigned short WORD;32
typedef unsigned long DWORD;33
typedef long LONG;34
typedef unsigned char BYTE;35

36
/* 位图文件头结构 14字节 */37
typedef struct tagBITMAPFILEHEADER {38
WORD bfType;39
DWORD bfSize;40
WORD bfReserved1;41
WORD bfReserved2;42
DWORD bfOffBits;43
} BITMAPFILEHEADER;44

45
/* 位图信息头结构 40字节 */46
typedef struct tagBITMAPINFOHEADER {47
DWORD biSize; // 结构长度 40B48
LONG biWidth;49
LONG biHeight;50
WORD biPlanes; // 151
WORD biBitCount; // 表示颜色要用到的位数52
DWORD biCompression; // 压缩格式53
DWORD biSizeImage; // 位图占用字节数=biWidth'(4的整倍数)*biHeight54
LONG biXPelsPerMeter; // 水平分辨率55
LONG biYPelsPerMeter; // 垂直分辨率56
DWORD biClrUsed; // 本图像用到的颜色数57
DWORD biClrImportant; // 本图像的重要颜色数58
} BITMAPINFOHEADER;59

60
/* 调色板 4字节 */61
typedef struct tagRGBQUAD {62
BYTE rgbBlue;63
BYTE rgbGreen;64
BYTE rgbRed;65
BYTE rgbReserved;66
} RGBQUAD;67

68
/* 定义图像信息 */69
typedef struct tagBITMAPINFO {70
BITMAPINFOHEADER bmiHeader;71
RGBQUAD bmiColors[1];72
} BITMAPINFO;73

74
/* 定义位图图像 */75
typedef struct _Bitmap76
{77
BITMAPFILEHEADER bmfh;78
BITMAPINFOHEADER bmih;79
int width;80
int height;81
int bitCount; // 8 或者2482
int imageSize; // 图像数据大小(imageSize=height*widthStep)字节83
BYTE* imageData;//排列的图像数据84
int widthStep; //排列的图像行大小85
}Bitmap;86

87
/**88
* 位图创建函数 创建一个Bitmap结构,并为图像数据分配空间89
*90
* 使用方法:91
* Bitmap *bmp=(Bitmap*)malloc(sizeof(Bitmap));92
* ret=CreateBitmap(bmp,50,50,3);93
*/94
int CreateBitmap(Bitmap* bmp, int width, int height, int bitCount)95
{96
bmp->width=width;97
bmp->height=height;98
bmp->bmih.biWidth=width;99
bmp->bmih.biHeight=height;100

101
bmp->widthStep=(int)((width*bitCount+31)/32)*4; //计算排列的宽度102
bmp->imageSize=bmp->height*bmp->widthStep*sizeof(BYTE);//计算排列的图像大小103

104
if(bitCount==8)105
{106
bmp->bitCount=8;107
bmp->bmfh.bfType=0x4d42; //注意是4d42 这个地方折磨我一下午啊108
bmp->bmfh.bfReserved1=0;109
bmp->bmfh.bfReserved2=0;110
bmp->bmih.biBitCount=8;111
bmp->bmih.biSize=40;112
bmp->bmih.biPlanes=1;113
bmp->bmfh.bfSize=54+256*4+height*bmp->widthStep;114
bmp->bmfh.bfOffBits=1078;115
bmp->bmih.biBitCount=8;116
bmp->bmih.biCompression=0;117
bmp->bmih.biSizeImage=bmp->imageSize;118
bmp->bmih.biClrUsed=0;119
bmp->bmih.biClrImportant=0;120
bmp->bmih.biXPelsPerMeter=0;121
bmp->bmih.biYPelsPerMeter=0;122
}123
else if (bitCount==24)124
{125
bmp->bitCount=24;126
bmp->bmfh.bfType=0x4d42;127
bmp->bmih.biBitCount=24;128
bmp->bmfh.bfReserved1=0;129
bmp->bmfh.bfReserved2=0;130
bmp->bmih.biSize=40;131
bmp->bmih.biPlanes=1;132
bmp->bmfh.bfSize=54+height*bmp->widthStep;133
bmp->bmfh.bfOffBits=54;134
bmp->bmih.biBitCount=24;135
bmp->bmih.biSizeImage=bmp->imageSize;136
bmp->bmih.biClrUsed=0;137
bmp->bmih.biCompression=0;138
bmp->bmih.biClrImportant=0;139
bmp->bmih.biXPelsPerMeter=0;140
bmp->bmih.biYPelsPerMeter=0;141
}142
else143
{144
printf("Error(CreateBitmap): only supported 8 or 24 bits bitmap.\n");145
return -1;146
}147

148
bmp->imageData=(BYTE*)malloc(bmp->imageSize); //分配数据空间149
if(!(bmp->imageData))150
{151
printf("Error(CreateBitmap): can not allocate bitmap memory.\n");152
return -1;153
}154
return 0;155
}156

157
/**158
* 位图指针释放函数 释放位图数据空间159
*160
* 使用方法:161
* ReleaseBitmap(bmp);162
*/163
void ReleaseBitmap(Bitmap* bmp)164
{165
free(bmp->imageData);166
bmp->imageData=NULL;167
free(bmp);168
bmp=NULL;169
}170

171
/**172
* 路径检查函数:是否为BMP文件,是否可读173
* 正确返回0,错误返回-1174
*175
* 使用方法176
* ret=CheckPath(path);177
*/178
int CheckPath(char *path)179
{180
FILE *fd;181
int len = strlen(path) / sizeof(char);182
char ext[3];183
//check whether the path include the characters "bmp" at end184
strncpy(ext, &path[len - 3], 3);185
if (!(ext[0] == 'b' && ext[1] == 'm' && ext[2] == 'p')) {186
printf("Error(CheckPath): the extension of the file is not bmp.\n");187
return -1;188
}189

190
//check whether the file can be read or not191
fd = fopen(path, "r");192
if (!fd)193
{194
printf("Error(CheckPath): can not open the file.\n");195
return -1;196
}197
fclose(fd);198

199
return 0;200
}201

202
/**203
* 从文件中读取位图函数204
* 正确返回0,错误返回-1205
*206
* 使用方法:207
* bmp=(Bitmap*)malloc(sizeof(Bitmap));208
* ret=ReadBitmap(path, bmp);209
*/210
int ReadBitmap(char* path, Bitmap* bmp)211
{212
int ret;213
FILE *fd;214

215
//检查路径是否可读216
ret=CheckPath(path);217
if(ret==-1)218
{219
printf("Error(ReadBitmap): the path of the image is invalid.\n");220
return -1;221
}222

223
//打开文件224
fd=fopen(path,"rb");225
if(fd==0)226
{227
printf("Error(ReadBitmap): can not open the image.\n");228
return -1;229
}230

231
//读取文件信息头 14字节232
fread(&(bmp->bmfh.bfType),sizeof(WORD),1,fd);233
fread(&(bmp->bmfh.bfSize),sizeof(DWORD),1,fd);234
fread(&(bmp->bmfh.bfReserved1),sizeof(WORD),1,fd);235
fread(&(bmp->bmfh.bfReserved2),sizeof(WORD),1,fd);236
fread(&(bmp->bmfh.bfOffBits),sizeof(DWORD),1,fd);237

238
//读取位图信息头 40字节239
fread(&(bmp->bmih.biSize),sizeof(DWORD),1,fd);240
fread(&(bmp->bmih.biWidth),sizeof(DWORD),1,fd);241
fread(&(bmp->bmih.biHeight),sizeof(DWORD),1,fd);242
fread(&(bmp->bmih.biPlanes),sizeof(WORD),1,fd);243
fread(&(bmp->bmih.biBitCount),sizeof(WORD),1,fd);244
fread(&(bmp->bmih.biCompression),sizeof(DWORD),1,fd);245
fread(&(bmp->bmih.biSizeImage),sizeof(DWORD),1,fd);246
fread(&(bmp->bmih.biXPelsPerMeter),sizeof(DWORD),1,fd);247
fread(&(bmp->bmih.biYPelsPerMeter),sizeof(DWORD),1,fd);248
fread(&(bmp->bmih.biClrUsed),sizeof(DWORD),1,fd);249
fread(&(bmp->bmih.biClrImportant),sizeof(DWORD),1,fd);250

251
//创建位图结构252
ret=CreateBitmap(bmp, bmp->bmih.biWidth, bmp->bmih.biHeight, bmp->bmih.biBitCount);253
if(ret==-1)254
{255
printf("Error(CreateBitmap): can not CreateBitmap.\n");256
return -1;257
}258

259
//读取图像数据260
//由于4字节对齐格式261
fseek(fd,bmp->bmfh.bfOffBits,SEEK_SET); //定位到图像数据区262
ret=fread(bmp->imageData,bmp->imageSize,1,fd);263
if(ret==0)264
{265
if(feof(fd)) //if the file pointer point to the end of the file266
{267
}268
if(ferror(fd)) //if error happened while read the pixel data269
{270
printf("Error(ReadBitmap): can not read the pixel data.\n");271
fclose(fd);272
return -1;273
}274
}275

276
//关闭文件277
fclose(fd);278
return 0;279
}280

281
/**282
* 保存位图到文件中去283
* 正确返回0,错误返回-1284
*285
* 使用方法:286
* bmp=(Bitmap*)malloc(sizeof(Bitmap));287
* ret=SaveBitmap(path, bmp);288
*/289
int SaveBitmap(char* path, Bitmap* bmp)290
{291
int ret;292
FILE *fd;293

294
//检查路径是否正确295
int len = strlen(path) / sizeof(char);296
char ext[3];297
//check whether the path include the characters "bmp" at end298
strncpy(ext, &path[len - 3], 3);299
if (!(ext[0] == 'b' && ext[1] == 'm' && ext[2] == 'p'))300
{301
printf("Error(SaveBitmap): the extension of the file is not bmp.\n");302
return -1;303
}304

305
//打开文件306
fd=fopen(path,"wb");307
if(fd==0)308
{309
printf("Error(SaveBitmap): can not open the image.\n");310
return -1;311
}312

313
//保存文件信息头 14字节314
fwrite(&(bmp->bmfh.bfType),sizeof(WORD),1,fd);315
fwrite(&(bmp->bmfh.bfSize),sizeof(DWORD),1,fd);316
fwrite(&(bmp->bmfh.bfReserved1),sizeof(WORD),1,fd);317
fwrite(&(bmp->bmfh.bfReserved2),sizeof(WORD),1,fd);318
fwrite(&(bmp->bmfh.bfOffBits),sizeof(DWORD),1,fd);319

320
//保存位图信息头 40字节321
fwrite(&(bmp->bmih.biSize),sizeof(DWORD),1,fd);322
fwrite(&(bmp->bmih.biWidth),sizeof(DWORD),1,fd);323
fwrite(&(bmp->bmih.biHeight),sizeof(DWORD),1,fd);324
fwrite(&(bmp->bmih.biPlanes),sizeof(WORD),1,fd);325
fwrite(&(bmp->bmih.biBitCount),sizeof(WORD),1,fd);326
fwrite(&(bmp->bmih.biCompression),sizeof(DWORD),1,fd);327
fwrite(&(bmp->bmih.biSizeImage),sizeof(DWORD),1,fd);328
fwrite(&(bmp->bmih.biXPelsPerMeter),sizeof(DWORD),1,fd);329
fwrite(&(bmp->bmih.biYPelsPerMeter),sizeof(DWORD),1,fd);330
fwrite(&(bmp->bmih.biClrUsed),sizeof(DWORD),1,fd);331
fwrite(&(bmp->bmih.biClrImportant),sizeof(DWORD),1,fd);332

333
//如果为8位,则 保存调色板334
RGBQUAD pal[256];335
int i;336
if(bmp->bitCount==8)337
{338
for(i=0;i<256;i++)339
{340
pal[i].rgbBlue=i;341
pal[i].rgbGreen=i;342
pal[i].rgbRed=i;343
pal[i].rgbReserved=0;344
}345
if(fwrite(pal,sizeof(RGBQUAD)*256,1,fd)!=1)346
{347
printf("Error(SaveBitmap): can not write Color Palette.\n");348
return -1;349
}350
}351

352

353
//保存图像数据354
ret=fwrite(bmp->imageData,bmp->imageSize,1,fd);355
if(ret!=1)356
{357
printf("Error(SaveBitmap): can not save the pixel data.\n");358
return -1;359
}360

361
//关闭文件362
fclose(fd);363

364
return 0;365
}366

367
#endif // BMP_H_INCLUDED368

369

370

371

372

373
/*374
****************************************Copyright (c)**************************************************375
** Feisky376
** http://www.cnblogs.com/feisky/377
**378
**------------------------------------- File Info ------------------------------------------------------379
** File name: basicprocess.h380
** Last modified Date: 2009-9-28381
** Last Version: 1.0382
** Descriptions: 位图图像基本处理函数 图像格式转换383
**384
** Created by: Feisky385
** Created date: 2009-9-28386
** Version: 1.0387
** Descriptions: Preliminary version.388
**389
**------------------------------------------------------------------------------------------------------390
*/391

392
#ifndef BASICPROCESS_H_393
#define BASICPROCESS_H_394

395
#include "bmp.h"396
#include <math.h>397
/**398
* <font color="#3f7f5f">位图图像基本处理函数 图像格式转换</font>399
*/400
int RGB2Gray(Bitmap* src, Bitmap* dst)401
{402
int ret;403
int n=0,i,j;404
BYTE r,g,b,gray;405

406
//检查图像格式是否合法407
if(src->bitCount!=24)408
{409
printf("Error(RGB2Gray): the source image must be in RGB format.\n");410
return -1;411
}412

413
//为dst图像分配数据空间414
ret=CreateBitmap(dst,src->width,src->height,8);415
if(ret==-1)416
{417
printf("Error(RGB2Gray): can't create target image.\n");418
return -1;419
}420

421
//计算灰度数据422
for(i=0;i<src->height;i++)423
{424
n=0;425
for(j=0;j<src->width*3;j++,n++)426
{427
b=*(src->imageData+src->widthStep*(src->height-1-i)+j);428
j++;429
g=*(src->imageData+src->widthStep*(src->height-1-i)+j);430
j++;431
r=*(src->imageData+src->widthStep*(src->height-1-i)+j);432
gray=(r*19595 + g*38469 + b*7472) >> 16;433
*(dst->imageData+dst->widthStep*(dst->height-1-i)+n)=gray;434
}435
}436

437
return 0;438
}439

440
/**441
* Gray2RGB442
*443
* 使用方法:444
* bmp=(Bitmap*)malloc(sizeof(Bitmap));445
* ret=ReadBitmap(path, bmp);446
* dstbmp=(Bitmap*)malloc(sizeof(Bitmap));447
* ret=Gray2RGB(bmp,dstbmp);448
*/449
int Gray2RGB(Bitmap* src, Bitmap* dst)450
{451
int ret;452
int n=0,i,j;453
BYTE r;454

455
//检查图像格式是否合法456
if(src->bitCount!=8)457
{458
printf("Error(Gray2RGB): the source image must be in gray scale.\n");459
return -1;460
}461

462
//为dst图像分配数据空间463
ret=CreateBitmap(dst,src->width,src->height,24);464
if(ret==-1)465
{466
printf("Error(Gray2RGB): can't create target image.\n");467
return -1;468
}469

470
//计算灰度数据471
for(i=0;i<src->height;i++)472
{473
n=0;474
for(j=0;j<src->width;j++,n++)475
{476
r=*(src->imageData+src->widthStep*(src->height-1-i)+j);477
*(dst->imageData+dst->widthStep*(dst->height-1-i)+n)=r;478
n++;479
*(dst->imageData+dst->widthStep*(dst->height-1-i)+n)=r;480
n++;481
*(dst->imageData+dst->widthStep*(dst->height-1-i)+n)=r;482
}483
}484

485
return 0;486
}487

488
/**489
* Gray2BW 图像二值化490
*491
* 使用方法:492
* bmp=(Bitmap*)malloc(sizeof(Bitmap));493
* ret=ReadBitmap(path, bmp);494
* dstbmp=(Bitmap*)malloc(sizeof(Bitmap));495
* ret=Gray2BW(bmp,dstbmp);496
*/497
int Gray2BW(Bitmap* src, Bitmap* dst, int threshold)498
{499
int ret;500
int n=0,i,j;501
BYTE r;502

503
//检查图像格式是否合法504
if(src->bitCount!=8)505
{506
printf("Error(Gray2BW): the source image must be in gray scale.\n");507
return -1;508
}509

510
//为dst图像分配数据空间511
ret=CreateBitmap(dst,src->width,src->height,8);512
if(ret==-1)513
{514
printf("Error(Gray2BW): can't create target image.\n");515
return -1;516
}517

518
//计算灰度数据519
for(i=0;i<src->height;i++)520
{521
for(j=0;j<src->width;j++,n++)522
{523
r=*(src->imageData+src->widthStep*(src->height-1-i)+j);524
if(r>=threshold)525
{526
n=255;527
}528
else529
{530
n=0;531
}532
*(dst->imageData+dst->widthStep*(dst->height-1-i)+j)=n;533
}534
}535

536
return 0;537
}538

539
/**540
* rgb2hsv541
* r,g,b values are from 0 to 1542
* h = [0,360], s = [0,1], v = [0,1]543
* if s == 0, then h = -1 (undefined)544
* 使用方法:545
* rgb2hsv(0.2,0.3,0.3,&x,&y,&z);546
*/547
void rgb2hsv(float R, float G, float B, float *H, float *S, float* V)548
{549
float min, max, delta,tmp;550
tmp = R<G?R:G;551
min = tmp<B?tmp:B;552
tmp = R>G?R:G;553
max = tmp>B?tmp:B;554
*V = max; // v555

556
delta = max - min;557

558
if( max != 0 )559
*S = delta / max; // s560
else561
{562
// r = g = b = 0 // s = 0, v is undefined563
*S = 0;564
*H = -1;565
return;566
}567
if( R == max )568
*H = ( G - B ) / delta; // between yellow & magenta569
else if( G == max )570
*H = 2 + ( B - R ) / delta; // between cyan & yellow571
else572
*H = 4 + ( R - G ) / delta; // between magenta & cyan573

574
(*H) *= 60; // degrees575
if( *H < 0 )576
(*H) += 360;577
}578

579

580
/**581
* hsv2rgb582
* r,g,b values are from 0 to 1583
* h = [0,360], s = [0,1], v = [0,1]584
* if s == 0, then h = -1 (undefined)585
* 使用方法:586
* hsv2rgb(60,0.3,0.5,&x,&y,&z);587
*/588
void hsv2rgb(float H, float S, float V, float *R, float *G, float *B)589
{590
int i;591
float f, p, q, t;592

593
if( S == 0 )594
{595
*R =*G = *B = V;596
return;597
}598

599
H /= 60; // sector 0 to 5600
i = floor( H );601
f = H - i; // factorial part of h602
p = V * ( 1 - S );603
q = V * ( 1 - S * f );604
t = V * ( 1 - S * ( 1 - f ) );605

606
switch( i )607
{608
case 0:609
*R = V;610
*G = t;611
*B = p;612
break;613
case 1:614
*R = q;615
*G = V;616
*B = p;617
break;618
case 2:619
*R = p;620
*G = V;621
*B = t;622
break;623
case 3:624
*R = p;625
*G = q;626
*B = V;627
break;628
case 4:629
*R = t;630
*G = p;631
*B = V;632
break;633
default: // case 5:634
*R = V;635
*G = p;636
*B = q;637
break;638
}639
}640

641
/**642
* 直方图均衡化643
* 返回 0正确 -1错误644
*/645
int HistEqualization(Bitmap* dstBmp, Bitmap* srcBmp)646
{647
return 0;648
}649

650
/*651
* 中值滤波652
*/653

654
int MedFilt(Bitmap* dstBmp, Bitmap* srcBmp)655
{656
return 0;657
}658

659
#endif /* BASICPROCESS_H_ */
