barus's diary

とても真面目なblogですにゃ

C++でニューラルネットワークによる手書き文字認識その④(マウス入力データの文字認識) for VS2017 VC++

ニューラルネットワークと深層学習で紹介されていた

Pythonで書かれたコードnetwork.pyC++に落とし込みしてきた。

 

Pythonで書かれたコード(network.py)で文字認識

     network.py

文字画像データを読み込む  

     mnist.cpp         mnist.h

文字認識① 

     network.cpp      network.h

     neurons.cpp     neurons.h 

     NNmodel.cpp    

文字認識② 

文字認識③

    MYSTR.cpp     MYSTR.h

    file.cpp             file.h    

 

その際、以下の6万個の手書きの文字数字データを基に

訓練データとテストデータをランダムに選択し

 

train-images.idx3-ubyte 文字数字画像データ

train-labels.idx1-ubyte  文字数字画像データの正解ラベル

 

ニューラルネットワーク機械学習して用いてきた。

今回は、VC++GUI画面より28x28のマスに、

マウス入力で入力して「文字解析」する。

以下のような感じでマウスで入力し「文字解析」ボタンを押す。

f:id:hatakeka:20170722080434p:plain

 

今回は、前回の文字認識③

学習記憶した正答率約8割のネットワーク(784 500 10)を利用する。

 

 

 

 

訓練データの文字解析

 f:id:hatakeka:20170722114556p:plain

訓練データを「Index VIew」ボタンで表示出来るようにした。

 

起動時に「訓練データ」を読み込むので

①にて1~6万までの数字を

入力し、②の「Index View」ボタンを押すと

文字画像データと、この文字の解答が表示される。

③の「文字解析」ボタンにて画像データを判定した結果が

横のテキストボックスに表示される。

 

Indexをランダムに選び「文字解析」してみた。

Index=1009【〇】   Index=2003【〇】  Index=3001【〇】

f:id:hatakeka:20170722091018p:plainf:id:hatakeka:20170722091119p:plainf:id:hatakeka:20170722091244p:plain

Index=4001【〇】   Index=5003【〇】  Index=6007【〇】

f:id:hatakeka:20170722091514p:plainf:id:hatakeka:20170722091555p:plainf:id:hatakeka:20170722091630p:plain

Index=7001【〇】   Index=8009【〇】  Index=9001【〇

f:id:hatakeka:20170722091709p:plainf:id:hatakeka:20170722091739p:plainf:id:hatakeka:20170722091818p:plain

Index=10007【〇】

f:id:hatakeka:20170722091853p:plain

 

出来すぎの結果だが、

サンプル10個で100%だった。

 

マウス入力による文字解析

 

f:id:hatakeka:20170722115330p:plain

今度はマウス入力で文字解析してみた。

①にてマウスで入力

②にて「文字解析」

③に結果が表示される。

④でリセット

当然ながら、訓練データにはない文字列である。 

マウス入力0【〇】、 1【〇、 2【✖

f:id:hatakeka:20170722074822p:plainf:id:hatakeka:20170722074844p:plainf:id:hatakeka:20170722074933p:plain

マウス入力3【✖】  4【✖】  5【〇】

f:id:hatakeka:20170722075014p:plainf:id:hatakeka:20170722075141p:plainf:id:hatakeka:20170722074701p:plain

マウス入力6【✖】  7【〇】  8【✖】

f:id:hatakeka:20170722075348p:plainf:id:hatakeka:20170722075430p:plainf:id:hatakeka:20170722075528p:plain

マウス入力9【✖】

f:id:hatakeka:20170722075701p:plain

 

ここまで10回試して、正答率は40%。

訓練データでは線が太いのでわざと、ここから意識して文字を太くしてみた。

 

マウス入力0【〇】、 1【〇、 2【〇

f:id:hatakeka:20170722080127p:plainf:id:hatakeka:20170722080202p:plainf:id:hatakeka:20170722080049p:plain

マウス入力3【〇】、 4【〇、 5【〇

f:id:hatakeka:20170722080241p:plainf:id:hatakeka:20170722080356p:plainf:id:hatakeka:20170722080434p:plain

マウス入力6【✖】、 7【✖、 8【✖

f:id:hatakeka:20170722080511p:plainf:id:hatakeka:20170722080622p:plainf:id:hatakeka:20170722080818p:plain

マウス入力9【✖】

f:id:hatakeka:20170722080939p:plain

 

正答率は、60%。

若干正答率は上がったが、サンプル数が少ないので何とも言えない。

20回テストしてみて50%でした。

 

以上から、訓練データ以外のマウス入力については正答率50%と

正答率は低くなった。

 

理由を考えてみた。

 

マウスで入力した数字を

ふかんして見てみると、MNISTのデータとみて

なんかちょっと違う。

恐らく、手書きの場合と

マウス入力の違いの文字の特徴が差異として現れたかもしれません。

 

6万のサンプル数ではまだまだ、

サンプル不足(学習不足)のようです( ゚Д゚) 

 

  

ソースコード 

ソリューションは以下のような感じでwindowsアプリケーションを作成します。

(※windowsアプリケーションを作成についてのサンプルはここを参照。)

f:id:hatakeka:20170722070738p:plain

NNCoreフォルダを作成し、そこに今まで紹介したソースを配置しています。

(※別フォルダのインクルードの仕方はここを参考にして下さい。)

それぞれのソースは以下の記事を参照して下さい。

赤文字が今回の修正や追加部分となります。

 

文字画像データを読み込む  

     mnist.cpp         mnist.h

文字認識① 

     network.cpp      network.h

     neurons.cpp     neurons.h 

     NNmodel.cpp     NNmodel.h   ・・・修正、追加

文字認識② 

文字認識③

    MYSTR.cpp     MYSTR.h

    file.cpp             file.h    

 

今回の記事で新たに追加

NNmodelGUI.cpp     NNmodelGUI.h ・・・VC++windowsアプリケーションShow.cpp   Show.h ・・・表示に関するクラス

NNmodelGUI.rc         ・・・ダイアログ、IDD_DIALOG1追加

f:id:hatakeka:20170722095959p:plain

IDは以下のような感じ。

f:id:hatakeka:20170722095845p:plain

 

今回は「文字解析」ボタンと、「Index View」ボタンの機能について作成する。

「訓練データ追加」と「学習」ボタン機能は作らない。

 

 

プロジェクト>プロパティ>構成プロパティ>全般

では、マルチバイト文字にしてます。

f:id:hatakeka:20170619164120p:plain

それぞれのクラスヘッダーの、クラス関係をいじっています。

以下のような感じ。

 mnist.h

class mnist:
public file
{

network.h

class network :
public MYSTR,mnist,file
{

neurons.h

class neurons
{
public:

 

NNmodel.h

class NNmodel:
public file
{

 

MYSTR.h

class MYSTR :
public neurons,file
{

 

file.h

class file
{

 

NNmodel.cpp

 

#include "stdafx.h"
#include "NNmodel.h"


NNmodel::NNmodel()
{
}


NNmodel::~NNmodel()
{
}

//ネットワークを指定して、miniの文字解析
TEST_RESLT NNmodel::inputone(string str_nets, string filepath, MNIST_compact mini)
{
	network net;

	//----------------------------
	//string netsを vector<int>に変換
	//----------------------------
	vector<int> nets;
	vector<string> n = net.split(str_nets, " ");
	for (int i = 0; n.size() > i; i++)
	{
		nets.push_back(atoi(n[i].c_str()));
	}

	//----------------------------
	//ネットワークを作成
	//#net = network.Network([784, 30, 10])
	//#net.SGD(training_data, 30, 10, 3.0, test_data=test_data)
	//----------------------------
	net.Network(nets);

	//----------------------------------------------
	//ファイルから重みとバイアスを初期化
	//----------------------------------------------
	Delta delta_file;
	if (net.read_weight_and_biases(filepath, &delta_file, net._neurons[0]))
	{
		net._neurons[0]._biases = delta_file._biases;
		net._neurons[0]._weights = delta_file._weights;
		cout << "以前の重みとバイアスを読み込み" << endl;
	}


	//-----------------------------------------
	//テストデータ表示
	mnist mn;
	cout << "テストデータ表示" << endl;
	for (int i = 0; mini.images.size() > i; i++)
	{
		mn.print_images(mini.images.at(i), mini.labels.at(i));
		cout << mini.index << endl;
	}
	cout << "--" << endl;
	//-----------------------------------------

	//----------------------------------------------
	//テスト画像データの要素数
	//----------------------------------------------
	TEST_RESLT rlt = net.feedforward(mini);
	return rlt;
}



//訓練データを読み込み
void NNmodel::readmnist()
{

	char cdir[255];
	GetCurrentDirectory(255, cdir);
	string currentpath = cdir;
	file::printf_ex("現在カレントディレクトリ %s\n", currentpath.c_str());

	//----------------------------
	//トレーニングデータ読み込み
	//----------------------------
	network net;
	mnist mnist;
	int testdata_n = 60000; //訓練データの上限
	MNIST_dataset training_data;
	int index = 3;
	cout << "tradingdata reading.. index=" << index << endl;
	training_data.training_images = mnist.read_training_images(currentpath + "/train-images.idx3-ubyte", index);
	training_data.training_labels = mnist.read_training_labels(currentpath + "/train-labels.idx1-ubyte", index);
	//indexを割り振る
	vector<int> indexint;
	for (int i = 0; training_data.training_images.size() > i; i++)training_data.index.push_back(i);
	//イメージとラベルをマージする
	//vector<MNIST_compact> trainingdata_ = net.marge(&training_data);
	_mnist = net.marge(&training_data);

}

//訓練データからIndexを指定して取り出し
MNIST_compact NNmodel::getmnist(int index)
{
	MNIST_compact rlt;

	for (int i = 0; _mnist.size() > i; i++)
	{
		if (_mnist[i].index == index)
		{
			rlt = _mnist[i];
			return rlt;
			
		}
	}

	return rlt;

}


//訓練データを追加
void NNmodel::addmymnist(MNIST_compact mini)
{
	mnist mnist;
	mnist.addmymnist(mini);
}

 

 

 

NNmodel.h

 

#pragma once


#include <stdio.h>
#include <stdlib.h> // exit()
#include <fcntl.h>

#include <iostream>  // for debug writing
#include <string>    // useful for reading and writing

#include <fstream>   // ifstream, ofstream
#include <sstream>   // istringstream

#include "file.h"

#include "mnist.h"
//#include "bitmap.h"
#include "network.h"

/*
+NNcore
 +file.cpp
 +mnist.cpp
 +MYSTR.cpp
 +network.cpp
 +neurons.cpp
 +NNmodel.cpp
プロパティ>VC++ディレクトリ>>インクルードファイル
NNcore;$(IncludePath)
*/

class NNmodel:
	public file
{
public:
	NNmodel();
	~NNmodel();
	vector<MNIST_compact> _mnist; //訓練データ読み込み

//	int main(int argc, char **argv);
//	int main(char *argv);

	//訓練データとテストデータを利用し、ニューラルネットワークで学習
//	void nntest(vector<int> nets);

	//ネットワークを指定して、学習させた重みデータを用いてminiの文字解析
	TEST_RESLT inputone(string nets, string filepath, MNIST_compact mini);

	//訓練データを読み込み
	void readmnist();

	//訓練データからIndexを指定して取り出し
	MNIST_compact getmnist(int index);


	//訓練データを追加
	void addmymnist(MNIST_compact mini);

};

    

 

 

NNmodelGUI.cpp

 

// NNmodelGUI.cpp : アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"
#include "NNmodelGUI.h"



#define MAX_LOADSTRING 100

// グローバル変数:
HINSTANCE hInst;                                // 現在のインターフェイス
WCHAR szTitle[MAX_LOADSTRING];                  // タイトル バーのテキスト
WCHAR szWindowClass[MAX_LOADSTRING];            // メイン ウィンドウ クラス名

// このコード モジュールに含まれる関数の宣言を転送します:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: ここにコードを挿入してください。

	//	ダイアログボックスの表示 追加4
	DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG1), 0, (DLGPROC)DlgMain);

	
	return 0;


    // グローバル文字列を初期化しています。
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_NNMODELGUI, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // アプリケーションの初期化を実行します:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_NNMODELGUI));

    MSG msg;

    // メイン メッセージ ループ:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}


//追加
LRESULT CALLBACK DlgMain(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
	TCHAR buf[256];
	TCHAR number[256];
	TCHAR scale[256];
	SYSTEMTIME LocalTime;
	DWORD s, sz;
	static HWND hEdit1, hEdit2, hEdit3, hEdit4, hEdit5;

	static unsigned short int x = -10, y = -10;

	switch (msg) {
	case WM_MOUSEMOVE:
		if (wParam & MK_LBUTTON) {
			XYPOINT pos;
			pos.x = LOWORD(lParam);
			pos.y = HIWORD(lParam);
			InvalidateRect(hDlg, NULL, FALSE);
			
			_show.movemouse(hDlg, pos);
		}
		return 0;
	case WM_LBUTTONDOWN: {//マウスボタンイベント
		XYPOINT pos;
		PAINTSTRUCT ps;

		pos.x = pos.y = 0;
		pos.x = LOWORD(lParam);
		pos.y = HIWORD(lParam);

		_show.movemouse(hDlg, pos);

		break;
	}
	case WM_INITDIALOG:
	{
		
		//ネットワークの設定
		hEdit1 = GetDlgItem(hDlg, IDC_EDIT1);
		SetWindowText(hEdit1, _TEXT("784 500 10"));
		//学習ファイル
		hEdit3 = GetDlgItem(hDlg, IDC_EDIT3);
		SetWindowText(hEdit3, _TEXT("BWparameter.txt"));

		//MNIST 読み込み
		_nn.readmnist();

		return TRUE;
	}
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case IDC_BUTTON5://学習
		{
			//未実装

			return TRUE;
		}
		case IDC_BUTTON4://訓練データを追加
		{
			//新しい訓練データの正解
			hEdit5 = GetDlgItem(hDlg, IDC_EDIT5);
			GetWindowText(hEdit5, number, sizeof(number) / sizeof(TCHAR));
			
			//手書きデータをimgに入れる。
			MNIST_compact img;
			img.images.push_back(_show._image);//画像データ
			img.labels.push_back(atoi(number));//解答
			img.index = 0; //Index

			_nn.addmymnist(img);
			
			return TRUE;
		}
		case IDC_BUTTON2://MNIST 読み込み
		{
			_nn.readmnist();

			return TRUE;
		}
		case IDC_BUTTON3://Index表示
		{
			
			InvalidateRect(hDlg, NULL, TRUE);
			_show.bordreset(hDlg);

			//Index読み込み
			hEdit4 = GetDlgItem(hDlg, IDC_EDIT4);
			GetWindowText(hEdit4, number, sizeof(number) / sizeof(TCHAR));
			

			MNIST_compact img = _nn.getmnist(atoi(number));
			_show.IndexShow(hDlg, img);

			//ラベル表示
			static HWND htxt;
			htxt = GetDlgItem(hDlg, IDC_STATIC_TXT1);
			string txt = to_string(img.labels.front());
			txt = "解答 " + txt;
			SetWindowText(htxt, _TEXT(txt.c_str()));

			//判定表示
			hEdit2 = GetDlgItem(hDlg, IDC_EDIT2);
			SetWindowText(hEdit2, _TEXT(""));

			return TRUE;
		}
		case IDC_BUTTON1://リセット
		{
			InvalidateRect(hDlg, NULL, TRUE);
			_show.bordreset(hDlg);

			//判定表示
			hEdit2 = GetDlgItem(hDlg, IDC_EDIT2);
			SetWindowText(hEdit2, _TEXT(""));

			return TRUE;
		}
		case IDOK:
		{
			XYPOINT pos;
			//画面に手書きした数字を表示
			for (int i = 0; _show._image.size() < i; i++)
			{
				pos.y = i / MASS_X;
				pos.x = i % MASS_X;
				if (_show._image[i] < 0)_nn.file::printf_ex("1");
				else _nn.file::printf_ex("0");
				if((i+1)% MASS_X==0)_nn.file::printf_ex("\n");
			}
			//手書きデータをimgに入れる。
			MNIST_compact img;
			img.images.push_back(_show._image);//画像データ
			img.labels.push_back(-1); //解答
			img.index = 0; //Index


			TCHAR str_network[256];
			TCHAR str_efile[256];
			//Network.を読み込み
			hEdit1 = GetDlgItem(hDlg, IDC_EDIT1);
			GetWindowText(hEdit1, str_network, sizeof(str_network) / sizeof(TCHAR));
			//学習ファイル.を読み込み
			hEdit3 = GetDlgItem(hDlg, IDC_EDIT3);
			GetWindowText(hEdit3, str_efile, sizeof(str_efile) / sizeof(TCHAR));
			//文字解析
			TEST_RESLT rlt = _nn.inputone(str_network, str_efile,img);

			//判定表示
			hEdit2 = GetDlgItem(hDlg, IDC_EDIT2);
			string txt = to_string(rlt.x);
			SetWindowText(hEdit2, _TEXT(txt.c_str()));

			return TRUE;
		}
		case IDCLOSE:
			PostQuitMessage(0); //終了
			return TRUE;
		default:
			return FALSE;

		}
	case WM_PAINT:
	{
		_show.bordreset(hDlg);

	}

	break;
	default:
		return FALSE;
	}
	return TRUE;
}


//
//  関数: MyRegisterClass()
//
//  目的: ウィンドウ クラスを登録します。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_NNMODELGUI));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_NNMODELGUI);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   関数: InitInstance(HINSTANCE, int)
//
//   目的: インスタンス ハンドルを保存して、メイン ウィンドウを作成します。
//
//   コメント:
//
//        この関数で、グローバル変数インスタンス ハンドルを保存し、
//        メイン プログラム ウィンドウを作成および表示します。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // グローバル変数インスタンス処理を格納します。

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  関数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的:    メイン ウィンドウのメッセージを処理します。
//
//  WM_COMMAND  - アプリケーション メニューの処理
//  WM_PAINT    - メイン ウィンドウの描画
//  WM_DESTROY  - 中止メッセージを表示して戻る
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 選択されたメニューの解析:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: HDC を使用する描画コードをここに追加してください...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// バージョン情報ボックスのメッセージ ハンドラーです。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

 

 

 

NNmodelGUI.h

 

#pragma once

#include "resource.h"

#include<iostream>
using namespace std;

#include "Show.h" 


static Show _show;
static NNmodel _nn;

//	ダイアログボックスプロシージャー 追加2
LRESULT CALLBACK DlgMain(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);

 

 

 

Show.cpp

 

#include "stdafx.h"
#include "Show.h"


Show::Show()
{
	_image.resize(MASS_X*MASS_Y);
	for (int i = 0; MASS_X*MASS_Y > i; i++)_image[i] = 0;
}


Show::~Show()
{
}

void Show::IndexShow(HWND hWnd, MNIST_compact mini)
{
	XYPOINT pos;

	for (int i = 0; mini.images[0].size() > i; i++)
	{
		pos.y = i / MASS_X;
		pos.x = i % MASS_X;
		if (mini.images[0][i] > 0)
		{
			file::printf_ex("1");
			InvalidateRect(hWnd, NULL, FALSE);
			rectangle(hWnd, pos);
			_image[i] = 1;
		}
		else
		{
			file::printf_ex("0");
			_image[i] = 0;
		}

		if((i+1) % MASS_X == 0)file::printf_ex("\n");

	}

	
}

void Show::movemouse(HWND hWnd, XYPOINT pos)
{
	if (pos.x < START_X ||
		pos.y < START_Y ||
		pos.x > START_X + MASS_SIZE*MASS_X ||
		pos.y > START_Y + MASS_SIZE*MASS_Y
		)return;//枠外

	XYPOINT top = pos;
	
	top.x -= START_X;
	top.y -= START_Y;

	top.x = top.x / MASS_SIZE;
	top.y = top.y / MASS_SIZE;

	int num = top.y*MASS_Y + (top.x);
	_image[num] = 1;

	

	file::printf_ex("(%d,%d)(%d,%d) %d\n", pos.x, pos.y, top.x, top.y, num);
	
	
	rectangle(hWnd, top);
	
}

void Show::rectangle(HWND hWnd, XYPOINT pos)
{

	InvalidateRect(hWnd, NULL, FALSE);

	PAINTSTRUCT ps;
	HDC hdc = BeginPaint(hWnd, &ps);
	//塗りつぶし
	HBRUSH hBrushCyan, hOldBrush;
	hBrushCyan = CreateSolidBrush(RGB(255, 0, 255));
	hOldBrush = (HBRUSH)SelectObject(hdc, hBrushCyan);  // ブラシを選択
	XYPOINT p1,p2;
	p1.x = (START_X)+pos.x*MASS_SIZE;
	p1.y = (START_Y)+pos.y*MASS_SIZE;
	p2.x = (START_X + MASS_SIZE) + pos.x*MASS_SIZE;
	p2.y = (START_Y + MASS_SIZE) + pos.y*MASS_SIZE;

	Rectangle(hdc, p1.x, p1.y, p2.x, p2.y);

	/*  Rectangle()	矩形を表示
	hdc	デバイスコンテキスト
	50, 30	矩形の左上の座標
	200, 80	矩形の右下の座標 */

	SelectObject(hdc, hOldBrush);       // ブラシを戻す
	DeleteObject(hBrushCyan);           // ブラシを削除

	EndPaint(hWnd, &ps);

}

void Show::bordreset(HWND hWnd)
{
	HPEN        hPen;
	hPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); // 幅1ピクセルの黒ペン
	PAINTSTRUCT ps;
	HDC hdc = BeginPaint(hWnd, &ps);

	for (int i = 0; MASS_X+1 > i; i++)
	{
		//縦線
		int x1 = START_X + MASS_SIZE*i;
		int y1 = START_Y;
		int x2 = START_X + MASS_SIZE*i;
		int y2 = START_Y + MASS_SIZE*MASS_Y;
		MoveToEx(hdc, x1, y1, NULL);
		LineTo(hdc, x2, y2);
	}
	for (int i = 0; MASS_Y+1 > i; i++)
	{
		//横線
		int x1 = START_X;
		int y1 = START_Y + MASS_SIZE*i;
		int x2 = START_X + MASS_SIZE*MASS_X;
		int y2 = START_Y + MASS_SIZE*i;
		MoveToEx(hdc, x1, y1, NULL);
		LineTo(hdc, x2, y2);
	}

	//クリア
	for (int i = 0; MASS_X*MASS_Y > i; i++)_image[i] = 0;

	DeleteObject(hPen);
	EndPaint(hWnd, &ps);

}



 

 

 

Show.h

 

#pragma once

#include "stdio.h"

#include <string>
#include <iostream>
#include <fstream>
#include <vector>

#include "NNmodel.h"


#define MASS_X 28
#define MASS_Y 28
#define MASS_SIZE 10 
#define START_X 30  //右側に正
#define START_Y 80  //下側に正


using namespace std;

//------------------------------------
//XY座標
//------------------------------------
typedef struct _XYPOINT {
	int x;
	int y;
}XYPOINT;

class Show:
	public MYSTR,file
{
public:
	Show();
	~Show();
	vector<uint8_t> _image;

	//数字入力マスリセット
	void bordreset(HWND hDlg);
	//マウスの動きを記録
	void movemouse(HWND hDlg, XYPOINT pos);
	//塗りつぶし
	void rectangle(HWND hWnd, XYPOINT pos);
	//Indexの文字列を表示
	void IndexShow(HWND hWnd, MNIST_compact mini);

};



 

Debugモードでは処理が遅くなるので

Releaseモードで実行して下さい。

 

 

case WM_INITDIALOG:
{

 //ネットワークの設定
 hEdit1 = GetDlgItem(hDlg, IDC_EDIT1);
 SetWindowText(hEdit1, _TEXT("784 500 10"));
 //学習ファイル
 hEdit3 = GetDlgItem(hDlg, IDC_EDIT3);
 SetWindowText(hEdit3, _TEXT("BWparameter.txt"));

 //MNIST 読み込み
 _nn.readmnist();

 return TRUE;
}

 

 

起動時にWM_INITDIALOGが呼ばれて、

ネットワークを作成し、

学習したファイル「BWparameter.txt」を読み込み。

訓練データを読み込んでいます。

 

case WM_PAINT:
{
  _show.bordreset(hDlg);

}

 

にて、入力マス線を表示しています。

 

case WM_MOUSEMOVE://マウスの動き
 if (wParam & MK_LBUTTON) {
 XYPOINT pos;
 pos.x = LOWORD(lParam);
 pos.y = HIWORD(lParam);
 InvalidateRect(hDlg, NULL, FALSE);

  _show.movemouse(hDlg, pos);
 }
 return 0;
case WM_LBUTTONDOWN: {//マウスボタンイベント
 XYPOINT pos;
 PAINTSTRUCT ps;

 pos.x = pos.y = 0;
 pos.x = LOWORD(lParam);
 pos.y = HIWORD(lParam);

 _show.movemouse(hDlg, pos);

 break;
}

 

にて、マウスの動きと、右クリックのイベントを拾っている。

 

case IDC_BUTTON3://Index表示
{
 InvalidateRect(hDlg, NULL, TRUE);
 _show.bordreset(hDlg);

 //Index読み込み
 hEdit4 = GetDlgItem(hDlg, IDC_EDIT4);
 GetWindowText(hEdit4, number, sizeof(number) / sizeof(TCHAR));

 MNIST_compact img = _nn.getmnist(atoi(number));
 _show.IndexShow(hDlg, img);

 //ラベル表示
 static HWND htxt;
 htxt = GetDlgItem(hDlg, IDC_STATIC_TXT1);
 string txt = to_string(img.labels.front());
 txt = "解答 " + txt;
 SetWindowText(htxt, _TEXT(txt.c_str()));

 //判定表示
 hEdit2 = GetDlgItem(hDlg, IDC_EDIT2);
 SetWindowText(hEdit2, _TEXT(""));

 return TRUE;
}

 

にて、「Index View」ボタン処理。

訓練データのIndexを取得し画面に表示しています。

 

case IDOK:
{
 XYPOINT pos;
 //画面に手書きした数字を表示
 for (int i = 0; _show._image.size() > i; i++)
 {
  pos.y = i / MASS_X;
  pos.x = i % MASS_X;
  if (_show._image[i] > 0)_nn.file::printf_ex("1");
  else _nn.file::printf_ex("0");
  if*1;
 //学習ファイル.を読み込み
 hEdit3 = GetDlgItem(hDlg, IDC_EDIT3);
 GetWindowText(hEdit3, str_efile, sizeof(str_efile) / sizeof(TCHAR));
 //文字解析
 TEST_RESLT rlt = _nn.inputone(str_network, str_efile,img);

 //判定表示
 hEdit2 = GetDlgItem(hDlg, IDC_EDIT2);
 string txt = to_string(rlt.x);
 SetWindowText(hEdit2, _TEXT(txt.c_str()));

 

 return TRUE;
}

 

 

マウス入力したデータをMNIST_compact 型のimgに入れて

 

TEST_RESLT rlt = _nn.inputone(str_network, str_efile,img);

 

にて、文字解析している。

str_networkにはネットワーク情報。

str_efileに学習したファイル。

imgにマウスで入力した文字データ。

 

case IDC_BUTTON1://リセット
{
 InvalidateRect(hDlg, NULL, TRUE);
 _show.bordreset(hDlg);

 //判定表示
 hEdit2 = GetDlgItem(hDlg, IDC_EDIT2);
 SetWindowText(hEdit2, _TEXT(""));

 return TRUE;
}

 

にて、ボードと、入力データをクリアしている。

 

 

以上で、手書きの入力データを判定できるようになった。

精度を上げるためには、マウスで入力したデータを訓練データに追加すればよい。

すると、MNIST SETデータに頼らずとも

オリジナルの訓練データを自前で用意できる下準備がそろうことになる。

 

発展・応用として、

ひらがなとか、漢字とかも同様の手法で

一応、判定出来るようになるが、精度は低いと思う( ゚Д゚)。

 

 

 

以上です。

 

 

 

 

 

 

 

*1:i+1)% MASS_X==0)_nn.file::printf_ex("\n");
 }
 //手書きデータをimgに入れる。
 MNIST_compact img;
 img.images.push_back(_show._image);//画像データ
 img.labels.push_back(-1); //解答
 img.index = 0; //Index


 TCHAR str_network[256];
 TCHAR str_efile[256];
 //Network.を読み込み
 hEdit1 = GetDlgItem(hDlg, IDC_EDIT1);
 GetWindowText(hEdit1, str_network, sizeof(str_network) / sizeof(TCHAR