INDEX

*INDEX

作ったもの、メモしておきたい知識など

deeplab v3+でオリジナルデータを学習させる

deeplab v3+ で自分のデータセットを使ってセグメンテーション出来るよう学習させてみました。
deeplab v3+のモデルと詳しい説明はこちら
github.com


データセットの準備

まず学習させるためのデータセットを作成します。
今回は画像中の木をセグメンテーションすることにしました。なので、背景領域と木領域の2クラスに分類することを学習させます。

用意するもの

①元の画像
②ラベル画像 を1セットとしたデータセットを用意します。
f:id:azurex:20190301210712j:plain
f:id:azurex:20190301210731p:plain
元の画像はjpg、ラベル画像はpng(モノクロ画像)で用意しました。
ラベル画像は、背景をrgb(0,0,0)、木領域をrgb(1,1,1)としてペイントソフトで手動で作成しました。もし2クラス以上にに分類したいときはr(n,n,n)でさらに塗り分ければ良いです。
ただし初期設定ではrgb(255,255,255)は無視されます。これはsegmentation_dataset.py で設定されているので変更可能です。

元画像とラベル画像は必ず同じ名前(例:1.jpgと1.png)をつけて保存します。

フォルダ分け

用意したデータセットをフォルダ分けします。
/deeplab/datasets/にoriginalという名前で新規ファイルを用意します。
そして以下のようなフォルダ群を新規作成します。

original
-img
  -train
  -trainval
  -val
-lbl
  -train
  -trainval
  -val
-lst
  -train
  -trainval
  -val

imgのtrainには訓練用画像、trainvalには訓練時の評価用画像、valには評価画像を入れます。
lblにもimgで入れた画像とセットとなるラベル画像をそれぞれ入れます。
lstには画像の名前を並べたテキストファイルを用意します。

例えばimg/train、val/trainに以下のような画像セットを入れていた場合は
f:id:azurex:20190301214522j:plainf:id:azurex:20190301214529j:plain
このように拡張子を除いた画像の名前を羅列したテキストファイルを作成してlst/trainに保存します。
f:id:azurex:20190301214652j:plain

TFRecordに変換

データセットが用意できたら、次はTensorFlow用の形式である「TFRecord」に変換する必要があります。
これは/deeplab/datasets/build_voc2012_data.pyを利用して変更します。

/deeplab/datasets/download_and_convert_voc2012.shを見てみると、以下のような記述があります。

echo "Converting PASCAL VOC 2012 dataset..."
python ./build_voc2012_data.py \
  --image_folder="${IMAGE_FOLDER}" \
  --semantic_segmentation_folder="${SEMANTIC_SEG_FOLDER}" \
  --list_folder="${LIST_FOLDER}" \
  --image_format="jpg" \
  --output_dir="${OUTPUT_DIR}"

これを参考にしてオリジナルデータセットをTFRecordに変換するconvert_dataset.shを作成します。
以下のように記述すると訓練データのデータセットを変換出来ます。

python ./build_voc2012_data.py \
  --image_folder="./original/img/train" \
  --semantic_segmentation_folder="./original/lbl/train" \
  --list_folder="./original/lst/train" \
  --image_format="jpg" \
  --output_dir="./original/tfrecord" \

最後のoutput_dirでどこに変換後のTFRecordファイルを保存するか指定しています。
指定したフォルダに変換後のファイルが作成されていればOKです。

設定の追加

segmentation_dataset.pyにデータセットの設定が記載されているので、ここに自分で作成したデータセットの設定を記述します。
以下のように新しいデータセットの内容を記述します。

_ORIGINAL_INFORMATION = DatasetDescriptor(
    splits_to_sizes={
        'train': 50,  # num of samples in images/training
        'val': 10,  # num of samples in images/validation
    },
    num_classes=3,
    ignore_label=255,
)

また、下記のように、'original'という設定を_DATASETS_INFORMATIONに追加します。
これで学習実行時にデータセットにoriginalを指定すると、自作のデータセット設定を適応します。

_DATASETS_INFORMATION = {
    'cityscapes': _CITYSCAPES_INFORMATION,
    'pascal_voc_seg': _PASCAL_VOC_SEG_INFORMATION,
    'ade20k': _ADE20K_INFORMATION,
    'original':_ORIGINAL_INFORMATION
}

学習

準備が完了したので学習を行ってみます。
学習はdeeplab/test_local.shのファイルの一部を変更して行います。
このtest_local.shは、実行するとPASCALデータセットを用いた学習を行います。
これを自作のデータセットを参照するようにフォルダパス等を変更します。

フォルダパスを変更した後、--dataset= の部分を_DATASETS_INFORMATIONに新規追加したoriginを参照するように変更します。

--dataset = origin

これでtest_local.shを実行することによって自作データセットで学習が行えます。

結果

訓練画像60枚で500エポック程度学習を回した結果
f:id:azurex:20190318184909p:plain

Gitのメモ

自分用にGitの使い方の基本をまとめました。


以下のサイトを参考にさせていただきました。
Gitの基本知識から知りたい場合はこちらがおススメです。
backlog.com

コマンドの一覧はこちらがおススメです。
qiita.com


用語

Gitで使用される用語について簡単にまとめました。
自分なりの解釈なので厳密には違うかもしれません。

リポジトリ ファイルを保存する場所 ローカルとリモートがある
インデックス リポジトリに反映させる変更を保持する addコマンドで登録する
コミット 変更をリポジトリに保存する 具体的には前回との差分を保存する
プッシュ ローカルの保存結果をリモートリポジトリに反映させる
クローン リポジトリのコピーを作成する
プル リポジトリの変更を自分のリポジトリに反映させる
作業ディレクト ファイルの変更などを行う作業用のフォルダなど
マージ ブランチ内の変更を別のブランチに統合させる

つまり、Gitを使って複数人でバージョン管理をするには

  1. リモートリポジトリを作る
  2. 各々がローカルリポジトリを作成する
  3. 作業フォルダを作り、作業を進める
  4. ファイルの変更などをインデックスにaddする
  5. インデックスの内容をローカルリポジトリにコミットする
  6. ローカルリポジトリからリモートリポジトリにプッシュする
  7. ブランチを作成している場合は、衝突が無いことを確認し、masterブランチにマージする
  8. 他の人はリモートからmasterブランチの内容をプルし、自分のローカルを更新する
  9. ④~⑧を繰り返す

という流れになります。

リポジトリを作成する

まず、例として「test」というフォルダを作成し、これをローカルリポジトリにします。
testフォルダの場所でコマンドプロンプトを起動します。

git init

これでカレントフォルダ内に「.git」という隠しフォルダが生成されます。

f:id:azurex:20190215002401j:plain
この場合「.git」がリポジトリ、「test」が作業フォルダになります。

コミットする

コミット時のユーザー名、メールアドレスの登録
git config --global user.name <ユーザー名>
git config --global user.email <メールアドレス>
addする

コミットする前にインデックスに変更を行ったファイルを登録します。

git add <ファイル or フォルダ>

以下のようにすると新規作成・変更のあったファイルのみ登録することができます。

git add .
コミット前のインデックスの確認
git status
変更内容の差分を確認する
git diff <ファイル>

コミット

コミットを行います。
m "コメント" でコメントを付けてコミットできます。
git commit だけだとコミット後に別でコメントを記入するvimのようなアプリが起動します。

git commit -m "commit comment"

以上でローカルリポジトリでの作業は終了です。
ここからはリモートリポジトリを使用する場合のプッシュ、プルなどの使い方についてです。

リモートリポジトリを作る

リモートリポジトリを作成します

git init --bare

これでカレントディレクトリをリモートリポジトリにします。

クローン元のリポジトリからプル

リモートリポジトリからプルを行い、ローカルリポジトリを最新状態に更新します。

git pull

クローン元のリポジトリにプッシュ

リモートリポジトリにローカルリポジトリから変更を反映させます。

git push

ブランチの作成

今のままだとmasterブランチにコミットすることになってしまうので、ブランチの作成をします。

git branch <branchname>

作成したら以下で確認を行います。

git branch

f:id:azurex:20190226114319j:plain

作成したブランチに移動します。

git checkout <branch>

statusコマンドで確認すると、作成したtest1ブランチに移動していることが確認できました。
f:id:azurex:20190226114559j:plain

マージする

まず現在のブランチからmasterブランチに移動します。
その後、以下のコマンドでmasterにtest1の変更をマージします。

git merge <commit>


これでtest1ブランチの内容がmasterブランチに取り込まれます。




基本的なものは以上です。
別の記事でgithubを利用する方法についても書きたいと思います。

TensoFlowでCycleGanを実装する

はじめに

Ganの派生であるCycleGanの論文を読んだので、実際に動かしてみました。

論文はこちら

[1703.10593] Unpaired Image-to-Image Translation using Cycle-Consistent Adversarial Networks


GitHubはこちら

https://github.com/junyanz/CycleGAN

https://github.com/xhujoy/CycleGAN-tensorflow


①は論文の著者であるJun-Yan Zhuさんが公開しているプログラムで、②はTensorflow用のプログラムです。

①はLuaで実装されており、私はTensoflowで実行したかったため②を使用しました。


サンプル実行

コマンドプロンプトを起動して、ダウンロードしてきたフォルダまで移動させます。

cd 移動先のフォルダ

で移動します。

移動先のフォルダをコマンドプロンプト上にドラッグアンドドロップすると、フォルダの場所を自動で記述してくれます。

画像セットのダウンロード


まずは学習に必要な訓練画像と、テスト画像のセットをダウンロードします。

コマンドプロンプト

bash ./download_dataset.sh horse2zebra

と打つと、馬とシマウマの画像セットをダウンロードすることができます。


ダウンロードできる画像セットは以下のようになっています。

  • apple2orange (リンゴとミカン)
  • summer2winter_yosemite (ヨセミテ国立公園の夏と冬)
  • horse2zebra (馬とシマウマ)
  • monet2photo (モネの絵と写真)
  • cezanne2photo (セザンヌの絵と写真)
  • ukiyoe2photo (浮世絵と写真)
  • vangogh2photo (ゴッホの絵と写真)
  • maps (空撮画像と地図)
  • cityscapes (道の画像とセグメンテーションを行った画像)
  • facades (建物の壁とセグメンテーションを行った画像)
  • iphone2dslr_flower (iphoneで撮影した花の画像と一眼レフカメラで奥をぼかした画像)
  • ae_photos (テスト用:風景の画像セット)

最後のae_photosはテスト画像のセットのようで、主に外国の風景の画像が入っていました。


もし、コマンドでのダウンロードに失敗した場合は、直接ダウンロード先のサイトに飛び、そこからzipファイルをダウンロードするという方法もあります。

download_dataset.shを適当なテキストエディタで開くと、コード内にダウンロード先のURLが記載されています。

そのURLにアクセスするとzipフォルダが並んでいるので、クリックして必要なものをダウンロードします。


ダウンロードしてきたフォルダを見ると、画像はそれぞれtrainA、trainB、testA、testBというフォルダに分類されています。

f:id:azurex:20180627110708p:plain

CycleGanでは
A→Bへ変換(画像生成)するGenerator&その生成画像を見極めるDiscriminator
B→Aへ変換(画像生成)するGenerator'&その生成画像を見極めるDiscriminator'というように相互に学習させるため、集合Aと集合Bの訓練画像・テスト画像を用意する必要があります。


自分でデータセットを用意する場合も、trainA、trainB、testA、testBという構成で作成すれば良いようです。


学習させる


では、リンゴとミカンの画像セットで学習を行ってみます。

ここから先はhttps://github.com/xhujoy/CycleGAN-tensorflowでダウンロードしたプログラムの実行コマンドになります。他のプログラムをダウンロードした場合はそれぞれREADME.mdで確認してください。


学習はコマンドプロンプトで以下のようにして実行します。

CUDA_VISIBLE_DEVICES=0 python main.py --dataset_dir=apple2orange

CUDA_VISIBLE_DEVICES=0 の部分はGPUの設定なので、詳しくはCUDAで調べてみてください。

GPUを搭載していない場合はCUDA_VISIBLE_DEVICES=0 の部分は記述しなくてよいです。


テスト

学習後のモデルを用いてテストを行います。

CUDA_VISIBLE_DEVICES=0 python main.py --dataset_dir=apple2orange --phase=test --which_direction=AtoB


この際、学習したモデルはcheckpointsに保存されます。

これでテストができました。


結果

リンゴとミカンの訓練画像で1時間学習しました。

f:id:azurex:20180627135456j:plain

おおう・・・

やはり1時間ではなかなか難しいですね。

少なくとも10時間は学習させる必要があると思います。

終わり

CycleGanを実装してサンプルを動かしてみました。

今後、自分で集めた画像を用いて学習を行ってみたいと思います。

Atomでpython

タイトル通り、Atomでpyton環境を整備したのでメモしておこうと思います。

Atomのインストール

https://atom.io/
ここからダウンロードします。今はバージョン1.27.2でした。

起動したら、Welcome Guideが開くと思うので、「Install a Package」→「Open Install」の順にクリック
f:id:azurex:20180612002338p:plain:w500
f:id:azurex:20180612002433p:plain:w500

すると検索ボックスが出てくるので、「atom-runner」で検索し、出てきたパッケージをインストールします。
これで完了です。実際に動くか確認してみます。


動作確認

簡単なpythonコードを書きます。

print("test")

こんな感じ
これを一回保存します。名前は"test.py"のように付けました。

では実行してみます。「Alt + R」で実行できます。
f:id:azurex:20180612003335p:plain
無事に実行できるとこんな感じになります。

もしpathのエラーが出たら、「Python.exe」があるフォルダにpathを通してから、もう一度実行すると良いです。(私の場合はそれで上手くいきました)


文字化け対策

先ほどのように英語を表示させる以外に、日本語を表示させようとすると、このままでは文字化けしてしまいます。
なので、日本語を表示させたい場合は文字化けしないように設定を行うことが必要です。
Atomの「ファイル」→「起動スクリプト」をクリックしてinit.coffeeを開きます。
そこに

process.env.PYTHONIOENCODING = "utf-8";

を最後に追加して(#いらない)、Atomを再起動すると文字化けが無くなります。

atom-runnerは設定を変更するとCやC++C#も実行できるようになるみたいなのでやったら追記したいです。

OpenGLで全方位シューティングを作った

OpneGLで何でも良いから作品を作って提出する、という課題のときに作ったもの。

FPSっぽいゲームを作ろう!と、FPSの知識があまりない状態で作った記憶があります。




環境

  • visualstudio2013
  • GLUT3.0.0



完成した物

これがプレイ画面です。右にあるのはマップを俯瞰で写したものです。
:自機
:遅い敵 緑:早い敵
黄色:回復アイテム

f:id:azurex:20180608223243p:plain


f:id:azurex:20180608224406p:plain

Zボタンで弾を撃て、スペースでジャンプして回復アイテムをとります。
HPが0になるとゲームオーバーです。

f:id:azurex:20180608223620p:plain

敵を倒すとポイントが加算されていきます。

他の挙動としては

  • マップの外には出れないようにする(見えない壁にぶつかる)
  • 敵と回復アイテムはランダムな時間にランダムな場所で生成
  • 自機は十字キーの↑↓で移動、←→で回転する(1人称視点でFPSっぽい!!)
  • 敵は自機に向かって距離を詰めてくる。追いかけられる。

など。



コード

シューティング部分のあたり判定や弾の生成などは、ネットでよく見かけるものなので割愛。

OpenGLで工夫したところのコードを紹介します。


① 俯瞰図

3Dの全方位シューティングを初めて作って気づいたのは俯瞰図が絶対いるということです。
(2Dだと有り難みがわからないけれど、3Dだと真後ろから生まれた敵に気づかずやられる。)

ということで俯瞰図の表示の仕方についてメモしておきます。


まずはOpenGLで表示する画面関数を二つ用意します。

//メイン画面
void DRAW_MONITER_1(int x, int y, int w, int h)
{ /*ここにメイン画面で写す内容を書く*/}

//俯瞰画面
void DRAW_MONITER_2(int x, int y, int w, int h)
{/*ここに俯瞰画面で写すない内容を書く*/}

これをdisplay関数で呼び出します

void display(void)
{
	/* 初期化 */
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);   /* 画面を消去 */
	glMatrixMode(GL_MODELVIEW);  /* 幾何(描画位置など設定する)モード */
	glLoadIdentity();  /* 幾何を初期化する */
	glLightfv(GL_LIGHT0, GL_POSITION, lightpos);  // 光源の位置

        DRAW_MONITER_1(0, 0, winWidth - 255, winHeight);  /*メイン画面呼び出し*/
	DRAW_MONITER_2(750, 290, 250, 250); /*俯瞰画面呼び出し*/
	glutSwapBuffers();
}

2画面を横に並べるために、ウィンドウ幅(winWidth)を余分にとって調整しています。

で、このdisplay関数をmain関数内で

glutDisplayFunc(display); /* 画面表示 */

こんな感じで呼び出せば2画面表示が出来ます。
(自分は、初期化する関数を全部含んだ自作関数Init()をつくってそれをmain関数内で呼び出しました。)



次に、片方の画面を俯瞰画面にするために、画面を写すカメラの位置を設定します。

設定はvoid DRAW_MONITER_2の中にglutLookAt()を呼び出して行います。

カメラの位置は以下のように設定します。

gluLookAt(CameraX, CameraY, CameraZ, /*カメラ位置*/
		0, 0, 0, /*カメラの注視点*/
		0.0, 1.0, 0.0); /*カメラのxyz軸に対してどのくらい上に位置するか(Up値)*/

float CameraAzimuth = 90.0;   /* カメラの位置(方位角) */
float CameraElevation = 90.0;    /* カメラの位置(仰角) */
float CameraDistance = 11;    /* カメラの位置(原点からの距離) */
CameraX = CameraDistance * cos(CameraAzimuth * RAD) * cos(CameraElevation * RAD);
CameraY = CameraDistance * sin(CameraElevation * RAD);
CameraZ = CameraDistance * sin(CameraAzimuth * RAD) * cos(CameraElevation * RAD);

カメラの方位角と仰角を90度にして、距離はマップ全体がちゃんと写る位置に調節します。

これでカメラが上方から原点を写した状態になります。

あとはメイン画面と同じ位置に自機、敵、弾などを表示することでマップを俯瞰した画面をつくることができます。



②1人称視点+キーに合わせてカメラ位置の移動

FPSっぽさに必要なもの、それは1人称視点です。(たぶん)

カメラ位置の設定を工夫する事で、キー入力に合わせてカメラ位置を変えています。

gluLookAt(Cube.ShiftX, Cube.ShiftY, Cube.ShiftZ,
   Cube.ShiftX + cos(g_theta), Cube.ShiftY, Cube.ShiftZ + sin(g_theta),
   0.0, 1.0, 0.0);

このCubeは自機の構造体で、自機の位置や回転角度、HPなどの値を保持しています。

カメラの位置は自機の位置と同じ位置を設定します。

// 自機の位置
typedef struct {
	float ShiftX;
	float ShiftY;
	float ShiftZ;
	float RotateX;
	float RotateY;
	float RotateZ;
	int hp;
	int point;
} Geometry;
Geometry Cube;

カメラの注視点は、キー入力が行われるたびにg_thetaの値を変更することで、左右に動くようにしています。
キー入力がされると以下のように値を変更します。

	// 視点の移動(右回転)
	if (right_r == 1){
		g_theta += 0.004;
	}
	// 視点の移動(左回転)
	if (left_r == 1){
		g_theta -= 0.004;
	}

これでキー入力に応じて視点が代わり、1人称視点カメラとなります。






まとめ

  • 俯瞰図があるとゲームとして面白くなる
  • もっとボムとか弾の種類を増やしたい(全方位に飛んでく弾とか)
  • 敵も跳ねたりするやつとか、ボスとかを作りたい




GitHubソースコードを置いたので全文が気になる方はどうぞ
https://github.com/MYMYtk/FpsGame-byOpenGL

ARを触ってみた

初投稿で何を投稿しようかなとネタを探していたのですが、
ARでTVっぽいものを作って表示させる課題をやったことがあったので、それを紹介します。
 

 

【環境】

・Windows10

・Visualstudio2013

ARToolKit 2.72.1

OpenCV 3.0.0

・GLUT3.7.6

 ARではおなじみのARToolKitを使用しました。基本このARToolKitのsimpleLiteを元に作成しました。

 

①テレビをOpenGLで作る

CG技術のない学生が考えたテレビっぽいもの

f:id:azurex:20180529132158p:plain

(古い・・・)

全部四角でテレビを作ってみました。リアルを捨ててかわいい感じのテレビを目指しました。
四角の記述はこんな感じで4点を指定しています。

glVertex3d ( -0.4 , -0.4, 0.5);/* A*/ glVertex3d ( -0.3 , -0.4, 0.5);/* A1 */
glVertex3d ( -0.5 , -0.4, 0.0);/* A2 */ glVertex3d ( -0.6 , -0.4 , 0.0);/* A3 */

 
見えずらいのですが、足の側面や本体の側面は色を濃くして影っぽくしたりしています。



②カメラの映像を表示
先ほどのテレビにカメラからの映像を写してみます。
パソコンにつないだカメラからの映像がリアルタイムで写ります。これだけでも結構面白いです。
f:id:azurex:20180529133227p:plain

 OpenCVから映像を取得し、画像に切り出して、先ほどのテレビの白い部分にテクスチャとして貼り付けています。
よく言われていることですが、OpenCVOpenGLでは原点の位置が左上と左下と異なるので、変換してから貼り付ける必要があります。

capture >> frame ;
cv :: flip (frame , frame , 0);

(cv::flip()で反転させている)



③AR表示させる
マーカーの上にテレビを表示させてみます。
f:id:azurex:20180529134342p:plain

実際に表示されるとおおー!となります。
カメラ1でマーカーを写して、カメラ2でテレビの中の映像を写すといった感じです。
(カメラを両手に持ちながらさらにスクショをとるのが難しかった・・・)



④おまけ機能:色を変える
おまけとしてAR表示させた状態でテレビの色を変える機能をつけてみました。
実行中にキーボードの「R」「G」「B」を押すと、RGBの値がそれぞれ+0.1されます。
「E」「F」「V」を押すと逆にRGBの値が-0.1されます。
これで自分の好みの色にテレビの色が変えられます。
f:id:azurex:20180529135319p:plain
綺麗な色をピックアップしたもの


⑤おまけ機能:時間表示
おまけ2としてテレビといえば右上の時刻表示かな、と思ったのでやってみました。
現在時間はtime関数で取得できます。

time_t t = time ( NULL );

取得した時刻を文字データにして、画面右上に表示させます。
このときに画面上ぴったりに配置すると、テクスチャと同じ表面に表示されておかしなことになるので、画面の表面よりほんの少し前に出してあげるといいです。

f:id:azurex:20180529140224p:plain

見えにくいですが時間と曜日が表示されています。



【感想】
ARToolKitを使うとすぐARで遊べて楽しい
・カメラを2台設定するのが難しかった。そこでかなり時間を取られた。
・CGでかっこいいリアルなTVを作ってみたい


参考サイト
http://kougaku-navi.net/ARToolKit/