MFC对话框绘制灰度直方图.docx
- 文档编号:27119466
- 上传时间:2023-06-27
- 格式:DOCX
- 页数:23
- 大小:1.15MB
MFC对话框绘制灰度直方图.docx
《MFC对话框绘制灰度直方图.docx》由会员分享,可在线阅读,更多相关《MFC对话框绘制灰度直方图.docx(23页珍藏版)》请在冰豆网上搜索。
MFC对话框绘制灰度直方图
MFC对话框绘制灰度直方图
一.程序运行结果
该篇文章主要是在上一篇文章基础上进行的讲解,其中当打开一BMP图像后,点击”直方图“-》”显示原图直方图“如下。
二.灰度直方图原理
什么是灰度直方图?
灰度直方图(histogram)是灰度级的函数,描述的是图像中每种灰度级像素的个数,反映图像中每种灰度出现的频率。
横坐标是灰度级,纵坐标是灰度级出现的频率。
对于连续图像,平滑地从中心的高灰度级变化到边缘的低灰度级。
直方图定义为:
其中A(D)为阈值面积函数:
为一幅连续图像中被具有灰度级D的所有轮廓线所包围的面积。
对于离散函数,固定ΔD为1,则:
H(D)=A(D)-A(D+1)
色彩直方图是高维直方图的特例,它统计色彩的出现频率,即色彩概率分布信息。
通常这需要一定的量化过程,将色彩分成若干互不重叠的种类。
一般不直接在RGB色彩空间中统计,而是在将亮度分离出来后,对代表色彩部分的信息进行统计,如在HSI空间的HS子空间、YUV空间的UV子空间,以及其它反映人类视觉特点的彩色空间表示中进行。
其中直方图的计算方法如下:
依据定义,若图像具有L(通常L=256,即8位灰度级)级灰度,则大小为MxN的灰度图像f(x,y)的灰度直方图hist[0…L-1]可用如下计算获得。
1、初始化hist[k]=0;k=0,…,L-1
2、统计hist[f(x,y)]++;x=0,…,M-1,y=0,…,N-1
3、归一化hist[f(x,y)]/=M*N
那么说了这么多,直方图究竟有什么作用呢?
在使用轮廓线确定物体边界时,通过直方图更好的选择边界阈值,进行阈值化处理;对物体与背景有较强对比的景物的分割特别有用;简单物体的面积和综合光密度IOD可以通过图像的直方图求得。
三.程序实现
1.建立直方图对话框
第一步:
创建Dialog
将视图切换到ResourceView界面,选中Dialog右键鼠标新建一个Dialog,并新建一个名为IDD_DIALOG_ZFT,设置成下图对话框。
右键添加属性如下:
对话框-原始直方图-IDD_DIALOG_ZFT
组框-RGB-IDC_STATIC_RGB
图像-框架-IDC_STATIC_KJ-蚀刻(重点:
有它才能添加直方图在此处,注意GetDlgItem()函数中是IDC而不是IDD对话框)
添加蚀刻线(图像蚀刻形成的直线)形如图中的3个矩形框,并添加静态文本:
Red、Green、Blue、红、绿、蓝、像素、平均灰度、中值灰度、标准差;这些静态文本都是IDC_STATIC且为默认属性
添加红色4个值(Static)、绿色4个值、蓝色4个值,分别为:
IDC_STATIC_XS_RED(GREENBLUE)对应像素XS
IDC_STATIC_PJHD_RED(GREENBLUE)对应平均灰度PJHD
IDC_STATIC_ZZHD_RED(GREEDBLUE)对应中值灰度ZZHD
IDC_STATIC_BZC_RED(GREENBLUE)对应标准差BZC
第二步:
建立类向导MFCClassWizard
(1)在对话框资源模板空白区双击鼠标(Ctrl+W),创建一个新类,命名为CImageZFTDlg会自动生成它的.h和.cpp文件。
在类向导中选中类名CImageZFTDlg,IDs为CImageZFTDlg,WM_INITDIALOG建立这个函数用于初始化。
(2)打开类向导,选择MemberVariables页面,添加如下变量,类型均为CString。
像素m_redXS、m_greenXS、m_blueXS
标准差m_redBZC、m_greeenBZC、m_blueBZC
平均灰度m_redPJHD、m_greenPJHD、m_bluePJHD
中值灰度m_redZZHD、m_greenZZHD、m_blueZZHD
(3)在View.cpp中添加直方图的头文件#inlcude"ImageZFTDlg.h"
第三步:
设置菜单栏调用直方图对话框
(1)将视图切换到ResourceView界面,选中Menu,在IDR_MAINFRAM中添加菜单项“直方图”,菜单属性中选择“弹出”,在“直方图”中添加子菜单“显示原图直方图”。
(2)设置其属性为ID_ZFT_YT(显示直方图原图),同时建立类向导,选择ID_ZFT_YT(IDs),通过COMMAND建立显示直方图函数OnZftYt()。
第四步:
添加代码及计算4个值
在ImageProcessingView.cpp中添加如下代码,注释中有如何求平均灰度、中值灰度和标准差的消息算法过程。
[cpp] viewplain copy
1.//引用显示直方图头文件
2.#include "ImageZFTDlg.h"
3.#include "math.h"
4.
5./*全局变量在TestZFTDlg.cpp中引用 用extern*/
6.int Red[256],Green[256],Blue[256];
7.
8./**************************************************/
9./* 添加直方图显示功能,并在直方图下方显示相关信息
10./* 如平均灰度、中值灰度、标准差和像素总数
11./* ID_ZFT_YT:
直方图原图显示
12./**************************************************/
13.void CImageProcessingView:
:
OnZftYt()
14.{
15. if(numPicture==0) {
16. AfxMessageBox("载入图片后才能显示原图直方图!
",MB_OK,0);
17. return;
18. }
19. AfxMessageBox("显示原图直方图!
",MB_OK,0);
20. CImageZFTDlg dlg;
21.
22. //打开临时的图片
23. FILE *fpo = fopen(BmpName,"rb");
24. fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);
25. fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);
26.
27. int i,j;
28. for(j=0;j<256;j++) { //定义数组并清零
29. Red[j]=0;
30. Green[j]=0;
31. Blue[j]=0;
32. }
33.
34. //计算4个数据
35. unsigned char red,green,blue;
36. int IntRed,IntGreen,IntBlue; //强制转换
37. double sumRedHD=0,sumGreenHD=0,sumBlueHD=0; //记录像素总的灰度值和
38. for(i=0; i 39. { 40. fread(&red,sizeof(char),1,fpo); 41. IntRed=int(red); 42. sumRedHD=sumRedHD+IntRed; 43. if( IntRed>=0 && IntRed<256 ) Red[IntRed]++; //像素0-255之间 44. 45. fread(&green,sizeof(char),1,fpo); 46. IntGreen=int(green); 47. sumGreenHD=sumGreenHD+IntGreen; 48. if( IntGreen>=0 && IntGreen<256 ) Green[IntGreen]++; 49. 50. fread(&blue,sizeof(char),1,fpo); 51. IntBlue=int(blue); 52. sumBlueHD=sumBlueHD+IntBlue; 53. if( IntBlue>=0 && IntBlue<256 ) Blue[IntBlue]++; 54. } 55. fclose(fpo); 56. 57. //像素: int型转换为CString型 58. dlg.m_redXS.Format("%d",m_nImage); 59. dlg.m_greenXS.Format("%d",m_nImage); 60. dlg.m_blueXS.Format("%d",m_nImage); 61. 62. //平均灰度值: 计算24位bmp图片的灰度值,我是记录RGB中的所有平均值 63. float pinRedHD,pinGreenHD,pinBlueHD; 64. pinRedHD=sumRedHD*3/m_nImage; 65. pinGreenHD=sumGreenHD*3/m_nImage; //平均灰度=总灰度/总像素 66. pinBlueHD=sumBlueHD*3/m_nImage; 67. 68. dlg.m_redPJHD.Format("%.2f",pinRedHD); 69. dlg.m_greenPJHD.Format("%.2f",pinGreenHD); 70. dlg.m_bluePJHD.Format("%.2f",pinBlueHD); 71. 72. /****************************************************************/ 73. /* 中值灰度: 算法重点(黄凯大神提供) 74. /* 中值灰度: 所有像素中的中位数,应该所有像素排序找到中间的灰度值 75. /* 算法: num[256]记录各灰度出现次数,sum+=num[i],找到sum=总像素/2 76. /****************************************************************/ 77. int sumRedZZHD=0,sumGreenZZHD=0,sumBlueZZHD=0; 78. int redZZHD,greenZZHD,blueZZHD; 79. for(i=0;i<256;i++) 80. { 81. sumRedZZHD=sumRedZZHD+Red[i]; 82. if(sumRedZZHD>=m_nImage/6) //m_nImage被分成3份RGB并且sum=总像素/2 83. { 84. redZZHD=i; 85. break; 86. } 87. } 88. for(i=0;i<256;i++) 89. { 90. sumGreenZZHD=sumGreenZZHD+Green[i]; 91. if(sumGreenZZHD>=m_nImage/6) //m_nImage被分成3份RGB并且sum=总像素/2 92. { 93. greenZZHD=i; 94. break; 95. } 96. } 97. for(i=0;i<256;i++) 98. { 99. sumBlueZZHD=sumBlueZZHD+Blue[i]; 100. if(sumBlueZZHD>=m_nImage/6) //m_nImage被分成3份RGB并且sum=总像素/2 101. { 102. blueZZHD=i; 103. break; 104. } 105. } 106. 107. dlg.m_redZZHD.Format("%d",redZZHD); 108. dlg.m_greenZZHD.Format("%d",greenZZHD); 109. dlg.m_blueZZHD.Format("%d",blueZZHD); 110. 111. /******************************************************************/ 112. /*标准差: 标准差=方差的算术平方根 113. /* 方差s^2=[(x1-x)^2+(x2-x)^2+......(xn-x)^2]/n 114. /* 算法: 不用开m_nImage数组进行计算 用num[256]中数进行 115. /* 方差=(平均灰度-i)*(平均灰度-i)*Red[i] 有Red[i]个灰度值为i的数 116. /******************************************************************/ 117. float redBZC,greenBZC,blueBZC; //标准差 118. double redFC=0,greenFC=0,blueFC=0; //方差 119. for(i=0;i<256;i++) 120. { 121. redFC=redFC+(pinRedHD-i)*(pinRedHD-i)*Red[i]; //有Red[i]个像素i 122. greenFC=greenFC+(pinGreenHD-i)*(pinGreenHD-i)*Green[i]; 123. blueFC=blueFC+(pinBlueHD-i)*(pinBlueHD-i)*Blue[i]; 124. } 125. 126. redBZC=sqrt(redFC*3/m_nImage); 127. greenBZC=sqrt(greenFC*3/m_nImage); 128. blueBZC=sqrt(blueFC*3/m_nImage); 129. 130. dlg.m_redBZC.Format("%.2lf",redBZC); 131. dlg.m_greenBZC.Format("%.2lf",greenBZC); 132. dlg.m_blueBZC.Format("%.2lf",blueBZC); 133. 134. //重点必须添加该语句才能弹出对话框 135. if(dlg.DoModal()==IDOK) 136. { 137. 138. } 139.} 第五步: 此时运行结果如下图所示,打开图片可以显示参数。 2.建立对话框与View联系并绘制直方图 重点(极其重要*) (1)如何在MFC中(View中)实现对子对话框的画图或直方图响应? 解决方法: 在子对话框中.cpp文件中实现画图响应,不要再View.cpp中实现,否则图像会以menu背景为坐标,而在ImageZFTDlg.cpp中建立OnPaint函数实现画图,它默认会以子对话框为标准。 (2)如何把View.cpp中的图片像素直方图信息传递给子对话框ImageZFTDlg.cpp呢? 解决方法: 如果自定义ImageStruct.h中建立全局变量,每个.cpp中引用该头文件调用总是报错(未知),所以我在View.h中建立一个全局变量intRed[256];再在子文件.cpp中函数里调用该全局变量即可externintRed[256],这是非常重要的一个C语言知识。 (3)画图函数OnPaint()参考源代码中详细注释。 如何绘制坐标轴、文字、图像,其实自己绘制而没调用第三方库还是挺有意思的。 第一步: 建立画直方图函数OnPaint 打开类向导(Ctrl+W),类名选择CImageZFTDlg,IDs选择CImageZFTDlg,在Message函数中建立WM_PAINT映射,默认函数名为OnPaint建立函数voidCImageZFTDlg: : OnPaint() 第二步: 绘制直方图大致思想如下 (1)重点: 获取要绘制直方图的位置和图像资源的对应号ID(IDC_STATIC_KJ框架),我当时认为绘制直方图只能绘制到”图像“控件IDC中,不能是对话框IDD。 CWnd*pWnd=GetDlgItem(IDC_STATIC_KJ); CDC*pDC=pWnd->GetDC(); (2)获取对话框矩形的长和宽 CRectrectpic; GetDlgItem(IDC_STATIC_KJ)->GetWindowRect(&rectpic); (3)创建画笔对象并对画笔进行颜色设置 CPen*RedPen=newCPen(); RedPen->CreatePen(PS_SOLID,1RGB(255,0,0)); (4)选中当前画笔并保存以前画笔 CGdiObject*RedOlderPen=pDC->SelectObject(RedPen); (5)绘制直方图(图像坐标自己算) 矩形pDC->Rectangle(9,327,312,468); 移动pDC->MoveTo(15,331); 直线pDC->LineTo(15,488); 文字pDC->TextOut(15+48*i,450,str); (6)恢复以前画笔 pDC->SelectObject(RedOlderPen); deleteRedPen; ReleaseDC(pDC); 第三步: 源代码与详细注释思想 在ImageZFTDlg.cpp中修改OnPaint函数: [cpp] viewplain copy 1.//****************绘制原图直方图*********************// 2.void CImageZFTDlg: : OnPaint() 3.{ 4. CPaintDC dc(this); // device context for painting 5. 6. // TODO: Add your message handler code here 7. 8. /********************************************************************************/ 9. /* 重点知识: (XX) 10. /* 如何在View.cpp中把一个变量的值传给其它对话框 11. /* 12. /* 错误一: 在View.h中定义的pubic变量只能在View.cpp中用 13. /* 错误二: 定义一个Struct.h中存全局变量,在2个函数中分别调用#include "Struct.h" 14. /*
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- MFC 对话框 绘制 灰度 直方图