【数字图像处理】四.MFC对话框绘制灰度直方图
生活随笔
收集整理的這篇文章主要介紹了
【数字图像处理】四.MFC对话框绘制灰度直方图
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
? ? ? ? 本文主要講述基于VC++6.0 MFC圖像處理的應(yīng)用知識,主要結(jié)合自己大三所學(xué)課程《數(shù)字圖像處理》及課件進(jìn)行回憶講解,主要通過MFC單文檔視圖實(shí)現(xiàn)點(diǎn)擊彈出對話框繪制BMP圖片的灰度直方圖,再獲取平均灰度、中指灰度和標(biāo)準(zhǔn)差等值。文章比較詳細(xì)基礎(chǔ),希望該篇文章對你有所幫助~
? ? ? ?【數(shù)字圖像處理】一.MFC詳解顯示BMP格式圖片
? ? ? ?【數(shù)字圖像處理】二.MFC單文檔分割窗口顯示圖片
? ? ? ?【數(shù)字圖像處理】三.MFC實(shí)現(xiàn)圖像灰度、采樣和量化功能詳解
? ? ? ??免費(fèi)資源下載地址:
? ? ? ??http://download.csdn.net/detail/eastmount/8757243
? ? ? ??對于連續(xù)圖像,平滑地從中心的高灰度級變化到邊緣的低灰度級。直方圖定義為:
? ? ? ??其中A(D)為閾值面積函數(shù):為一幅連續(xù)圖像中被具有灰度級D的所有輪廓線所包圍的面積。對于離散函數(shù),固定ΔD為1,則:H(D)=A(D)-A(D+1)
? ? ? ??色彩直方圖是高維直方圖的特例,它統(tǒng)計色彩的出現(xiàn)頻率,即色彩概率分布信息。
? ? ? ??通常這需要一定的量化過程,將色彩分成若干互不重疊的種類。一般不直接在RGB色彩空間中統(tǒng)計,而是在將亮度分離出來后,對代表色彩部分的信息進(jìn)行統(tǒng)計,如在HSI空間的HS子空間、YUV空間的UV子空間,以及其它反映人類視覺特點(diǎn)的彩色空間表示中進(jìn)行。
? ? ? ? 其中直方圖的計算方法如下:
? ? ? ??依據(jù)定義,若圖像具有L(通常L=256,即8位灰度級)級灰度,則大小為MxN的灰度圖像f(x,y)的灰度直方圖hist[0…L-1]可用如下計算獲得。
? ? ? ? 1、初始化 hist[k]=0; k=0,…,L-1?
? ? ? ? 2、統(tǒng)計 hist[f(x,y)]++; x=0,…,M-1, y =0,…,N-1?
? ? ? ? 3、歸一化 hist[f(x,y)]/=M*N?
? ? ? ? 那么說了這么多,直方圖究竟有什么作用呢?
? ? ? ? 在使用輪廓線確定物體邊界時,通過直方圖更好的選擇邊界閾值,進(jìn)行閾值化處理;對物體與背景有較強(qiáng)對比的景物的分割特別有用;簡單物體的面積和綜合光密度IOD可以通過圖像的直方圖求得。
? ? ? ? 將視圖切換到ResourceView界面,選中Dialog右鍵鼠標(biāo)新建一個Dialog,并新建一個名為IDD_DIALOG_ZFT,設(shè)置成下圖對話框。
? ? ? ? 右鍵添加屬性如下:
? ? ? ? 對話框-原始直方圖-IDD_DIALOG_ZFT
? ? ? ? 組框-RGB-IDC_STATIC_RGB
? ? ? ? 圖像-框架-IDC_STATIC_KJ-蝕刻(重點(diǎn):有它才能添加直方圖在此處,注意GetDlgItem()函數(shù)中是IDC而不是IDD對話框)
? ? ? ? 添加蝕刻線(圖像蝕刻形成的直線)形如圖中的3個矩形框,并添加靜態(tài)文本:Red、Green、Blue、紅、綠、藍(lán)、像素、平均灰度、中值灰度、標(biāo)準(zhǔn)差;這些靜態(tài)文本都是IDC_STATIC且為默認(rèn)屬性
? ? ? ? 添加紅色4個值(Static)、綠色4個值、藍(lán)色4個值,分別為:
? ? ? ? IDC_STATIC_XS_RED(GREEN BLUE)對應(yīng)像素XS
? ? ? ? IDC_STATIC_PJHD_RED(GREEN BLUE)對應(yīng)平均灰度PJHD
? ? ? ? IDC_STATIC_ZZHD_RED(GREED BLUE)對應(yīng)中值灰度ZZHD
? ? ? ? IDC_STATIC_BZC_RED(GREEN BLUE)對應(yīng)標(biāo)準(zhǔn)差BZC
? ? ? ?第二步:建立類向?qū)FC ClassWizard
? ? ? ? (1) 在對話框資源模板空白區(qū)雙擊鼠標(biāo)(Ctrl+W),創(chuàng)建一個新類,命名為CImageZFTDlg會自動生成它的.h和.cpp文件。在類向?qū)е羞x中類名CImageZFTDlg,IDs為CImageZFTDlg,WM_INITDIALOG建立這個函數(shù)用于初始化。
? ? ? ? (2) 打開類向?qū)?#xff0c;選擇Member Variables頁面,添加如下變量,類型均為CString。
? ? ? ? 像素 m_redXS、m_greenXS、m_blueXS
? ? ? ? 標(biāo)準(zhǔn)差 m_redBZC、m_greeenBZC、m_blueBZC
? ? ? ? 平均灰度 m_redPJHD、m_greenPJHD、m_bluePJHD
? ? ? ? 中值灰度 m_redZZHD、m_greenZZHD、m_blueZZHD
? ? ? ? (3) 在View.cpp中添加直方圖的頭文件 #include "ImageZFTDlg.h"
? ? ? ? 第三步:設(shè)置菜單欄調(diào)用直方圖對話框
? ? ? ? (1) 將視圖切換到ResourceView界面,選中Menu,在IDR_MAINFRAM中添加菜單項(xiàng)“直方圖”,菜單屬性中選擇“彈出”,在“直方圖”中添加子菜單“顯示原圖直方圖”。
? ? ? ? (2) 設(shè)置其屬性為ID_ZFT_YT(顯示直方圖原圖),同時建立類向?qū)?#xff0c;選擇ID_ZFT_YT(IDs),通過COMMAND建立顯示直方圖函數(shù)OnZftYt()。
? ? ? ?第四步:添加代碼及計算4個值
? ? ? ? 在ImageProcessingView.cpp中添加如下代碼,注釋中有如何求平均灰度、中值灰度和標(biāo)準(zhǔn)差的消息算法過程。
//引用顯示直方圖頭文件 #include "ImageZFTDlg.h" #include "math.h"/*全局變量在TestZFTDlg.cpp中引用 用extern*/ int Red[256],Green[256],Blue[256];/**************************************************/ /* 添加直方圖顯示功能,并在直方圖下方顯示相關(guān)信息 /* 如平均灰度、中值灰度、標(biāo)準(zhǔn)差和像素總數(shù) /* ID_ZFT_YT:直方圖原圖顯示 /**************************************************/ void CImageProcessingView::OnZftYt() {if(numPicture==0) {AfxMessageBox("載入圖片后才能顯示原圖直方圖!",MB_OK,0);return;}AfxMessageBox("顯示原圖直方圖!",MB_OK,0);CImageZFTDlg dlg;//打開臨時的圖片F(xiàn)ILE *fpo = fopen(BmpName,"rb");fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);int i,j;for(j=0;j<256;j++) { //定義數(shù)組并清零Red[j]=0;Green[j]=0;Blue[j]=0;}//計算4個數(shù)據(jù)unsigned char red,green,blue;int IntRed,IntGreen,IntBlue; //強(qiáng)制轉(zhuǎn)換double sumRedHD=0,sumGreenHD=0,sumBlueHD=0; //記錄像素總的灰度值和for(i=0; i<m_nImage/3; i++ ) {fread(&red,sizeof(char),1,fpo);IntRed=int(red);sumRedHD=sumRedHD+IntRed;if( IntRed>=0 && IntRed<256 ) Red[IntRed]++; //像素0-255之間fread(&green,sizeof(char),1,fpo);IntGreen=int(green);sumGreenHD=sumGreenHD+IntGreen;if( IntGreen>=0 && IntGreen<256 ) Green[IntGreen]++;fread(&blue,sizeof(char),1,fpo);IntBlue=int(blue);sumBlueHD=sumBlueHD+IntBlue;if( IntBlue>=0 && IntBlue<256 ) Blue[IntBlue]++;}fclose(fpo);//像素:int型轉(zhuǎn)換為CString型 dlg.m_redXS.Format("%d",m_nImage);dlg.m_greenXS.Format("%d",m_nImage);dlg.m_blueXS.Format("%d",m_nImage);//平均灰度值:計算24位bmp圖片的灰度值,我是記錄RGB中的所有平均值 float pinRedHD,pinGreenHD,pinBlueHD; pinRedHD=sumRedHD*3/m_nImage;pinGreenHD=sumGreenHD*3/m_nImage; //平均灰度=總灰度/總像素pinBlueHD=sumBlueHD*3/m_nImage;dlg.m_redPJHD.Format("%.2f",pinRedHD);dlg.m_greenPJHD.Format("%.2f",pinGreenHD);dlg.m_bluePJHD.Format("%.2f",pinBlueHD);/****************************************************************//* 中值灰度:算法重點(diǎn)(黃凱大神提供) /* 中值灰度:所有像素中的中位數(shù),應(yīng)該所有像素排序找到中間的灰度值 /* 算法:num[256]記錄各灰度出現(xiàn)次數(shù),sum+=num[i],找到sum=總像素/2 /****************************************************************/int sumRedZZHD=0,sumGreenZZHD=0,sumBlueZZHD=0;int redZZHD,greenZZHD,blueZZHD;for(i=0;i<256;i++){sumRedZZHD=sumRedZZHD+Red[i];if(sumRedZZHD>=m_nImage/6) //m_nImage被分成3份RGB并且sum=總像素/2{redZZHD=i;break;}}for(i=0;i<256;i++){sumGreenZZHD=sumGreenZZHD+Green[i];if(sumGreenZZHD>=m_nImage/6) //m_nImage被分成3份RGB并且sum=總像素/2{greenZZHD=i;break;}}for(i=0;i<256;i++){sumBlueZZHD=sumBlueZZHD+Blue[i];if(sumBlueZZHD>=m_nImage/6) //m_nImage被分成3份RGB并且sum=總像素/2{blueZZHD=i;break;}}dlg.m_redZZHD.Format("%d",redZZHD);dlg.m_greenZZHD.Format("%d",greenZZHD);dlg.m_blueZZHD.Format("%d",blueZZHD);/******************************************************************//*標(biāo)準(zhǔn)差:標(biāo)準(zhǔn)差=方差的算術(shù)平方根 /* 方差s^2=[(x1-x)^2+(x2-x)^2+......(xn-x)^2]/n /* 算法:不用開m_nImage數(shù)組進(jìn)行計算 用num[256]中數(shù)進(jìn)行 /* 方差=(平均灰度-i)*(平均灰度-i)*Red[i] 有Red[i]個灰度值為i的數(shù) /******************************************************************/float redBZC,greenBZC,blueBZC; //標(biāo)準(zhǔn)差double redFC=0,greenFC=0,blueFC=0; //方差for(i=0;i<256;i++){redFC=redFC+(pinRedHD-i)*(pinRedHD-i)*Red[i]; //有Red[i]個像素igreenFC=greenFC+(pinGreenHD-i)*(pinGreenHD-i)*Green[i];blueFC=blueFC+(pinBlueHD-i)*(pinBlueHD-i)*Blue[i];}redBZC=sqrt(redFC*3/m_nImage);greenBZC=sqrt(greenFC*3/m_nImage);blueBZC=sqrt(blueFC*3/m_nImage);dlg.m_redBZC.Format("%.2lf",redBZC);dlg.m_greenBZC.Format("%.2lf",greenBZC);dlg.m_blueBZC.Format("%.2lf",blueBZC); //重點(diǎn)必須添加該語句才能彈出對話框if(dlg.DoModal()==IDOK){} }? ? ? ? 第五步:此時運(yùn)行結(jié)果如下圖所示,打開圖片可以顯示參數(shù)。
? ? ? ? (1) 如何在MFC中(View中)實(shí)現(xiàn)對子對話框的畫圖或直方圖響應(yīng)?
? ? ? ? 解決方法:在子對話框中.cpp文件中實(shí)現(xiàn)畫圖響應(yīng),不要再View.cpp中實(shí)現(xiàn),否則圖像會以menu背景為坐標(biāo),而在ImageZFTDlg.cpp中建立OnPaint函數(shù)實(shí)現(xiàn)畫圖,它默認(rèn)會以子對話框?yàn)闃?biāo)準(zhǔn)。
? ? ? ? (2) 如何把View.cpp中的圖片像素直方圖信息傳遞給子對話框ImageZFTDlg.cpp呢?
? ? ? ? 解決方法:如果自定義ImageStruct.h中建立全局變量,每個.cpp中引用該頭文件調(diào)用總是報錯(未知),所以我在View.h中建立一個全局變量int Red[256];再在子文件.cpp中函數(shù)里調(diào)用該全局變量即可extern int Red[256],這是非常重要的一個C語言知識。
? ? ? ? (3) 畫圖函數(shù)OnPaint()參考源代碼中詳細(xì)注釋。
? ? ? ? 如何繪制坐標(biāo)軸、文字、圖像,其實(shí)自己繪制而沒調(diào)用第三方庫還是挺有意思的。
? ? ? ? 第一步:建立畫直方圖函數(shù)OnPaint
? ? ? ? 打開類向?qū)?Ctrl+W),類名選擇CImageZFTDlg,IDs選擇CImageZFTDlg,在Message函數(shù)中建立WM_PAINT映射,默認(rèn)函數(shù)名為OnPaint建立函數(shù)void CImageZFTDlg::OnPaint()
? ? ? ? 第二步:繪制直方圖大致思想如下
? ? ? ? (1) 重點(diǎn):獲取要繪制直方圖的位置和圖像資源的對應(yīng)號ID(IDC_STATIC_KJ 框架),我當(dāng)時認(rèn)為繪制直方圖只能繪制到”圖像“控件IDC中,不能是對話框IDD。
? ? ? ? CWnd *pWnd = GetDlgItem(IDC_STATIC_KJ);
? ? ? ? CDC *pDC = pWnd->GetDC();
? ? ? ? (2) 獲取對話框矩形的長和寬
? ? ? ? CRect rectpic;
? ? ? ? GetDlgItem(IDC_STATIC_KJ)->GetWindowRect(&rectpic);
? ? ? ? (3) 創(chuàng)建畫筆對象并對畫筆進(jìn)行顏色設(shè)置
? ? ? ? CPen *RedPen = new CPen();
? ? ? ? RedPen->CreatePen(PS_SOLID,1RGB(255,0,0));
? ? ? ? (4) 選中當(dāng)前畫筆并保存以前畫筆
? ? ? ? CGdiObject *RedOlderPen = pDC->SelectObject(RedPen);
? ? ? ? (5) 繪制直方圖(圖像坐標(biāo)自己算)
? ? ? ? 矩形 pDC->Rectangle(9,327,312,468);
? ? ? ? 移動 pDC->MoveTo(15,331);
? ? ? ? 直線 pDC->LineTo(15,488);
? ? ? ? 文字 pDC->TextOut(15+48*i,450,str);
? ? ? ? (6) 恢復(fù)以前畫筆
? ? ? ? pDC->SelectObject(RedOlderPen);
? ? ? ? delete RedPen;
? ? ? ? ReleaseDC(pDC);
? ? ? ? 第三步:源代碼與詳細(xì)注釋思想
? ? ? ? 在ImageZFTDlg.cpp中修改OnPaint函數(shù):
//****************繪制原圖直方圖*********************// void CImageZFTDlg::OnPaint() {CPaintDC dc(this); // device context for painting// TODO: Add your message handler code here/********************************************************************************//* 重點(diǎn)知識:(百度) /* 如何在View.cpp中把一個變量的值傳給其它對話框 /* /* 錯誤一:在View.h中定義的pubic變量只能在View.cpp中用 /* 錯誤二:定義一個Struct.h中存全局變量,在2個函數(shù)中分別調(diào)用#include "Struct.h" /* /* 解決方法一: (CSDN 不會)參數(shù)用 A& a 兩個對話框里都可以訪問a /* 解決方法二: (CSDN 不會)重載 /* /* 解決:在View.cpp中定義全局變量 void CBmpDrawView::OnZftYt() 前面 并函數(shù)中操作 /* 在dialog的cpp中即void CTestZFTDlg::OnPaint()中在定義一個extern int a /********************************************************************************/extern int Red[256],Green[256],Blue[256];/*寫在該空間中可以省略Invalidate()語句*//*獲取控件的CDC指針*/CRect rectpic;GetDlgItem(IDC_STATIC_KJ)->GetWindowRect(&rectpic);int x,y;x=rectpic.Width();y=rectpic.Height();CWnd *pWnd=GetDlgItem(IDC_STATIC_KJ);CDC *pDC=pWnd->GetDC();/***********************//*重點(diǎn):畫直方圖 紅色/**********************/CPen *RedPen=new CPen(); //創(chuàng)建畫筆對象RedPen->CreatePen(PS_SOLID,1,RGB(255,0,0)); //紅色畫筆CGdiObject *RedOlderPen=pDC->SelectObject(RedPen); //選中當(dāng)前紅色畫筆并保存以前的畫筆/*畫圖*/pDC->Rectangle(9,16,312,147); //畫一個矩形框pDC->MoveTo(15,20); //繪制坐標(biāo)軸pDC->LineTo(15,128); //Y豎軸pDC->LineTo(305,128); //X橫軸pDC->MoveTo(305,128); //繪制X箭頭pDC->LineTo(300,123); //繪制上邊箭頭pDC->MoveTo(305,128); pDC->LineTo(300,133); //繪制下邊箭頭pDC->MoveTo(15,20); //繪制Y箭頭pDC->LineTo(10,25); //繪制左邊箭頭pDC->MoveTo(15,20);pDC->LineTo(20,25); //繪制右邊箭頭/**********************************************************************//* TextOut函數(shù)功能: /* 該函數(shù)用當(dāng)前選擇的字體、背景顏色和正文顏色將一個字符串寫到指定位置 /* BOOL TextOut(HDC hdc,int x,int y,LPCTSTR str,int numStr) /* 表示:x起始坐標(biāo),y起始坐標(biāo),字符串,字符串中字符個數(shù) /*/* SetTextColor函數(shù)功能: /* 設(shè)置指定設(shè)備環(huán)境(HDC)的字體顏色 /* SetTextColor (HDC, COLORREF) 如:SetTextColor(HDC,RGB(255,0,0)); /**********************************************************************/CString str;int i;for(i=0;i<=5;i++) //寫X軸刻度線{str.Format("%d",i*50); //0-255之間添加6個刻度值pDC->SetTextColor(RGB(255,0,255)); //設(shè)置字體顏色pDC->TextOut(15+48*i,130,str); //輸出字體pDC->MoveTo(15+48*i,128); //繪制X軸刻度pDC->LineTo(15+48*i,125);}for(i=0;i<=5;i++) //寫Y軸刻度線{pDC->MoveTo(15,128-20*i); //繪制Y軸刻度pDC->LineTo(18,128-20*i);}/*繪制直方圖主要的代碼*/for(i=1;i<256;i++){pDC->MoveTo(15+i,128);if( (128-16) > (Red[i]/40) )pDC->LineTo(15+i,128-(Red[i]/40)); elsepDC->LineTo(15+i,16); //超過矩形的畫矩形高}/**********************//*重點(diǎn):畫直方圖 綠色/**********************/CPen *GreenPen=new CPen(); //創(chuàng)建畫筆對象GreenPen->CreatePen(PS_SOLID,1,RGB(0,255,0)); //綠色畫筆CGdiObject *GreenOlderPen=pDC->SelectObject(GreenPen); pDC->Rectangle(9,167,312,308); //畫一個矩形框pDC->MoveTo(15,171); //繪制坐標(biāo)軸pDC->LineTo(15,288); //Y豎軸pDC->LineTo(305,288); //X橫軸pDC->MoveTo(305,288); //繪制X箭頭pDC->LineTo(300,283); //繪制上邊箭頭pDC->MoveTo(305,288);pDC->LineTo(300,293); //繪制下邊箭頭pDC->MoveTo(15,171); //繪制Y箭頭pDC->LineTo(10,176); //繪制左邊箭頭pDC->MoveTo(15,171);pDC->LineTo(20,176); //繪制右邊箭頭for(i=0;i<=5;i++) //寫X軸刻度線{str.Format("%d",i*50); //0-255之間添加6個刻度值pDC->SetTextColor(RGB(255,0,255)); //設(shè)置字體顏色pDC->TextOut(15+48*i,290,str); //輸出字體pDC->MoveTo(15+48*i,288); //繪制X軸刻度pDC->LineTo(15+48*i,285);}for(i=0;i<=5;i++) //寫Y軸刻度線{pDC->MoveTo(15,288-20*i); //繪制Y軸刻度pDC->LineTo(18,288-20*i);}/*繪制直方圖主要的代碼*/for(i=1;i<256;i++){pDC->MoveTo(15+i,288);if( (288-167) > (Green[i]/40) )pDC->LineTo(15+i,288-(Green[i]/40)); elsepDC->LineTo(15+i,167); //超過矩形的畫矩形高}/**********************//*重點(diǎn):畫直方圖 藍(lán)色/***************((*****/CPen *BluePen=new CPen(); //創(chuàng)建畫筆對象BluePen->CreatePen(PS_SOLID,1,RGB(0,0,255)); //藍(lán)色畫筆CGdiObject *BlueOlderPen=pDC->SelectObject(BluePen); pDC->Rectangle(9,327,312,468); //畫一個矩形框pDC->MoveTo(15,331); //繪制坐標(biāo)軸pDC->LineTo(15,448); //Y豎軸pDC->LineTo(305,448); //X橫軸pDC->MoveTo(305,448); //繪制X箭頭pDC->LineTo(300,443); //繪制上邊箭頭pDC->MoveTo(305,448);pDC->LineTo(300,453); //繪制下邊箭頭pDC->MoveTo(15,331); //繪制Y箭頭pDC->LineTo(10,336); //繪制左邊箭頭pDC->MoveTo(15,331);pDC->LineTo(20,336); //繪制右邊箭頭for(i=0;i<=5;i++) //寫X軸刻度線{str.Format("%d",i*50); //0-255之間添加6個刻度值pDC->SetTextColor(RGB(255,0,255)); //設(shè)置字體顏色pDC->TextOut(15+48*i,450,str); //輸出字體pDC->MoveTo(15+48*i,448); //繪制X軸刻度pDC->LineTo(15+48*i,445);}for(i=0;i<=5;i++) //寫Y軸刻度線{pDC->MoveTo(15,448-20*i); //繪制Y軸刻度pDC->LineTo(18,448-20*i);}/*繪制直方圖主要的代碼*/for(i=1;i<256;i++){pDC->MoveTo(15+i,448);if( (448-327) > (Blue[i]/40) )pDC->LineTo(15+i,448-(Blue[i]/40)); elsepDC->LineTo(15+i,327); //超過矩形的畫矩形高}//恢復(fù)以前的畫筆pDC->SelectObject(RedOlderPen);pDC->SelectObject(GreenOlderPen);pDC->SelectObject(BlueOlderPen);delete RedPen;delete GreenPen;delete BluePen;ReleaseDC(pDC);return;// Do not call CDialog::OnPaint() for painting messages }? ? ? ? 此時運(yùn)行程序即可顯示直方圖。
? ? ? ? 最后還是希望文章對你有所幫助,如果文章有不足或錯誤之處,請海涵~文章不僅僅講述了直方圖相關(guān)的知識,同時文章也給你提供了一種繪制坐標(biāo)圖像的思想和詳細(xì)注釋。有時候一直懷疑回憶這些知識會讓我停滯不前,但心安即好,何必在意!
? ? ? ? 從來沒有什么終南捷徑和大神,真正的捷徑只有三個:堅持、專注、認(rèn)真。其他的都是細(xì)枝末節(jié),做到這三個,其他的自然而然都會擁有。——同學(xué)CY
? ? ? (By:Eastmount 2015-5-31 下午3點(diǎn) ??http://blog.csdn.net/eastmount/)
? ? ? ?【數(shù)字圖像處理】一.MFC詳解顯示BMP格式圖片
? ? ? ?【數(shù)字圖像處理】二.MFC單文檔分割窗口顯示圖片
? ? ? ?【數(shù)字圖像處理】三.MFC實(shí)現(xiàn)圖像灰度、采樣和量化功能詳解
? ? ? ??免費(fèi)資源下載地址:
? ? ? ??http://download.csdn.net/detail/eastmount/8757243
一. 程序運(yùn)行結(jié)果
? ? ? ? 該篇文章主要是在上一篇文章基礎(chǔ)上進(jìn)行的講解,其中當(dāng)打開一張BMP圖像后,點(diǎn)擊”直方圖“-》”顯示原圖直方圖“如下。二. 灰度直方圖原理
? ? ? ? 什么是灰度直方圖?
? ? ? ? 灰度直方圖(histogram)是灰度級的函數(shù),描述的是圖像中每種灰度級像素的個數(shù),反映圖像中每種灰度出現(xiàn)的頻率。橫坐標(biāo)是灰度級,縱坐標(biāo)是灰度級出現(xiàn)的頻率。
? ? ? ??對于連續(xù)圖像,平滑地從中心的高灰度級變化到邊緣的低灰度級。直方圖定義為:
? ? ? ??其中A(D)為閾值面積函數(shù):為一幅連續(xù)圖像中被具有灰度級D的所有輪廓線所包圍的面積。對于離散函數(shù),固定ΔD為1,則:H(D)=A(D)-A(D+1)
? ? ? ??色彩直方圖是高維直方圖的特例,它統(tǒng)計色彩的出現(xiàn)頻率,即色彩概率分布信息。
? ? ? ??通常這需要一定的量化過程,將色彩分成若干互不重疊的種類。一般不直接在RGB色彩空間中統(tǒng)計,而是在將亮度分離出來后,對代表色彩部分的信息進(jìn)行統(tǒng)計,如在HSI空間的HS子空間、YUV空間的UV子空間,以及其它反映人類視覺特點(diǎn)的彩色空間表示中進(jìn)行。
? ? ? ? 其中直方圖的計算方法如下:
? ? ? ??依據(jù)定義,若圖像具有L(通常L=256,即8位灰度級)級灰度,則大小為MxN的灰度圖像f(x,y)的灰度直方圖hist[0…L-1]可用如下計算獲得。
? ? ? ? 1、初始化 hist[k]=0; k=0,…,L-1?
? ? ? ? 2、統(tǒng)計 hist[f(x,y)]++; x=0,…,M-1, y =0,…,N-1?
? ? ? ? 3、歸一化 hist[f(x,y)]/=M*N?
? ? ? ? 那么說了這么多,直方圖究竟有什么作用呢?
? ? ? ? 在使用輪廓線確定物體邊界時,通過直方圖更好的選擇邊界閾值,進(jìn)行閾值化處理;對物體與背景有較強(qiáng)對比的景物的分割特別有用;簡單物體的面積和綜合光密度IOD可以通過圖像的直方圖求得。
三. 程序?qū)崿F(xiàn)
1.建立直方圖對話框
? ? ? ?第一步:創(chuàng)建Dialog? ? ? ? 將視圖切換到ResourceView界面,選中Dialog右鍵鼠標(biāo)新建一個Dialog,并新建一個名為IDD_DIALOG_ZFT,設(shè)置成下圖對話框。
? ? ? ? 右鍵添加屬性如下:
? ? ? ? 對話框-原始直方圖-IDD_DIALOG_ZFT
? ? ? ? 組框-RGB-IDC_STATIC_RGB
? ? ? ? 圖像-框架-IDC_STATIC_KJ-蝕刻(重點(diǎn):有它才能添加直方圖在此處,注意GetDlgItem()函數(shù)中是IDC而不是IDD對話框)
? ? ? ? 添加蝕刻線(圖像蝕刻形成的直線)形如圖中的3個矩形框,并添加靜態(tài)文本:Red、Green、Blue、紅、綠、藍(lán)、像素、平均灰度、中值灰度、標(biāo)準(zhǔn)差;這些靜態(tài)文本都是IDC_STATIC且為默認(rèn)屬性
? ? ? ? 添加紅色4個值(Static)、綠色4個值、藍(lán)色4個值,分別為:
? ? ? ? IDC_STATIC_XS_RED(GREEN BLUE)對應(yīng)像素XS
? ? ? ? IDC_STATIC_PJHD_RED(GREEN BLUE)對應(yīng)平均灰度PJHD
? ? ? ? IDC_STATIC_ZZHD_RED(GREED BLUE)對應(yīng)中值灰度ZZHD
? ? ? ? IDC_STATIC_BZC_RED(GREEN BLUE)對應(yīng)標(biāo)準(zhǔn)差BZC
? ? ? ?第二步:建立類向?qū)FC ClassWizard
? ? ? ? (1) 在對話框資源模板空白區(qū)雙擊鼠標(biāo)(Ctrl+W),創(chuàng)建一個新類,命名為CImageZFTDlg會自動生成它的.h和.cpp文件。在類向?qū)е羞x中類名CImageZFTDlg,IDs為CImageZFTDlg,WM_INITDIALOG建立這個函數(shù)用于初始化。
? ? ? ? (2) 打開類向?qū)?#xff0c;選擇Member Variables頁面,添加如下變量,類型均為CString。
? ? ? ? 像素 m_redXS、m_greenXS、m_blueXS
? ? ? ? 標(biāo)準(zhǔn)差 m_redBZC、m_greeenBZC、m_blueBZC
? ? ? ? 平均灰度 m_redPJHD、m_greenPJHD、m_bluePJHD
? ? ? ? 中值灰度 m_redZZHD、m_greenZZHD、m_blueZZHD
? ? ? ? (3) 在View.cpp中添加直方圖的頭文件 #include "ImageZFTDlg.h"
? ? ? ? 第三步:設(shè)置菜單欄調(diào)用直方圖對話框
? ? ? ? (1) 將視圖切換到ResourceView界面,選中Menu,在IDR_MAINFRAM中添加菜單項(xiàng)“直方圖”,菜單屬性中選擇“彈出”,在“直方圖”中添加子菜單“顯示原圖直方圖”。
? ? ? ? (2) 設(shè)置其屬性為ID_ZFT_YT(顯示直方圖原圖),同時建立類向?qū)?#xff0c;選擇ID_ZFT_YT(IDs),通過COMMAND建立顯示直方圖函數(shù)OnZftYt()。
? ? ? ?第四步:添加代碼及計算4個值
? ? ? ? 在ImageProcessingView.cpp中添加如下代碼,注釋中有如何求平均灰度、中值灰度和標(biāo)準(zhǔn)差的消息算法過程。
//引用顯示直方圖頭文件 #include "ImageZFTDlg.h" #include "math.h"/*全局變量在TestZFTDlg.cpp中引用 用extern*/ int Red[256],Green[256],Blue[256];/**************************************************/ /* 添加直方圖顯示功能,并在直方圖下方顯示相關(guān)信息 /* 如平均灰度、中值灰度、標(biāo)準(zhǔn)差和像素總數(shù) /* ID_ZFT_YT:直方圖原圖顯示 /**************************************************/ void CImageProcessingView::OnZftYt() {if(numPicture==0) {AfxMessageBox("載入圖片后才能顯示原圖直方圖!",MB_OK,0);return;}AfxMessageBox("顯示原圖直方圖!",MB_OK,0);CImageZFTDlg dlg;//打開臨時的圖片F(xiàn)ILE *fpo = fopen(BmpName,"rb");fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);int i,j;for(j=0;j<256;j++) { //定義數(shù)組并清零Red[j]=0;Green[j]=0;Blue[j]=0;}//計算4個數(shù)據(jù)unsigned char red,green,blue;int IntRed,IntGreen,IntBlue; //強(qiáng)制轉(zhuǎn)換double sumRedHD=0,sumGreenHD=0,sumBlueHD=0; //記錄像素總的灰度值和for(i=0; i<m_nImage/3; i++ ) {fread(&red,sizeof(char),1,fpo);IntRed=int(red);sumRedHD=sumRedHD+IntRed;if( IntRed>=0 && IntRed<256 ) Red[IntRed]++; //像素0-255之間fread(&green,sizeof(char),1,fpo);IntGreen=int(green);sumGreenHD=sumGreenHD+IntGreen;if( IntGreen>=0 && IntGreen<256 ) Green[IntGreen]++;fread(&blue,sizeof(char),1,fpo);IntBlue=int(blue);sumBlueHD=sumBlueHD+IntBlue;if( IntBlue>=0 && IntBlue<256 ) Blue[IntBlue]++;}fclose(fpo);//像素:int型轉(zhuǎn)換為CString型 dlg.m_redXS.Format("%d",m_nImage);dlg.m_greenXS.Format("%d",m_nImage);dlg.m_blueXS.Format("%d",m_nImage);//平均灰度值:計算24位bmp圖片的灰度值,我是記錄RGB中的所有平均值 float pinRedHD,pinGreenHD,pinBlueHD; pinRedHD=sumRedHD*3/m_nImage;pinGreenHD=sumGreenHD*3/m_nImage; //平均灰度=總灰度/總像素pinBlueHD=sumBlueHD*3/m_nImage;dlg.m_redPJHD.Format("%.2f",pinRedHD);dlg.m_greenPJHD.Format("%.2f",pinGreenHD);dlg.m_bluePJHD.Format("%.2f",pinBlueHD);/****************************************************************//* 中值灰度:算法重點(diǎn)(黃凱大神提供) /* 中值灰度:所有像素中的中位數(shù),應(yīng)該所有像素排序找到中間的灰度值 /* 算法:num[256]記錄各灰度出現(xiàn)次數(shù),sum+=num[i],找到sum=總像素/2 /****************************************************************/int sumRedZZHD=0,sumGreenZZHD=0,sumBlueZZHD=0;int redZZHD,greenZZHD,blueZZHD;for(i=0;i<256;i++){sumRedZZHD=sumRedZZHD+Red[i];if(sumRedZZHD>=m_nImage/6) //m_nImage被分成3份RGB并且sum=總像素/2{redZZHD=i;break;}}for(i=0;i<256;i++){sumGreenZZHD=sumGreenZZHD+Green[i];if(sumGreenZZHD>=m_nImage/6) //m_nImage被分成3份RGB并且sum=總像素/2{greenZZHD=i;break;}}for(i=0;i<256;i++){sumBlueZZHD=sumBlueZZHD+Blue[i];if(sumBlueZZHD>=m_nImage/6) //m_nImage被分成3份RGB并且sum=總像素/2{blueZZHD=i;break;}}dlg.m_redZZHD.Format("%d",redZZHD);dlg.m_greenZZHD.Format("%d",greenZZHD);dlg.m_blueZZHD.Format("%d",blueZZHD);/******************************************************************//*標(biāo)準(zhǔn)差:標(biāo)準(zhǔn)差=方差的算術(shù)平方根 /* 方差s^2=[(x1-x)^2+(x2-x)^2+......(xn-x)^2]/n /* 算法:不用開m_nImage數(shù)組進(jìn)行計算 用num[256]中數(shù)進(jìn)行 /* 方差=(平均灰度-i)*(平均灰度-i)*Red[i] 有Red[i]個灰度值為i的數(shù) /******************************************************************/float redBZC,greenBZC,blueBZC; //標(biāo)準(zhǔn)差double redFC=0,greenFC=0,blueFC=0; //方差for(i=0;i<256;i++){redFC=redFC+(pinRedHD-i)*(pinRedHD-i)*Red[i]; //有Red[i]個像素igreenFC=greenFC+(pinGreenHD-i)*(pinGreenHD-i)*Green[i];blueFC=blueFC+(pinBlueHD-i)*(pinBlueHD-i)*Blue[i];}redBZC=sqrt(redFC*3/m_nImage);greenBZC=sqrt(greenFC*3/m_nImage);blueBZC=sqrt(blueFC*3/m_nImage);dlg.m_redBZC.Format("%.2lf",redBZC);dlg.m_greenBZC.Format("%.2lf",greenBZC);dlg.m_blueBZC.Format("%.2lf",blueBZC); //重點(diǎn)必須添加該語句才能彈出對話框if(dlg.DoModal()==IDOK){} }? ? ? ? 第五步:此時運(yùn)行結(jié)果如下圖所示,打開圖片可以顯示參數(shù)。
2.建立對話框與View聯(lián)系并繪制直方圖
? ? ? ? 重點(diǎn)(極其重要*)? ? ? ? (1) 如何在MFC中(View中)實(shí)現(xiàn)對子對話框的畫圖或直方圖響應(yīng)?
? ? ? ? 解決方法:在子對話框中.cpp文件中實(shí)現(xiàn)畫圖響應(yīng),不要再View.cpp中實(shí)現(xiàn),否則圖像會以menu背景為坐標(biāo),而在ImageZFTDlg.cpp中建立OnPaint函數(shù)實(shí)現(xiàn)畫圖,它默認(rèn)會以子對話框?yàn)闃?biāo)準(zhǔn)。
? ? ? ? (2) 如何把View.cpp中的圖片像素直方圖信息傳遞給子對話框ImageZFTDlg.cpp呢?
? ? ? ? 解決方法:如果自定義ImageStruct.h中建立全局變量,每個.cpp中引用該頭文件調(diào)用總是報錯(未知),所以我在View.h中建立一個全局變量int Red[256];再在子文件.cpp中函數(shù)里調(diào)用該全局變量即可extern int Red[256],這是非常重要的一個C語言知識。
? ? ? ? (3) 畫圖函數(shù)OnPaint()參考源代碼中詳細(xì)注釋。
? ? ? ? 如何繪制坐標(biāo)軸、文字、圖像,其實(shí)自己繪制而沒調(diào)用第三方庫還是挺有意思的。
? ? ? ? 第一步:建立畫直方圖函數(shù)OnPaint
? ? ? ? 打開類向?qū)?Ctrl+W),類名選擇CImageZFTDlg,IDs選擇CImageZFTDlg,在Message函數(shù)中建立WM_PAINT映射,默認(rèn)函數(shù)名為OnPaint建立函數(shù)void CImageZFTDlg::OnPaint()
? ? ? ? 第二步:繪制直方圖大致思想如下
? ? ? ? (1) 重點(diǎn):獲取要繪制直方圖的位置和圖像資源的對應(yīng)號ID(IDC_STATIC_KJ 框架),我當(dāng)時認(rèn)為繪制直方圖只能繪制到”圖像“控件IDC中,不能是對話框IDD。
? ? ? ? CWnd *pWnd = GetDlgItem(IDC_STATIC_KJ);
? ? ? ? CDC *pDC = pWnd->GetDC();
? ? ? ? (2) 獲取對話框矩形的長和寬
? ? ? ? CRect rectpic;
? ? ? ? GetDlgItem(IDC_STATIC_KJ)->GetWindowRect(&rectpic);
? ? ? ? (3) 創(chuàng)建畫筆對象并對畫筆進(jìn)行顏色設(shè)置
? ? ? ? CPen *RedPen = new CPen();
? ? ? ? RedPen->CreatePen(PS_SOLID,1RGB(255,0,0));
? ? ? ? (4) 選中當(dāng)前畫筆并保存以前畫筆
? ? ? ? CGdiObject *RedOlderPen = pDC->SelectObject(RedPen);
? ? ? ? (5) 繪制直方圖(圖像坐標(biāo)自己算)
? ? ? ? 矩形 pDC->Rectangle(9,327,312,468);
? ? ? ? 移動 pDC->MoveTo(15,331);
? ? ? ? 直線 pDC->LineTo(15,488);
? ? ? ? 文字 pDC->TextOut(15+48*i,450,str);
? ? ? ? (6) 恢復(fù)以前畫筆
? ? ? ? pDC->SelectObject(RedOlderPen);
? ? ? ? delete RedPen;
? ? ? ? ReleaseDC(pDC);
? ? ? ? 第三步:源代碼與詳細(xì)注釋思想
? ? ? ? 在ImageZFTDlg.cpp中修改OnPaint函數(shù):
//****************繪制原圖直方圖*********************// void CImageZFTDlg::OnPaint() {CPaintDC dc(this); // device context for painting// TODO: Add your message handler code here/********************************************************************************//* 重點(diǎn)知識:(百度) /* 如何在View.cpp中把一個變量的值傳給其它對話框 /* /* 錯誤一:在View.h中定義的pubic變量只能在View.cpp中用 /* 錯誤二:定義一個Struct.h中存全局變量,在2個函數(shù)中分別調(diào)用#include "Struct.h" /* /* 解決方法一: (CSDN 不會)參數(shù)用 A& a 兩個對話框里都可以訪問a /* 解決方法二: (CSDN 不會)重載 /* /* 解決:在View.cpp中定義全局變量 void CBmpDrawView::OnZftYt() 前面 并函數(shù)中操作 /* 在dialog的cpp中即void CTestZFTDlg::OnPaint()中在定義一個extern int a /********************************************************************************/extern int Red[256],Green[256],Blue[256];/*寫在該空間中可以省略Invalidate()語句*//*獲取控件的CDC指針*/CRect rectpic;GetDlgItem(IDC_STATIC_KJ)->GetWindowRect(&rectpic);int x,y;x=rectpic.Width();y=rectpic.Height();CWnd *pWnd=GetDlgItem(IDC_STATIC_KJ);CDC *pDC=pWnd->GetDC();/***********************//*重點(diǎn):畫直方圖 紅色/**********************/CPen *RedPen=new CPen(); //創(chuàng)建畫筆對象RedPen->CreatePen(PS_SOLID,1,RGB(255,0,0)); //紅色畫筆CGdiObject *RedOlderPen=pDC->SelectObject(RedPen); //選中當(dāng)前紅色畫筆并保存以前的畫筆/*畫圖*/pDC->Rectangle(9,16,312,147); //畫一個矩形框pDC->MoveTo(15,20); //繪制坐標(biāo)軸pDC->LineTo(15,128); //Y豎軸pDC->LineTo(305,128); //X橫軸pDC->MoveTo(305,128); //繪制X箭頭pDC->LineTo(300,123); //繪制上邊箭頭pDC->MoveTo(305,128); pDC->LineTo(300,133); //繪制下邊箭頭pDC->MoveTo(15,20); //繪制Y箭頭pDC->LineTo(10,25); //繪制左邊箭頭pDC->MoveTo(15,20);pDC->LineTo(20,25); //繪制右邊箭頭/**********************************************************************//* TextOut函數(shù)功能: /* 該函數(shù)用當(dāng)前選擇的字體、背景顏色和正文顏色將一個字符串寫到指定位置 /* BOOL TextOut(HDC hdc,int x,int y,LPCTSTR str,int numStr) /* 表示:x起始坐標(biāo),y起始坐標(biāo),字符串,字符串中字符個數(shù) /*/* SetTextColor函數(shù)功能: /* 設(shè)置指定設(shè)備環(huán)境(HDC)的字體顏色 /* SetTextColor (HDC, COLORREF) 如:SetTextColor(HDC,RGB(255,0,0)); /**********************************************************************/CString str;int i;for(i=0;i<=5;i++) //寫X軸刻度線{str.Format("%d",i*50); //0-255之間添加6個刻度值pDC->SetTextColor(RGB(255,0,255)); //設(shè)置字體顏色pDC->TextOut(15+48*i,130,str); //輸出字體pDC->MoveTo(15+48*i,128); //繪制X軸刻度pDC->LineTo(15+48*i,125);}for(i=0;i<=5;i++) //寫Y軸刻度線{pDC->MoveTo(15,128-20*i); //繪制Y軸刻度pDC->LineTo(18,128-20*i);}/*繪制直方圖主要的代碼*/for(i=1;i<256;i++){pDC->MoveTo(15+i,128);if( (128-16) > (Red[i]/40) )pDC->LineTo(15+i,128-(Red[i]/40)); elsepDC->LineTo(15+i,16); //超過矩形的畫矩形高}/**********************//*重點(diǎn):畫直方圖 綠色/**********************/CPen *GreenPen=new CPen(); //創(chuàng)建畫筆對象GreenPen->CreatePen(PS_SOLID,1,RGB(0,255,0)); //綠色畫筆CGdiObject *GreenOlderPen=pDC->SelectObject(GreenPen); pDC->Rectangle(9,167,312,308); //畫一個矩形框pDC->MoveTo(15,171); //繪制坐標(biāo)軸pDC->LineTo(15,288); //Y豎軸pDC->LineTo(305,288); //X橫軸pDC->MoveTo(305,288); //繪制X箭頭pDC->LineTo(300,283); //繪制上邊箭頭pDC->MoveTo(305,288);pDC->LineTo(300,293); //繪制下邊箭頭pDC->MoveTo(15,171); //繪制Y箭頭pDC->LineTo(10,176); //繪制左邊箭頭pDC->MoveTo(15,171);pDC->LineTo(20,176); //繪制右邊箭頭for(i=0;i<=5;i++) //寫X軸刻度線{str.Format("%d",i*50); //0-255之間添加6個刻度值pDC->SetTextColor(RGB(255,0,255)); //設(shè)置字體顏色pDC->TextOut(15+48*i,290,str); //輸出字體pDC->MoveTo(15+48*i,288); //繪制X軸刻度pDC->LineTo(15+48*i,285);}for(i=0;i<=5;i++) //寫Y軸刻度線{pDC->MoveTo(15,288-20*i); //繪制Y軸刻度pDC->LineTo(18,288-20*i);}/*繪制直方圖主要的代碼*/for(i=1;i<256;i++){pDC->MoveTo(15+i,288);if( (288-167) > (Green[i]/40) )pDC->LineTo(15+i,288-(Green[i]/40)); elsepDC->LineTo(15+i,167); //超過矩形的畫矩形高}/**********************//*重點(diǎn):畫直方圖 藍(lán)色/***************((*****/CPen *BluePen=new CPen(); //創(chuàng)建畫筆對象BluePen->CreatePen(PS_SOLID,1,RGB(0,0,255)); //藍(lán)色畫筆CGdiObject *BlueOlderPen=pDC->SelectObject(BluePen); pDC->Rectangle(9,327,312,468); //畫一個矩形框pDC->MoveTo(15,331); //繪制坐標(biāo)軸pDC->LineTo(15,448); //Y豎軸pDC->LineTo(305,448); //X橫軸pDC->MoveTo(305,448); //繪制X箭頭pDC->LineTo(300,443); //繪制上邊箭頭pDC->MoveTo(305,448);pDC->LineTo(300,453); //繪制下邊箭頭pDC->MoveTo(15,331); //繪制Y箭頭pDC->LineTo(10,336); //繪制左邊箭頭pDC->MoveTo(15,331);pDC->LineTo(20,336); //繪制右邊箭頭for(i=0;i<=5;i++) //寫X軸刻度線{str.Format("%d",i*50); //0-255之間添加6個刻度值pDC->SetTextColor(RGB(255,0,255)); //設(shè)置字體顏色pDC->TextOut(15+48*i,450,str); //輸出字體pDC->MoveTo(15+48*i,448); //繪制X軸刻度pDC->LineTo(15+48*i,445);}for(i=0;i<=5;i++) //寫Y軸刻度線{pDC->MoveTo(15,448-20*i); //繪制Y軸刻度pDC->LineTo(18,448-20*i);}/*繪制直方圖主要的代碼*/for(i=1;i<256;i++){pDC->MoveTo(15+i,448);if( (448-327) > (Blue[i]/40) )pDC->LineTo(15+i,448-(Blue[i]/40)); elsepDC->LineTo(15+i,327); //超過矩形的畫矩形高}//恢復(fù)以前的畫筆pDC->SelectObject(RedOlderPen);pDC->SelectObject(GreenOlderPen);pDC->SelectObject(BlueOlderPen);delete RedPen;delete GreenPen;delete BluePen;ReleaseDC(pDC);return;// Do not call CDialog::OnPaint() for painting messages }? ? ? ? 此時運(yùn)行程序即可顯示直方圖。
? ? ? ? 最后還是希望文章對你有所幫助,如果文章有不足或錯誤之處,請海涵~文章不僅僅講述了直方圖相關(guān)的知識,同時文章也給你提供了一種繪制坐標(biāo)圖像的思想和詳細(xì)注釋。有時候一直懷疑回憶這些知識會讓我停滯不前,但心安即好,何必在意!
? ? ? ? 從來沒有什么終南捷徑和大神,真正的捷徑只有三個:堅持、專注、認(rèn)真。其他的都是細(xì)枝末節(jié),做到這三個,其他的自然而然都會擁有。——同學(xué)CY
? ? ? (By:Eastmount 2015-5-31 下午3點(diǎn) ??http://blog.csdn.net/eastmount/)
總結(jié)
以上是生活随笔為你收集整理的【数字图像处理】四.MFC对话框绘制灰度直方图的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【数字图像处理】三.MFC实现图像灰度、
- 下一篇: 【数字图像处理】五.MFC图像点运算之灰