ndk-build를 사용하여 안드로이드 프로젝트에 OpenCV 라이브러리를 추가하는 과정을 설명합니다.
테스트 환경은 다음과 같습니다.
-
Windows 10
-
OpenCV 3.4.1
-
Android Studio 3.1.2
-
Android 8.1 (API 27)
다음 순서로 설명합니다.
1. 안드로이드 프로젝트 생성
2. 프로젝트에 OpenCV 라이브러리 추가
3. ndk-build 사용한 NDK + OpenCV 카메라 예제
4. 참고
마지막 업데이트 : 2018. 5. 30
1. 안드로이드 프로젝트 생성
1-1. 안드로이드 스튜디오에서 Include C++ Support 체크 박스를 체크하지 않고 새로운 프로젝트를 생성합니다.

1-2. Minimum SDK를 API 14 이상으로 해야 appcompat-v7라이브러리를 사용할 수 있습니다.
여기에서는 API 15로 진행하겠습니다.

1-3. 프로젝트에 추가할 액티비티로 Empty Activity를 선택합니다.

1-4. 하위 버전과의 호환성을 고려한다면 Backwards Compatibility (AppCompat) 체크박스를 체크한 상태로 둡니다.
MainActivity 액티비티에서 Activity 클래스 대신에 AppCompatActivity 클래스를 사용하게 됩니다.

2. 프로젝트에 OpenCV 라이브러리 추가
2-1. https://github.com/opencv/opencv/releases 에서 안드로이드를 위한 OpenCV 라이브러리( opencv-3.4.1-android-sdk.zip)를 다운로드 받습니다.

압축을 풀은 후, OpenCV-android-sdk 디렉토리를 적당한 곳에 복사해줍니다.
본 포스팅에서는 OpenCV-android-sdk 디렉토리를 C:\에 복사한 것으로 가정하고 설명하겠습니다.

2-2. 앞에서 진행한 프로젝트 생성이 완료되기를 기다리기 위해 안드로이드 스튜디오 왼쪽 아래에 Gradle build finished 메시지가 보인 후에 진행합니다.

OpenCV 라이브러리 모듈을 프로젝트로 가져오기 위해 메뉴에서 File > New > Import Module를 선택합니다.
Source directory 입력란 옆에 있는 버튼을 클릭합니다.

OpenCV-android-sdk 디렉토리 하위에 있는 sdk\java 디렉토리를 선택합니다.

정상적으로 경로가 추가되었다면 Module name에 openCVLibrary341이 보입니다.

Finish 버튼을 클릭합니다.

2-3. 프로젝트에 추가된 OpenCV 라이브러리(openCVLibrary341) 모듈에서 요구하는 API 14용 SDK 플랫폼 패키지가 설치안되어 있으면 다음처럼 에러가 발생합니다.
Failed to find target with hash string 'android-14' in: C:\Users\webnautes\AppData\Local\Android\Sdk Install missing platform(s) and sync project |
에러 메시지에 보이는 Install missing platform(s) and sync project를 클릭하여 API 14용 SDK 플랫폼 패키지를 설치해도 해결됩니다.
하지만 본 포스팅에선 안드로이드 프로젝트에서 사용하는 SDK 플랫폼 버전과 일치시키는 방법을 사용하겠습니다.
에러를 해결하려면 openCVLibrary341 모듈에 포함되어 있는 build.gradle를 수정해야 합니다.
하지만 프로젝트 패널이 Android 뷰인 현재 상태에서는 안드로이드 프로젝트에 추가된 openCVLibrary341 모듈을 확인할 수 없습니다.

아래 캡쳐 화면처럼 프로젝트 패널을 Project 뷰로 변경해야(빨간색 사각형 부분) 안드로이드 프로젝트에 추가된 openCVLibrary341 모듈을 확인할 수 있습니다.

app 모듈의 build.gradle에 설정되어 있는 compileSdkVersion, minSdkVersion, targetSdkVersion 값을 openCVLibrary341 모듈의 build.gradle에서 똑같이 사용하도록 수정해야 합니다.

아래 표처럼 app 모듈의 build.gradle에 설정되어 있는 SDK 버전값(빨간색)을 openCVLibrary341 모듈의 build.gradle에서 똑같이 입력해주면 됩니다.
app\ build.gradle |
openCVLibrary341 \ build.gradle |
android {
compileSdkVersion 27
defaultConfig {
applicationId "com.tistory.webnautes.useopencvwithcmake"
minSdkVersion 15
targetSdkVersion 27
|
android {
compileSdkVersion 27
buildToolsVersion "27.0.3"
defaultConfig {
minSdkVersion 15
targetSdkVersion 27
} |
변경사항을 프로젝트에 적용하기 위해 코드 편집기 오른쪽 위에 보이는 노란색줄에 있는 Try Again을 클릭합니다.

2-4. 안드로이드 스튜디오의 왼쪽 아래에 Gradle build finished라는 메시지가 보일 때까지 기다립니다.

app 모듈에서 openCVLibrary341 라이브러리 모듈을 사용하도록 설정해줘야 합니다.
메뉴에서 File > Project structure를 선택한 후, 왼쪽에 있는 리스트에서 app를 선택합니다.

Dependencies 탭을 선택하고 오른쪽 상단에 위치한 초록색 +를 클릭합니다.

메뉴에서 Module dependency를 선택합니다.

추가했던 openCVLibrary341 모듈이 보입니다. OK버튼을 클릭합니다.

이제 openCVLibrary341 모듈을 app 모듈에서 사용할 수 있게 설정되었습니다. 이제 OK 버튼을 클릭하여 Project Structure 창을 닫습니다.

2-5. 안드로이드 스튜디오의 왼쪽 아래에 Gradle build finished라는 메시지가 보일 때 까지 기다립니다.

C:\OpenCV-android-sdk\sdk\native에 위치한 libs 디렉토리를 안드로이드 프로젝트의 app\src\main에 복사해줍니다.
본 예제대로 따라했다면 위치는 다음과 같습니다.
C:\Users\사용자이름\AndroidStudioProjects\UseOpenCVwithndkbuild\app\src\main
이제 디렉토리 이름을 JniLibs로 변경합니다. 변경하지 않으면 문제가 발생합니다.
안드로이드 스튜디오의 Project 패널에서 추가 된것을 확인 할 수 있습니다.
Android Studio 3.0에서 다음과 같은 에러가 발생하는 것을 보고 필요없는 작업인 것을 알았습니다.
Error:Execution failed for task ':app:transformNativeLibsWithMergeJniLibsForDebug'.
> More than one file was found with OS independent path 'lib/arm64-v8a/libopencv_java3.so'
3. ndk-build 사용한 NDK + OpenCV 카메라 예제
3-1. 안드로이드 스튜디오에서 ndk-build를 사용하여 C/C++ 코드를 컴파일 및 디버그하기 위해서는 다음 2가지가 필요합니다.
안드로이드에서 JAVA 코드와 C/C++ 코드를 같이 사용할 수 있게 해줍니다.
C/C++ 코드를 디버그하기 위해 사용되는 디버거입니다. 설치해주면 예전에 잡히지 않았던 에러나
예외상황이 검출되며 에러 발생한 C/C++ 코드 위치를 알려줍니다.
안드로이드 스튜디오 메뉴에서 Tools > SDK Manager를 선택합니다.
메뉴에 SDK Manager 항목이 보이지 않는다면 툴바에서 아래 아이콘을 클릭합니다.
SDK Tools 탭에서 CMake, NDK, LLDB를 선택하고 Apply 버튼을 클릭하면 다운로드 및 설치가 진행됩니다.

3-2. AppCompatActivity 클래스를 사용한 액티비티에서 타이틀바를 없애기 위해서 styles.xml 파일에 다음 코드(노란색)를 추가합니다.

<resources>
<!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <!-- No Title Bar--> <item name="windowActionBar">false</item> <item name="windowNoTitle">true</item> </style>
</resources> |
그리고 상태바도 없애기 위해 MainActivity의 onCreate() 메소드에 다음 코드(노란색)가 필요합니다.
나중에 언급하는 자바 코드에 포함되어 있으므로 여기에선 추가할 필요 없습니다.
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_main); |
참고 http://commin.tistory.com/63
3-3. 레이아웃 파일 activity_main.xml 을 다음 코드로 대체합니다.

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" > <org.opencv.android.JavaCameraView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/activity_surface_view" />
</LinearLayout> |

3-4. 매니페스트 파일 AndroidManifest.xml 에 다음 코드(노란색)를 추가합니다.

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.tistory.webnautes.useopencvwithcmake">
<uses-permission android:name="android.permission.CAMERA"/> <uses-feature android:name="android.hardware.camera" android:required="false"/> <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/> <uses-feature android:name="android.hardware.camera.front" android:required="false"/> <uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>
<supports-screens android:resizeable="true" android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:anyDensity="true" />
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:screenOrientation="landscape" android:configChanges="keyboardHidden|orientation">
<intent-filter> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application>
</manifest> |
앱에서 안드로이드 디바이스의 카메라에 접근하기 위해서는 필요한 퍼미션 입니다.
<uses-permission android:name="android.permission.CAMERA"/> <uses-feature android:name="android.hardware.camera" android:required="false"/> <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/> <uses-feature android:name="android.hardware.camera.front" android:required="false"/> <uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/> |
android:screenOrientation 속성을 landscape로 해주어야 OpenCV JAVA API를 사용하여 전체화면에 카메라 영상을 보여 줄 수 있습니다.
android:screenOrientation="landscape" |
3-5. JNI(Java Native Interface)를 사용하여 C/C++ 함수를 호출하는 JAVA 코드를 작성합니다.
자바코드 파일 MainActivity.java를 다음 코드로 대체합니다.

package com.tistory.webnautes.useopencvwithndk_build;
import android.annotation.TargetApi; import android.content.DialogInterface; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.SurfaceView; 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.Mat;
public class MainActivity extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener2 {
private static final String TAG = "opencv"; private CameraBridgeViewBase mOpenCvCameraView; private Mat matInput; private Mat matResult;
public native void ConvertRGBtoGray(long matAddrInput, long matAddrResult);
static { System.loadLibrary("opencv_java3"); System.loadLibrary("native-lib"); }
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { @Override public void onManagerConnected(int status) { switch (status) { case LoaderCallbackInterface.SUCCESS: { mOpenCvCameraView.enableView(); } break; default: { super.onManagerConnected(status); } break; } } };
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //퍼미션 상태 확인 if (!hasPermissions(PERMISSIONS)) {
//퍼미션 허가 안되어있다면 사용자에게 요청 requestPermissions(PERMISSIONS, PERMISSIONS_REQUEST_CODE); } }
mOpenCvCameraView = (CameraBridgeViewBase)findViewById(R.id.activity_surface_view); mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE); mOpenCvCameraView.setCvCameraViewListener(this); mOpenCvCameraView.setCameraIndex(0); // front-camera(1), back-camera(0) mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS); }
@Override public void onPause() { super.onPause(); if (mOpenCvCameraView != null) mOpenCvCameraView.disableView(); }
@Override public void onResume() { super.onResume();
if (!OpenCVLoader.initDebug()) { Log.d(TAG, "onResume :: Internal OpenCV library not found."); OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_2_0, this, mLoaderCallback); } else { Log.d(TAG, "onResum :: 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) {
matInput = inputFrame.rgba();
if ( matResult != null ) matResult.release(); matResult = new Mat(matInput.rows(), matInput.cols(), matInput.type());
ConvertRGBtoGray(matInput.getNativeObjAddr(), matResult.getNativeObjAddr());
return matResult; }
//여기서부턴 퍼미션 관련 메소드 static final int PERMISSIONS_REQUEST_CODE = 1000; String[] PERMISSIONS = {"android.permission.CAMERA"};
private boolean hasPermissions(String[] permissions) { int result;
//스트링 배열에 있는 퍼미션들의 허가 상태 여부 확인 for (String perms : permissions){
result = ContextCompat.checkSelfPermission(this, perms);
if (result == PackageManager.PERMISSION_DENIED){ //허가 안된 퍼미션 발견 return false; } }
//모든 퍼미션이 허가되었음 return true; }
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch(requestCode){
case PERMISSIONS_REQUEST_CODE: if (grantResults.length > 0) { boolean cameraPermissionAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
if (!cameraPermissionAccepted) showDialogForPermission("앱을 실행하려면 퍼미션을 허가하셔야합니다."); } break; } }
@TargetApi(Build.VERSION_CODES.M) private void showDialogForPermission(String msg) {
AlertDialog.Builder builder = new AlertDialog.Builder( MainActivity.this); builder.setTitle("알림"); builder.setMessage(msg); builder.setCancelable(false); builder.setPositiveButton("예", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id){ requestPermissions(PERMISSIONS, PERMISSIONS_REQUEST_CODE); } }); builder.setNegativeButton("아니오", new DialogInterface.OnClickListener() { public void onClick(DialogInterface arg0, int arg1) { finish(); } }); builder.create().show(); }
} |
3-6. javah를 사용하여 자바 코드에서 선언한 네이티브 메소드에 대응하는 C/C++ 함수가 선언되어 있는 헤더 파일을 jni 디렉토리에 자동으로 생성합니다.
우선 javah를 실행하기 편하게 안드로이드 스튜디오의 외부 도구로 등록합니다. 메뉴에서 File > Settings를 선택합니다.
Settings 창의 왼쪽 목록에서 Tools > External Tools를 선택하고 오른쪽에서 초록색 +를 클릭합니다.

아래처럼 각 항목의 내용을 채웁니다.
◼ Name
javah
◼ Description
Android Tool - javah
◼ Advanced Options를 클릭하고 다음 두 항목을 체크
Make console active on message in stdout
Make console active on message in stderr
◼ Program
Android Studio에서 번들로 제공되는 OpenJDK를 사용하면 program에 다음처럼 입력합니다.
C:\Program Files\Android\Android Studio\jre\bin\javah.exe
Oracle의 JDK를 사용한다면 program에 다음처럼 입력합니다. 사용하는 jdk 버전에 따라 디렉토리 위치가 다를 수 있습니다.
C:\Program Files\Java\jdk1.8.0_131\bin\javah.exe
◼ Arguments :
-v -jni -d $ModuleFileDir$/src/main/jni $FileClass$
◼ Working directory:
$SourcepathEntry$
다 입력하고나서 OK를 클릭하여 설정을 저장합니다.

External Tools에 javah가 추가되었습니다.

MainActivity를 선택하고 마우스 우클릭 후 보이는 메뉴에서 External Tools > javah를 선택합니다.

문제 없이 진행되면 다음과 같은 메시지가 출력됩니다.
"C:\Program Files\Android\Android Studio\jre\bin\javah.exe" -v -jni -d C:\Users\webnautes\AndroidStudioProjects\UseOpenCVwithndkbuild\app/src/main/jni com.tistory.webnautes.useopencvwithndk_build.MainActivity
[Creating file RegularFileObject[C:\Users\webnautes\AndroidStudioProjects\UseOpenCVwithndkbuild\app\src\main\jni\com_tistory_webnautes_useopencvwithndk_build_MainActivity.h]]
Process finished with exit code 0 |
코드에 한글이 포함된 경우 다음과 같은 메시지들이 같이 출력되는데 결과를 얻는데 상관없으므로 무시하셔도 됩니다.
Error: unmappable character for encoding MS949 |
app / src / main 디렉토리 아래에 jni 디렉토리가 생성되고 그 안에 패키지 이름이 포함된 헤더 파일(여기엔선 com_tistory_webnautes_useopencvwithndk_build_MainActivity.h)이 생성됩니다.
자바 코드의 패키지 이름(com.tistory.webnautes.useopencvwithndk_build)과 액티비티 이름(MainActivity)의 조합으로 헤더 파일의 이름이 결정됩니다.

3-7. 자바에서 선언한 네이티브 메소드를 위한 C/C++ 구현을 jni / main.cpp 파일에 작성합니다.
main.cpp는 공유 라이브러리 파일로 컴파일 되어 자바 코드에서 로드되어 사용됩니다.
jni 디렉토리를 선택한 후, 마우스 우클릭하여 메뉴에서 New > File을 선택합니다.

새로 생성할 파일 이름으로 main.cpp를 적고 OK를 클릭합니다.

jni 폴더에 main.cpp 파일이 생성됩니다.

main.cpp 파일에 다음 헤더파일 2개를 추가합니다.
두 번째 헤더파일은 앞에서 생성한 헤더파일 이름을 적어줍니다.
#include <jni.h> #include "com_tistory_webnautes_useopencvwithndk_build_MainActivity.h" |
두번째 헤더파일을 열어보면 JAVA에서 선언한 네이티브 메소드에 대응하는 JNI 함수가 다음 처럼 정의되어 있습니다.
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_tistory_webnautes_useopencvwithndk_build_MainActivity */
#ifndef _Included_com_tistory_webnautes_useopencvwithndk_build_MainActivity #define _Included_com_tistory_webnautes_useopencvwithndk_build_MainActivity #ifdef __cplusplus extern "C" { #endif #undef com_tistory_webnautes_useopencvwithndk_build_MainActivity_PERMISSIONS_REQUEST_CODE #define com_tistory_webnautes_useopencvwithndk_build_MainActivity_PERMISSIONS_REQUEST_CODE 1000L /* * Class: com_tistory_webnautes_useopencvwithndk_build_MainActivity * Method: ConvertRGBtoGray * Signature: (JJ)V */ JNIEXPORT void JNICALL Java_com_tistory_webnautes_useopencvwithndk_1build_MainActivity_ConvertRGBtoGray (JNIEnv *, jobject, jlong, jlong);
#ifdef __cplusplus } #endif #endif |
생성된 JNI 함수를 main.cpp 파일에 복사해오고(파란색 부분) 추가로 필요한 코드를( 노란 부분) 추가합니다.
#include <jni.h> #include "com_tistory_webnautes_useopencvwithndk_build_MainActivity.h"
#include <opencv2/opencv.hpp>
using namespace cv;
extern "C"{
JNIEXPORT void JNICALL Java_com_tistory_webnautes_useopencvwithndk_1build_MainActivity_ConvertRGBtoGray( JNIEnv *env, jobject instance, jlong matAddrInput, jlong matAddrResult){
Mat &matInput = *(Mat *)matAddrInput; Mat &matResult = *(Mat *)matAddrResult;
cvtColor(matInput, matResult, CV_RGBA2GRAY);
} }
|
3-8. ndk-build를 사용하여 공유 라이브러리(.so)를 빌드하기 위해서는 Android.mk 파일과 Application.mk 파일을 작성해줘야 합니다.
jni 디렉토리를 선택한 후, 마우스 우클릭하여 메뉴에서 New > File을 선택합니다.
새로 생성할 파일 이름으로 Android.mk를 적고 OK를 클릭합니다.

Text가 선택된 상태에서 OK를 클릭합니다.

jni 디렉토리에 Android.mk 파일이 생성됩니다.

Android.mk 파일에 다음 내용을 추가합니다.
공유 라이브러리를 생성할 경우에는 사용되는 외부 라이브러리 정보와 직접 작성한 C/C++ 소스코드 관련 정보를 입력합니다.
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS)
#opencv library OPENCVROOT:= C:\OpenCV-android-sdk OPENCV_CAMERA_MODULES:=on OPENCV_INSTALL_MODULES:=on OPENCV_LIB_TYPE:=SHARED include ${OPENCVROOT}\sdk\native\jni\OpenCV.mk
LOCAL_MODULE := native-lib LOCAL_SRC_FILES := main.cpp LOCAL_LDLIBS += -llog
include $(BUILD_SHARED_LIBRARY) |
Android.mk를 생성한 방법대로 jni 디렉토리에 Application.mk 파일도 TEXT 파일로 생성합니다.

Application.mk 파일에 다음 내용을 추가합니다.
컴파일시 사용되는 여러 변수들을 정의합니다.
APP_OPTIM := debug APP_ABI := all APP_PLATFORM := android-27
APP_STL := gnustl_static APP_CPPFLAGS := -frtti -fexceptions NDK_TOOLCHAIN_VERSION := clang
APP_BUILD_SCRIPT := Android.mk |
3-9. app 모듈의 build.gradle에 ndk-build를 사용하여 C/C++ 빌드가 이루어지기 위해 필요한 내용을 추가해줍니다. (노란색 줄)

android {
compileSdkVersion 27
defaultConfig {
applicationId "com.tistory.webnautes.useopencvwithndk_build"
minSdkVersion 15
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
ndkBuild {
path 'src/main/jni/Android.mk'
}
}
} |
3-10. Sync Now를 클릭하여 Gradle build를 시작합니다.

문제 없으면 왼쪽 아래에 Gradle build finished라는 메시지가 보입니다.

빌드 후, 안드로이드 폰에 설치하여 실행시켜 보면 안드로이드폰의 방향에 따라 카메라 영상도 같이 회전합니다.



안드로이드 OS 7.0(API 24) 폰에서 테스트해봤습니다.)
4. 참고
Android Studio에서 안드로이드 프로젝트 생성시 Include C++ Support 체크박스를 설정한 경우 생성되는 소스 코드
https://developer.android.com/ndk/guides/index.html
https://developer.android.com/studio/projects/add-native-code.html
https://github.com/googlesamples/android-ndk
http://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html
http://docs.opencv.org/2.4/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.html
출처 : http://webnautes.tistory.com/923