2011/02/23

Android NDK側のメモリーリークを調べる

Androidのカメラプレビューを使って色々と実験しているのですが、
1分ぐらい動かしていたらアプリが勝手に落ちてしまいます。

不思議に思い、DDMSを使ってメモリの使用量を監視してみましたが、
使用率が70%位を維持しつつ、いきなりアプリが落ちる現象が繰り返し発生しました。
ログメッセージには、

   CameraServer   binderDie() start Client died


といった感じのメッセージが表示されていました。

なんだろコレ。。。
ググってもあまり情報が出てきません。

ネイティブ側にgetPixelsで取得したプレビューデータを渡しているところを
コメントアウトしたところ、現象は起こりませんでした。

となると、ネイティブ側に何か問題があるようです。

メモリー使用率やCPU使用率を見張るために、下記のコマンドを実行しました。

  adb shell
  top -m 10

AndroidはLinuxをベースにしており、adb shellを使うとLinuxコマンドを一部使えるようになります。
topというコマンドを実行すると、定期的にプロセスごとのCPU使用率やメモリ使用量を画面に表示してくれます。


topコマンドの出力例:
User 87%, System 7%, IOW 0%, IRQ 0%
User 293 + Nice 0 + Sys 25 + Idle 17 + IOW 0 + IRQ 0 + SIRQ 0 = 335


  PID CPU% S  #THR     VSS     RSS PCY UID      Name
   31  47% S     6  24708K   6296K  fg media    /system/bin/mediaserver
  210  43% R     6 106636K  24272K  fg app_29   com.camera.preview
  234   2% R     1    892K    384K  fg root     top
   52   2% S    41 148860K  30340K  fg system   system_server
    5   0% S     1      0K      0K  fg root     khelper
    6   0% S     1      0K      0K  fg root     suspend
    7   0% S     1      0K      0K  fg root     kblockd/0
    8   0% S     1      0K      0K  fg root     cqueue
    9   0% S     1      0K      0K  fg root     kseriod
   10   0% S     1      0K      0K  fg root     kmmcd

修正前のプログラムでは、com.camera.previewのVSSという数字がだんだんと大きくなっていき、
それにつれて他のプロセスが消され、最後にcom.camera.previewが消されてしまいました。

これぞ”メモリーリーク”と呼ばれ、忌み嫌われているアレです。
まぁ、リークさせてるのは私のコードなのですが。。。

コードを調べたところ、今回はOpenCVの画像形式であるIplImageの開放漏れがメモリーリークを引き起こしていました。

NDKを使ったアプリを作成する場合は、topコマンドを使用して常にメモリ使用量を見張るようにした方が良さそうです。

ではまた。

2011/02/21

カメラプレビューの画像もOpenCVで処理

複数のサイトを参考にしながら組んでみたら、
カメラプレビューの画像もOpenCVに渡せました。






まだ、1分ぐらいすると落ちてしまいます。どこかでメモリリークでも起こっているのかも。


つぎはIS01のほうでもやってみたいと思います。

AndroidでOpenCV1.1を動かす(2)

前回の続き。

OpenCV1.1の準備
3.OpenCV 1.1pre1 のソースコードをダウンロードして適当なところに展開します。
4.フォルダの中にある、cvフォルダとcxcoreを、jniフォルダの中に移動させます。

5.Android.mkファイルを作成します。
Android.mkファイルの内容は下記のとおりです。

LOCAL_PATH:= $(call my-dir)

##############
##   OpenCV
##############

include $(CLEAR_VARS)
#cxcore
LOCAL_MODULE    := cxcore

#includeファイルの場所を指定
LOCAL_C_INCLUDES := \
       $(LOCAL_PATH)/cv/include \
    $(LOCAL_PATH)/cxcore/include


LOCAL_CFLAGS := $(LOCAL_C_INCLUDES:%=-I%)
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -ldl

#ソースファイルを指定(cxcoreフォルダにあったソースを全部登録)
LOCAL_SRC_FILES := \
        cxcore/src/cxalloc.cpp \
        cxcore/src/cxarithm.cpp \
        cxcore/src/cxarray.cpp \
        cxcore/src/cxcmp.cpp \
        cxcore/src/cxconvert.cpp \
        cxcore/src/cxcopy.cpp \
        cxcore/src/cxdatastructs.cpp \
        cxcore/src/cxdrawing.cpp \
        cxcore/src/cxdxt.cpp \
        cxcore/src/cxerror.cpp \
        cxcore/src/cximage.cpp \
        cxcore/src/cxjacobieigens.cpp \
        cxcore/src/cxlogic.cpp \
        cxcore/src/cxlut.cpp \
        cxcore/src/cxmathfuncs.cpp \
        cxcore/src/cxmatmul.cpp \
        cxcore/src/cxmatrix.cpp \
        cxcore/src/cxmean.cpp \
        cxcore/src/cxmeansdv.cpp \
        cxcore/src/cxminmaxloc.cpp \
        cxcore/src/cxnorm.cpp \
        cxcore/src/cxouttext.cpp \
        cxcore/src/cxpersistence.cpp \
        cxcore/src/cxprecomp.cpp \
        cxcore/src/cxrand.cpp \
        cxcore/src/cxsumpixels.cpp \
        cxcore/src/cxsvd.cpp \
        cxcore/src/cxswitcher.cpp \
        cxcore/src/cxtables.cpp \
        cxcore/src/cxutils.cpp

include $(BUILD_STATIC_LIBRARY)




include $(CLEAR_VARS)

LOCAL_MODULE    := cv
LOCAL_C_INCLUDES := \
       $(LOCAL_PATH)/cv/include \
    $(LOCAL_PATH)/cxcore/include

LOCAL_CFLAGS := $(LOCAL_C_INCLUDES:%=-I%)
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -ldl

LOCAL_SRC_FILES := \
        cv/src/cvaccum.cpp \
        cv/src/cvadapthresh.cpp \
        cv/src/cvapprox.cpp \
        cv/src/cvcalccontrasthistogram.cpp \
        cv/src/cvcalcimagehomography.cpp \
        cv/src/cvcalibinit.cpp \
        cv/src/cvcalibration.cpp \
        cv/src/cvcamshift.cpp \
        cv/src/cvcanny.cpp \
        cv/src/cvcolor.cpp \
        cv/src/cvcondens.cpp \
        cv/src/cvcontours.cpp \
        cv/src/cvcontourtree.cpp \
        cv/src/cvconvhull.cpp \
        cv/src/cvcorner.cpp \
        cv/src/cvcornersubpix.cpp \
        cv/src/cvderiv.cpp \
        cv/src/cvdistransform.cpp \
        cv/src/cvdominants.cpp \
        cv/src/cvemd.cpp \
        cv/src/cvfeatureselect.cpp \
        cv/src/cvfilter.cpp \
        cv/src/cvfloodfill.cpp \
        cv/src/cvfundam.cpp \
        cv/src/cvgeometry.cpp \
        cv/src/cvhaar.cpp \
        cv/src/cvhistogram.cpp \
        cv/src/cvhough.cpp \
        cv/src/cvimgwarp.cpp \
        cv/src/cvinpaint.cpp \
        cv/src/cvkalman.cpp \
        cv/src/cvlinefit.cpp \
        cv/src/cvlkpyramid.cpp \
        cv/src/cvmatchcontours.cpp \
        cv/src/cvmoments.cpp \
        cv/src/cvmorph.cpp \
        cv/src/cvmotempl.cpp \
        cv/src/cvoptflowbm.cpp \
        cv/src/cvoptflowhs.cpp \
        cv/src/cvoptflowlk.cpp \
        cv/src/cvpgh.cpp \
        cv/src/cvposit.cpp \
        cv/src/cvprecomp.cpp \
        cv/src/cvpyramids.cpp \
        cv/src/cvpyrsegmentation.cpp \
        cv/src/cvrotcalipers.cpp \
        cv/src/cvsamplers.cpp \
        cv/src/cvsegmentation.cpp \
        cv/src/cvshapedescr.cpp \
        cv/src/cvsmooth.cpp \
        cv/src/cvsnakes.cpp \
        cv/src/cvstereobm.cpp \
        cv/src/cvstereogc.cpp \
        cv/src/cvsubdivision2d.cpp \
        cv/src/cvsumpixels.cpp \
        cv/src/cvsurf.cpp \
        cv/src/cvswitcher.cpp \
        cv/src/cvtables.cpp \
        cv/src/cvtemplmatch.cpp \
        cv/src/cvthresh.cpp \
        cv/src/cvundistort.cpp \
        cv/src/cvutils.cpp

include $(BUILD_STATIC_LIBRARY)



####################
## 各プロジェクト用
###################


include $(CLEAR_VARS)

LOCAL_MODULE    := cvtest
LOCAL_C_INCLUDES := \
      $(LOCAL_PATH)/cv/include \
    $(LOCAL_PATH)/cxcore/include \
    
LOCAL_CFLAGS := $(LOCAL_C_INCLUDES:%=-I%)
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -ldl -llog \
                -L$(TARGET_OUT) -lcxcore -lcv

LOCAL_SRC_FILES := cvtest.cpp

LOCAL_STATIC_LIBRARIES := cxcore cv

include $(BUILD_SHARED_LIBRARY)



ライブラリのビルド
プロジェクトフォルダのトップに移動して、ndk-buildを実行します。
opencvのライブラリもビルドされるので、最初は5分〜10分かかりますが、
それ以後は数秒で終わります。


ライブラリのビルドが終わると、


.
.
Compile++ thumb  : cv <= cvswitcher.cpp
Compile++ thumb  : cv <= cvtables.cpp
Compile++ thumb  : cv <= cvtemplmatch.cpp
Compile++ thumb  : cv <= cvthresh.cpp
Compile++ thumb  : cv <= cvundistort.cpp
Compile++ thumb  : cv <= cvutils.cpp
StaticLibrary  : libcv.a
SharedLibrary  : libcvtest.so
Install        : libcvtest.so => libs/armeabi/libcvtest.so

といった形でlibsフォルダにライブラリがコピーされ、Androidに組み込める状態になります。
あとはeclipseでプロジェクトを更新(F5キー)し、Androidプロジェクトを実行すればOKです。

ソースコード一式は、別館の方に保存してあります。

ではまた。

AndroidでOpenCV1.1を動かす(1)

AndroidにOpenCVを載せる手順を書いていきます。
私の環境は下記のとおりです。

PC:ubuntu desktop 10.04
NDK: AndroidNDK r5b
IDE: Eclipse
Android:IS01(1.6),IS03(2.1)


プロジェクトの作成

1.EclipseでAndroidプロジェクトを作成します。
プロジェクト名    :opencvtest
パッケージ名    :com.opencvtest.test
アクティビティ名 :opencv
プラットフォーム :1.6または2.1

※レイアウトで、linearlayoutを追加しorientationをverticalに設定しておいてください。


Java側のコードは下記のとおりです。


package com.opencvtest.test;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.LinearLayout;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

public class main extends Activity {
       
    static
       {  //jnitestライブラリを読み込みます
       System.loadLibrary("jnitest");   
    }
        //ネイティブ側で実装している関数
    public native void opencvtestfunc(int[] pixels, int x, int y);

   /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
         
    //今回はリソースからビットマップを生成し、
    //intの配列に変換したあとネイティブ側に処理を任せます。
    //処理後の画像と、処理前の画像を上下に並べて表示します。
        Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.android);
        Bitmap subbmp = bmp.copy(Bitmap.Config.RGB_565, true);

        int width = subbmp.getWidth();
        int height = subbmp.getHeight();

        int pixels[] = new int[width * height];
        int cnt = 0;
        subbmp.getPixels(pixels, 0, width, 0, 0, width, height);

            //ネイティブ側で何らかの処理を行う
        opencvtestfunc(pixels,subbmp.getWidth(),subbmp.getHeight());
        
        subbmp.setPixels(pixels, 0, width, 0, 0, width, height);
        LinearLayout llayout = (LinearLayout)findViewById(R.id.llayout);

        ImageView binarizeBmp = new ImageView(this);
        binarizeBmp.setImageBitmap(subbmp);
        ImageView normalBmp = new ImageView(this);
        normalBmp.setImageBitmap(bmp);

        llayout.addView(normalBmp);
        llayout.addView(binarizeBmp);
        
    }
}


2.プロジェクトフォルダに、jniフォルダを作成します。
3.jniフォルダの中に、cまたはc++のソースファイルを作ります。今回は cvtest.cppとします。
cvtest.cppの中身は下記のとおりです。

#include <string.h>
#include <jni.h>
#include "cv.h"
#include "cxcore.h"

#ifdef __cplusplus
    extern "C" {
#endif

IplImage* pImage = NULL;
IplImage* loadPixels(int* pixels, int width, int height);
     void  savePixels(int* pixcels,IplImage* srcImage);

JNIEXPORT void JNICALL Java_com_opencvtest_test_opencv_opencvtestfunc
  (JNIEnv *env, jobject thiz, jintArray colors, jint w, jint h)
{
    int r, g, b;
    int cnt;

    //Colorの配列を取り出す(配列のポインタを取得)
    jint* colorArray = env->GetIntArrayElements(colors,0);

    //Color(int) の配列から、IplImageを作る
    if (pImage != NULL)
    {
        cvReleaseImage(&pImage);
         pImage = NULL;
    }
    pImage = loadPixels(colorArray,w,h);

    //画像の作成完了。あとは色々と画像処理をします

    IplImage *work=cvCreateImage(cvGetSize(pImage),IPL_DEPTH_8U,1);
    IplImage *edge=cvCloneImage(work);

    //輪郭保存用のストレージを確保
    CvMemStorage *storage = cvCreateMemStorage (0);
    CvSeq *firstcontour=NULL;

    cvCvtColor(pImage,work,CV_BGR2GRAY);
    cvThreshold(work,edge,100,255,CV_THRESH_BINARY);

    //輪郭を探します。Ncには探した輪郭の数が入ります。
    //CV_RETR_LISTに設定すると、見つけた輪郭がすべて同じレベルに入ります。。
    //first=輪郭1⇔輪郭2⇔輪郭3⇔輪郭4
    //これだと内側と外側の両方の輪郭が同じレベルに入るので、外側だけほしい場合はCV_LETR_CCOMPに設定します。

    int Nc=cvFindContours (edge, storage, &firstcontour, sizeof (CvContour),CV_RETR_LIST);
    for(CvSeq * seq=firstcontour;seq!=NULL;seq=seq->h_next)
    {
    cvDrawContours(pImage,seq,cvScalar(200,0,50),cvScalar(0,255,0),0,2,8);
     }

      //IplImageの値を、int配列に戻す
    savePixels(colorArray,pImage);

    //リソースの開放
    if(pImage!=NULL)
    {
        cvReleaseImage(&pImage);
        pImage=NULL;
    }

    if(work!=NULL)
    {
        cvReleaseImage(&work);
    }

    if(edge!=NULL)
    {
        cvReleaseImage(&edge);
    }

    env->ReleaseIntArrayElements(colors, colorArray, 0);

}


//////////////////////////////////////////////////
//int配列から、3チャネルのIplImageを作成します
//////////////////////////////////////////////////
IplImage* loadPixels(int* pixels, int width, int height)
{
    int x, y;
    IplImage *img = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 3);
    unsigned char* base = (unsigned char*) (img->imageData);
    unsigned char* ptr;
    for (y = 0; y < height; y++) {
        ptr = base + y * img->widthStep;
        for (x = 0; x < width; x++)
        {
            // blue
            ptr[3 * x] = pixels[x + y * width] & 0xFF;
            // green
            ptr[3 * x + 1] = pixels[x + y * width] >> 8 & 0xFF;
            // red
            ptr[3 * x + 2] = pixels[x + y * width] >> 16 & 0xFF;
        }
    }
    return img;
}


/////////////////////////////////////////////////////////////////////////
///3チャネル、IPL_DEPTH_8UのIplImageの画像データを、int配列に反映させます
/////////////////////////////////////////////////////////////////////////
void savePixels(int* pixcels,IplImage* srcImage)
{
    int x, y;
    int w,h;
    int pixvalue;
    w=srcImage->width;
    h=srcImage->height;

    unsigned char* base = (unsigned char*) (srcImage->imageData);
    unsigned char* ptr;
    for (y = 0; y < h; y++)
    {
        ptr = base + y * srcImage->widthStep;
        for (x = 0; x < w; x++)
        {
            pixvalue=(ptr[3 * x])|(ptr[3 * x + 1]<<8)|(ptr[3 * x + 2] <<16);
             pixcels[x + y * w] =pixvalue;
        }
     }
}

#ifdef __cplusplus
}
#endif

つづく

2011/02/20

AndroidでOpenCVが動きました

IS03を購入してから2ヶ月、ようやくAndroidにOpenCVを載せることができました。

最新のバージョンであるOpenCV2.2のソースフォルダには、Androidフォルダとサンプルコードがありますが、ノーマルのNDKでは動かなかったり、SWIGというソフトをインストールしないといけなかったりと、手間暇がかかって面倒だなぁ、とおもってました。


もう少し手軽な方法でAndroidにOpenCVを載せる方法が無いものか、
と探したところ、下記のページを発見。
OpenCV in Android

上記のページによると、AndroidNDK4だけでOpenCV1.1が使えるとのこと。

というわけで、サンプル等を参考にして実際にOpenCVで輪郭検出をやってみました。

OpenCVを使った最初のアプリ















これでAndroid&OpenCVで遊ぶ土台を作ることができました。

OpenCV1.1なので機能は少なめかもしれませんが、OpenCV2.2を試行錯誤して載せるよりかは
こちらの方が安定する気がします。

手順を忘れないために、数回に分けて記事としてアップしたいと思います。



ではまた。


2011-06-24 追記:
OpenCV2.3でも動くようになりました ー>  Android+OpenCV2.3の環境構築に挑戦

2011/02/19

詳解OpenCVの練習問題(3)

詳解OpenCVの練習問題を解いていきます。
(3章の練習問題)

ソースコードは別館の方に保存してあります。


5. ROIの練習。 210x210 のシングルチャネルのバイト画像を作成します。
それを0に設定する。ROIとcvSet()を使って、値が増加するピラミッドを作成します。
ピラミッドの境界の幅は10ピクセルで、内側の境界になるたび値が10ずつ増加します。

作成した画像


















6.一つの画像に対して、複数の画像ヘッダを使用します。
100x100以上の画像を読み込みます。
画像ヘッダを新たに2つ作り、orignin depth,チャネル数,widthstepを
読み込んだ画像と同じに設定します。
2つの画像ヘッダのwidthは20、Heightは30に設定します。
最後に、画像データのポインタを、(5,10)と(50,60)をさすように設定します。
新しい画像ヘッダをcvNotに渡します。
その後、画像を表示します。



作成した画像













7.cvComp()を使ってマスクを作る練習をします。
画像を読み込み、cvSpritを使用して3チャネルに分離します。
a.赤の画像を表示します。
b.緑のプレーン画像の複製を2個作ります。(clone1 clone2)
c.緑のプレーン画像の中にある、最小値と最大値を見つけます。
d.clone1の値をthreash(unsigned char)((最大値-最小値)/2)に設定します。
e.clone2を0に設定し、cvCmp(green_image,clone1,clone2,CV_CMP_GE)を使って結果を表示します。


元画像














緑チャネルの画像














マスクの画像














ではまた。

2011/02/16

Android NDKでグレースケール化プログラムを作る

今日はAndroid NDKをいじっていました。

OpenCVで画像処理をするためには、Java側から画像データを渡す必要があります。
(カメラの画像、SDカードに保存した画像などなど)

ビットマップをネイティブに渡すには、GetPixelsを使って、int配列に変換してから渡すようです。

以下の記事に、Javaからネイティブにビットマップデータを渡すやり方が書いてあったので、参考にしてプログラムを書いてみました。

NDKを使って、nativeに処理をさせる@Tech Booster

コードを色々といじって分かったのですが、Android側でGetPixelsを使ってビットマップの配列データを取得すると、1ピクセルあたり4バイトのデータが出来上がるようです。

リファレンスを見ると、
GetPixels Returns in pixels[] a copy of the data in the bitmap. Each value is a packed int representing a Color.
となっているので、元のビットマップの形式に関係なく、4バイトのColor型に変換されて出てくると理解して良さそうです。

GetPixcelsを使ってint配列を取り出し、ネイティブに渡すことができたので、次はRGBへの分離を行います。

intからR、G、Bチャネルのデータを取り出すコードは下記のとおりです。

//4バイトのintから、R,G,Bのチャネルを取り出す
int r,g,b,result;
r = bmpColors[cnt]&0x00FF0000;
r = r >>16;//右に16ビットシフト
g = bmpColors[cnt]&0x0000FF00;
g = g >>8;//右に8ビットシフト
b = bmpColors[cnt]&0x000000FF;

//この後処理をする

//戻す
result=(r<<16)|(g<<8)|b;



これで渡されたものを、何もしないで元に戻すことができるようになりました。
上が元のBitmap、下がCの関数を通ってきたBitmap















次はカラーをグレースケールに変換してみましょう。

グレースケールに変換する公式は、
Y=(R*0.29891+ G*0.58661+ B*0.11448);
のようなので、早速実装してみます。

//
//グレースケールの公式
float fY=(r*0.29891+ g*0.58661+ b*0.11448);
int Y=trunc(fY);
//グレースケールでもどす
int result = (Y<<16) | (Y << 8) | Y;

グレースケールにしたものが下記の画像となります。















画像の渡し方がわかったので、次はOpenCVのソースコードを組み込む方法を調べていきたいと思います。

ではまた。

2011/02/15

詳解OpenCVの練習問題(2)

今日も詳解OpenCVの練習問題を少し解いていきたいと思います。


問題1:
  byte型の3チャネルをもつ、100x100サイズの配列を作る。

問題2:
  cvCircleを使って、その中に円を描画する。


問題3:
  cvPtr2Dを使って、緑の四角形を描画する。

問題4:
  ポインタ計算を使って、(20,5)と(40,20)の間に緑の四角形を描画する。

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



問題1の解説:
OpenCVではcvCreateMat( int rows, int cols, int type ); を使うことで、行列を作ることができます。今回はbyte型の3チャネルなので、CV_8UC3を指定します。

CvMat* matArray=cvCreateMat(100,100,CV_8UC3);


で、3チャネル、byte型、100 x 100サイズの行列を作ることができます。
OpenCVのリファレンスによると、チャネルの型は下記のような書式で定義されているようです。
 CV_<ビット深度>{U|S|F}C<チャネル数>



問題2の解説:
cvCircleは、
void cvCircle(CvArr* img, CvPoint center, int radius, CvScalar color, int thickness=1, int lineType=8, int shift=0)
という関数になっていて、imgのcenterに半径radiusの円を描きます。線の色はcolor、線の幅はthickness、線の種類はlineTypeで指定できます。

今回は円を書くだけなので、
 cvCircle(matArray,cvPoint(50,50),20,CV_RGB(255,0,0),1,8,0);
で(50,50)の位置に円を書くことができます。


問題3の解説:
cvPtr2D関数は、指定した配列の行、列へのポインタを返してくれます。

uchar* cvPtr2D(const CvArr* arr, int idx0, int idx1, int* type=NULL);
typeはオプションで、NULLを入れなかった場合は配列の要素の型情報が出力されるようです。

今回は型情報は必要無いので、
  uchar *ptr;
  ptr=(uchar *)cvPtr2D(matArray,y,x,NULL);
  *ptr=255;

でy行x列の値を255に設定することができます。
ただし、OpenCVはBGRBGRBGR...というようにメモリ上にデータを格納しているので、緑のチャネルを指定する場合はptrに1を加える必要があります。


問題4の解説:
私もあまり分かってないポインタ演算。

CvMat構造体のヘッダを見ると、
  int type;
  int step;
  int* refcount;
 union
 {
     uchar* ptr;
     short*  s;
     int*      i;
     float*  fl;
    double* db; 
} data;
//以下省略


となっています。
このunionというのは共用体というもので、定義してあるもののうち、どれか1種類を格納できる、というものです。詳しくは共用体・union/構造体との違い@C/C++StudyRoom を参照ください。


今回の配列はbyte型のデータ配列なので、uchar* ptrにデータが入っているはずです。

というわけで、
  matArray->data.ptr[0]
でデータの先頭、つまり(0,0)の青チャネルにアクセスできます。

同じように、
matArray->(x+y*matArray->cols)*3+1]=255;
で(x,y)の緑チャネルの値を255に設定することができました。

ポインタ演算が一番高速だとは思うのですが、ポインタ計算がちょっと大変ですね。

ではまた。


2011/02/14

Android NDKに触ってみる

OpenCVの勉強とともに、Androidの勉強もしています。
OpenCVをAndroidで動かすために、避けて通れないのがAndroid NDK。

というわけで今日はAndroid NDKの設定をやっていきたいと思います。

まずはAndroid developers のサイトにいきます。 http://developer.android.com/intl/ja/sdk/ndk/index.html
これを書いている時点では、ndk-r5bが最新のようです。

にインストールの方法が書いてありました。
(テキトー翻訳です。大筋は合っている。。。はず)

Installing the NDK(NDKをインストール)
NDKをインストールするためには、下記の2ステップを行う必要があります。
1.このページのテーブルのリンクから、あなたのコンピュータの環境に対応するパッケージをダウンロードします。
2.ダウンロードしたNDKを解凍します。解凍後、android-ndk-<version>というフォルダが出来上がります。(今回はandroid-ndk-r5b) 必要であれば、NDKのフォルダ名を変更しても構いません。 このドキュメントでは、NDKディレクトリを<ndk>と表します。
さあ、これでNDKを使うことができるようになりました。


Getting Started with the NDK(NDKを使ってみる)
NDKのインストールが完了したら、NDKに入っているドキュメントを少し読んで見てください。ドキュメントは<ndk>/docs/にあります。
OVERVIEW.HTMLをざっと読めば、使い方が大体分かると思います。
一般的なNDKの使い方は下記のとおりとなります。
1.ネイティブのソースコードを<project>/jni/フォルダに置きます。
2.<project>/jni/Android.mk を作成します。
3.(オプション)<project>/jni/Application.mk を作成します。
4.ネイティブコードを'ndk-build' スクリプトを使ってビルドします。ndk-buildはNDKの直下にあります。
    cd <project>
     <ndk>/ndk-build
     ビルドツールは、必要な共有ライブラリをアプリケーションのプロジェクトディレクトリにコピーします。
5.最後に、いつものようにアプリケーションをコンパイルします。SDKツールが共有ライブラリを.apkファイルに組み込みます。
完全な情報はNDKパッケージのドキュメントに含まれています。


ほうほう。。

というわけで早速いつものHello!Worldを作っていきましょう。
まずはNDKをダウンロードして解凍します。 NDKのインストールはこれで終わり。

Eclipseを起動し、Androidアプリケーションのプロジェクトを作ります。
ちょっと手抜きをして、NDKのsampleフォルダにHello-jniというフォルダがあるので
そこからjniフォルダをコピーしてきます。

jniフォルダにある、Android.mkファイルの中身は下記のとおりとなっていました。
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello-jni.c
include $(BUILD_SHARED_LIBRARY)

うーん、、、hello-jni.cからhello-jniというものを作るためのものなのでしょうか。 詳細はまだ調べていません。

また、hello-jni.cというファイルもあったので開いてみます。
jstring Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz )
{
    return (*env)->NewStringUTF(env, "Hello from JNI !");
}

なんか長い間数名があります。
もしかしてプロジェクトの名前空間の名前を関数に埋め込む必要があるのでしょうか。

hello-jni.javaファイルから、onCreate等をコピーしてきます。

public class hellondk extends Activity
{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
         TextView  tv = new TextView(this);
        tv.setText( stringFromJNI() );
        setContentView(tv);
    }


   public native String  stringFromJNI();

   static {
        System.loadLibrary("hello-jni");
    }
}

まずはC言語の方からコンパイル。
cd <hellondkのプロジェクトのパス>
ndk-build
>Compile thumb  : hello-jni <= hello-jni.c
>SharedLibrary  : libhello-jni.so
>Install        : libhello-jni.so => libs/armeabi/libhello-jni.so
なにやらライブラリができて、libs/armeabi/hello-jni.soにコピーされたようです。

Eclipseの方に戻って、Androidプロジェクトをビルド。
ビルドでエラーが出なかったので実行。










おや?エラーが出ました
LogCatには、
No implementation found for nativ Lcom/sample/hellondk/hellondk;./stirngFromJNI() Ljava/lang/String;
と出てます。
間数名も変更しないとダメみたいですね。

Java_com_example_hellojni_HelloJni_stringFromJNI
から
Java_com_sample_hellondk_hellondk_stringFromJNI
に間数名を変えて、ndk-buildしてから再度Androidアプリの方をビルド。










すると、Hello JNI と文字が出ました。
とりあえずはJNIを使うAndroidアプリが出来たようです。

ただ、Android.mkファイルの作り方や、CのソースコードにあったJNIEnv* env などについてはもう少し調べる必要がありそうです。

ではまた。

2011/02/13

久々の更新

冬のボーナスでついに買ったKindle DXにComputer Vision: Algorithms and Applicationsを入れ、Google翻訳やスペースアルクを使いながら解読作業を行っています。
この本には人間の顔とトラの顔のモーフィングや、複数の写真からパノラマ写真を作ったりと、色々とおもしろそうなネタが散りばめられています。

モーフィングの技術を使って、劇団四季のキャッツみたいな感じにできたら面白いなと思いながらも、それを実際に自分で組もうとなると理論と数式がよくわからないという致命的な問題が。


モーフィングを覚えるにはまだ経験値が足りないようなので、詳解OpenCVに載っている例題を解きながら勉強していくのが良さそうです。

というわけで、まずは最初の方の練習問題を解くことにしました。
(練習問題は詳解OpenCVを参照しています)

3.9 練習問題
1. cxtypes.hを開いて変換ヘルパー関数を探す。(types_c.h に変わっているようです)
1-a. 負の浮動小数点を任意に選び、絶対値、四捨五入、切り捨てを計算する。
1-b. 乱数をいくつか作成する。
1-c. CvPoint2D32fを作って、CvPointに変換する。
1-d. CvPointを作って、CvPoint2D32fに変換する。


作ったものの実行例:

1-a 負の浮動小数点を選んで絶対値、四捨五入、切り捨てを計算
任意の値=-76.543210
絶対値   =76.543210
四捨五入=-77.000000
切り捨て=-76.000000

1-b 乱数をいくつか作成する

(CvRNGは、ランダム値のシードとなるものなので、
シードが同じだと毎回同じ乱数が出てくる)
乱数のシード 整数の乱数 浮動小数点の乱数
839736200 1700997968 0.659561
-117595571 1486822913 0.291075
717184477 -567729758 0.960677
750142485 1419626450 0.103419

1-c. CvPoint2D32fを作って、CvPointに変換する
CvPoint32f (x,y)=123.139999,89.456398 CvPoint (x,y)=123,89

1-d. CvPointを作って、CvPoint2D32fに変換する
CvPoint (x,y)=100,145 CvPoint32f (x,y)=100.000000,145.000000


ソースコードは別館の方に保存してあります。

詳解OpenCVの練習問題が終わったら、Computer Visionの練習問題に取り組みたいと思います。
ではまた。