⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 onrdlg.cpp

📁 数字识别之神经网络法用C++写的
💻 CPP
字号:
/* ============================================= *\
 * File:		OnrDlg.cpp						 *
 * Description: 主对话框头文件                   *
 *				包括所有的函数                   *
\* ============================================= */

#include "stdafx.h"
#include "Onr.h"
#include "OnrDlg.h"

#include <fstream.h>
#include <time.h>
#include <stdlib.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	virtual BOOL OnInitDialog();
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

BOOL CAboutDlg::OnInitDialog() 
{
	CDialog::OnInitDialog();
	
	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// COnrDlg dialog

COnrDlg::COnrDlg(CWnd* pParent) : CDialog(COnrDlg::IDD, pParent) {
	//{{AFX_DATA_INIT(COnrDlg)
	//}}AFX_DATA_INIT

	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void COnrDlg::DoDataExchange(CDataExchange* pDX) {
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(COnrDlg)
	DDX_Control(pDX, IDC_TRAINSPIN, m_Trainspin);
	DDX_Control(pDX, IDC_TESTSPIN, m_Testspin);
	DDX_Control(pDX, IDC_TRAININFO, m_TrainInfo);
	DDX_Control(pDX, IDC_INFOBOX, m_cInfoBox);
	DDX_Control(pDX, IDC_TESTWINDOW, m_TestWindow);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(COnrDlg, CDialog)
	//{{AFX_MSG_MAP(COnrDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_TRAIN, OnTrain)
	ON_BN_CLICKED(IDC_CLASSIFY, OnClassify)
	ON_NOTIFY(UDN_DELTAPOS, IDC_TRAINSPIN, OnDeltaposTrainspin)
	ON_NOTIFY(UDN_DELTAPOS, IDC_TESTSPIN, OnDeltaposTestspin)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

///////////////////////////////////////////////////////////////////////////////
// 初始化对话框
BOOL COnrDlg::OnInitDialog() 
{
	CDialog::OnInitDialog();

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL) 
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty()) 
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon((HICON)(LoadImage(AfxGetResourceHandle(), 
		MAKEINTRESOURCE(IDR_MAINFRAME), IMAGE_ICON, 16, 16, 0)), FALSE);

	// 设置滚动条的范围
	m_Testspin.SetRange(0,NN_NUMBERS-1);
	m_Trainspin.SetRange(0,NN_NUMBERS-1);
	m_Testspin.SetPos(0);
	m_Trainspin.SetPos(0);
	
	// 打开文件
	ifstream data("learn.dat", ios::nocreate);
	ifstream test("test.dat", ios::nocreate);

	// 检查文件的有效性
	if (!test || !data) 
	{
		MessageBox("No learning or test data present.", "Cannot run...",
			MB_OK | MB_ICONERROR);
	}

	// 读文件
	for(int i=0;i<NN_NUMBERS;i++) 
	{
		for(int j=0;j<NN_RESX * NN_RESY;j++) 
		{
			int onoff;
			test >> onoff;
			m_bTestData[i][j] = onoff;

			data >> onoff;
			m_bNumbers[i][j] = onoff;
		}
	}

	// 设置初始要画的数字指针
	m_ipDrawNum = &m_bNumbers[0][0];

	return true;
}

void COnrDlg::OnSysCommand(UINT nID, LPARAM lParam) {
	if ((nID & 0xFFF0) == IDM_ABOUTBOX) 
	{
		CAboutDlg dlgAbout;

		dlgAbout.DoModal();
	}
	else 
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

void COnrDlg::OnPaint() 
{
	if (IsIconic())	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	} else {
		CPaintDC dc(this);
		CDialog::OnPaint();

		// 这种方法画矩形可能有点迂回,不过的确可以工作
		CRect rect;

		m_TestWindow.GetClientRect(&rect);
		m_TestWindow.ClientToScreen(&rect);
		ScreenToClient(&rect);
		dc.FrameRect(&rect, &CBrush(RGB(0,0,0)));
		rect.DeflateRect(1,1);
		dc.FillSolidRect(rect, RGB(255,255,255));

		// 画数字
		DrawNumber(&dc, m_ipDrawNum, &rect);
	}
}

HCURSOR COnrDlg::OnQueryDragIcon() 
{
	return (HCURSOR) m_hIcon;
}

///////////////////////////////////////////////////////////
// 由于某些原因,如果采用"Maxinum Speed"优化将引起程序运行的
// 失败,所以先关掉优化,在该段代码结束后再打开优化功能
///////////////////////////////////////////////////////////

#pragma optimize( "", off )

//////////////////////////////////////////////////////////////////////////
//  训练数据
void COnrDlg::OnTrain() 
{
	srand((unsigned)time(NULL));			// 设置随机数
	m_cInfoBox.ResetContent();
	
	// 所有权值0初始化
	memset(&m_fWeights,0,sizeof(m_fWeights));

	// 产生一些带有随机噪声的训练数据
	m_cInfoBox.InsertString(-1, "Generating noisy data...");
	int num = 0;
	for (int i=0;i<NN_NUMBERS * NN_NOISY;i++) 
	{
		for(int j=0;j<NN_RESX * NN_RESY;j++) 
		{
			if (rand() % 100 < 7) 
			{
				m_iNoisy[i][j] = !m_bNumbers[num][j];
			} 
			else 
				m_iNoisy[i][j] = m_bNumbers[num][j];
		}
		
		if ((float)(i+1)/NN_NOISY == (i+1)/NN_NOISY && i != 0) 
			num++;
	}

	m_cInfoBox.InsertString(-1, "Learning...");

	RunNet(true);

	m_TrainInfo.SetWindowText("Complete...");
	m_cInfoBox.InsertString(-1,"Training Complete!");
	GetDlgItem(IDC_CLASSIFY)->EnableWindow(true);
}

///////////////////////////////////////////////////////////////////////////
// 画数字
void COnrDlg::DrawNumber(CDC *dc, int *cell, CRect *rect) 
{
	// 下面的计算主要是使得数字画在矩形筐的正中央
	CPoint tl;
	tl.x = rect->Width()/2 - (NN_RESX*10)/2;
	tl.y = rect->Height()/2 - (NN_RESY*10)/2;

	tl.Offset(rect->TopLeft());
	int ix = tl.x;
	
	// 产生一些围绕数字的红色虚线
	CPen pen;
	if (pen.CreatePen(PS_DOT, 1, RGB(127,0,0))) 
	{
		CPen *pOldPen = dc->SelectObject(&pen);

		CPoint pt1 = rect->TopLeft();
		CPoint pt2 = rect->BottomRight();
		dc->MoveTo(tl.x-5, tl.y-2);
		dc->LineTo(tl.x+NN_RESX*10+5, tl.y-2);
		dc->MoveTo(tl.x-5, tl.y+10*NN_RESY+1);
		dc->LineTo(tl.x+NN_RESX*10+5, tl.y+10*NN_RESY+1);
		dc->MoveTo(tl.x-2, tl.y-7);
		dc->LineTo(tl.x-2, tl.y+NN_RESY*10+7);
		dc->MoveTo(tl.x+NN_RESX*10+2, tl.y-7);
		dc->LineTo(tl.x+NN_RESX*10+2, tl.y+NN_RESY*10+7);

		dc->SelectObject(pOldPen);
	}

	// 显示一些文本
	CRect format(tl.x+1, tl.y+NN_RESY*10+2,
				 tl.x+NN_RESX*10-1, tl.y+NN_RESY*10+18);
	if (m_bDisplayString) 
	{
		CFont fnt;
		if (fnt.CreatePointFont(80,"Arial")) 
		{
			CFont* pOldFont = dc->SelectObject(&fnt);

			dc->SetTextColor(RGB(0,0,127));
			dc->DrawText(m_Str,format,DT_CENTER);
			dc->SelectObject(pOldFont);
		}	
	}

	// 画数字
	for(int i=0;i<NN_RESX*NN_RESY;i++) 
	{
		if ((*cell) == 1) 
		{
			dc->FillSolidRect(tl.x,tl.y,10,10,RGB(0,0,0));
		}
		tl.x += 10;
		if (tl.x == ix + 10*NN_RESX) 
		{
			tl.x = ix;
			tl.y += 10;
		}
		cell++;
	}
}

//////////////////////////////////////////////////////////////////////////////////
// 识别
void COnrDlg::OnClassify() 
{
	int pos = (BYTE)m_Testspin.GetPos();
	
	m_ipDrawNum = &m_bTestData[pos][0];
	InvalidateNumber();

	float d[NN_NUMBERS];
	for(int j=0;j<NN_NUMBERS;j++) 
	{
		d[j] = 0;
		for(int k=0;k<NN_RESX * NN_RESY;k++) 
		{
			d[j] += m_fWeights[j][k]*m_bTestData[pos][k];
		}
	}
	
	int bestind = 0;
	for(j=1;j<NN_NUMBERS;j++) 
		if (d[j] > d[bestind]) 
			bestind = j;

	// 计算画文本的区域
	CRect size;
	m_TestWindow.GetClientRect(&size);
	int x = size.Width()/2  - (NN_RESX*10)/2;
	int y = size.Height()/2 - (NN_RESY*10)/2;
	CRect format(x,size.Height()-y+1, size.Width()-x, size.Height()-5);
	
	m_bDisplayString = true;
	GetText(m_Str, bestind+1);
}

/////////////////////////////////////////////////////////////////////////////
// 运行网络
// 参数:training-为真时表示网络出于训练状态
void COnrDlg::RunNet(bool training) 
{
	// 检测带有噪声的数据
	float d[NN_NUMBERS];
	int cycles = 0;//初始化循环次数为0
	bool correct;

	do 
	{
		correct = true;
		for(int i=0;i<NN_NUMBERS * NN_NOISY;i++) 
		{
			for(int j=0;j<NN_NUMBERS;j++) 
			{
				d[j] = 0;
				for(int k=0;k<NN_RESX * NN_RESY;k++) 
				{
					d[j] += m_fWeights[j][k]*m_iNoisy[i][k];
				}
			}
			
			int bestind = 0;
			for(j=1;j<NN_NUMBERS;j++) 
				if (d[j] > d[bestind]) 
					bestind = j;
			
			int realval = (int)(i/NN_NOISY);
			if (bestind == realval) continue;
			
			if (training) 
			{
				CString result;
				result.Format("Guessed %d instead of %d.",bestind, realval);
				m_TrainInfo.SetWindowText(result);
				correct = false;
				
				for(j=0;j<NN_RESX * NN_RESY;j++) 
				{
					m_fWeights[bestind][j] -= m_iNoisy[i][j];
					m_fWeights[realval][j] += m_iNoisy[i][j];
				}
			}
		}
		
		SetDlgItemInt(IDC_ITERATE, ++cycles);
	} while (!correct && cycles <= NN_MAXITER);
	
	//如果循环次数太大还每得到结果则退出
	if (cycles >= NN_MAXITER) 
	{
		MessageBox("Training has timed-out.",
			"Error in Training", MB_OK | MB_ICONINFORMATION);
		return;
	}
}

//恢复系统优化
#pragma optimize( "", on )

////////////////////////////////////////////////////////////////////////
// 处理训练数据的滚动条
void COnrDlg::OnDeltaposTrainspin(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR;
	int iPos = pNMUpDown->iPos;
	int iDelta = pNMUpDown->iDelta;
	
	m_bDisplayString = false;

	if ((iPos == 0 && iDelta == -1) || (iPos == NN_NUMBERS-1 && iDelta == 1)) 
	{
		if (iDelta == -1) 
		{
			m_ipDrawNum = &m_bNumbers[0][0];
			InvalidateNumber();
		} 
		else 
		{
			m_ipDrawNum = &m_bNumbers[NN_NUMBERS-1][0];
			InvalidateNumber();
		}
	} 
	else 
	{
		m_ipDrawNum = &m_bNumbers[iPos+iDelta][0];
		InvalidateNumber();
	}
	
	*pResult = 0;
}

//////////////////////////////////////////////////////////////////////////////////
// 处理测试数据的滚动条
void COnrDlg::OnDeltaposTestspin(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR;
	int iPos = pNMUpDown->iPos;
	int iDelta = pNMUpDown->iDelta;

	m_bDisplayString = false;

	if ((iPos == 0 && iDelta == -1) || (iPos == NN_NUMBERS-1 && iDelta == 1)) 
	{
		if (iDelta == -1) 
		{
			m_ipDrawNum = &m_bTestData[0][0];
			InvalidateNumber();
		} 
		else 
		{
			m_ipDrawNum = &m_bTestData[NN_NUMBERS-1][0];
			InvalidateNumber();
		}
	} 
	else 
	{
		m_ipDrawNum = &m_bTestData[iPos+iDelta][0];
		InvalidateNumber();
	}
	
	*pResult = 0;
}

//////////////////////////////////////////////////////////////////////////
// 得到数字相对应的文本
void COnrDlg::GetText(CString &str, int num) {
	switch (num) {
	case 1:
		str = "Result:1"; 
		break;
	case 2:
		str = "Result:2"; 
		break;
	case 3:
		str = "Result:3"; 
		break;
	case 4:
		str = "Result:4"; 
		break;
	case 5:
		str = "Result:5"; 
		break;
	case 6:
		str = "Result:6"; 
		break;
	case 7:
		str = "Result:7"; 
		break;
	case 8:
		str = "Result:8"; 
		break;
	case 9:
		str = "Result:9"; 
		break;
	default:
		str = "Undetermined";
	}
}

///////////////////////////////////////////////////////////////////////////////
// 强制重画
void COnrDlg::InvalidateNumber() 
{
	CRect rect;
	
	m_TestWindow.GetClientRect(&rect);
	m_TestWindow.ClientToScreen(&rect);
	ScreenToClient(&rect);

	InvalidateRect(rect, false);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -