灰度图的理解
前提知識
用十六進制查看圖像文件需要注意:
beyond compare/notpad++查看圖像的十六進制文件,數值數據是小端模式存放的二進制和數據在內存中的表現一致,只是大于1字節的數據在內存中賦值(通過結構體賦值也是一樣的)給相應的整型時,不用大小端轉換,賦值后會直接得到整型的結果。
1. 什么是灰度圖?
灰度圖的RGB值相等
灰度圖調色板的值就是ARGB 205,0,0,0到205,255,255,255的像素值,灰度圖就是黑白兩色在深度上面的變化256種黑白灰度顏色,不同于單純黑白兩色。灰度圖的位圖數據部分存放的是灰度圖調色板的索引。
在非圖像學術領域,灰度圖的照片,灰度圖的電影,也叫黑白照片和黑白電視,黑白電影。
一般使用8位的灰度圖,但是醫學,航拍中需要更高的精度,而采用16位灰度圖像。
2.灰度圖的作用?
例如視頻目標跟蹤和識別時,第一步就是要轉換為灰度圖?,F有的成熟分析算法多是基于灰度圖像的,灰度圖像綜合了真彩色位圖的RGB各通道的信息。
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; }
總結
- 上一篇: 用蒙特卡洛法实现对排队等待问题的计算机模
- 下一篇: QuickBooks 2018 For