안드로이드에서 OpenCV 써보기

여기서 이어지는 글입니다. 원래 출처인 http://webnautes.tistory.com/923 이 글을 보고 맥에서 따라 해보는데 굳이 필요 없는 부분이라던지 맥이라 조금씩 다른 부분 고쳐가면서 정리한 글입니다.

참고 사이트
http://docs.opencv.org/2.4/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.html
http://webnautes.tistory.com/923

그럼 시작…

New Module

File->New-> Import Module을 선택합니다.
그리고 OpenCV SDK에서 위 이미지 처럼 sdk/java 폴더를 선택합니다.
(자동검색해서 하위 선택하지 않을까 해서 그냥 SDK상위 폴더 선택하니
이클립스 플젝이나 안드로이드 스튜디오 기본형식 폴더가 아님 안된다고 에러뜸)

New Module

좋은 내용이니 잘 새겨들읍시다.. ㅋㅋㅋ
추가 다하고 보면 에러 납니다.

이거 기본 build.gradle이 안드로이드 SDK  14로 설정되어 있어서 생기는 문제입니다.

apply plugin: 'com.android.library'

android {
    compileSdkVersion 14
    buildToolsVersion "19.1.0"

    defaultConfig {
        minSdkVersion 8
        targetSdkVersion 21
    }

이렇게 되어 있는데 이걸 아래와 같이 바꿨습니다.

apply plugin: 'com.android.library'

android {
    compileSdkVersion 22
    buildToolsVersion "24.0.1"

    defaultConfig {
        minSdkVersion 22
        targetSdkVersion 22
    }

본인 컴에 깔려있는 빌드툴 버전이랑 SDK 버전에 맞게 고치면 됩니다.

Project Structure

⌘ + ; 를 눌러서 Project Structure… 로 들어가서 위 이미지 처럼 Module dependency를 추가합니다.

Choose Modules

아까 추가한 OpenCVLibrary310를 선택해줍니다.
jni 폴더에 아래 내용으로  android.mk 파일을 추가해줍니다.

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

#opencv
OPENCVROOT:= /Users/linsoo/Desktop/Develop/SDK/OpenCV-android-sdk
OPENCV_CAMERA_MODULES:=on
OPENCV_INSTALL_MODULES:=on
OPENCV_LIB_TYPE:=SHARED

include ${OPENCVROOT}/sdk/native/jni/OpenCV.mk

LOCAL_SRC_FILES := kr_co_linsoo_opencvtest_MainActivity.cpp
LOCAL_LDLIBS += -llog
LOCAL_MODULE := nativegray

include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)

jni 폴더에 아래 내용으로  Application.mk 파일을 추가해줍니다.

APP_ABI := armeabi
APP_PLATFORM := android-22
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions

하이라이트 부분 본인의 컴에 맞게 잘 확인해서 수정합니다.
이제 app의 build.gradle 을 수정합니다.

apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion "24.0.1"

    defaultConfig {
        applicationId "kr.co.linsoo.opencvtest"
        minSdkVersion 22
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
//------------------------------------------------------------------------------------------
//여기 추가된부분
android {
    sourceSets {
        main {
            jni.srcDirs = []
        }
    }
}


task ndkBuild(type: Exec, description: 'Compile JNI source via NDK') {
    def ndkDir = project.plugins.findPlugin('com.android.application').sdkHandler.getNdkFolder()
    commandLine "$ndkDir/ndk-build",
            'NDK_PROJECT_PATH=build/intermediates/ndk',
            'NDK_LIBS_OUT=src/main/jniLibs',
            'APP_BUILD_SCRIPT=src/main/jni/Android.mk',
            'NDK_APPLICATION_MK=src/main/jni/Application.mk'
}

tasks.withType(JavaCompile) {
    compileTask -> compileTask.dependsOn ndkBuild
}
//------------------------------------------------------------------------------------------

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:22.2.1'
    compile project(':openCVLibrary310')
}

중간에
//———————-
여기 추가된 부분
…..
//———————-
의 내용을 추가해줍니다.
이제 AndroidManifest.xml에 카메라 권한을 넣어줍니다.

출처에는 이것저것 넣긴 했는데 그냥

<uses-permission android:name="android.permission.CAMERA"/>

이거 하나만 넣어도 되긴 합니다.

다른건 설치할때 권한 표기 안하게 하는거랑 화면 사이즈 관련해서 옵션이라
activity_main.xml에는 다른것들도 넣어도 상관없지만 일단 샘플이니

<org.opencv.android.JavaCameraView
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:id="@+id/activity_surface_view" />

이거 하나만 넣어줍니다. (실제 카메라 뷰가 나올거)

MainActivity.java 파일

package kr.co.linsoo.opencvtest;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.CvType;
import org.opencv.core.Mat;

public class MainActivity extends Activity implements CameraBridgeViewBase.CvCameraViewListener2 {

    public native int convertNativeGray(long matAddrRgba, long matAddrGray);

    private Mat mRgba, mGray;
    private CameraBridgeViewBase mOpenCvCameraView;

    static {
        System.loadLibrary("opencv_java3");
    }

    private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS:{
                    Log.i("linsoo", "OpenCV loaded successfully");
                    System.loadLibrary("nativegray");// Load Native module
                    mOpenCvCameraView.enableView();
                } break;
                default:
                    super.onManagerConnected(status);
                    break;
            }
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().addFlags( WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        setContentView(R.layout.activity_main);

        mOpenCvCameraView = (CameraBridgeViewBase)findViewById(R.id.activity_surface_view);
        mOpenCvCameraView.setCvCameraViewListener(this);
    }

    @Override
    public void onPause()
    {
        super.onPause();
        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();
    }

    @Override
    public void onResume()
    {
        super.onResume();
        if (OpenCVLoader.initDebug() == false) {
            Log.d("linsoo", "Internal OpenCV library not found.");
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this,mLoaderCallback);
        } else {
            Log.d("linsoo", "OpenCV library found inside package. Using it!");
            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
    }

    public void onDestroy() {
        super.onDestroy();
        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();
    }

    @Override
    public void onCameraViewStarted(int width, int height) {
    }

    @Override
    public void onCameraViewStopped() {
    }

    @Override
    public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
        if (inputFrame.rgba() != null) {
            mRgba = inputFrame.rgba();
            if ( mGray!=null )
                mGray.release();
            mGray = new Mat( inputFrame.rgba().rows(),inputFrame.rgba().cols(), CvType.CV_8UC1);
            convertNativeGray(mRgba.getNativeObjAddr(),mGray.getNativeObjAddr());
        }
        return mGray;
    }
}

대충 어떻게 돌아가는건지는 알겠는데
일일히 설명할 필요는 없을거 같고 opencv 매뉴얼 찾아보면 의미를 알수 있겠죠

kr_co_linsoo_opencvtest_MainActivity.cpp

//
// Created by linsoo on 2016. 8. 26..
//

#include "kr_co_linsoo_opencvtest_MainActivity.h"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <stdio.h>

using namespace std;
using namespace cv;

int toGray(Mat img, Mat& gray);
JNIEXPORT jint JNICALL Java_kr_co_linsoo_opencvtest_MainActivity_convertNativeGray(JNIEnv*, jobject, jlong addrRgba, jlong addrGray);

//-------------------------------------------------------
JNIEXPORT jint JNICALL Java_kr_co_linsoo_opencvtest_MainActivity_convertNativeGray(JNIEnv*, jobject, jlong addrRgba, jlong addrGray) {

    Mat& mRgb = *(Mat*)addrRgba;
    Mat& mGray = *(Mat*)addrGray;

    int conv;
    jint retVal;
    conv = toGray(mRgb, mGray);
    retVal = (jint)conv;
    return retVal;
}

int toGray(Mat img, Mat& gray){
    cvtColor(img, gray, CV_RGBA2GRAY);
    if (gray.rows == img.rows && gray.cols == img.cols)
        return (1);
    return(0);
}

 kr_co_linsoo_opencvtest_MainActivity.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class kr_co_linsoo_opencvtest_MainActivity */

#ifndef _Included_kr_co_linsoo_opencvtest_MainActivity
#define _Included_kr_co_linsoo_opencvtest_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     kr_co_linsoo_opencvtest_MainActivity
 * Method:    hello
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jint JNICALL Java_kr_co_linsoo_opencvtest_MainActivity_convertNativeGray(JNIEnv*, jobject, jlong addrRgba, jlong addrGray);

#ifdef __cplusplus
}
#endif
#endif

이정도 소스 넣어주고 빌드 하면 실행된다.

OpenCV Test

OpenCV는 윈도우에서 카메라 영상 받아서 뿌려주는거 밖에 안해봐서 잘은 모르지만
요거 좀 응용하면 윈도우에서 쓰던거 처럼 쓸수는 있을듯 싶음.

다음엔 NDK 가지고 예전에 java로 이미지 확대 하던 소스 두개 성능 비교하는거 만들어 봐야 겠음.
똑같은 소스 가지고 돌렸을때 차이가 있는지… (내가 코드 못만들어서 느린건지 java라 느렸던건지 궁금했음)


Comments

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다