この記事、何?
画像の切り出しとかはできたから、「認識」に取り組みたい。
興味が持てるような内容にしようかと思って、ルービックキューブを解かせようかと。
「解かせる」といっても、20手で解ける神の数字がロジックとかあるらしいので、そこではなく、写真からキューブの状態を取得できる部分にAI関連を使う。
(なので、実際に「解く」ロジックは多分組み込まない、かも?)
まあ、少しかじってみたらすぐにあれこれやるのは難しそうなので、少しずつ。
まずは、UIだけ作ったからここからPythonでOpenCVして読み込むところまで。
作ろうとしたもの
段階的にこんな感じ
1.表示(+保持)
まずは6面分の情報を持たないと始まらない。
内部でその包括的な情報のクラスを作ってその中に変数を定義。そして表示。
表示は・・・どうしようか悩んだけど、3Dをいい感じで表現できる用意がないので、展開図で全面分の情報を出し、
菱形を27個並べて斜めから眺めた感じの全体図にしてみた。
確認用に、手元の買ってきたルービックキューブをshuffleした状態の色パターンを再現させるボタンを付けた。
2.動く
3方向に3列任意の列を動かせるので、それらを指示できるようにしてみた。
まあ、見てわかりづらいけど、
A.右手前に回す → RightDown
B.左手前に回す → LeftDown
C.各層を反時計回りに回す → TurnRight
で、それぞれ3列を独立に指定可能にしてみた。
で、各イベントののち、展開図と全体図を再描写。
(回転方法は90度のみ。180度とか、反対方法(=270度)は複数回クリックする、ということで。)
3.検証
動作検証を兼ねて、遊んでみた。
Shuffleボタンを押して、それぞれのボタンを考えて延々とクリッククリック・・・
勘所が違うので普段の数十倍時間がかかったけど解けたのでOKでしょ、多分。
4.静止画読み取り(各面ごと)
PythonでOpenCVを使って、各面を指定して静止画から9つ読み込んだ色情報と差し替えようかと。
1枚で1つの静止画を読む前提で、どの面のPictureをファイルなのかということを指定。
そしたらファイル洗濯で指定されたパスと併せてPythonに処理させてもらったらそれを表示。
それを6回繰り返す。
5.静止画読み取り(まとめて)
3x3x3のルービックキューブは最低2枚写真を撮影すればよい。
センターキューブ間の相対位置が変わらないので、それで行ける(はず)。
なので、全体図のような写真を反対向きから撮影した写真のペアだけ渡してそれらの面の認識をして展開。
とりあえずイメージできているのはここまでかな。
気が向けば、ルービックキューブ解けない人向けに、
「写真撮って解析ボタンを押すとそのキューブをどう回せば解けますよ~」
といえるツール作ってもいいかも。(需要あるか・・・?)
あっ、神の数字が実際に機能するところ見てみたい気もするから、それを移植してみても面白いかも?
できたもの
主題のPythonのほうにこだわりたいのでUIはほんとこれだけで。
上記の3.検証、まで。4.のUIのコントロールをちょっとだけ追加。
まあ・・・わかりづらいね。
主目的が画像解析を深めることで、最低限目的を果たしているのでひとまずこれで妥協しよう。。。
気が向いたら人様が見てわかるものにするかも。
絵
感想
動作検証で実際にUI上のクリックでアプリ内で解いてみたときの感想、「疲れた」。
やっぱ、人間の「認識」ってすごいわ。3次元をイメージするためにくるくる動かしてみてみる。
それができないので解きかけの状態と完全一致するように買ってきたキューブを合わせて考えたりしてみた。
そう、普通に6面そろえるよりそっちのほうがだいぶ難しいのよ。
そういう意味では、shuffleボタンを押して、それと同じものを作ってみる、というのがこのツールの正しい使い方かも。
次回予告
しまった・・・ここまで、この投稿にコードが載せられていない。。。
というわけで、今のC#側のスタブがこれ。
この外部仕様を満たすものをPython(と、C#->Pythonの呼び出し処理)で作る。
//フォーム上のクリックイベント
private void BtnSetPicture_Click(object sender, EventArgs e)
{
int flg = -1;
var dialog = new OpenFileDialog();
dialog.ShowDialog();
string filePath = dialog.FileName;
//MessageBox.Show(filePath);
if (optWhite.Checked == true) { flg = 0; }
if (optYellow.Checked == true) { flg = 1; }
if (optBlue.Checked == true) { flg = 2; }
if (optRed.Checked == true) { flg = 3; }
if (optGreen.Checked == true) { flg = 4; }
if (optOrange.Checked == true) { flg = 5; }
wholeCube.SetColors(flg, Python.ReadColors(flg.ToString()));
RefreshView();
}
//共通関数側の呼び出し処理(スタブ)
public static class Python
{
public static string ReadColors(string filePath)
{
return ReadDummyColors(filePath);
}
private static string ReadDummyColors(string filePath)
{
string ret = "";
if (filePath == "0") { ret = "000000000"; }
else if (filePath == "1") { ret = "111111111"; }
else if (filePath == "2") { ret = "222222222"; }
else if (filePath == "3") { ret = "333333333"; }
else if (filePath == "4") { ret = "444444444"; }
else if (filePath == "5") { ret = "555555555"; }
return ret;
}
}