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
つづく