2011/03/29

QR Code DetectorをAndroidに移植

前回作ったQR Code Detectorを、Androidに移植してみました。
まずはQRコードを検出して、QRコードの周りに線を引くところまで。


今のところ、320x240の解像度で取り込んだ画像をQRコード検出の処理にかけると、
だいたい150ms程度で結果が返ってきます。
現段階では作業用のIplImageを毎回生成しているので、この辺をキレイにすると処理時間が短くなると思います。

これをもう少し研究して、Android携帯でLEGOのキオスクみたいな感じのアプリを作れると
いろいろと遊べそうな感じがします。

OpenGLの勉強もしないとなぁ~。。。
ではまた。

2011/03/28

3W LED点灯実験

今日は3W LEDをeneloopで動かすと、どれぐらい持つのかの実験をしてみました。

電源:充電済みeneloop 4本 直列つなぎ
室温:20℃

単三4本で4~5時間ぐらいは問題なさそう
最初のほうは電流が多く流れるために電池が速く減り、後半は電流が少なくなっていくので
電池の減りが遅くなっていきました。

発熱も気になっていましたが、大きいヒートシンクをつけたため、30℃ぐらいで収まることがわかりました。部屋の温度が高い場合どうなるのかは気になるところ。

電流は終盤でも150mA程度流れており、十分簡易照明として使えそうです。

ではまた。

2011/03/27

3W LEDで簡易照明を作りました

私は神奈川県に住んでいるのですが、今回の地震の影響で、計画停電がたびたび起こっています。
停電時に部屋の中を照らすために、以前秋月電子から購入していた3WのLED(OPTOSUPPLY OSW4XME3C1S)を使って、照明を作ってみました。

今回の回路図は下記の通りです。

3W LED照明の回路

電源にはeneloopを4本直列につなぐと4.8Vになり、LEDの点灯時の順方向電圧がだいたい4Vなので、0.8(V)/2(Ω)=0.4(A)=400mAぐらい流れる計算になります。
また、最大電流以上に流れないよう、800mAで切断されるポリスイッチも保護素子として回路に組み込みました。


ユニバーサル基板に取り付けると、下記のような形になりました。



2Ωの抵抗が無かったので、5Wのセメント抵抗3つを並列につけました(実測2.1Ω)

基板の右下にあるのがポリスイッチで、400mAで切断されるタイプのものを2個並列にしてあります

3WのLEDともなると発熱量が結構ありそうなので、大きめのヒートシンク(61x30x30mm 秋月電子で100円)をつけてみました。5Vの電源を使って15分ほど点灯してみましたが、ヒートシンクの温度は大体30℃程度でした。今度時間があるときに、長時間点灯したときの温度を計測してみたいと思います。



暗いところで点灯実験したのが下記の画像です。
直視すると目に残像が残るぐらいの明るさです。
天井に向けてつけておけば部屋全体を照らしてくれそうです。
暗いところで点灯実験

ではまた。

2011/03/21

Android.mk.htmlを勝手に解釈

Android.mkファイルの作り方がよくわからなかったので、
NDKのDocフォルダにあるANDROID.MK.HTMLファイルを読んでみました。

ついでなので、前半を翻訳したものを記事として載せておきます。
変数一覧については、kwlogさんのところが見やすいです。


Android.mk.html  (android ndk r5bより)

はじめに
-------------
このドキュメントは、CまたはC++でかかれたコードをAndroid.mkファイルを通じて
ビルドする方法を説明します。以下の項目を理解するために、あらかじめdocs/OVERVIEW.HTMLに
目を通しておくことをオススメします。

概要
---------
Android.mkファイルは、あなたのソースファイルをビルドシステムに説明するために
仕様されます。具体的に言うと、
- このファイルは、GNU Makefileのごく一部となります。ビルドシステムにより、このファイルは
  1回もしくは複数回解釈されます。そのため、Androkd.mkファイルに宣言する変数の数を
  最小限に抑えるよう努めてください。また、ある変数が解釈中に定義されていないことは想定しないでください。


- ファイル構文は、あなたのソースファイルを共有ライブラリまたは静的ライブラリとしてグループ化できるように設計されています。
  共有ライブラリのみが、あなたのアプリケーションパッケージにインストール(またはコピー)されます。
  静的ライブラリは、共有ライブラリを生成するために使用することができます。
  また、1つのAndroid.mkファイルに複数のモジュールを定義することも、複数のモジュールを複数のAndroid.mkファイルに定義することもできます。

- ビルドシステムはあなたのために色々な作業を行います。たとえば、あなたはAndroid.mkファイルにヘッダファイルや
  依存関係を記載する必要はありません。NDKビルドシステムは、自動的にこれらを計算します。
  これは、あなたが新しいバージョンのNDKにアップデートするときも、Android.mkファイルに手を加えなくとも
  新しいプラットフォームの恩恵を受けることが出来ることも意味します。


例:
---------------
構文の細かいところを説明する前に、簡単な例として"hello JNI"プロジェクトについて考えてみます。
このプロジェクトは、下記ディレクトリにあります。
    apps/hello-jni/project

このディレクトリにあるフォルダを見てみると、
- 'src'ディレクトリはAndroidプロジェクトのためのJavaのソースファイルが含まれています。
- 'jni'ディレクトリには、ネイティブのソースコードが含まれています。(今回は'jni/hello-jni.c')
    このソースファイルは、VMアプリケーションに文字列を返す関数を実装した、共有ライブラリのソースコードです。

- 'jni/Android.mk'ファイルは、このソースから共有ライブラリを作成するようにNDKビルドシステムに通知しています。
このファイルの中身は、下記のようになっています。

   --------------- 切り取り線 ------------------
   LOCAL_PATH := $(call my-dir)
   include $(CLEAR_VARS)
   LOCAL_MODULE    := hello-jni
   LOCAL_SRC_FILES := hello-jni.c
   include $(BUILD_SHARED_LIBRARY)
   --------------- 切り取り線 ------------------

さて、それぞれの行について説明していきましょう。

  LOCAL_PATH := $(call my-dir)

Android.mkファイルは、LOCAL_PATHを定義するところから始めなければなりません。
この変数は、開発ツリーのソースファイルを見つけるために使用されます。この例では、マクロ関数'my-dir'は
ビルドシステムから提供され、Android.mkファイルがあるディレクトリを返します。


  include $(CLEAR_VARS)

CLEAR_VARSは、LOCAL_PATH以外のLOCALで始まるMakefileの各種変数
(LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, などなど)をクリアします。

  LOCAL_MODULE := hello-jni

LOCAL_MODULEは、それぞれのモジュールを一意に識別するために必要なものです。
また、LOCAL_MODULEの名前は重複してはならず、スペースも含めてはいけません。
ビルドシステムは、自動的にライブラリのプレフィックスとサフィックスを追加するので、
名称にlibなどをつける必要はありません。
例:fooという名前が指定され、共有ライブラリを作成する場合は、libfoo.soというファイル名で出力されます。

[重要]
もし、モジュール名を'libfoo'とした場合、ビルドシステムは'lib'をプレフィックスとして追加することはありません。つまり、生成されるファイルはlibfoo.soとなります。


  LOCAL_SRC_FILES := hello-jni.c

LOCAL_SRC_FILESは、cまたはc++のソースファイルのリストでなければなりません。
ここには、ヘッダファイルやインクルードファイルを記載する必要はありません。
ビルドシステムが、必要なファイルをを自動的に計算するためです。

C++のソースファイルの拡張子は'.cpp'であることに注意してください。
ただ、LOCAL_CPP_EXTENSIONで指定することで拡張子を指定することもできます。
(注意:拡張子の前のドットを忘れないでください。)


  include $(BUILD_SHARED_LIBRARY)

BUILD_SHARED_LIBRARYはNDKビルドシステムから提供される変数で、
最後に実行された'include $(CLEAR_VARS)'以降に指定された、LOCAL_XXX変数を元に、
必要なファイルなどをビルドするために集めるスクリプトを指します。
静的ライブラリを作成する場合は、BUILD_STATIC_LIBRARYを使用します。

以後、各種変数の説明なので省略しました。

ではまた。

2011/03/20

詳解OpenCVの練習問題(5)

今日は5章の練習問題を解いていきます。

 1.特徴の多い画像をロードし、cvSmooth()でsmoothtype=CV_GAUSSIANを用いて平滑化を行います。
a.平滑化ウィンドウサイズを3x3、5x5、9x9、11x11に設定して、結果を表示します。

色々なウィンドウサイズで平滑化




b.5x5のガウシアンフィルタで2回平滑化したものと、11x11でのフィルタで平滑化したものの結果を比べます。

5x5を2回と11x11を1回
結果:5x5を2回と11x11は似たような結果になるが、11x11の方がぼやけた画像になる。


2.100x100のシングルチャネルの画像を作成します。画像をクリアし、中心のピクセルを255に設定します。
a.画像を5x5のガウシアンフィルタを用いて平滑化して結果を表示します。
b.同じように9x9のガウシアンフィルタでも平滑化を行います。


5x5と9x9
c.5x5のフィルタを2回行ったものと、9x9を1回行ったものを比較します。


結果:5x5を2回行ったものと、9x9を1回行ったものは似たような結果になるが、5x5を2回行ったものの方が全体的な輝度が高いように見える


3.何か画像をロードし、色々な条件でcvSmoothを実行します。
a. param1=param2=9に設定して実行します。param3は色々な値(1,4,6)に設定して結果を比べます。


param1=param2=9, param3=1,4,6

b. param1=param2=0に設定して、param3を色々な値にして結果を比べます。
param1=param2=0,param3=1,4,6

aのときよりも、bの方がぼやけた画像になりました。

c. param1=param2=0にして、param3=1,param4=9に設定して平滑化します。
d. param1=param2=0にして、param3=9,param4=1に設定して平滑化します。
e. cの条件で平滑化し、さらにdの条件で平滑化します。
f. param1=param=2=9、param3=param4=0でフィルタをかけたものと、eの結果を比べます。

c,d,e,f
eとfの結果を比べると、eの方がかなりぼやけた画像になります。
なぜeがぼやけた画像になるかというと、ガウシアン平滑化の場合は下記のような数式でパラメータが計算されるためです。
つまり、param1=param2=0,param3=param4=9の場合は、
param1=param2=57,param3=param4=0にほぼ等しくなる、と考えて良さそうです。
実際に57x57のウィンドウで平滑化をかけると、eと似たような結果になりました。

bがaよりもぼやけた画像になったのは、param1とparam2に0が設定されているので、
param3からウィンドウサイズを計算したため、大きなウィンドウサイズ(param3=4のときは23,param3=6のときは37)になったのでぼやけた画像になったためと思われます。


ソースコードはいつものように別館に置いてあります。

ではまた。

2011/03/19

マーカー検出器(マイクロQRコード版) その3

QRコード検出器の続きです。

QRコードの検出と射影変換処理まではできたので、今回はデコード処理部分です。

最初はマイクロQRコードの仕様どおりにデコードできるものを作ろうとしましたが、
JISの規格(http://www.jisc.go.jp/ JISX0510二次元コードシンボル-QRコード-基本仕様)
を見てもデコードの仕組みがよくわからず、結局独自デコードにすることにしました。

独自デコードの中身は以下のような感じです。
1.11セルのマイクロQRコードを使うことにする
2.2行目と3行目の値のみを使い、6ビット(32通り)のマーカーを識別できるようにする
3.黒く塗りつぶされているところを1とする(QRコードと同じ)

pic.1 独自デコードのビット配置
 とりあえず実装してみた結果が下記の通りです。
pic2 検出テスト


上の画像の場合は、2行目と3行目の一番右側が黒く塗りつぶされているので、
2^0+2^3=9 でこのQRコードの識別番号は9となります。

4行目以降のマスは、白と黒が1対1になるように適当に塗りつぶしているだけで、
特に意味はありません。
今度はAndroidに移植して、どれぐらいの速度で実行できるかをテストしたいと思います。

ではまた。






3rd part of Micro QR Code detector.
At first, I tried to decode according to the specification.
But I could not understand how to do it.

So I decided create my decode rule.
My decode rule is below.
1,Uses only MicroQR version M1 (11 modules).
2,Uses 2nd and 3rd row to decode.(Please see pic.1)
3,Black module means 1, white module means 0.


Source code:http://sites.google.com/site/playwithopencv/microqrcode

2011/03/11

マーカー検出器(マイクロQRコード版) その2

マイクロQRコードをOpenCVを使って検出するプログラムの続きです。
前回は位置マーカー(QRコードの左上にある、二重になっている四角)を検出することはできていましたが、向きまではわかりませんでした。


二値化した画像にcvErodeを使うと、マーカー全体が1つの大きな四角形になり、
その四角形の輪郭と位置マーカーの4点を比較することでマーカーの向いている方向もわかるようになりました。








次はこのマイクロQRコードをデコードする部分を作りたいと思います。


ではまた。

2011/03/09

マーカー検出器(マイクロQRコード版)作成中

以前やっていた、自作マーカーを検出するやつの続きとして、
今度はマイクロQRコードを検出するものを作っています。

OpenCVのFindChessboradのソースを参考にしながら、とりあえずマーカーの基準点となる
四角形をかなりの精度で検出することができるようになりました。
ただ、現時点では方向が不定なので、もう少し研究する必要はあります。
(基準マーカーの左右に、1セルおきに白と黒が並んでいるので、
これを検出できれば方向の決定ができるようになると思います。)


ひとまず、現時点では以下のような感じです。




















処理時間は、
   マーカー基準点の検出 6ms
ホモグラフィ行列の計算 10ms
  射影変換        15ms
でトータル30msぐらいです。

パソコンで動作させるなら問題なさそうですが、Androidでやるにはちょっと厳しいかも。

マイクロQRコードのデコードライブラリも見当たらないので、
ひとまず独自デコードでQRコードの情報を読み取ってみようかと思います。

今後の作業予定
・マーカーの中身を解読する
・射影変換の精度を上げる
・立方体か何かをQRコードの上に表示させて遊ぶ


ではまた。

2011/03/01

詳解OpenCVの練習問題(4)

3章の練習問題は大体終わったので、
今日は4章の練習問題をやります。


1.a  動画を読み込み、「そのままの画像」、「グレイスケールに変換した画像」、「Cannyフィルタを実行した画像」をそれぞれウィンドウに名前をつけて表示する。
1.b  上記3つの画像を1つの画像に並べて表示する。(高さは同じ、幅が3倍の画像)

作ったものを実行した画面が下記のとおりとなります。
上3つが、それぞれ別のウィンドウに表示したもので、
下の横長のウィンドウが、3つの画像を1つにまとめたものです。













ソースコードは別館の方に置いてあります。



今回、グレースケール化とCannyフィルタはすぐできたのですが、
3つの画像を1つにまとめるのに少し時間がかかりました。

最初はIplImageのヘッダを作って、画像データのポインタをcvPtr2Dで取得すれば大丈夫かなと思ってコードを組んだのですが、コピーして出来上がった画像は、1枚の画像が3枚にスライスされて並べられたような状態になりました。


色々いじった結果、widthStepの値を元の画像の3倍にするとうまくいきました。
widthStepは、次の行の最初のピクセル(例えば0,0から0,1)へのバイト数を表しているようです。widthStepは何かなーと以前から思っていましたが、今回この練習問題で理解することができました。

以下のコードは、3つの画像ヘッダに、データポインタを割り当てるところの部分的なコードとなります。



IplImage* src;
    src=cvQueryFrame(capture);
    //グレイスケールとCanny用
    IplImage* imgGray=cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,1);
    IplImage* imgCanny=cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,1);

    //3つの画像を1つにまとめるためのもの
    IplImage* imgFusion=cvCreateImage(cvSize(src->width*3,src->height),IPL_DEPTH_8U,3);
    IplImage* headerSrc=cvCreateImageHeader(cvGetSize(src),IPL_DEPTH_8U,3);
    IplImage* headerGray=cvCreateImageHeader(cvGetSize(src),IPL_DEPTH_8U,3);
    IplImage* headerCanny=cvCreateImageHeader(cvGetSize(src),IPL_DEPTH_8U,3);

    //ヘッダに、imgFusionの画像データのポインタの先頭、1/3,2/3の位置を指定する
    headerSrc->widthStep=src->widthStep*3;
    headerSrc->origin=src->origin;
    headerSrc->imageData=(char *)cvPtr2D(imgFusion,0,0,NULL);

    headerGray->widthStep=src->widthStep*3;
    headerGray->origin=src->origin;
    headerGray->imageData=(char *)cvPtr2D(imgFusion,0,src->width,NULL);

    headerCanny->widthStep=src->widthStep*3;
    headerCanny->origin=src->origin;
    headerCanny->imageData=(char *)cvPtr2D(imgFusion,0,src->width*2,NULL);




ではまた。