前提知识
用十六进制查看图像文件需要注意:
beyond compare/notpad++查看图像的十六进制文件,数值数据是小端模式存放的二进制和数据在内存中的表现一致,只是大于1字节的数据在内存中赋值(通过结构体赋值也是一样的)给相应的整型时,不用大小端转换,赋值后会直接得到整型的结果。
1. 什么是灰度图?
灰度图的RGB值相等
灰度图调色板的值就是ARGB 205,0,0,0到205,255,255,255的像素值,灰度图就是黑白两色在深度上面的变化256种黑白灰度颜色,不同于单纯黑白两色。灰度图的位图数据部分存放的是灰度图调色板的索引。
在非图像学术领域,灰度图的照片,灰度图的电影,也叫黑白照片和黑白电视,黑白电影。
一般使用8位的灰度图,但是医学,航拍中需要更高的精度,而采用16位灰度图像。
2.灰度图的作用?
3.真彩色图片转换为灰度图的常用方法?
第一种方法是根据YUV的颜色空间中,Y的分量的物理意义是点的亮度,由该值反映亮度等级,根据RGB和YUV颜色空间的变化关系可建立亮度Y与R、G、B三个颜色分量的对应:Y=0.3R+0.59G+0.11B,以这个亮度值表达图像的灰度值。
第二种方法使求出每个像素点的R、G、B三个分量的平均值,然后将这个平均值赋予给这个像素的三个分量。
具体这两种方法,根据什么应用更应该选择哪一种方法,我还不了解,知道的麻烦告诉我下,非常感激。
win32下代码实现例子:
#include <stdio.h> #include <string> #include <math.h> #include <windows.h> using namespace std; // 灰度图公式函数指针,缺点是函数调用降低了执行效率,优点是可以灵活的选择灰化公式 typedef int (*GrayFunction)(int nRed, int nGreen, int nBlue); // Gray = R*0.3+G*0.59+B*0.11 int RegularGray(int nRed, int nGreen, int nBlue) { // 转换为整型和位运算除法,可以更有效的提高效率 float fGray = /*float(*/0.3f * nRed + 0.59f * nGreen + 0.11f * nBlue; return int(fGray); } // Gray=(R+G+B)/3; int AverageGray(int nRed, int nGreen, int nBlue) { float fGray = float(nRed + nGreen + nBlue) / 3; return int(fGray); } //将位图转换为256色灰度图 void ToGray(const string& srcFile,const string& desFile, GrayFunction func) { BITMAPFILEHEADER bmfHeader; BITMAPINFOHEADER bmiHeader; FILE *pFile; if ((pFile = fopen(srcFile.c_str(),"rb")) == NULL) { printf("open bmp file error."); exit(-1); } //读取文件和Bitmap头信息 fseek(pFile,0,SEEK_SET); fread(&bmfHeader,sizeof(BITMAPFILEHEADER),1,pFile); fread(&bmiHeader,sizeof(BITMAPINFOHEADER),1,pFile); //先不支持16位位图 int bitCount = bmiHeader.biBitCount; if (bitCount == 16) { exit(-1); } double byteCount = (double)bitCount / 8; int nClr = 0; if (bitCount < 16) { nClr = bmiHeader.biClrUsed ? bmiHeader.biClrUsed : 1 << bitCount; if (nClr > 256) nClr = 0; } //读取调色板 RGBQUAD *quad = NULL; if (nClr > 0) { quad = new RGBQUAD[nClr]; fread(quad,sizeof(RGBQUAD) * nClr,1,pFile); } int srcW = bmiHeader.biWidth; int srcH = bmiHeader.biHeight; //原图像每一行去除偏移量的字节数 int lineSize = bitCount * srcW >> 3; //偏移量,windows系统要求每个扫描行按四字节对齐 // 数n加上一个数r-1,又与上非r-1,其实是求得数n加上足够的偏移后[n, n+r-1]内的关于r的唯一倍数k。 // 数k是数n不经过填充或者经过最小填充后的是r的倍数。 // alignBytes是不用填充或者填充后的,相对于原来的数,填充的字节数。 int alignBytes = (((bmiHeader.biWidth * bitCount + 31) & ~31) >> 3) - ((bmiHeader.biWidth * bitCount) >> 3); //原图像缓存 int srcBufSize = lineSize * srcH; BYTE* srcBuf = new BYTE[srcBufSize]; int i,j; //读取文件中数据 for (i = 0; i < srcH; i++) { // 按照BYTE读取进来,也就是BGRA形式读取进来到内存里面了。 fread(&srcBuf[lineSize * i],lineSize,1,pFile); fseek(pFile,alignBytes,SEEK_CUR); } //256色位图调色板 RGBQUAD testData,*pTestData = new RGBQUAD;// RGBQUAD结构体默认构造函数是给每个通道赋值204,new时候是给每个分量205. RGBQUAD *quadDes = NULL; quadDes = new RGBQUAD[256]; for (i = 0; i < 256; i++) { //灰度图的RGB值相等 // 灰度图调色板的值就是ARGB 205,0,0,0到205,255,255,255的像素值,灰度图就是黑白两色在深度上面的变化256种,不同于单纯黑白两色。 // 在非图像学术领域,灰度图的照片,灰度图的电影,也叫黑白照片和黑白电视,黑白电影。 quadDes[i].rgbBlue = quadDes[i].rgbGreen = quadDes[i].rgbRed = i; testData = quadDes[i]; //printf("testData: %d: %d: %d: %d\n",i,i,i,quadDes[i].rgbReserved); } delete pTestData; //灰度图每个像素采用8位表示,每行对齐的字节数(包括对齐填充字节),window需要按照4字节对齐。 int nLineByteCountIncludeAlign = (((srcW * 8 + 31) & ~31) >> 3); // 高度也是一个像素一个字节,所以desBufSize是总的图片位图数据字节数 int desBufSize = nLineByteCountIncludeAlign * srcH; BYTE *desBuf = new BYTE[desBufSize]; //每个扫描行占用字节数 int desLineSize = nLineByteCountIncludeAlign/*((srcW * 8 + 31) >> 5) * 4*/; for (i = 0; i < srcH; i++) { for (j = 0; j < srcW; j++) { //从调色板中读取RGB值 if (nClr > 0) { // 获得位图数据表示的调色板索引值 unsigned int pos = srcBuf[i * lineSize + int(j * byteCount)]; // 根据调色板索引到调色板取位图像素 desBuf[i * desLineSize + j] = func( quad[pos].rgbRed, quad[pos].rgbGreen, quad[pos].rgbBlue ); } else { // 直接从真彩色的位图数据中取得像素转换为灰度图索引 //srcBuf是BGRA方式将位图数据读取到内存里面去了 desBuf[i * desLineSize + j] = func( srcBuf[i * lineSize + int(j * byteCount) + 2] , \ srcBuf[i * lineSize + int(j * byteCount) + 1], \ srcBuf[i * lineSize + int(j * byteCount)] ); //printf("PixelIndexData: %d\n",desBuf[i * desLineSize + j]); } } } //创建目标文件 HFILE hfile = _lcreat(desFile.c_str(),0); //文件头信息 BITMAPFILEHEADER nbmfHeader; nbmfHeader.bfType = 0x4D42; nbmfHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD) + srcW * srcH; nbmfHeader.bfReserved1 = 0; nbmfHeader.bfReserved2 = 0; nbmfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD); //Bitmap头信息 BITMAPINFOHEADER bmi; bmi.biSize=sizeof(BITMAPINFOHEADER); bmi.biWidth=srcW; bmi.biHeight=srcH; bmi.biPlanes=1; bmi.biBitCount=8; bmi.biCompression=BI_RGB; bmi.biSizeImage=0; bmi.biXPelsPerMeter=0; bmi.biYPelsPerMeter=0; bmi.biClrUsed= 256; bmi.biClrImportant=0; //写入文件头信息 _lwrite(hfile,(LPCSTR)&nbmfHeader,sizeof(BITMAPFILEHEADER)); //写入Bitmap头信息 _lwrite(hfile,(LPCSTR)&bmi,sizeof(BITMAPINFOHEADER)); if (quadDes) { _lwrite(hfile,(LPCSTR)quadDes,sizeof(RGBQUAD) * 256); } //写入图像数据 _lwrite(hfile,(LPCSTR)desBuf,desBufSize); _lclose(hfile); if (quad) { delete[] quad; quad = NULL; } if (quadDes) { delete[] quadDes; quadDes = NULL; } } int main(int argc, char* argv[]) { string srcFile("f://data//apple.bmp"); string desFile("f://data//applegray2.bmp"); ToGray(srcFile,desFile, AverageGray/*RegularGray*/); system("pause"); return 0; }