灰度图是指用灰度表示的图像,灰度是在白色和黑色之间分的若干个等级,其中最常用的是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 }
转换前的图像:
转换后的图像:
联系客服