いつもの作業の備忘録

作業を忘れがちな自分のためのブログ

【C#】ウィンドウ内での画面遷移をコントロール切り替えで実装

C#を初めて触って簡単なところにもかかわらず躓いたのでメモ。サンプルアプリはページ下部にGitHupへのリンクがあるのでそちらから。

0.作成するプログラム

あるウィンドウ上のボタン操作によって表示画面が切り替わるプログラム。イメージ(完成品)は以下の通り。
f:id:whg_res:20160409235721p:plain
↓button1を押す  button2を押す↑
f:id:whg_res:20160409235729p:plain

初心者的感覚で上の動作を言語化すると「C#のフォームアプリケーションでウィンドウ内の画面遷移をしたい」だったのだが、これらのキーワードではなかなか思い通りのページに辿り着けなかった。より良い実装も存在するように思うが、ページ数が少ないうちは結構簡単でいいと思っている。

1.プロジェクト作成

Visual Studio 2013の場合はプロジェクトを新規作成し、C#Windowsフォームアプリケーションを選択する。
フォーム上にPanelコントロールを画面いっぱいに配置。このPanelコントロールにあらかじめ表示したいページを登録しておいて、ボタンクリックイベントが発生した時にどのページを表示するか選択する方針で実装。遷移先の2ページは個別にレイアウトを検討したいのでユーザコントロールとして作成した。下図参照。
f:id:whg_res:20160410102523p:plain

2.ページ遷移のコード

ポイントは2つのユーザコントロールをForm.cs内でpublic staticで確保すること。これによりUserControl1とUserControl2からもそれぞれのインスタンス(ctr1とctr2)にアクセスしてVisibleの値を変更できる。

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication3
{
    public partial class Form1 : Form
    {
        //staticで宣言することでインスタンスを固定
        public static UserControl1 ctr1;
        public static UserControl2 ctr2;

        public Form1()
        {
            InitializeComponent();
            ctr1 = new UserControl1();
            ctr2 = new UserControl2();

            //パネルにコントロール1、2を追加
            panel1.Controls.Add(ctr1);
            panel1.Controls.Add(ctr2);

            //コントロール1のみを見えるようにする
            ctr1.Visible = true;
            ctr2.Visible = false;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
        }
        private void panel1_Paint(object sender, PaintEventArgs e)
        {
        }
    }
}

UserControl1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication3
{
    public partial class UserControl1 : UserControl
    {
        public UserControl1()
        {
            InitializeComponent();
        }

        private void UserControl1_Load(object sender, EventArgs e)
        {
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Form1.ctr1.Visible = false;
            Form1.ctr2.Visible = true;
        }
    }
}

UserControl2.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication3
{
    public partial class UserControl2 : UserControl
    {
        public UserControl2()
        {
            InitializeComponent();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Form1.ctr1.Visible = true;
            Form1.ctr2.Visible = false;
        }
    }
}

今回作成したSW
GitHub - whg-res/cs_form_jump_sample: c# form application page jump example


※参考
https://github.com/whg-res/cs_form_jump_sample/
http://blog.hiros-dot.net/?p=4815
http://qa.atmarkit.co.jp/q/4935

【Caffe】C++でCaffeをAPI的に使う

前回の記事でWindows環境でCaffeを利用できるようにした。これを別プロジェクトからAPI的に利用したかったので実施した。実力不足で不恰好になっているため、改善方法は継続して調査。作成したプログラムは少し重いがGitHubで公開中なので、本ページの最後から辿ってもらいたい。

0.方針

本来ならlibcaffeプロジェクトで作成するlibファイルを別プロジェクトから参照できるようにすべきだが、どうしてもVisualStudioでの設定が出来なかった。そのため前回の記事でコンパイルしたCaffeの中にあるcaffeプロジェクトをDLL作成用に変更し、libcaffeのラッパーを作成する。ちなみに、解決できていないエラーは以下のようなもの。レイヤーの読み込みがうまくいっていないらしい。
c++ - Caffe layer creation failure - Stack Overflow
Google グループ

1.caffeプロジェクトをDLL出力に変更

下記サイトを参考にcaffeプロジェクトの出力をDLLに変更。ただし、今回のプリプロセッサはCAFFE_WRAPとした。
Visual C++でのDLLの一般的な作成方法(暗黙的リンク) - Gobble up pudding

2.caffeプロジェクトにcaffe_wrapper.cpp/caffe_wrapper.hを作成

DLLのソースを記述。外部から呼べる関数run_test()はMNISTの数字識別を行う関数を利用させていただいている。この関数内ではlibcaffeの関数を自由に使うことが出来るので、CaffeAPIを使ったプログラミングはこの中で行うことになる。もともとあったcaffe.cppは使わないので削除。
※このlibcaffeを直接利用するcaffeプロジェクトと同じものを自作できれば問題ないのだがどうしても設定が違っているようでうまくいかなかった。

caffe_wrapper.cpp

#include "caffe_wrapper.h"
#include <stdexcept>

namespace CaffeUtil
{
	caffe_wrapper::caffe_wrapper(){

	}

	void caffe_wrapper::run_test(string img_path, string model_path, string def_path){
		// get a testing image and display
		Mat img = imread(img_path);
		cvtColor(img, img, CV_BGR2GRAY);
		imshow("img", img);
		waitKey(1);

		// Set up Caffe
		Caffe::set_mode(Caffe::GPU);
		int device_id = 0;
		Caffe::SetDevice(device_id);
		LOG(INFO) << "Using GPU";

		// Load net
		Net<float> net(def_path, TEST);
		string model_file = model_path;
		net.CopyTrainedLayersFrom(model_file);

		// set the patch for testing
		vector<Mat> patches;
		patches.push_back(img);

		// push vector<Mat> to data layer
		float loss = 0.0;
		boost::shared_ptr<MemoryDataLayer<float> > memory_data_layer;
		memory_data_layer = boost::static_pointer_cast<MemoryDataLayer<float>>(net.layer_by_name("data"));

		vector<int> labels(patches.size());
		memory_data_layer->AddMatVector(patches, labels);

		// Net forward
		const vector<Blob<float>*> & results = net.ForwardPrefilled(&loss);
		float *output = results[1]->mutable_cpu_data();

		// Display the output
		for (int i = 0; i < 10; i++) {
			printf("Probability to be Number %d is %.3f\n", i, output[i]);
		}
		waitKey(0);
	}
}

caffe_wrapper.h

#pragma once
#ifdef CAFFE_WRAP
#define CWDLL_API __declspec(dllexport)
#else
#define CWDLL_API __declspec(dllimport)
#endif

#include <string>
#include "caffe/caffe.hpp"
#include "caffe/util/io.hpp"
#include "caffe/blob.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "boost/smart_ptr/shared_ptr.hpp"
#include "caffe/layers/memory_data_layer.hpp"

// Caffe's required library
#pragma comment(lib, "libcaffe.lib")

// enable namespace
using namespace std;
using namespace cv;
using namespace boost;
using namespace caffe;
namespace CaffeUtil
{

	class CWDLL_API caffe_wrapper
	{
	public:
		// コンストラクタ&デストラクタ
		caffe_wrapper();
		~caffe_wrapper() {};

		// テスト処理実行関数
		void run_test(string img_path, string model_path, string def_path);

	};

}

3.DLL利用側プロジェクト作成

新規プロジェクトcaffe_userを作成する。通常のコンソールプロジェクトでOK。以下に示す内容でDLLを利用するソース(main.cpp)を記述。
追加のインクルードディレクトリ、追加のライブラリディレクトリにCUDA、CuDNN、OpenCV、Boostなど3rdParty製ライブラリのパスを通す。これらはlibcaffeをビルドする際にNuGetで取得しNugetPackagesのディレクトリに保存されているはずである。同じものである必要があるため注意深くパスを通す。また、追加の依存ファイルにcaffe.libを追加。

main.cpp

#include "../caffe/caffe_wrapper.h"
#include <iostream>
#include <iomanip>
#include <vector>

using namespace CaffeUtil;

int main()
{
	caffe_wrapper *cw = new caffe_wrapper();
	string input =	"..\\..\\..\\data\\images\\mnist_0.png";
	string model =	"..\\..\\..\\data\\mnist\\lenet_iter_10000.caffemodel";
	string def =	"..\\..\\..\\data\\mnist\\lenet_test-memory-1.prototxt";
	cw->run_test(input, model, def);

	return 0;
}

4.ビルド&実行

ビルドはlibcaffe→caffe→caffe_userの順で実行する。最終的にcaffe_user.exeが出来るのでそれを実行すると文字認識のプログラムが走る。
★注意★実行時には各種dllが必要なため、dllと同じフォルダにcaffe_user.exeを配置して実行すること。

参考までに私が作成したプロジェクトを以下のサイトにアップロードする。細かなインクルード・リンクまわりやプロジェクトの設定で説明し切れていないものがあると思うので参照していただきたい。
GitHub - whg-res/caffe-from_c: a suggestion to use caffe in windows from C


※参考
http://fa11enprince.hatenablog.com/entry/2014/06/20/015808
https://initialneil.wordpress.com/2015/07/15/caffe-vs2013-opencv-in-windows-tutorial-i/
https://github.com/whg-res/caffe-from_c/

【Caffe】Windows7にCaffeをインストール

Windows環境でCaffeを利用できるようにする。基本的にはダウンロードしたCaffeのREADME.mdをそのままなぞっている。面倒なライブラリ周りをあまり気にしなくてよくなっているのでUbuntuより簡単かもしれない。

1.Visual Studio 2013をインストール

今回は Community 2013 を利用。最初、2015でも試してみたが後の CUDA 7.5 のインストール時に2015はサポートしていないと警告が出たので諦めた。以下のサイトからインストーラーをダウンロードし、実行。時間がかかる。
Downloads | Visual Studio Official Site

2.CUDA 7.5をインストール

次項でダウンロードするCaffeでCUDA 7.5がデフォルト設定になっていたのでそれに従ってCUDA 7.5をインストール。以下のサイトからインストーラーをダウンロードし、実行。ダウンロードに時間がかかる。
CUDA 8.0 Downloads | NVIDIA Developer

3.Caffeのソースをダウンロード

以下のサイトからCaffeのソース(ZIPファイル)をダウンロードし、適当なところに解凍。解凍して得られるcaffe-masterのフォルダを以降、<CAFFE_HOME>とする。
https://github.com/BVLC/caffe/tree/windows

4.CuDNN v4をダウンロード

以下のサイトからCuDNN v4をダウンロード。以前書いた通り、事前にDeveloper登録が必要。
NVIDIA cuDNN | NVIDIA Developer

解凍して生まれるcudaフォルダを<CAFFE_HOME>直下に配置

5.Minicondaインストール

以下のサイトからMinicondaをダウンロードし、EXE実行でインストール。Python 2.7のWindows 64bit向けを選択する。
Miniconda — Conda

コマンドプロンプトを立ち上げ、以下のコマンドで必要なライブラリをインストール

> conda install --yes numpy scipy matplotlib scikit-image pip
> pip install protobuf

6.ビルド設定ファイルを編集

<CAFFE_HOME>/windows/CommonSettings.props.exampleをコピーしCommonSettings.propsを作成する。さらにその内容を以下の通り変更する(2か所)。これで先ほどのCuDNNの読み込みと、Minicondaの利用が可能になる。

CommonSettings.props

[変更前]
<!-- CuDNN 3 and 4 are supported -->
        <CuDnnPath></CuDnnPath>
        <ScriptsDir>$(SolutionDir)\scripts</ScriptsDir>
:
:
<PythonSupport>false</PythonSupport>


[変更後]
<!-- CuDNN 3 and 4 are supported -->
        <CuDnnPath>$(SolutionDir)\..</CuDnnPath>
        <ScriptsDir>$(SolutionDir)\scripts</ScriptsDir>
:
:
<PythonSupport>true</PythonSupport>

7.ビルド実行

Caffe.slnを立ち上げてビルドの準備を行う。以下の手順で各プロジェクトファイルの設定を変更。Debug、Release両方のモードで変更することを忘れずに。

  • プロジェクトを右クリックでプロパティ表示
  • [構成プロパティ]→[C/C++]→[一般]→[警告をエラーとして扱う] を確認
  • YesになっているのでNoに変更

※通常、そのままビルドを実行すればいいはずだが、私の環境ではコードページ932にふさわしくない文字が含まれるという警告が出た。ソースを確認すると、コメントのところのようだったので無視しても問題ないと判断した。

8.パスの設定

pycaffeにパスを通す。<CAFFE_HOME>\Build\x64\Release\pycaffe\caffeフォルダを<python_root>\lib\site-packagesフォルダにコピー。

※<python_root>はMinicondaインストール時にできるC:\Minicondaフォルダ(あるいはAnacondaフォルダ)を指定。
※ちなみにコマンドプロンプトで実行できるEXEが <CAFFE_HOME>\Build\x64\Release に生成されている。

9.動作確認

コマンドプロンプトを開き、pythonを立ち上げ、import caffe がエラーなく通ればインストール完了。また、<CAFFE_HOME>\Build\x64\Releaseに移動してcaffe.exeを実行して使い方のメッセージが表示されれば問題なくEXEが生成されていることが確認できる。


実は、必要なライブラリ群のインストールはVisual Studioでのビルド時にインストールされている。NuGetというライブラリのパッケージ管理システムが自動で必要なライブラリを判定しインストールしているという仕組み。

ここまでできればModel Zooから落としてきたモデルを実行することも可能となっていた(VGG16の物体認識モデルの動作確認済み)。