catalinaの備忘録

ソフトウェアやハードウェアの備忘録。後で逆引きできるように。

画像認識で機械学習が必要そうという結論に至るまでのメモ

考えを整理するための個人的なメモ的なものです。かたりぃなです。

画像・動画・音声などから特定の「もの」を認識したいことって多々ありますよね。
少なくとも私の中ではしょっちゅうあります。

さて、今回の記事は物体認識の問題色々調べた結果を自分の中で整理するためのメモです。
基本概念の整理が目的です。

物体認識とは

まず物体認識とは何ぞや?という問いについて。物体認識を問題領域の観点から大別して2系統あるようです。

  1. 特定物体認識
  2. 一般物体認識

まず1について。これが私の欲しい物体認識です。
既知の物体Aについて、画像中のどこに物体Aが存在するか(もしくは存在しない)を調べる

2ですが、画像が何を示しているものなのかを言い当てる(車の画像!とか)物体認識です。
画像処理以外の方面の知識も必要となってくるので、こちらは当面は保留とします。

特定物体認識の古典的手法

古くからあるアルゴリズムとして、次の2つをあたってみました。

  1. 物体の形状認識
  2. テンプレートマッチング

まず形状認識ですが、これは物体の形状を表現しやすいデータへと変換し、目的の形状と一致しているかを調べる方法です。
OpenCVの関数でいうとFindContourなどで形状抽出し、shape::match(内部的にはモーメントの比較)などが考えられます。
輪郭線などの画像の特徴的な点は、後で述べる特徴量という考え方にも関連してきそうです。

テンプレートマッチングは使い古された手法で、対象画像とテンプレート画像を比較し、
テンプレート画像をスライドさせていき、もっともよくマッチする場所を探すというものです。

テンプレートマッチングの課題

テンプレートマッチングは外界からのノイズに弱いという課題があります。
外界からのノイズとして、カメラの性能もそうですが、カメラ自身の置かれている環境の影響もあります。
特にwebカメラから取り込む画像というものは外界の影響をふんだんにうけます。(部屋の照明、手ぶれ、など)
テンプレートマッチングでは、対象画像に事前処理を施すことによって不安定要素を取り除くというアプローチが見られます。
照明が安定しないのであれば、適応的に輝度調整を行って誤検出となりうる影を取り除くなど。
(ロバスト設計、ロバスト性などの言葉がキーワード)
このように外界の不安定要素に対して固有に対処して乗り切ることもできますが、外界のすべての事象を事前に予測しておくことは困難です。

またテンプレートマッチングのアルゴリズムの特性上、画像の回転・拡大に弱いという問題があります。
これは、テンプレートマッチングがピクセル単位での比較が基本となっていることに起因します。
OpenCVが提供している比較式を眺めてみても、これは明らかです。
http://docs.opencv.org/master/df/dfb/group__imgproc__object.html#gsc.tab=0
ピクセル単位での比較ではなく、もっと抽象的な比較が行えれば問題解決に向けて大きく前進しそうです。

特徴量という考え方

特徴量といえば顔検出のHaarLikeが真っ先にあがります。
あとはHOGで人体検出などといった文献も見受けられます。
SIFTやSURF,最近のOpenCVではAKAZEが実装されて、AKAZEが今の注目ではないでしょうか。

さて、特徴量の詳細はもっと有用なサイトに譲るとして、
特徴量による画像比較は、検出したい物体について「ピクセルの値(RGB値など)の表現ではなく、その物体をもっともよく表現するポイントを数値で表現し、そのポイントごとの数値で比較しよう」
というアプローチと解釈しています。

さて、特徴量の具体的な例ですが、HaarLikeでは明度差、HOGでは輝度の勾配方向を使って表現します。
このようにして求められた値が特徴的な場所(キーポイント)を特徴点と呼ぶようです。

特徴記述

特徴量がわかったとして、その特徴量をソフトウェア内で表現する必要があります。
これが特徴記述と呼ばれるものです。
単純な差分値であればスカラー、勾配であればベクトルといった表現のほかにも、バイナリとしてどのように表現するかなど、色々なアプローチがあります。

特徴点マッチング

ここまでで特徴点とその表現方法が定義されました。
あとはマッチングするだけです。
OpenCVで提供されている特徴点マッチングのアルゴリズムも色々あって、
特徴記述によっては使える・使えないなどがあります。

特徴量とロバスト

さて、ロバスト性の話に戻ってきました。
画像のマッチングを行うとき、回転不変、拡縮不変、アフィン不変などと呼ばれていますが、
特徴量を使えばこれらの不変性を得られるのか(頑強であるか)といえばそうでもありません。
特徴量ごとににこれらの耐性は違っているので、適切な手法を選ぶ必要がありそうです。
こちらのサイトに代表的な特徴量ごとの測定結果が乗っていたので参考になりました。
AKAZE特徴量の紹介と他特徴量との比較 - 遥かへのスピードランナー

学術的には回転に対して頑健でない特徴量を回転不変にするための提案など色々あがっているようです。
そのあたりも含めて自分で実際に何かを作るときの手順は

  1. 特徴量をそのまま使った場合に「何ができるのか・何ができないのか」を理解し、選定する
  2. 目的機能を満足しない項目に対するフォロー(目的機能を満たすためのロバスト設計)

といった手順が考えられます。

じゃあAKAZE使えばいいんじゃない?と思われますが、私がやりたいことを実現するための課題はまだ残っています。
解決したもの

  • 回転不変、拡縮不変なアルゴリズムであること(そういう特性をもった特徴量を利用する。例えばAKAZE)

残っている課題

  • 外界からのノイズ耐性

機械学習

任意の特徴量のマッチングによって回転、拡大、照明に対してそれなりの耐性は付加できそうというところまで来ました。
それでも外界からの影響は少なからず受けますし、影響自体をゼロにすることは現実的ではありません。(専用部屋でのみ使うソフトですとかであれば話は別かもしれませんが。)
現実世界でのノイズとして

  • 撮影機材そのものの品質(解像度どのくらい?)
  • 検出対象物の個体差(検出対象物に傷、凹みなどがついているかもしれない)
  • 撮影環境(ピントが若干ずれているとか)

などが考えられます。
ここにあげた例が全てではなく、外界からの影響をすべて予測することは非常に難しい問題です。
検出対象の物体についても、品質ばらつきが少ない工業製品であればいいのですが、動物の種類や人間の顔に代表されるような「基本的な構造は同じだが、対象物固有の特徴が付加されているもの」は、これまでの手法だけでは個体差を吸収しきれないため、実現はやや困難です。

さて、いよいよ本題の機械学習です。概念的な部分を中心に調べてみました。
機械学習というアプローチに対する私なりの解釈は
「外界からの影響(撮影機材、環境、対象物の品質ばらつき)を完全に制御することは不可能なので、外界からどのような影響があるのかという統計をとって、その統計情報をもとに判断しよう」
というアプローチだと認識してます。
これは現実的な解ですし、最近はこういったアプローチ(機械でできることは機械でやろうよ)が増えていくと思っています。
機械学習って単語カッコイイです。ただ、本質的な部分は統計(だと私は勝手に思っている)なので、「何を学習させたいのか」を明確にしたうえで利用することが重要だと考えています。

私自身はまだ実際に動かしてはいないのですが、目的にあった機械学習のアプローチを選択しておかないと、学習結果が収束しない・期待した精度が得られないという問題に陥りがちのようです。
難しい分野ではありますが、後述のディープラーニング含めて伸びてくる分野だと思っているので、色々試してみたいところです。

ディープラーニング

機械学習のことを調べていると、このキーワードがよく引っかかります。
ディープラーニングの目的を機械学習の延長上から自分なりに解釈してみると、

  • 機械学習は、学習モデルをプログラマが決める(特徴量の抽出手法、比較方法など)
  • ディープラーニングは、学習モデルそのものも、学習させる

といったイメージです。このあたりはまだ深くは理解していないので、ちょっと自信ないです。
これが真だと仮定して続きを書きます。
こうして機械学習と比べてみるとディープラーニングのほうがさらに汎用化でき、実装コストも下がりそうな気がします。
現実的には学習のコスト(機械学習のコスト)が非常に大きいという課題もあるようです。
加えて、自分自身がスキルを身に着けるためにかかるコスト(時間)が極端に増大するうえに、基礎をおろそかにしていては「まずはやってみた」から抜け出せずに「工夫してみた」にたどり着けない恐れがあります。
現在の私の技術力では、まず機械学習から順に追っていくのが良さそうですし、モチベーションも保てそうです。

結論

ディープラーニング、機械学習、特徴量など色々調べてみましたが、
「ソフトウェア機能実現における課題解決手段であって、それ自体が目的ではない」といったところでしょうか。
プリミティブな技術要素だけでなく、その技術要素のあらまし、目的を明確にしておくという、ごく当たり前の結論に帰着しました。まる。
技術的な結論にしたかったのに、哲学的になってしまってちょっと残念。

今後の展望

あとで読むドキュメントのメモ。

OpenCV3.0についてる機械学習モジュール
OpenCV: Machine Learning

OpenCV3.0にOCR(文字認識)としてtextモジュールが増えてるのでどんなものか見てみるのもいいかもしれない。

上二つはまだよくわかりません。
OpenCV: cv::text::BaseOCR Class Reference

openGLES2.0で基本的なポリゴンレンダリングしてみた

適当にやっていると妙なところで躓きます。かたりぃなです。
今回はOpenGLES2.0を叩いてみました。

実験環境

今更GLES2.0でいいの?

最新のGLESを使ってみたいとは思うのですが、シェーダーの数が増えていて不安要因となっています。
シェーダー書いたことないので、まずは単純なシェーダー(vertex,flagmentの2つのみ)で実現するGLSL2.0をやってみることにしました。
一通り理解してからバージョンあげていったほうが最終的に近道になるだろうと思っています。
そもそも最新版で使いたい機能があるのかといわれると「?」という状態ですし。

躓いたポイント

  • シェーダープログラミングのデバッグ方法が分からない
  • 頂点、UVともに正しいのに、テクスチャが表示されない

躓いたポイントを整理したうえでコードを示します。

そもそもの根本で躓いてしまったのは、自分の中にある3Dグラフィックエンジンの知識が古すぎたのだと思います。
知識の古さについてですが、具体的には

になります。
上にあげた世代の古い3Dグラフィックエンジンは固定機能パイプラインのみ備えていて、基本的な処理は固定機能パイプラインが実現してくれていました。
固定機能としてライティング済み頂点とかトランスフォーム・ライティング済み頂点とか幾つかの頂点フォーマットに対するレンダリング機能がありました。
(DirectX5世代でいうとDrawPrimitiveの引数で指定する頂点形式でTLVERTEXとかLVERTEXとかあった気がします)

現代の3Dグラフィックでは固定機能パイプラインは存在せず、必要な機能はシェーダーとしてプログラミングする必要があります。
そのため「まず基本機能を実現するシェーダーを書く」ってところに時間がかかりました。

3DグラフィックAPIとしての汎用性は高くなりましたが、プログラミングのための最初の敷居が大幅に上がったと感じます。
理解してしまえばどうということはないのですが。

さて、躓いたポイントを順に整理してみます。
まずは一枚の四角形ポリゴンを出力してみて、頂点、色、UV、テクスチャを貼れるところまで。
VisualStudioでもAndroidStudioでもGLESプロジェクトのスケルトンを出力させると単純なプリミティブのレンダリングまでは行えるので、単純なプリミティブのレンダリングはできているという前提で話を進めます。

シェーダーのデバッグ方法がわからない

プログラム始めたばかりの初心者がデバッグ方法わからないというのと同じレベルですね。
まずシェーダーの概念と基本的な処理について理解して、いざ自分でコードを書き始めると詰まるポイントです。
シェーダーではC/C++でいうところのprintfデバッグはおろか、デバッガでのブレークポイントや変数ウォッチができないようです。
じゃあどうするか?
printfデバッグもデバッガの変数ウォッチも本質は「プログラムの状態を認識できる何かが出力できればいい」というところです。
シェーダーからの出力を視認できる形(とりあえず出力される色をデバッグとして使う)GLSL書いてみました。
(何かの拍子に詰まったときに振り返れるようにデバッグ用のコードも残しておきました。)

追加したデバッグ機能でシェーダーが期待通りに動いているかをアトリビュート単位で確認していきます。(頂点、UV、カラー、テクスチャetc.)
そんなこんなで一通り記述しおわったシェーダーがこちら。法線の処理はまだ入れていませんが、それはまたの機会に。(照明の機能を実装するときにいじくりまわすことでしょう。きっと。)
・頂点シェーダー

        // attributeは、頂点シェーダーへの入力の定義
        attribute vec4 vPosition;
        attribute vec4 color;
        attribute vec2 texcoord;
        // 頂点シェーダーでのverying修飾子はフラグメントシェーダーへ渡すことを意味する
        varying vec4 vColor;
        varying vec2 vTexcoord;
        // uniformはC/C++から固定で渡されるパラメータを意味する
        uniform mat4 projectionMatrix;
        void main() {
            gl_Position = projectionMatrix*vPosition;     // 頂点は透視変換かけるだけ
            vColor = color;                                   // 色はそのまま
            vTexcoord = texcoord;                            // テクスチャ座標もそのまま
        }

・フラグメントシェーダー

        precision mediump float;
        // フラグメントシェーダーでのverying修飾子は頂点シェーダーからの入力を意味する
        varying vec4 vColor;
        varying vec2 vTexcoord;
        // uniformはC/C++から固定で渡されるパラメータを意味する
        uniform sampler2D texture;
        void main() {
            gl_FragColor = texture2D(texture, vTexcoord)*vColor;
//            gl_FragColor = vColor;                                             // color情報のデバッグ用
//            gl_FragColor = vec4(vTexcoord.s, vTexcoord.t, vTexcoord.s, 1.0);   // uv座標のデバッグ用
//            gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);                           // 頂点情報のデバッグ用(すべて白色ポリゴンで出す)
        }

テクスチャが表示されないとき(プリミティブが真っ黒になる)

さて、OpenGLのテクスチャ回りでは毎度のことながらテクスチャ画像のファイルフォーマットどうするかとか、本来のやりたいことから外れた話になってしまいます。
せっかく環境にOpenCVを組み込んだのでこいつを使うことにします。

肝心のGLES2.0ではテクスチャの扱いが大幅に変わっていたので苦労しました。
最初は適当にマニュアル流し読みしながらパラメータ書いていったのですが、妙なところで詰まりました。
具体的には、ミップマップOFFでテクスチャ作っておきながら、テクスチャの補完パラメータでミップマップ使うような指定したりなど。
テクスチャの設定回りですが、他にも変更点があるようで、GLES1.0世代ではglEnableでテクスチャ有効化などをやっていましたが、これはシェーダーに取って代わられたため不要となったようです。

glActiveTextureとglBindTextureですが、このあたりはまだ完全には理解していないので、またの機会に。
(マルチテクスチャでレンダリングするときはglActiveTextureを使って複数のテクスチャユニットにglBindTextureしていくことになりそう)



テクスチャ生成のコードはこちら。

GLuint create_texture(cv::Mat& fileImage)
{
    // opencvの機能を使ってテクスチャ画像をデコードする
    rawImage = cv::imdecode(fileImage, 1);  // 第二引数に1を指定し、RGBフォーマットで取り出す

    glActiveTexture(GL_TEXTURE0);
    GLuint tex_id;
    glGenTextures(1, &tex_id);    // テクスチャはまずは一個だけ実験
    glBindTexture(GL_TEXTURE_2D, tex_id);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexImage2D(GL_TEXTURE_2D,
                 0,                              // ミップマップレベル
                 GL_RGB,                         // openGLの内部で保持するカラーフォーマット
                 rawImage.rows, rawImage.cols,   // 画像の幅、高さ
                 0,                              // 境界線の太さ
                 GL_RGB,                         // 元の画像カラーフォーマット
                 GL_UNSIGNED_BYTE,               // 画素のデータ型
                 rawImage.data);                 // 画像データの先頭アドレス
    // テクスチャの拡大縮小方法の指定。ミップマップなしで生成したテクスチャにミップマップありパラメータ指定すると真っ黒になったりするので注意
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    // テクスチャの繰り返し方法の指定
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    return tex_id;
}

後回しになりましたが、頂点バッファ、インデックスバッファなども作っておきます。
ほとんど同じような手順になるので頂点バッファを作る例。
パラメータは公式ドキュメントを参考に。
特にglBufferDataの最後の引数はデータがどこに置かれるかにも関わりそうなので、要注意です。

create_vertex_buffer(GLuint element_num, const GLfloat *elements)
{
    GLuint buff_id;
    glGenBuffers(buffer_num, &buff_id);

    glBindBuffer(GL_ARRAY_BUFFER, buff_id);

    GLuint  bufferSize_of_bytes = sizeof(GLfloat)*element_num;
    glBufferData(GL_ARRAY_BUFFER, bufferSize_of_bytes, elements, GL_STATIC_DRAW);

    return buff_id;
}


あとはシェーダーに対して頂点、UV、色情報、テクスチャ、変換行列を設定してレンダリングしてあげれば完成です。

    // 投影変換行列の設定
    glUniformMatrix4fv(projectionMatrixLocation, 1, GL_FALSE, projectionMatrix);

    // テクスチャユニットの利用準備
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, gTexture);
    glUniform1i(gtextureUniformLocation, 0);    // 0盤目のテクスチャユニット=GL_TEXTURE0を使うよう指示

    // 頂点シェーダーの利用準備
    glEnableVertexAttribArray(gvertexAttributeLocation);        // 頂点シェーダーを有効化
    glBindBuffer(GL_ARRAY_BUFFER, gVertexBuffer);     // 頂点バッファを関連付ける
    glVertexAttribPointer(gvertexAttributeLocation,     // 頂点シェーダーのハンドル
                          3,                    // 頂点フォーマットに含まれる要素数
                          GL_FLOAT,            // 頂点の各要素の型
                          GL_FALSE,            // 正規化の有無(法線で使う)
                          0,                    // 頂点間のストライド
                          0);   // 頂点データのアドレス

    // 頂点シェーダーでのUVバッファの利用準備
    glEnableVertexAttribArray(gtexcoordAttributeLocation);        // 頂点シェーダーを有効化
    glBindBuffer(GL_ARRAY_BUFFER, gUvBuffer);     // UVバッファを関連付ける
    glVertexAttribPointer(gtexcoordAttributeLocation, 2, GL_FLOAT, GL_FALSE, 0, 0);

    // 頂点シェーダーでの色情報の利用準備
    glEnableVertexAttribArray(gcolorAttributeLocation);
    glBindBuffer(GL_ARRAY_BUFFER, gColorBuffer);
    glVertexAttribPointer(gcolorAttributeLocation, 4, GL_FLOAT, GL_FALSE, 0, 0);

    // インデックスバッファを指定してレンダリング
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gIndexBuffer);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

「~~AttributeLocation」は事前にglGetAttribLocation(program, "symbol")で取得しておきます。
これはシェーダーでattribute定義した変数への入力位置を示すハンドルです。

シェーダーへの入力は次の3ステップで実現できました。
1, glEnableVertexAttribArrayでシェーダーへの入力を有効化し、
2, glBindBufferでシェーダーの入力に関連付けるバッファオブジェクトを指定
3, glVertexAttribPointerでシェーダーへの入力を指定
これらを各アトリビュート(頂点、UV、色 etc.)ごとに行います。

最後にインデックスバッファを関連付けてから、glDrawElementsを呼び出します。
その結果、ここまでに設定した各アトリビュートをシェーダーが読み込んでレンダリングしてくれます。

GLES2.0の感想

OpenGLを使うたびに思うのですが、「現在の状態」を意識しながらプログラミングする環境、ちょっと辛いです。
(bindBufferで何が関連付けられているかをプログラマが把握している必要がある。)
この辛みに拍車をかけるかのようにハンドルの指定方法などが、辛いと感じました(最初は特に)。
例えば、glActiveTextureではテクスチャユニットの指定はGL_TEXTURE0などを使うのにglUniform1iでシェーダーに指定するテクスチャユニットはただの値(0とか)だったりして、低レベルAPIであることを再認識させられます。
(ここでいう低レベルは、ハードウェアに近い抽象化されていないソフトウェアレイヤーという意味です。念のため。)

GPUというハードウェア相手にプログラミングしているのだから仕方のない部分だろうとは思います。
ただ、ふつうに叩き続けるにはちょっと厳しそうなので一枚レイヤーかぶせるとか、ひと工夫したいところです。

今後の展望

今回はアトリビュート単位にバッファを分けてレンダリングする実装にしました。
これをアトリビュートをすべてまとめて1つのバッファで済ませる実装もあるかと思います。
(例えば頂点情報をこんな構造体として定義して、頂点バッファはこれの配列として定義するなど。)

struct vertex{
    vec3 pos;
    vec3 normal;
    vec4 color;
    vec3 uv;
}

どちらのほうが高速かという疑問、そのうち試してみたいと思っています。
特にボーンアニメーションなどの実装をするときに関連するのですが、
ボーンの影響を受けて頂点情報を更新する場合、実際に更新が必要となるのはposとnormalだけかもしれません。
この場合、アトリビュートを分割しておいたほうが必要なものだけ転送できる=バス帯域が節約できる=結果として高速。だったりするのかもとか。(まだ試していない)

どこまでやるかはまだ未定ですが、シェーダー周りはなかなか興味深いものでした。
あとは設計思想的にDirectXのほうはどうなのだろうと気になっています。
MMEがHLSLで書かれてるらしいというのもDirectXが気になっている理由の一つです。

とりあえず今日はここまで。

OpenCVで気になったモジュールを試してみた

自分のやりたいことをやってる時間というのは充実しているものだと、最近はそう感じます。かたりぃなです。

FileStorageモジュール

XML/YAML形式でread/writeできる便利なモジュール。
ドキュメントはこちら。
OpenCV: cv::FileStorage Class Reference
今回の使い道は数学でいう行列(not画像)の保存/読み出しなどですね。

カメラキャリブレーションの結果の3つの行列(内部パラメータ、外部パラメータ、歪みパラメータ)をXML形式で出力する例。

	cv::FileStorage			fs(fileName, cv::FileStorage::WRITE);
	if (fs.isOpened()) {
		cv::write(fs, "extrinsic", cam_ext);
		cv::write(fs, "intrinsic", cam_int);
		cv::write(fs, "distortion", dist);
	}

こんな形でxmlファイルを吐き出してくれました。

<?xml version="1.0"?>
<opencv_storage>
<extrinsic type_id="opencv-matrix">
  <rows>4</rows>
  <cols>4</cols>
  <dt>d</dt>
  <data>
    -9.9305776780672794e-01 -3.4058957106407342e-02
    -1.1258888595035788e-01 -1.5680892918531333e+02
    1.1384691321616840e-03 -9.5990137467839998e-01
    2.8033561097112358e-01 2.2311113599467828e+02
    -1.1762216494672212e-01 2.7826127709644033e-01
    9.5327629152408078e-01 9.1508148149913336e+02 0. 0. 0. 1.</data></extrinsic>
<intrinsic type_id="opencv-matrix">
  <rows>3</rows>
  <cols>3</cols>
  <dt>d</dt>
  <data>
    9.2246418807201553e+02 0. 3.3045069724184663e+02 0.
    9.1528831192908842e+02 2.2281712474807887e+02 0. 0. 1.</data></intrinsic>
<distortion type_id="opencv-matrix">
  <rows>1</rows>
  <cols>5</cols>
  <dt>d</dt>
  <data>
    6.5901038525089195e-01 -6.1369184814174949e+00
    1.9503639590909172e-02 1.4560644371180317e-02 1.7415994956396972e+01</data></distortion>
</opencv_storage>

XMLを読み出すときも簡単に。

	cv::FileStorage fs(filename, cv::FileStorage::READ);
	fs["intrinsic"] >> intrinsic;
	fs["distortion"] >> distcoeffs;

このコード中では全部トップレベルに記述しましたが、fs["string"]の式はcv::FileNode型なので、階層構造も簡単に扱えそうですね。(試してはいない)

メモリ上にあるファイルイメージのデコード

画像ファイルのI/Oとファイルのデコードを分離したいケースがあります。
opencvの関数でメモリ上からデコードできそうなものがあったので試してみました。
ドキュメントはこちら
OpenCV: Image file reading and writing

まずファイルからメモリ上にファイルイメージそのまま展開します。
readでもfreadでもお好みのもので。環境によってはAAsset_readとかかもしれませんね。

    // メモリ上に展開されたpngファイルをデコードする
    cv::Mat fileImage(1, _size, CV_8UC1, _buf);     // _bufにはファイルのバイナリイメージそのまま入っている
    cv::Mat rawImage = cv::imdecode(fileImage, 1);  // 第二引数に1を指定し、RGBフォーマットで取り出す

これだけで_bufに格納してあった画像ファイルのデコードができました。
あとはrawImageを好きなように料理できます。

openCVのビルド情報をログに出す

ライブラリ使っているとありがちなのですが、バージョン違いだったり依存ライブラリの不足なんてことが稀によくあります。
問題を早く解決できるように、openCV自身のビルド情報をログに出しておくと便利です。

    std::cout << cv::getBuildInformation() << std::endl;

ArUcoモジュール

軽量なARライブラリらしいです。
opencv3.0以降、opencv_contribに含まれるようになってました。
ドキュメントはこちら。
OpenCV: ArUco Marker Detection

Arucoでの姿勢推定はcv::aruco::estimatePoseSingleMarkersとかで。
中身は当然のことながらcv::solvePnpでした。
パラレルforっぽい記述が見受けられたので、複数マーカーの場合はPNP問題を解く処理はパラレルに走るのかも?(詳しく調べてはいない)

cv::aruco::drawAxis()が何気に便利。デバッグとかの用途に使いやすいですね。
ARの姿勢推定もそうですが、三次元空間上の物体の姿勢を表示する機能って毎回作ってる気がします。
(加速度センサ・ジャイロセンサーから取ってきた値の確認とか)

oglモジュール

OpenCVOpenGLの相互運用のためのモジュールらしい。
このエントリ書き始めてから存在に気づいたので、また気が向いたら調べてみたいと思います。
ドキュメントはこちら。
OpenGL interoperability — OpenCV 3.0.0-dev documentation

AR,VRについて思うこと

今回のエントリとは若干話がそれますが、思っていることを書いてみます。
最近は某HMDが話題になったりと、VR界隈が賑やかでいいですね。
ARについても今回のエントリで書いたArUcoや一昔前のAR-Toolkitのようなマーカー型ARではなくマーカーレスARの研究も進んでいるようです。
技術的には新しいことが出てきて興味深いのですが、個人的にはVRもARも目的は「従来とは異なる特別な(新しい)ユーザー体験を提供する」ってところにあると思っています。

さて、この「特別な体験」というものが今のARでは作り手側から受け手側への一方通行になってしまっているのがすごく勿体ないと思います。
以前「グリコのポッキーのドラえもんの絵柄を専用アプリで撮影すると立体表示される」というARアプリがありましたが、
これって一発目のインパクトの強さだけで終わってしまうんですよね。「おお、すげー。」って。
VRも同様で、某イベントで「任意の角度から女性キャラの3Dモデルを眺めることができる」というものがありました。
これらのARやVR技術を使った表現方法はインパクトは強く、技術的にも興味深いものですが、何か物足りないと私は感じています。

先にあげた「一方通行」ですが、従来型のテレビや新聞などのマスメディアによる情報の伝達は、基本的に一方通行です。
ARにしてもVRにしても、ほとんどのケースでは従来型マスメディアと同様に一方通行になっているのが残念です。
さらにVRについては、HMDを利用する場合は「周囲の人間が楽しめない」という課題もあると考えています。

じゃあARに関して、どのような方式であれば先にあげた物足りなさ、残念さが解消できるのかを考えると、、、

  • 利用するコンテンツ(3Dモデルなど)はユーザー自身が選び、
  • 現実世界の物体との紐づけもユーザー自身が行える

などが考えられます。
ただ、このままでは「自分だけのAR環境に閉じこもる」だけになってしまうので、MMDが発展してきたように何らかの形で「発表・共有する場」があるといいかもしれません。

なんか整理つかなくなってきたのでこのくらいで。

androidアプリの開発環境を作り直してみた(androidstudio)

今度はandroidに手を出してみてます。かたりぃなです。
ずいぶんと前にいじってみたときはeclipseでコード書いて色々遊んでた気がします。

今どうなってるのかと調べてみると、eclipse用のandroid開発アドオンのサポート終わったらしく、
今後はAndroidStudioでよろしくとのこと。
http://developer.android.com/intl/ja/tools/help/adt.html

私は言われたとおりにやるのが嫌いな天邪鬼な性格なので、なんか他の方法でanroidで遊べないかなぁと調べてみました。
できればマルチプラットフォームAndroidWindows両方一度に開発みたいにできたらかっこいい。

候補

  1. C#でゴリゴリ書く for VS2015
  2. JavaScriptで書く for VS2015
  3. SilverLight+C# for VS2015

なんかVisualStudioに偏ってるけど、どんなものか大雑把に整理

VisualStudio2015を使って、C#でゴリゴリ書く

C#で記述したコードを走らせるためには.Netフレームワークが必要。
windows以外の環境ではxamalin上で走らせる(昔はmonoとか呼ばれてたアレですね)

VisualStudio2015を使って、JavaScriptで書く

JavaScriptHTML5使ってブラウザ上で動作するものを書く。
ブラウザを内包するアプリという形。下回りはCordova。

上記2つはこのページで軽く説明がありました。
特集:次期Visual Studioの全貌を探る:Visual Studio 2015におけるクロスプラットフォーム開発の選択肢 (2/3) - @IT
MSDNのページのほうが情報量は多いけど、偏ってる。当然といえば当然ですが。

SilverLight + C#

そろそろ終わるんじゃないかな。
Flashの対抗馬として頑張ろうとしてたけど、イマイチ感が拭えない。

時代はJavaScriptHTML5な感はあるけど、そこまで魅力を感じない。
やっぱり自分はネイティブな言語のほうが好きなんだなぁと。
というわけでAndroidStudio試してみることにしました。
Androidでhelloworldやっててもつまらないので、opencv走らせてみた。

AndroidStudioのインストール

そのままインストール。
JavaSDK, NDKなど関連するものまとめて入れる。

OpenCV3.0のインストール

OpenCV4Androidがあるのでそれを使いましょう。
このあたりの情報を参考にすればいけるはず。
Android StudioでOpenCV for Androidを使用する | Smartphone-Zine
OpenCV3.0.0 for AndroidStudio - Qiita

opencv.orgではeclipseを使った設定方法しか記述されていないのが辛い。
ANDROID | OpenCV

GradleでNDK-Buildできるよう設定する

C/C++でコード書きたい病を患っているので、設定する。
せっかくopencv放り込むんだから、C/C++から叩きたいですよね。
こういうビルドツールの情報調べるたびに思うのだけど、統一できないものなのかな。
C/C++出身としてはmakeでいいやんとか思ってしまう。

NDK-Buildの設定の前に、Gradleを2.5にアップグレードしないとAndroidStudioに怒られるのでやっておく。
表記法が大幅に変わっている様子。

このあたりのサイトを参考にしました
AndroidStudioでNDK(JNI)を活用する 準備編 - Qiita
Experimental Plugin User Guide - Android Tools Project Site

OpenCVのサンプルコードを動かしてみる

OpenCV-android-sdkのsamplesにあるコード動かすだけ。
OpenCVがあればカメラなんかの入出力回りも面倒見てくれるのでラクですね。
ちょっと気になったのでAndroidAPIを直接叩いてカメラ動かす方法を調べてみた。

AndroidのカメラAPI

Androidとしてはカメラ周りのAPI(android.hardware.camera)が非推奨になってた。
特定のAPIバージョン以上では、cameraの代わりcamera2を使いなさいとのこと。
android.hardware.camera2 | Android Developers
仕様が随分と変わってイベントハンドラを登録して、それに対応する処理を書いていくことになりそう。
カメラプレビューに対する加工も楽にできるかもしれません。
(OpenCVJava-IFにあるカメラAPI叩けば、Mat形式でフレームデータもらえるのでこれが一番ラクだと思います。)

WindowsのカメラAPI

ついでに調べてみました。
こちらは撮影・記録時はイベントハンドラによる処理を行えますが、プレビューの各フレームに対する処理はできなさそうです。
(たとえば色調補正をしたいとか。)
MediaCapture Class (Windows)

Windowsのカメラを使って、プレビューの各フレームに対する処理をしたい時は、Microsoft Media Foundationに従ってプログラミングする必要がありそうです。
たとえば、mediacapture-API一覧にあるStartPreviewToCustomSinkAsync と AddEffectAsync を使うために、引数IMediaExtensionを自作するなど。

Overview of the Media Foundation Architecture (Windows)
メディアストリームに対する変換の例はmediacaptureのサンプルでグレースケール化するというものがあります。
またカスタムメディアシンクを使ったサンプルはカスタムメディアシンクから得た映像をDirect3Dレンダリングするというものがあります。
どちらもMediaFoundationを使ったサンプルプログラムに含まれているので、Windowsでも何かやりたい時はこのあたりを参考にすればよさそうです。
軽く流し読みしたところ、ただのCOMっぽい。

Gradle使ってみた感想

一通り設定した結果、、、Javaの開発では必要なんですね。こういうの。
makeとshellで頑張ればいけなくはないですが、そうしてオレオレビルドツール作るくらいなら、こういうの使うほうが正解ですね。
とりあえずGradleでやってみたけど、自分の設定が悪いのか、非常に重いです。ストレスたまります。

AndroidStudioでandroid開発してみた感想

サンプルコードもそうだけど、UIの制御とビジネスロジックが混ざるのがいただけない。
AndroidGUIよくわかってない部分が多いけど、もっと綺麗に分離できる方法あるのだろうか?


実験や調べものしつつエントリを書いていたのでまとまりがなくなってしまいました。
今日はこのあたりで。

artoolkitのソースコードを眺めてみた

色々なことやってみてる最中です。かたりぃなです。

一昔前にミクさん召喚とか流行って以降あまり噂を聞かなかったartoolkitを調べてみました。

コードリーディングと言っても、軽く眺めた程度なので間違ってるところあるかと思います。

一番知りたいのはマーカーの姿勢推定の部分。

最近はwindowsストアアプリでも作ろうかなとも考えているので、うまく利用できたらいいなと思っています。

 

artoolkitって?

マーカーを使ったARを実現するためのライブラリ。

ニコ動あたりでartoolkitで検索かけると、色々と面白いものが見れると思います。

オープンソースのライブラリなので気軽に利用できるのですが、昔はライセンスがGPLだったのでアプリに組み込むにはちょっと……な状態でした。

今の最新バージョン(5.3.1)ではライセンスがLGPLv3になっているので、手軽に利用しやすくなったと思います。

本家:http://artoolkit.org/

リポジトリhttps://github.com/artoolkit/artoolkit5

 

とりあえずWindowsでビルドしてみた

configure-win32.vbsを叩くとソリューションができるのでビルドするだけ。

VisualStudio2015だとプロジェクトの設定がおかしいといわれた個所が幾つかあったので修正しました。

修正内容ですが、エラー番号をぐぐってMSDNのページ見れば分かる内容でした。

あとDLL不足が幾つかあったのでその都度修正。実験するだけでシステムディレクトリ汚すのはちょっと嫌なので、VisualStudioが見つけてくれるパスに置いておくことにしました。

 

コードリーディングの前に情報収集

古いライブラリ(私がコレ使って遊んだのはもう5年くらい前?)なので、だいたいのアルゴリズム有識者によって調べられています。

先人の知恵と機械の力(デバッガ)を使いつつ読み進めることにしました。

マーカーでの姿勢認識 - やねうらお−ノーゲーム・ノーライフ

理論上はartoolkitでマーカーの姿勢を推定するためには、「平行な線分2組からなる図形」であればいいわけですね。

具体的な形状としては、よく使われる「Hiro」と書かれた正方形マーカーなどですね。

適当な長方形図形でもマーカーとして使えそうです。

(mk_pattユーティリティで自前マーカー作るとき、長方形図形もマーカー候補として認識されました)

平行四辺形や菱形もマーカーに使えそうですが、当面はそんな形状使うことなさそうなので無視することにします。

 

サンプルプログラムを軽く眺めて全体像を掴む

simple_liteサンプルプログラムを眺めてみます。

カメラから映像を取り込み、マーカー上に立方体をレンダリングするだけというものです。

 

まず、カメラからの映像取り込みはMicrosoftのDirectShowを使っているようです。windowsストアアプリではMediaFoundationを使うことになるので、カメラ画像取り込み部分は他の手段に頼ることになりそうです。

試しにopencvのVideoCaptureクラスから取り込んだusbカメラの画像を使うように書き換えたところうまくいきました。(こちらも中身はDirectShowだったりすることもありますが。)

置き換えるならカラーフォーマットと解像度を合わせることを忘れずに。

 

立方体の3DレンダリングopenGLを使っていました。

座標系の変換でありがちな転置行列や逆行列を求めるコードは見当たらなかったので、artoolkitの内部の座標系はopenGLに合わせてあるのではないかと推測できます。

もしレンダリング周りをDirectXに置き換えるとしたらひと手間かかりそうです。

 

ここまでで、映像の入出力の手段はar-toolkitとは独立していると考えてよさそうです。

 

さて、肝心のアルゴリズムの部分。

arDetectMarker

arGetTransMat

arglCameraViewRH

あたりかと思います。

が、arglCameraViewRHの中身は3x4行列を4x4行列にしてスケールファクタを掛けてるだけでした。

 

なので、

arDetectMarker

arGetTransMat

あたりがartoolkitの肝ですね。

あ、マーカーの識別を忘れるところでした。

マーカーパターンの読み込みはarLoadPattでやっていて、識別するためのidを返してくれるようです。

マーカーを検出すると、検出したマーカーN個それぞれのidと信頼度がわかる仕組みのようです。

 

arDetectMarker

いわゆるマーカー検出処理ですね。

この関数が定義されているファイルarDetectMarker.cではグローバルに状態変数を持って何やらやっているっぽい。

直前のフレームの情報を保持しているので(光の加減などで)マーカー消失したとき対策か何かかもしれませn。きっと?

さらに下の階層に下ると、arLabeling.cではグローバルに画像処理用のワークバッファを1024x1024保持してるみたいだけども、最近の高性能webカメラみたいに1600x1200とか突っ込んだらどうなるんだろう?

まあ、やってることは一般的な物体検出の手順っぽいので適当に流し読み。

私が知りたいのは↑でも紹介した物体の姿勢推定のアルゴリズムだけ。

他のアルゴリズムopencvなりの適当な画像処理ソリューションで何とかなるはず。

 

arLabelingでラベリングする

ラベリングですね。

arGetContourで輪郭線を抽出

輪郭を成す全てのピクセルの座標がx_coord,y_coordに入る

つまり点p(x_coord[n], y_coord[n])を描画すると輪郭線が見える。

check_squareで検出した輪郭線で四角形ができるのかチェック

ここで輪郭の頂点(vertex)を求めている。

つまりp(x_coord[vertex[n]],y_coord[vertex[n]])としてn=0,1,2,3で四角形をなす4つの頂点を表現できる。

arGetMarkerInfoでマーカーパターンと照合した結果の信頼度とかを返す。

この関数内でarGetLineを呼んでる。

arGetLine、よくわかっていないけれども、、、

↑で紹介したアルゴリズムの入り口の気配。

ここまでに検出した「四角形の1辺をなす全てのピクセル座標pn(x,y)」を「nx2」の行列としてとらえて、歪み補正などを加える。このnx2行列をPCA(=主成分分析。ここではKL展開して次元を落とすのが目的と考えたほうが捉えやすい?)

この処理を四角形を成す4辺全てに対してやっている。。。。

わかったようなわからないような?

arGetLineで得られた結果の数字を見てもいまいちピンと来ない。

このあたりは少しずつ勉強しよう。

 

最後にarGetCodeであらかじめ読み込まれているマーカーパターンとテンプレートマッチング

マッチング手法とモードは

AR_TEMPLATE_MATCHING_COLOR

AR_TEMPLATE_MATCHING_BW

AR_MATCHING_WITHOUT_PCA

AR_MATCHING_WITH_PCA

これだけっぽい。

 

arGetTransMat

この関数も調べ終わってから今回の投稿にしたかったのですが、疲れたのでこれくらいにします。

気が向いてまた調べることがあったら投稿するかもしれません。

 

気になったこと

カメラキャリブレーションなしでそれなりにARが実現できるってのはすごいですが、「それなりに」という部分が引っかかります。

サンプルプログラムではどうやって作られたのか謎なカメラ内部パラメータファイルを使っていました。

気になることはまだ残っていますが、ミッションクリティカルなシステムでなければ、このくらいの手軽さで使えるほうがユーザーウケはよさそうです。

何か新しいことに挑戦(visual studio2015, opencv3.0, VTK 等)

Windows10にアップグレードしてみると想像以上にまともになってて驚きました。かたりぃなです。

MetroUIは残念なコだったけど、Windows10になって以前は全画面で展開されてたMetroがスタートメニューの右にちょろっとくっついてくるだけでした。やっぱ全画面持っていったりしないってのは大事だね。

 

このアップグレードのおかげで何か作ってみようかなと趣味プログラムを復活するきっかけになりました。

とは言いつつ、Windows上でVM立ち上げてたら以前とやってることが同じ。これじゃあつまらない。

せっかくだしVisualStudio2015使って何かやることにしました。windowsストアアプリ開発者アカウントもあるんだし(ずっと使ってないけど)。

 

色々やったせいであたまごちゃごちゃなので、細かい手順は別の投稿で。

 

やってみたこと

  • VisualStudio2015を使う
  • VisualStudio2015からGitリポジトリ叩いてみた
  • Windows用のGitHubアプリがあるので入れてみた
  • Windows用のcmake(GUI版)を入れてみた
  • opencv 3.0を動かしてみた
  • VisualStudio上でのopencvデバッグ支援ツールがMSから出てたので試した
  • opencv のvizモジュールを使ってみた
  • vizを使うためにVTKをビルドしてみた
  • viz経由でopenglを叩く

 

VisualStudio2015

しばらくご無沙汰してる間にずいぶん進化してました。

すごいなと思った点をいくつか。

1, androidiOS向けのプロジェクトもビルドできるらしい。

試しにソリューションを生成してみたけど、android, iOS, windows-phoneそれぞれのプロジェクトが中に入ってた。なんかすげぇ。当面使うことないだろうけど。

2, gitとの連携がIDE上から直接できる

メニュー -> チーム -> 接続の管理でリポジトリに接続

Githubへの接続もできた。とりあえずcloneできるところまで試した。

コマンドラインに慣れてるせいか日本語訳されたメニューがちょっと気持ち悪い

まあいざとなればgit shellアプリがあるから、こいつ起動してコマンドラインからいじればなんとかなりそう。

 

windows版cmake

GUI画面見て戸惑ったけど、やってることはコマンドラインと同じだった。

configureしてgenerateするとVisualStudio向けソリューションとプロジェクトが生成される。

試しにopencvとvtkをcmakeしてからVisualStudioでビルドしてみた。

プロジェクト全ビルドした後で「install」プロジェクトをビルドしないとインストールされない点が気になったけど、だいたい想定通り。

躓いたポイントは、cmakeの時点でコンパイラチェックするので通常使用するコンパイラを指定しておかなきゃいけなかった。(昔入れてた2012とかの残骸があった)

これをしっかり確認して合わせておかないと、cmakeで生成されたプロジェクトで使用するコンパイラがVisualStudio2012になっていたりして、実行ファイルを作ろうとしたときにライブラリとのバージョン違いで怒られる(opencvは2012でビルドしたのにアプリを2015でビルドしてるからリンクできないよ!)と。

 

opencv 3.0とvtk

vtkはデータの可視化ツール。内部ではopenglを使ってるっぽい。(リンクエラー出て気づいた)。

opencvのmatクラスをそのまま渡せるし、色々と便利に使えそう。

使い方は、opencvとvtkそれぞれビルドしてライブラリ吐かせておく。アプリはそのライブラリをリンクするだけでOK。

NuGetからでも行けるかもしれない。試してないけど。

ビルドが成功したらinstallプロジェクトをビルドしてインストールしておくのを忘れずに。インストール先ディレクトリはcmakeで指定したところになる。(これ忘れてると、アプリのビルド時にincludeできないエラーでハマる)

ライブラリ名の命名規則が大きく変わってる「opencv_"モジュール名""バージョン番号""デバッグビルドの場合はdを付与".lib」

vtkとは関係ないけど、カメラキャプチャの機能があったので叩いてみた。(opencvからDirectShowを叩く)

やりたいことが簡単に実現できました。10行くらい。

 

visualstudioでのopencvデバッグ支援

画像処理のプログラムをかいてると、デバッグ大変ですよね?

適当な個所でcv::imwrite("test.jpg")とか。連番で出したいからitoaとか使って。。とか。めんどいというか、なんか色々大変。

MSがデバッグ支援拡張機能出してくれてました。

https://visualstudiogallery.msdn.microsoft.com/e682d542-7ef3-402c-b857-bbfba714f78d

これを使えば、VisualStudioのデバッガで変数をポチるだけで画像が表示される。すげー。

このページが一通り網羅してたので分かりやすいです。

http://www.buildinsider.net/small/opencv/06

 

opencvのvizモジュール

3Dビジュアライザ。openGLなんかの3Dレンダリングエンジンを叩いて画像を作れる。

このブログの以前のエントリでopencvで解析, Rでグラフ表示とかしてたのを、opencv上だけで実現できそう。

ドキュメントはこのあたり。

http://docs.opencv.org/2.4/modules/viz/doc/viz.html

モジュールの依存関係は アプリケーション -> opencv::viz -> VTK -> OpenGLっぽい。

最後のOpenGLはVTKのcmakeオプションで他のものも選択できそう。

ちなみにWindows上で動かすにはOpenGL32.Libをリンクする必要がある。*32.libとか名前ついてるのに"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib\x64"とかに置いてあったりして気持ち悪いけど。

 

まとめ・今後の展望

着手はじめたときはopenCV + DirectXで何かできないかなと思ってたけど、opencvからopenGL叩けるならDirectXにこだわる理由も無い気がしてきた。

無理せず何かを作っていこう。

 

気になったこと

opencvのcmakeオプションのWITH_FFMPEGも合わせて使えば、cv::vizのレンダリング結果をffmpegエンコードして動画にまとめちゃう~なんてこともできるのかも。

魔法陣ライトアッププロジェクト(プロトタイプ編)

魔法陣をつくりたいというこのプロジェクト。

まずはレーザーカッターを使って、魔法陣を作るところから。

以前つくった魔法陣コースターの大型版。

 

レーザーカッターの仕様

今回もこちらの工房をお借りしました。

http://makers-base.com/

このレーザーカッターの仕様では、イラストレータ形式のファイルならなんでもいけるとのこと。

ただ、イラストレータは僕にとっては高価なものなので、安価なフリーソフトを使う。

ベクトル形式の画像の定番。incscapeを使う。

https://inkscape.org/ja/

試行錯誤した結果、レーザーカッター用のデータとしてincscapeを使うときの注意点。

「複合パスは使わないこと」。

レーザーカッターのソフト側で複合パスを正しく処理できないっぽい。

 

組み立て(その1)

アクリル板をレーザーカッターをカット、刻印したものと同じサイズの黒い板材を敷いて組み立てる。合計12枚のアクリル板。直径1.6mになりました。

f:id:Catalina1344:20150329124645j:plain

昼間だとあまりきれいに輝かなさそうだったので、夜に公園で挑戦。

あまり騒ぐと近所迷惑になるので、ひそひそと。

さて、屋外なので魔法陣をライトアップするための電源が必要。

外で使うバッテリーはコレにしました。

TL230K 「リチウムDEチャージ23000 ACアダプタ付」 | 製品情報 | 多摩電子工業株式会社

テープLEDの最大出力が12V、2Aなので、それに耐えうる出力かつ長時間出せるもの。

つないでとりあえずライトアップしてみた結果。。。

f:id:Catalina1344:20150329124652j:plain

やったね!

アクリルの境界を光が透過できない(地面の凹凸も関係してそう)のか、全体がうまく光ってくれない。

魔法陣の内周からのライトアップじゃダメっぽい。

うーん、雰囲気はよかったんだけどなぁ。

 

組み立て(その2)

退却して、外周からライトアップするように改造。

魔法陣の外周が5mちょっとあるので、新しくテープLEDを買ってきた。

テープLED フルカラーRGB5m LED300粒: LED(発光ダイオード) 秋月電子通商 電子部品 ネット通販

ガワつくろうかとも思ったけども、まだうまくいくかどうかわからないので、後回し。

こういうのはうまく輝いてからやりましょう。

さて、このテープLEDだとバッテリーの出力がぎりぎり。

4Aまでいけるところを3.2Aまで使ってしまう。

他の駆動装置つけるときは再検討ですね。

さて、先ほどと同じように魔法陣を並べてテープLEDで囲んで、電源ON!

制御パーツ完成していないので、以前の魔法陣コースターのときに使ったコントローラセットを使う。

ぽちっとな。

f:id:Catalina1344:20150329124704j:plain

やったよ!

これならいけるよ!

コスプレしてこの魔法陣の前で悔しそうに

「畜生ォ 持って行かれた・・・・・・・・・・・・!!」とかできるよ!

(厳密にはあれのデザインとは違う)

まあ色々な衣装で応用できるように、「元ネタが特にないけど魔法陣っぽいデザイン」にしてるのでゆっくりやっていきましょう。