灰度图是指用灰度表示的图像,灰度是在白色和黑色之间分的若干个等级,其中最常用的是256级,也就是256级灰度图。灰度图在医学、航天等领域有着广泛的应用。
那么如何将一幅彩色图像转换为灰度图呢?根据人眼对红绿蓝三色的敏感程度,可以使用以下比例式进行转换:
Gray = R*0.3+G*0.59+B*0.11
这也是最常用的一种转换,另外还有一种常用的转换叫平均值法,即取去红绿蓝三色的平均值为灰度:
Gray=(R+G+B)/3;
下面来写一段程序实现第一种转换算法,显然对于对于一幅256级灰度图,每个像素采用一个字节表示足矣,即为8位位图。对于8位位图的保存,真正的RGB数据是保存在调色板里的,而实际的数据仅仅是保存的调色板内的像素值的索引。
1 #include "stdafx.h"
2 #include
<stdio.h>
3 #include
<string>
4 #include
<math.h>
5 #include
<windows.h>
6 using
namespace std;
7
8
9 //将位图转换为256色灰度图
10 void ToGray(const
string& srcFile,const
string& desFile)
11 {
12 BITMAPFILEHEADER bmfHeader;
13 BITMAPINFOHEADER bmiHeader;
14
15 FILE
*pFile;
16 if ((pFile
= fopen(srcFile.c_str(),"rb"))
== NULL)
17 {
18 printf("open bmp file error.");
19 exit(-1);
20 }
21 //读取文件和Bitmap头信息
22 fseek(pFile,0,SEEK_SET);
23 fread(&bmfHeader,sizeof(BITMAPFILEHEADER),1,pFile);
24 fread(&bmiHeader,sizeof(BITMAPINFOHEADER),1,pFile);
25 //先不支持16位位图
26 int bitCount
= bmiHeader.biBitCount;
27 if (bitCount
== 16)
28 {
29 exit(-1);
30 }
31 double byteCount
= (double)bitCount
/ 8;
32 int nClr
= 0;
33 if (bitCount
< 16)
34 {
35 nClr
= bmiHeader.biClrUsed
? bmiHeader.biClrUsed :
1 << bitCount;
36 if (nClr
> 256)
37 nClr
= 0;
38 }
39 //读取调色板
40 RGBQUAD
*quad
= NULL;
41 if (nClr
> 0)
42 {
43 quad
= new RGBQUAD[nClr];
44 fread(quad,sizeof(RGBQUAD)
* nClr,1,pFile);
45 }
46
47 int srcW
= bmiHeader.biWidth;
48 int srcH
= bmiHeader.biHeight;
49 //原图像每一行去除偏移量的字节数
50 int lineSize
= bitCount
* srcW >>
3;
51 //偏移量,windows系统要求每个扫描行按四字节对齐
52 int alignBytes
= (((bmiHeader.biWidth
* bitCount
+ 31)
& ~31)
>> 3)
53 - ((bmiHeader.biWidth
* bitCount)
>> 3);
54 //原图像缓存
55 int srcBufSize
= lineSize
* srcH;
56 BYTE* srcBuf
= new BYTE[srcBufSize];
57 int i,j;
58 //读取文件中数据
59 for (i
= 0; i
< srcH; i++)
60 {
61 fread(&srcBuf[lineSize
* i],lineSize,1,pFile);
62 fseek(pFile,alignBytes,SEEK_CUR);
63 }
64 //256色位图调色板
65 RGBQUAD
*quadDes
= NULL;
66 quadDes
= new RGBQUAD[256];
67 for (i
= 0; i
< 256; i++)
68 {
69 //灰度图的RGB值相等
70 quadDes[i].rgbBlue
= quadDes[i].rgbGreen
= quadDes[i].rgbRed
= i;
71 }
72 //灰度图每个像素采用8位表示
73 int desBufSize
= (((srcW
* 8
+ 31)
& ~31)
>> 3)
* srcH;
74 BYTE
*desBuf =
new BYTE[desBufSize];
75 //每个扫描行占用字节数
76 int desLineSize
= ((srcW
* 8
+ 31)
>> 5)
* 4;
77
78 for (i
= 0; i
< srcH; i++)
79 {
80 for (j
= 0; j
< srcW; j++)
81 {
82 //从调色板中读取RGB值
83 if (nClr
> 0)
84 {
85 unsigned
int pos
= srcBuf[i
* lineSize
+ int(j
* byteCount)];
86 desBuf[i
* desLineSize
+ j]
= 0.3
* quad[pos].rgbRed
87 +
0.59
* quad[pos].rgbGreen
88 +
0.11
* quad[pos].rgbBlue;
89 }
90 else
91 desBuf[i
* desLineSize
+ j]
= 0.3
* srcBuf[i
* lineSize
+ int(j
* byteCount)]
92 +
0.59
* srcBuf[i
* lineSize
+ int(j
* byteCount)
+ 1]
93 +
0.11
* srcBuf[i
* lineSize
+ int(j
* byteCount)
+ 2];
94 }
95 }
96
97 //创建目标文件
98 HFILE hfile
= _lcreat(desFile.c_str(),0);
99 //文件头信息
100 BITMAPFILEHEADER nbmfHeader;
101 nbmfHeader.bfType
= 0x4D42;
102 nbmfHeader.bfSize
= sizeof(BITMAPFILEHEADER)
+ sizeof(BITMAPINFOHEADER)
103 +
256
* sizeof(RGBQUAD)
+ srcW
* srcH;
104 nbmfHeader.bfReserved1
= 0;
105 nbmfHeader.bfReserved2
= 0;
106 nbmfHeader.bfOffBits
= sizeof(BITMAPFILEHEADER)
+ sizeof(BITMAPINFOHEADER)
+ 256
* sizeof(RGBQUAD);
107 //Bitmap头信息
108 BITMAPINFOHEADER bmi;
109 bmi.biSize=sizeof(BITMAPINFOHEADER);
110 bmi.biWidth=srcW;
111 bmi.biHeight=srcH;
112 bmi.biPlanes=1;
113 bmi.biBitCount=8;
114 bmi.biCompression=BI_RGB;
115 bmi.biSizeImage=0;
116 bmi.biXPelsPerMeter=0;
117 bmi.biYPelsPerMeter=0;
118 bmi.biClrUsed=
256;
119 bmi.biClrImportant=0;
120
121 //写入文件头信息
122 _lwrite(hfile,(LPCSTR)&nbmfHeader,sizeof(BITMAPFILEHEADER));
123 //写入Bitmap头信息
124 _lwrite(hfile,(LPCSTR)&bmi,sizeof(BITMAPINFOHEADER));
125 if (quadDes)
126 {
127 _lwrite(hfile,(LPCSTR)quadDes,sizeof(RGBQUAD)
* 256);
128 }
129 //写入图像数据
130 _lwrite(hfile,(LPCSTR)desBuf,desBufSize);
131 _lclose(hfile);
132 if (quad)
133 {
134 delete[] quad;
135 quad
= NULL;
136 }
137 if (quadDes)
138 {
139 delete[] quadDes;
140 quadDes
= NULL;
141 }
142 }
143
144 int main(int argc,
char* argv[])
145 {
146 string srcFile("e://t.bmp");
147 string desFile("e://gray.bmp");
148 ToGray(srcFile,desFile);
149 system("pause");
150 return
0;
151 }
转换前的图像:
转换后的图像: