2011/02/21

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

つづく

0 件のコメント:

コメントを投稿