1.项目名称:手写数字识别系统
2.项目内容
设计一个简单的手写数字识别系统,能够识别手写输入的数字1-9。目前像汉王公司推出了一系列的手写笔等产品,通过实现这样的一个简单功能可以有效地学习VC++基于MFC的编程,同时对于手写笔这样的产品的工作原理能够产生一定的了解!
3.功能设计思路
手写数字识别的难度在于其形状很多,对于规范的手写数字,可以采用模板匹配的方法,但是由于个人的字体不尽相同,导致数字可大可小,或胖或瘦,采用模板匹配就行不通了,一中解决方案是以数字的笔画特征区别手写的数字。以数字2为例,其笔画为向右,向左下,向右的顺序。
4.编码实施
首先建立一个利用向导生成一个MFC程序名为RegFigure;修改应用程序图标过程如下:
1.在资源视图中“Icon”下选择“IDR_MAINFRAME”图标,然后用“Delete”键删除。(在Windows的资源管理器里面,打开当前项目下的“res”子目录,删除相应的图标文件。)
2.在资源视图中“~.rc”下,用右键菜单的“import...”,然后在对话框中选择“Icon”导入一个图标。
3.在属性工具中,将新建的图标的ID改为“IDR_MAINFRAME”(默认为“IDI_ICON1”)。
在资源视图中右击“插入对话框”生成一个对话框IDD_DIG_Reg,利用MFC ClassWizard生成对话框类RegDlg,在类视图中右击RegDlg添加成员函数和成员变量,如图
增加手写数字输入菜单和手写数字输入工具按钮将ID号都改为:ID_Menu_Reg。为CRegFigureView类添加commond事件,并添加如下代码:
void CRegFigureView::OnMenuRegClick()
{
// TODO:点击手写数字输入功能,弹出识别对话框
RegDlg m_RegDlg;
m_RegDlg.DoModal();
}
将CAboutDlg类独立出来,生成两个文件CAboutDlg.h和CAboutDlg.cpp,这样就可以多次调用。
5.功能实现
手写数字识别显示
6.总结
本设计实现了简单的数字1-9的识别,最明显的一个问题就是6和0的识别会产生混淆,因此可以想象对于大型的文字识别系统,采用简单的方向判别还是不够的!对于大型的手写文字识别,目前向百度主页上已经采用的汉王手写识别采用了数据库技术和这样的一中方向判别的机制,这种机制对于文字的书写笔画要求很高。
附录代码
RegDlg.h添加代码如下:
1 enum Dir{left,right,up,down,none}; 2 struct Figure 3 { 4 Dir Direction[16]; //方向 0-left 1-right 2-up 3 -down 5 int DotCount ; //断笔数 6 }; 7 ///////////////////////////////////////////////////////////////////////////// 8 // RegDlg dialog 9 class RegDlg : public CDialog 10 { 11 // Construction 12 public: 13 RegDlg(CWnd* pParent = NULL); // standard constructor 14 void DrawGrid(int row,int col); 15 16 CRect m_rect; 17 BOOL m_Buttondowned; 18 CPen pen; 19 CPoint m_Startpt; 20 CPoint m_Endpt; 21 22 CPoint m_Prept; 23 Figure m_Figure; 24 int m_curpen; //当前笔数 25 BOOL m_Isreg; //是否开始识别 26 // Dialog Data 27 //{{AFX_DATA(RegDlg) 28 enum { IDD = IDD_DIG_Reg }; 29 CStatic m_Panel; 30 //}}AFX_DATA 31 // Overrides 32 // ClassWizard generated virtual function overrides 33 //{{AFX_VIRTUAL(RegDlg) 34 protected: 35 virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support 36 //}}AFX_VIRTUAL 37 // Implementation 38 protected: 39 HICON m_hIcon; 40 // Generated message map functions 41 //{{AFX_MSG(RegDlg) 42 // NOTE: the ClassWizard will add member functions here 43 virtual BOOL OnInitDialog(); 44 afx_msg void OnSysCommand(UINT nID, LPARAM lParam); 45 afx_msg void OnPaint(); 46 afx_msg HCURSOR OnQueryDragIcon(); 47 afx_msg void OnLButtonDown(UINT nFlags, CPoint point); 48 afx_msg void OnLButtonUp(UINT nFlags, CPoint point); 49 afx_msg void OnMouseMove(UINT nFlags, CPoint point); 50 afx_msg void OnReg(); 51 afx_msg void OnButton1(); 52 //}}AFX_MSG 53 DECLARE_MESSAGE_MAP() 54 }; 55 RegDlg.cpp代码: 56 #include "stdafx.h" 57 #include "RegFigure.h" 58 #include "RegDlg.h" 59 #include "AboutDlg.h" 60 #ifdef _DEBUG 61 #define new DEBUG_NEW 62 #undef THIS_FILE 63 static char THIS_FILE[] = __FILE__; 64 #endif 65 ///////////////////////////////////////////////////////////////////////////// 66 // RegDlg dialog 67 RegDlg::RegDlg(CWnd* pParent /*=NULL*/) : CDialog(RegDlg::IDD) 68 { 69 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); 70 m_Buttondowned = FALSE; 71 m_curpen= 0; 72 m_Isreg = FALSE; 73 m_Figure.DotCount = 0; 74 } 75 ///////////////////////////////////////////////////////////////////////////// 76 // RegDlg message handlers 77 BOOL RegDlg::OnInitDialog() 78 { 79 CDialog::OnInitDialog(); 80 // Add "About..." menu item to system menu. 81 // IDM_ABOUTBOX must be in the system command range. 82 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); 83 ASSERT(IDM_ABOUTBOX < 0xF000); 84 CMenu* pSysMenu = GetSystemMenu(FALSE); 85 if (pSysMenu != NULL) 86 { 87 CString strAboutMenu; 88 strAboutMenu.LoadString(IDS_ABOUTBOX); 89 if (!strAboutMenu.IsEmpty()) 90 { 91 pSysMenu->AppendMenu(MF_SEPARATOR); 92 pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); 93 } 94 } 95 // Set the icon for this dialog. The framework does this automatically 96 // when the application's main window is not a dialog 97 SetIcon(m_hIcon, TRUE); // Set big icon 98 SetIcon(m_hIcon, FALSE); // Set small icon 99 // TODO: Add extra initialization here 100 pen.CreatePen(PS_SOLID,5,RGB(0,100,0));//绿色画笔 101 m_Panel.GetClientRect(m_rect); 102 //MapWindowPoints(&m_Panel,m_rect); 103 m_Figure.DotCount = 0; 104 for (int i =0; i<16; i++) 105 { 106 m_Figure.Direction[i]= none; 107 } 108 return TRUE; // return TRUE unless you set the focus to a control 109 } 110 void RegDlg::OnSysCommand(UINT nID, LPARAM lParam) 111 { 112 if ((nID & 0xFFF0) == IDM_ABOUTBOX) 113 { 114 CAboutDlg dlgAbout; 115 dlgAbout.DoModal(); 116 } 117 else 118 { 119 CDialog::OnSysCommand(nID, lParam); 120 } 121 } 122 void RegDlg::OnPaint() 123 { 124 if (IsIconic()) 125 { 126 CPaintDC dc(this); // device context for painting 127 128 SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0); 129 130 // Center icon in client rectangle 131 int cxIcon = GetSystemMetrics(SM_CXICON); 132 int cyIcon = GetSystemMetrics(SM_CYICON); 133 CRect rect; 134 GetClientRect(&rect); 135 int x = (rect.Width() - cxIcon + 1) / 2; 136 int y = (rect.Height() - cyIcon + 1) / 2; 137 // Draw the icon 138 dc.DrawIcon(x, y, m_hIcon); 139 } 140 else 141 { 142 CDialog::OnPaint(); 143 } 144 //m_rect.CopyRect(CRect (50,80,200,300)); 145 } 146 // The system calls this to obtain the cursor to display while the user drags 147 // the minimized window. 148 HCURSOR RegDlg::OnQueryDragIcon() 149 { 150 return (HCURSOR) m_hIcon; 151 } 152 void RegDlg::OnLButtonDown(UINT nFlags, CPoint point) 153 { 154 m_Buttondowned = TRUE; 155 m_Startpt = point; 156 m_Prept = point; 157 158 if(m_rect.PtInRect(point)) 159 m_Figure.DotCount += 1; 160 161 CDialog::OnLButtonDown(nFlags, point); 162 } 163 void RegDlg::OnLButtonUp(UINT nFlags, CPoint point) 164 { 165 // m_curpen = 0; 166 m_Endpt = point; 167 m_Buttondowned = FALSE; 168 CDialog::OnLButtonUp(nFlags, point); 169 } 170 void RegDlg::OnMouseMove(UINT nFlags, CPoint point) 171 { 172 if (m_Buttondowned) 173 if (m_rect.PtInRect(point)) 174 { 175 CDC* pDC = m_Panel.GetDC(); 176 pDC->SelectObject(&pen); 177 pDC->MoveTo(point); 178 pDC->LineTo(CPoint(point.x+1,point.y+1)); 179 180 if (m_curpen>15) 181 return; 182 if (point.x>m_Prept.x+30) //向右 183 { 184 if (m_Figure.Direction[m_curpen]==none) 185 m_Figure.Direction[m_curpen] = right; 186 else if (m_Figure.Direction[m_curpen] != right) 187 { 188 m_curpen+=1; 189 m_Figure.Direction[m_curpen] = right; 190 } 191 m_Prept = point; 192 } 193 else if (point.y>m_Prept.y+30) 194 { 195 if (m_Figure.Direction[m_curpen]==none) 196 m_Figure.Direction[m_curpen] = down; 197 else if (m_Figure.Direction[m_curpen] != down) 198 { 199 m_curpen+=1; 200 m_Figure.Direction[m_curpen] = down; 201 } 202 m_Prept = point; 203 } 204 else if (point.x<m_Prept.x-30) 205 { 206 if (m_Figure.Direction[m_curpen]==none) 207 m_Figure.Direction[m_curpen] = left; 208 else if (m_Figure.Direction[m_curpen] != left) 209 { 210 m_curpen+=1; 211 m_Figure.Direction[m_curpen] = left; 212 } 213 m_Prept = point; 214 } 215 else if (point.y< m_Prept.y-30) 216 { 217 if (m_Figure.Direction[m_curpen]==none) 218 m_Figure.Direction[m_curpen]= up; 219 else if (m_Figure.Direction[m_curpen] != up) 220 { 221 m_curpen+=1; 222 m_Figure.Direction[m_curpen] = up; 223 } 224 m_Prept = point; 225 } 226 } 227 CDialog::OnMouseMove(nFlags, point); 228 } 229 void RegDlg::DrawGrid(int row, int col) 230 { 231 CDC* pDC = m_Panel.GetDC(); 232 CRect rect; 233 m_Panel.GetClientRect(rect); 234 for (int i = 0; i<row+1; i++) 235 { 236 pDC->MoveTo(0,i*30); 237 pDC->LineTo(rect.Width(),i*30); 238 } 239 for (int j = 0; j<col+1;j++) 240 { 241 pDC->MoveTo(j*30,0); 242 pDC->LineTo(j*30,rect.Height()); 243 } 244 } 245 void RegDlg::OnReg() 246 { 247 m_curpen=0; 248 if (m_Figure.Direction[0]==down) //判断1 249 if (m_Figure.Direction[1]==none) 250 { 251 if ( m_Figure.DotCount == 1) 252 { 253 MessageBox("1"); 254 255 for (int i =0; i<16; i++) 256 { 257 m_Figure.Direction[i]= none; 258 } 259 m_Figure.DotCount = 0; 260 m_Panel.Invalidate(); 261 return; 262 } 263 } 264 if (m_Figure.Direction[0]==right) //判断7 265 if (m_Figure.Direction[1]==down) 266 if (m_Figure.Direction[2]==none) 267 { 268 if ( m_Figure.DotCount == 1) 269 { 270 MessageBox("7"); 271 for (int i =0; i<16; i++) 272 { 273 m_Figure.Direction[i]= none; 274 } 275 m_Figure.DotCount = 0; 276 m_Panel.Invalidate(); 277 return; 278 } 279 } 280 if (m_Figure.Direction[0]==right) //判断2 281 if (m_Figure.Direction[1]==down) 282 { 283 if (m_Figure.DotCount == 1) 284 { 285 if (m_Figure.Direction[2]==left) 286 { 287 if (m_Figure.Direction[3]==right) 288 if ( m_Figure.Direction[4]==none) 289 { 290 MessageBox("2"); 291 for (int i =0; i<16; i++) 292 { 293 m_Figure.Direction[i]= none; 294 } 295 m_Figure.DotCount = 0; 296 m_Panel.Invalidate(); 297 return; 298 } 299 } 300 else if (m_Figure.Direction[2]==right) 301 { 302 if (m_Figure.Direction[3]==none) 303 { 304 MessageBox("2"); 305 306 for (int i =0; i<16; i++) 307 { 308 m_Figure.Direction[i]= none; 309 } 310 m_Figure.DotCount = 0; 311 m_Panel.Invalidate(); 312 return; 313 } 314 } 315 } 316 } 317 if (m_Figure.Direction[0]==right) //判断3 318 if (m_Figure.Direction[1]==down) 319 if (m_Figure.DotCount==1) 320 { 321 if (m_Figure.Direction[2]==left) 322 if (m_Figure.Direction[3]==right) 323 { 324 MessageBox("3"); 325 } 326 327 for (int i =0; i<16; i++) 328 { 329 m_Figure.Direction[i]= none; 330 } 331 m_Figure.DotCount = 0; 332 m_Panel.Invalidate(); 333 return; 334 } 335 if (m_Figure.Direction[0]==down)//判断4 336 if (m_Figure.Direction[1]==right) 337 if (m_Figure.DotCount ==2) 338 { 339 if (m_Figure.Direction[2]==down) 340 if(m_Figure.Direction[3]==none) 341 { 342 for (int i=0;i<16;i++) 343 { 344 m_Figure.Direction[i]=none; 345 } 346 m_Figure.DotCount =0; 347 m_Panel.Invalidate(); 348 MessageBox("4"); 349 return; 350 } 351 } 352 if (m_Figure.Direction[0]==down)//判断5 353 if (m_Figure.Direction[1]==right) 354 if (m_Figure.DotCount ==2) 355 if (m_Figure.Direction [2]==down) 356 if(m_Figure.Direction[3]==left) 357 if (m_Figure.Direction[4]==right) 358 if(m_Figure.Direction [5]==none) 359 { 360 MessageBox("5"); 361 for (int i=0;i<16;i++) 362 { 363 m_Figure.Direction [i]=none; 364 } 365 m_Figure.DotCount =0; 366 m_Panel.Invalidate(); 367 return; 368 } 369 if (m_Figure.DotCount ==1) 370 { 371 if (m_Figure.Direction[0]==left) 372 if (m_Figure.Direction[1]==down)//判断6,第一笔向左 373 if (m_Figure.Direction[2]==right) 374 if (m_Figure.Direction[3]==up) 375 if (m_Figure.Direction[4]==left) 376 { 377 MessageBox("6"); 378 for (int i=0;i<16;i++) 379 { 380 m_Figure.Direction [i]=none; 381 } 382 m_Figure.DotCount =0; 383 m_Panel.Invalidate(); 384 return; 385 } 386 if (m_Figure.Direction[0]==down)//判断6,第一笔向下,此处和0有冲突,需要计算画笔移动的距离 387 if (m_Figure.Direction[1]==right) 388 if (m_Figure.Direction[2]==up) 389 if (m_Figure.Direction[3]==left) 390 { 391 MessageBox("6"); 392 for (int i=0;i<16;i++) 393 { 394 m_Figure.Direction [i]=none; 395 } 396 m_Figure.DotCount =0; 397 m_Panel.Invalidate(); 398 return; 399 } 400 } 401 if (m_Figure.DotCount ==1) 402 { 403 if (m_Figure.Direction[0]==left)//判断8方式1 404 { 405 if (m_Figure.Direction[1]==down) 406 if (m_Figure.Direction[2]==left) 407 if (m_Figure.Direction[3]==up) 408 if (m_Figure.Direction[4]==right) 409 if (m_Figure.Direction[5]==up) 410 { 411 MessageBox("8"); 412 for (int i=0;i<16;i++) 413 { 414 m_Figure.Direction [i]=none; 415 } 416 m_Figure.DotCount =0; 417 m_Panel.Invalidate(); 418 return; 419 } 420 } 421 if (m_Figure.Direction[0]==down)//判断8方式2 422 { 423 if (m_Figure.Direction[1]==right) 424 if (m_Figure.Direction[2]==down) 425 if (m_Figure.Direction[3]==left) 426 if (m_Figure.Direction[4]==up) 427 if (m_Figure.Direction[5]==right) 428 { 429 MessageBox("8"); 430 for (int i=0;i<16;i++) 431 { 432 m_Figure.Direction [i]=none; 433 } 434 m_Figure.DotCount =0; 435 m_Panel.Invalidate(); 436 return; 437 } 438 } 439 if (m_Figure.Direction[0]==left)//判断8方式3 440 { 441 if (m_Figure.Direction[1]==down) 442 if (m_Figure.Direction[2]==right) 443 if (m_Figure.Direction[3]==down) 444 if (m_Figure.Direction[4]==left) 445 if (m_Figure.Direction[5]==up) 446 { 447 MessageBox("8"); 448 for (int i=0;i<16;i++) 449 { 450 m_Figure.Direction [i]=none; 451 } 452 m_Figure.DotCount =0; 453 m_Panel.Invalidate(); 454 return; 455 } 456 } 457 } 458 if (m_Figure.DotCount ==1) 459 { 460 if (m_Figure.Direction[0]==left)//判断9方式1 461 { 462 if (m_Figure.Direction[1]==down) 463 if (m_Figure.Direction[2]==right) 464 if (m_Figure.Direction[3]==up) 465 if (m_Figure.Direction[4]==down) 466 { 467 MessageBox("9"); 468 for (int i=0;i<16;i++) 469 { 470 m_Figure.Direction [i]=none; 471 } 472 m_Figure.DotCount =0; 473 m_Panel.Invalidate(); 474 return; 475 } 476 } 477 if (m_Figure.Direction[0]==left)//判断9方式2 478 { 479 if (m_Figure.Direction[1]==up) 480 // if (m_Figure.Direction[2]==right) 481 if (m_Figure.Direction[2]==right) 482 if (m_Figure.Direction[3]==down) 483 { 484 MessageBox("9"); 485 for (int i=0;i<16;i++) 486 { 487 m_Figure.Direction [i]=none; 488 } 489 m_Figure.DotCount =0; 490 m_Panel.Invalidate(); 491 return; 492 } 493 } 494 495 if (m_Figure.Direction[0]==right)//判断9方式3 496 { 497 if (m_Figure.Direction[1]==up) 498 if (m_Figure.Direction[2]==left) 499 if (m_Figure.Direction[3]==down) 500 { 501 MessageBox("9"); 502 for (int i=0;i<16;i++) 503 { 504 m_Figure.Direction [i]=none; 505 } 506 m_Figure.DotCount =0; 507 m_Panel.Invalidate(); 508 return; 509 } 510 } 511 } 512 for (int i =0; i<16; i++) 513 { 514 m_Figure.Direction[i]= none; 515 } 516 517 m_Figure.DotCount = 0; 518 m_Panel.Invalidate(); 519 } 520 void RegDlg::OnButton1() 521 { 522 // TODO: Add your control notification handler code here 523 DrawGrid(9,5);//画网格 524 }