반응형

ndk-build를 사용하여 안드로이드 프로젝트에 OpenCV 라이브러리를 추가하는 과정 및 OpenCV를 사용하여 카메라 영상을 그레이스케일로 변경하는 방법을 설명합니다.



현재 Android Studio에서 공식으로 지원하는 방식은 CMake를 사용하는 방식입니다. 


Android NDK + CMake + OpenCV 카메라 예제 및 프로젝트 생성방법

https://webnautes.tistory.com/1054 



아래 리스트 중 2022. 5. 8에 명시된 버전으로만 테스트를 진행했으며 다른 버전 사용시 문제가 발생할 가능성이 있습니다. 



2016. 05. 07  최초작성

 

~~~~~~~~~~~~~~~

 

2020. 02. 16 OpenCV 4.2.0, Android 10.0 

 

2020. 06. 27 Android Studio 4.0, OpenCV 4.3.0, Android 10.0

 

2021 04. 08 Android Studio 4.1.3, OpenCV 4.5.2, Android 11.0

 

2021. 9. 1 Android Studio 2020.3.1에서 OpenCV 모듈을 가져오려고 하면 위자드에서 Finish 버튼이 비활성화 상태로 유지되는 버그가 생긴듯합니다. 해결되기 전까진 수동으로 sdk 폴더를 안드로이드 프로젝트로 옮기고 build.gradle 파일을 수정해서 사용해야 하는 듯합니다.  자세한 내용은 https://stackoverflow.com/a/6873876

 

2021. 10. 22 CMake 방식에서 문제 없이 되는 것을 확인했습니다. NDK 방식 검증은 차후로 미루고 우선 본 포스트에서 OpenCV 모듈을 임포트 하는 부분만 어디를 참고해야 하는지 표시해두었습니다. 

 

2021. 10. 23 질문이 종종있어서 앞에서 언급했던 링크대로 포스트를 진행해보았습니다.

                      Android Studio Arctic Fox 2020.3.1 Patch 3

                      Android 11.0 API 30 

                      NDK 21.4.7075529

                     OpenCV 4.5.4

 

                       javah.exe 파일이 현재 없어서 진행할 수 없었습니다. javac -h를 사용하면 된다는데 쉽지 않네요

                    javac -h를 사용하여 해결했습니다.

 

                    jni 함수와 연결되는 메서드를 분리하여 별도의 자바 클래스에 포함시켜 진행했는데 마지막에 빌드중 헤더파일에서 에러가 발생합니다.  

 

                     해결되었습니다.

 

2022. 5. 8  아래사항으로 업데이트 후, 안드로이드 에뮬레이터로 테스트

                  Android Studio Bumblebee 2021.1.1 Patch 3

                  Android 12.0 API 31, Minimum SDK API 21로 설정

                  OpenCV 4.5.5

                  NDK 33-rc3




             0. 패키지 설치

1. 안드로이드 프로젝트 생성

2. 프로젝트에 OpenCV 라이브러리 추가

3. ndk-build 사용한 NDK + OpenCV 카메라 예제

4. 참고


관련 포스트

 

OpenCV와 NDK를 사용하여 Android에서 Face Detection(얼굴 검출)

https://webnautes.tistory.com/1087

0. 패키지 설치

 

1. 스크린샷처럼 SDK Manager를 실행합니다. 또는 안드로이드 스튜디오 메뉴에서 Tools > SDK Manager를 선택합니다.

 



2. 안드로이드 스튜디오에서 ndk-build를 사용하여 C/C++ 코드를 사용하기 위해서는 다음 패키지가 필요합니다.

 

  • The Android Native Development Kit (NDK)

안드로이드에서 JAVA 코드와 C/C++ 코드를 같이 사용할 수 있게 해줍니다.



SDK Tools 탭에서  NDK를 선택하고 Apply 버튼을 클릭하면 다운로드 및 설치가 진행됩니다.

포스트 진행 과정에서 추가로 다른 버전의 NDK가 설치될 수 있습니다. 

 



Android 12 API 31을 설치한 상태에서 진행했습니다.

 



3. 에뮬레이터를 생성해서 사용했습니다. Virtual Device Manager를 실행합니다. 

 



Create device를 클릭합니다.

 



Device로 Pixel XL을 선택하고 Next를 클릭합니다. 

 



S API 31에 보이는 Download를 클릭하여 시스템 이미지를 다운로드한 후, S  API 31가 선택된 상태에서 Next를 클릭합니다. 

 



왼쪽 아래에 보이는 Show Advanced Settings를 클릭합니다.

 



Camera 항목에 있는 Front와 Back을 모두 Webcam0로 바꾸로 Finish를 클릭합니다.

 



이제 에뮬레이터가 생성되었습니다. 

 



1. 안드로이드 프로젝트 생성

 

1-0.  New Project를 선택합니다. 

 



1-1. Empty Activity를 선택합니다. 

 



1-2. Name 항목에 프로젝트 이름을 적고, Language는 Java,  Minimum API level은 API 21을 선택하고 Finish를 클릭합니다.

 

본 포스트는 프로젝트 이름을 “Use OpenCV with ndk-build”로 한경우에 대해 작성되었기 때문에 프로젝트 이름이 다른 경우에는 변경이 필요한 부분이 있습니다. 포스트에 명시해놓았습니다.

 




2. 프로젝트에 OpenCV 라이브러리 추가

2-1. OpenCV 깃허브 저장소에서 opencv-4.5.5-android-sdk.zip 파일을 다운로드 합니다. 

https://github.com/opencv/opencv/releases 






압축을 풀어서 C:\에 복사해줍니다. 




2-2.  Android용 OpenCV가 다음 위치에 있는 것으로 가정하고 진행합니다.

C:\OpenCV-android-sdk

 




2-3.  현재는 이 방법은 진행하는데 문제가 있습니다. 문제가 수정될 때까지는  번거롭더라도 수동으로 모듈을 가져오는  2-4를 진행하세요. 참고 https://stackoverflow.com/a/68738767 

 

다음 버전에서 확인결과 해결되었습니다. 

Android Studio Bumblebee 2021.1.1 Patch 3



앞에서 진행한 프로젝트 생성이 완료되기를 대기합니다. 안드로이드 스튜디오의 상태 표시줄에 작업중 메시지가 사라져야 합니다. 

 



OpenCV 라이브러리 모듈을 프로젝트로 가져오기 위해 메뉴에서 File > New > Import Module를 선택합니다. 

 

Source directory 입력란 옆에 있는 버튼을 클릭합니다.

 



OpenCV-android-sdk 디렉토리 하위에 있는 sdk 디렉토리를 선택하고 OK 버튼을 클릭합니다. 

 




Finish 버튼을 클릭합니다.

 



2-4. 다음과 같은 에러가 발생합니다.

 

A problem occurred evaluating project ':sdk'.

> Plugin with id 'kotlin-android' not found.



현재 프로젝트 창에 sdk 모듈이 보이지 않습니다. 왼쪽 위에 있는 Android를 클릭하여 보이는 목록에서 Project를 클릭하여 프로젝트 창에 보여지는 것 Project 뷰로 변경해야 sdk 모듈이 보이게 됩니다. 

 



sdk 모듈(opencv)이 추가되었다면 다음처럼 보입니다. 

 



sdk 에 있는 build.gradle를 클릭합니다. 

 



상단에 보이는 apply plugin: 'kotlin-android'를 제거합니다.

 

변경전

 



변경후

 




오른쪽 위에 보이는 Try Again을 클릭합니다. 

 



문제 없으면 다음 로그가 보입니다.

 



build.gradle (:sdk)에서 targetSdkVersion 26에 빨간줄이 보이는데 Google Play를 사용하지 않으면 상관없어 보입니다. 실행시 문제가 되지 않았습니다. 

 



2-5. app 모듈에서 opencv 라이브러리 모듈을 사용하도록 설정해줘야 합니다.

메뉴에서 File > Project structure를 선택한 후,  왼쪽에 보이는 리스트에서 Dependencies를 선택합니다. 

 




Modules에서  app을 선택한 후, Declared Dependencies 문자열 아래에 보이는 +를 클릭하면 보이는 메뉴에서 Module Dependency를 선택합니다. 

 



앞에서 추가했던 opencv 모듈이 sdk 이름으로 보입니다. 체크한 후  OK버튼을 클릭합니다.

 



이제 sdk 모듈(opencv) app 모듈에서 사용할 수 있게 설정되었습니다. 

이제 OK 버튼을 클릭하여 Project Structure 창을 닫습니다.

 



2-6.  문제없다면 다음 로그가 보입니다.

 

 

3. ndk-build 사용한 NDK + OpenCV 카메라 예제

 

3-1. 이제 프로젝트 창을 Android 뷰로 변경합니다.

 



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

 

 



    <!-- Base application theme. -->
    <style name="Theme.UseOpenCVWithNdkbuild" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/purple_500</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/white</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_700</item>
        <item name="colorOnSecondary">@color/black</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Customize your theme here. -->
        <!-- No Title Bar-->
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>
</resources>

 

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"?>

    package="com.tistory.webnautes.useopencvwithndk_build">

    <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/Theme.UseOpenCVWithNdkbuild">
        <activity
            android:name=".MainActivity"
            android:screenOrientation="landscape"
            android:configChanges="keyboardHidden|orientation"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

 

3-5. 카메라로부터 영상을 가져오는 것은 JAVA 코드에서 하며 JNI(Java Native Interface)를 사용하여 C/C++ 함수를 호출하여 영상처리를 진행합니다. 

 

자바코드 파일 MainActivity.java를 다음 코드로 대체합니다.

첫번째 줄에 있는 package는 남겨둬야 합니다. 

 



import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

import android.content.DialogInterface;
import android.os.Bundle;
import android.annotation.TargetApi;
import android.content.pm.PackageManager;
import android.os.Build;
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;

import java.util.Collections;
import java.util.List;

import static android.Manifest.permission.CAMERA;


public class MainActivity extends AppCompatActivity
        implements CameraBridgeViewBase.CvCameraViewListener2 {

    private static final String TAG = "opencv";
    private Mat matInput;
    private Mat matResult;

    private CameraBridgeViewBase mOpenCvCameraView;



    static {
        System.loadLibrary("opencv_java4");
        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);

        mOpenCvCameraView = (CameraBridgeViewBase)findViewById(R.id.activity_surface_view);
        mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
        mOpenCvCameraView.setCvCameraViewListener(this);
        mOpenCvCameraView.setCameraIndex(0); // front-camera(1),  back-camera(0)
    }

    @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 = new Mat(matInput.rows(), matInput.cols(), matInput.type());

        Process.ConvertRGBtoGray(matInput.getNativeObjAddr(), matResult.getNativeObjAddr());

        return matResult;
    }


    protected List<? extends CameraBridgeViewBase> getCameraViewList() {
        return Collections.singletonList(mOpenCvCameraView);
    }


    //여기서부턴 퍼미션 관련 메소드
    private static final int CAMERA_PERMISSION_REQUEST_CODE = 200;


    protected void onCameraPermissionGranted() {
        List<? extends CameraBridgeViewBase> cameraViews = getCameraViewList();
        if (cameraViews == null) {
            return;
        }
        for (CameraBridgeViewBase cameraBridgeViewBase: cameraViews) {
            if (cameraBridgeViewBase != null) {
                cameraBridgeViewBase.setCameraPermissionGranted();
            }
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        boolean havePermission = true;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (checkSelfPermission(CAMERA) != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(new String[]{CAMERA}, CAMERA_PERMISSION_REQUEST_CODE);
                havePermission = false;
            }
        }
        if (havePermission) {
            onCameraPermissionGranted();
        }
    }

    @Override
    @TargetApi(Build.VERSION_CODES.M)
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        if (requestCode == CAMERA_PERMISSION_REQUEST_CODE && grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            onCameraPermissionGranted();
        }else{
            showDialogForPermission("앱을 실행하려면 퍼미션을 허가하셔야합니다.");
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }


    @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(new String[]{CAMERA}, CAMERA_PERMISSION_REQUEST_CODE);
            }
        });
        builder.setNegativeButton("아니오", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface arg0, int arg1) {
                finish();
            }
        });
        builder.create().show();
    }


}

 

패키지이름(com.tistory.webnautes.useopencvwithndk_build)에서 마우스 우클릭하여 메뉴에서 New > Java Class를 선택합니다.



Process를 입력하고 엔터를 누릅니다.

 



Process 클래스가 추가됩니다.

 



 Process 클래스 내용을 다음 내용으로 합니다.

첫번째 줄에 있는 package는 남겨둬야 합니다. 

 

public class Process {

    public static native void ConvertRGBtoGray(long matAddrInput, long matAddrResult);
}




3-6. Android Studio에서 javah.exe가 없어졌습니다. 정확히는 OpenJDK에서 빠진겁니다. 

대신 javac -h를 사용합니다. 

 

자바 코드에서 선언한 네이티브 메소드에 대응하는 C/C++ 함수가 선언되어 있는 헤더 파일을 jni 디렉토리에 자동으로 생성합니다.



프로젝트창을 Project로 변경합니다. 스크린샷에서 빨간색 사각형으로 표시한 부분을 Android에서 Project로 변경하면 됩니다. 

 




우선 javac를 실행하기 편하게 안드로이드 스튜디오의 외부 도구로 등록합니다. 메뉴에서 File > Settings를 선택합니다.

Settings 창의 왼쪽 목록에서 Tools > External Tools를 선택하고  + 아이콘을 클릭합니다.

 




아래처럼  각 항목의 내용을 채웁니다.

Advanced Options를 클릭해야 모든 항목이 보입니다.



◼ Name

javac

 

◼ Description

Android Tool - javac

 

◼ 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\javac.exe

 

Oracle의 JDK를 사용한다면 program에 다음처럼 입력합니다. 사용하는 jdk 버전에 따라 디렉토리 위치가 다를 수 있습니다. 

C:\Program Files\Java\jdk1.8.0_131\bin\javac.exe

 

◼ Arguments :

-h $ProjectFileDir$/app/src/main/jni  $FilePath$ -encoding utf-8

 

◼ Working directory:

$SourcepathEntry$



다 입력하고나서 OK를 클릭하여 설정을 저장합니다. 

 




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

 




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

 



문제 없이 진행되면 다음과 같은 메시지가  출력됩니다.

 

"C:\Program Files\Android\Android Studio\jre\bin\javac.exe" -h C:\Users\webnautes\AndroidStudioProjects\UseOpenCVwithndkbuild/app/src/main/jni C:\Users\webnautes\AndroidStudioProjects\UseOpenCVwithndkbuild\app\src\main\java\com\tistory\webnautes\useopencvwithndk_build\Process.java -encoding utf-8

Process finished with exit code 0




app / src / main 디렉토리 아래에 jni 디렉토리가 생성되고 그 안에 패키지 이름이 포함된 헤더 파일(여기엔선 com_tistory_webnautes_useopencvwithndk_build_Process.h)이 생성됩니다.

 

자바 코드의 패키지 이름(com.tistory.webnautes.useopencvwithndk_build)과  액티비티 이름(MainActivity)의 조합으로 헤더 파일의 이름이 결정됩니다. 

 





3-7. 자바에서 선언한 네이티브 메소드를 위한 C/C++ 구현을 cpp / 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_Process.h"

 

 

 

두번째 헤더파일을 열어보면  JAVA에서 선언한 네이티브 메소드에 대응하는 JNI 함수가 다음 처럼  정의되어 있습니다.

 

다음 코드를 참고하여 생성된 JNI 함수를 main.cpp 파일에 복사해오고(파란색 부분)

 추가로 필요한 코드를 추가합니다.(빨간색 부분)



#include <jni.h>
#include "com_tistory_webnautes_useopencvwithndk_build_Process.h"


#include <opencv2/opencv.hpp>

using namespace cv;


extern "C"{

    JNIEXPORT void JNICALL
  Java_com_tistory_webnautes_useopencvwithndk_1build_Process_ConvertRGBtoGray(
            JNIEnv *env,
            jclass instance,
            jlong matAddrInput,
            jlong matAddrResult){


        Mat &matInput = *(Mat *)matAddrInput;
        Mat &matResult = *(Mat *)matAddrResult;

        cvtColor(matInput, matResult, COLOR_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++ 소스코드 관련 정보를 입력합니다.

 

machine-woong님이 지적해주셨습니다. 진행하실때  OPENCVROOT가 현재 진행중인 프로젝트의 경로인지 확인하세요.

 

OpenCV 모듈이  프로젝트 폴더 내의 sdk 폴더에 존재합니다. 

 

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

#opencv library
OPENCVROOT:= C:\Users\webnautes\AndroidStudioProjects\UseOpenCVwithndkbuild\sdk
OPENCV_CAMERA_MODULES:=on
OPENCV_INSTALL_MODULES:=on
OPENCV_LIB_TYPE:=SHARED
include ${OPENCVROOT}\native\jni\OpenCV.mk


LOCAL_MODULE    := native-lib
LOCAL_SRC_FILES := main.cpp
LOCAL_LDLIBS += -llog

include $(BUILD_SHARED_LIBRARY)




jni 디렉토리를 선택한 후, 마우스 우클릭하여 메뉴에서 New > File을 선택합니다.

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

 




jni 에 Application.mk 파일이 생성됩니다.

 



 

Application.mk 파일에 다음 내용을 추가합니다. 컴파일시 사용되는 여러 변수들을 정의합니다. 

현재는 API 31을 사용하므로 APP_PLATFORM으로 android-31을 사용합니다.



APP_OPTIM := debug
APP_ABI := arm64-v8a armeabi-v7a x86 x86_64
APP_PLATFORM := android-31

APP_STL := c++_static
APP_CPPFLAGS := -frtti -fexceptions
NDK_TOOLCHAIN_VERSION := clang

APP_BUILD_SCRIPT := Android.mk




3-9. app 모듈의 build.gradle ndk-build를 사용하여 C/C++ 빌드가 이루어지기 위해 필요한 내용을 추가해줍니다. (노란색 줄)

 

 

    externalNativeBuild {

        ndkBuild {

            path 'src/main/jni/Android.mk'

        }

    }

 



3-10. Sync Now를 클릭하거나 메뉴에서 File > Sync Project with Gradle Files를 선택하여 Gradle build를 시작합니다.

 





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



포스트에서는 에뮬레이터를 사용하여 진행했습니다. 

스크린샷은 안드로이드 버전따라 차이가 있을 수 있습니다. 



앞에서 생성한 에뮬레이터가 선택된 상태에서 실행합니다.

 



While using the app을 선택합니다.

 



카메라 영상이 에뮬레이터 화면에 보여집니다. 

 



가로로 고정되도록 되어있기 때문에 안드로이드 폰을 가로로 두고 봐야 합니다.

전체화면으로 카메라 영상을 보기 위해선 가로로 영상을 봐야 했기 때문입니다. 

 

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

 

 

반응형

해보고 확인한 것을 문서화하여 기록합니다.


공부한 내용을 공유하는 공간입니다.
부족함이 있지만 도움이 되었으면 합니다.
잘못된 부분이나 개선점을 알려주시면 감사하겠습니다.



포스트 작성시에는 문제 없었지만 이후 문제가 생길 수 있습니다.
문제가 생기면 포스트와 바뀐 환경이 있나 먼저 확인해보세요.

질문을 남겨주면 가능한 빨리 답변드립니다.


제가 쓴 책도 한번 검토해보세요 ^^

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기

댓글을 달아 주세요

">
  1. 이전 댓글 더보기
  2. thumbnail
    mys
    2020.06.23 22:08

    not configured. Download it with SDK manager. Preferred NDK version is '20.0.5594570'. Log: C:\Users\사용자이름\AndroidStudioProjects\test\opencv\.cxx\ndk_locator_record.json
    import module에서 finish누르고 싱크과정에서 위와 같은 에러가 뜹니다 ㅠ 왜 이럴까요?
    SDK매니저에서 체크할건 다 체크했는데

    해결했습니다
    프로젝트스트럭쳐 들어가서
    ndk 폴더 위치를 잡아주니까 되네요
    ndk폴더위치가 자동으로 안잡혀있었네요

  3. thumbnail
    jyshin
    2020.07.24 16:27

    Gradle sync fail이 되네요.. 하기에 작성한 Android.mk가 없다고 하는데..

    Gradle project ndkBuild.path is C:\Users\j.y.shin\AndroidStudioProjects\UniTILE\app\src\main\jni\Android.mk but that file doesn't exist
    Affected Modules: app

    정작 작성한 Android.mk는 하기처럼 이상한 path에 있네요.
    C:\Users\j.y.shin\AndroidStudioProjects\UniTILE\sdk\libcxx_helper

    어떻게 하지요?

  4. thumbnail
    jyshin
    2020.07.27 11:01

    차근 차근 따라해 보니.. 빌드 성공했습니다.

    빌드 성공하면, .so파일이 생성되는거아닌가요? 어디에 .so가 있는지요? 그리고 .apk파일도 위치 알고 싶습니다.

  5. thumbnail
    fpfpthd
    2020.07.29 18:50

    혹시 현재도 질문 받으실진 모르겟지만 ㅠ
    External Tools에 javah 를 하면
    Exception in thread "main" java.lang.IllegalArgumentException: Not a valid class name:
    이런 오류가 나고 구글링 계속 해보고는 있는대 해결이 안나서요 ㅠㅠ
    알려주신 방법으로 내용을 다 채웟고 그중에
    어규먼트 부분에
    -v -jni -d $ModuleFileDir$/src/main/jni $FileClass$ -> $ModuleFileDir$ 이부분만 저한테 맞게 변경하였습니다.
    경로와 다른부분도 계속 바꿔가면서 해보는대 잘안되서 글남겨봅니다 ㅠㅠ

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2020.07.29 18:53 신고

      환경변수로 지정한 부분을 그대로 사용해봤나요?

    • thumbnail
      BlogIcon fpfpthd
      2020.07.29 18:56

      게시글 그대로 어규먼트 말씀이신가욤?? 넵 그대로도 해봤습니당 ㅠ

    • thumbnail
      BlogIcon fpfpthd
      2020.07.29 18:58

      혹시 환경변수로 지정한 부분 어떤부분을 말씀 하신건지 이해가 잘안가는대 예시 혹시 하나만 들어주실수있으신가요 ㅠㅠ

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2020.07.29 19:02 신고

      딸러 기호로 둘러싸인 이름들을 의미합니다.

    • thumbnail
      BlogIcon fpfpthd
      2020.07.29 19:09

      넵 ㅜ 그대로도 해보고 절대경로도 해봣지만 안되고잇네용 ㅠㅠ

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2020.07.29 19:14 신고

      안드로이드 스튜디오 버전이 어떻게 되나요?

    • thumbnail
      BlogIcon fpfpthd
      2020.07.29 19:16

      안드로이드 스튜디오 4.0 입니다!

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2020.07.29 23:47 신고

      혹시나해서 포스트대로 다시 해보니 잘됩니다. $로 둘러쌓인 문자열이 실제 실행시 변환된 값들을 참부하니 참고하여 다시 진행해보세요.

      실행결과를 비교해보세요

      "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]]

    • thumbnail
      BlogIcon fpfpthd
      2020.07.31 12:22

      감사합니다!! 해보겠습니다!

  6. thumbnail
    이문규
    2020.08.06 10:06

    안녕하세요. 예제 잘 따라해보았습니다.

    스마트폰에 탑재해서 gray로 잘 실행되는 것 까지 확인하였는데요.

    다시 안드로이드 스튜디오를 실행시켜보았더니, 소스코드창이 하나도 보이지 않고,

    High number of internal exceptions has been detected. This indicates a serious problem with the IDE. Please consider clean reinstall of Android Studio. If the problem persists, please report a bug by following the link below.

    라고 우측하단에 뜨네요.

    혹시 재설치 그냥 하면 되는걸까요?
    재설치는 완전 삭제로 해야될까요?

  7. thumbnail
    Favicon of https://qufdl4134.tistory.com BlogIcon 쿠루루루루앙

    javac -h (jni 폴더위치) (path/mainactivty.java) 이렇게 external tool로 작업하니까 mainactivity의 모든 import문에서 package xxxx does not exist랑 이외코드에서 symbol 에러가 생기는데 혹시 무엇때문인지 알수있을까요?
    그리고 헤더파일을 만드는 이유가 궁금합니다. 템플릿을 native c++로 작성하고 native-lib.cpp에 mainactivty와 연결할 함수 선언하면 차이가 없는데 별도로 선언하는 이유가 있을까요?

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2020.11.18 20:55 신고

      cmake 방식이 아닌 ndk-build 방식을 사용하려면 수동으로 헤더파일을 생성해줘야 합니다.

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2020.11.18 20:56 신고

      직접 명령어를 실행했나요?

    • thumbnail
      Favicon of https://qufdl4134.tistory.com BlogIcon 쿠루루루루앙
      2020.11.19 13:45 신고

      external tool 설정에서 올려주신 Arguments 대신에 javac -h (jni 폴더위치) (mainactivity.java)로 대신하여 실행하였습니다. 안드로이드 스튜디오상 터미널로도 동일하게 위 명령어로 실행했을때도 동일한 오류가 뜨네요. 처음엔 한글을 인식못해서 에러가 났고 한글을 다 지우고 다시 실행하니 mainactivy의 모든 import문에서 package xxxxx does not exist에러와 이외 코드에선 symbol 에러가 발생합니다..

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2020.11.19 14:11 신고

      터미널에서 직접 명령어로는 해보지 않아서 확답이 어렵습니다. 포스트 대로 해보셨나요?

  8. thumbnail
    Favicon of https://nayeon-zip.tistory.com BlogIcon nalong

    안녕하세요. 계속 똑같은 오류가 떠서 질문드립니다. ㅠㅠㅠ import module 단계를 하고나면, xml 에서 design editor is unavailable until a successful build 이런 오류가 뜹니다.. 처음에는 버전이 틀려서 생기는 오류인가 싶어서 4.4.0으로 다시 다운받았지만 똑같네요ㅠㅠ 구글링하여서 나오는 해결방법을 해봐도 똑같습니다...ㅠㅠㅠ 답답한 마음에 글남깁니다! 자료 항상 감사드립니다!

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2021.04.03 07:05 신고

      File > Sync Project with Gradles Files 와File > Invalidate Caches / Restart를 해보세요. 작업이 완료될때까지 대기했다 진행해야 합니다

    • thumbnail
      Favicon of https://nayeon-zip.tistory.com BlogIcon nalong
      2021.04.08 11:04 신고

      위에 말씀 해주신 방법을 해보았는데,
      똑같습니다 ㅠㅠㅠ
      OpenCV-android-sdk 디렉토리 하위에 있는 sdk 디렉토리를 선택하고 OK 버튼을 클릭합니다. 이 과정을 하고나면, 갑자기 저런 현상이 발생합니다.

      .프로젝트를 새로만들어도 안되는데, 다시 안드로이드를 설치해야하는것인가요?
      (안드로이드를 설치하고 초창기에 opencv를 하였을때는 문제없이 잘 되었는데, 갑자기 이런 오류가 생겨났습니다..).

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2021.04.08 11:06 신고

      OpenCV와 안드로이드 OS 버전을 포스트와 일치시켜서 해보세요.

    • thumbnail
      Favicon of https://nayeon-zip.tistory.com BlogIcon nalong
      2021.04.08 18:13 신고

      게시물에 게시된 것으로 해도 되지않네요 ㅠㅠㅠㅠ 저번 프로젝트에서는 올려주신 자료 그대로 만들어서 실행되었는데, 갑자기 되지않아 당황스럽네요ㅠㅠ다시 재설치만이 방법일까요? (항상 친절히 답변해주셔서 감사합니다.)

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2021.04.08 18:24 신고

      먼가 다른 문제인듯합니다. 에러메시지를 확인하다보면 원인을 찾을수 있습니다.

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2021.04.08 21:59 신고

      최신 버전의 안드로이드 스튜디오와 OpenCV를 사용하여 진행해봤는데 거의 수정없이 가능하네요..

      혹 안드로이드 스튜디오에서 처리중인데 진행하셔서 문제가 발생한 게 아닌가 싶습니다.

  9. thumbnail
    Favicon of https://rhdqnwnddlqslek.tistory.com BlogIcon 공부중1234

    제가 안드로이드 3.6버전 쓰고 있고 externINativeBuild{ndkBuild{path 'src/main/jni/Android.mk'}} 를 build.grable(App) 쪽에서 defaultConfig 뒤쪽에 넣어줬는데 그거 말고는 똑같이 주었습니다. 그런데 알 수 없는 오류라며 앱이 자꾸 팅기더군요.

    설치는 되긴하는데...3.6버전이라 안되는건가요? 해결방법을 없을까요? ㅠㅠ

    오류는 ERROR: executing external native build for ndkBuild C:\Users\User\AndroidStudioProjects\opencvex\app\src\main\jni\Android.mk
    Affected Modules: app

    이렇게 떴습니다.!

  10. thumbnail
    Favicon of https://rhdqnwnddlqslek.tistory.com BlogIcon 공부중1234

    아 ㅠㅠ그래서 이틀동안 밤새면서 했는데 버전 문제였군요 ㅠㅠ 감사합니다

  11. thumbnail
    Favicon of https://rhdqnwnddlqslek.tistory.com BlogIcon 공부중1234

    최신버전으로 업데이트 했는데 javah.exe 파일이 없어졌네요... javac.exe 로 대체하면 오류나고....4.2버전은 javah.exe 가 없나요??

  12. thumbnail
    Favicon of https://rhdqnwnddlqslek.tistory.com BlogIcon 공부중1234

    다시 지우고 다시 설치하니 javah 파일이 생겼네요... 3.6 에서 4.2 업그레이드 중에 오류가 있었나봅니다..

  13. thumbnail
    Favicon of https://otakuking.tistory.com BlogIcon 오덕왕

    이 코드로 잘 구현은 했는데 후면, 전면, 광각 카메라로 전환하는 기능을 넣고 싶어서 해봤더니 전환이 안 되네요. setCameraIndex에서 설정하면 처음 설정한 값으로만 유지되고 버튼 이벤트가 분명 작동하는데 disable로 화면을 한 번 껐다 켜도 바뀐 인덱스로 적용이 되지 않습니다. 다른 예제들을 보면 mainactivity 자체를 껐다 켜야되는 것 같은데 이건 무리고 다른 방법이 있을까요?

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2021.07.22 12:49 신고

      현재 사용중인 OpenCV의 안드로이드용 카메라 API가 아닌 안드로이드 SDK에 포함된 안드로이드용 카메라 API를 사용해야 할거 같습니다

    • thumbnail
      Favicon of https://otakuking.tistory.com BlogIcon 오덕왕
      2021.07.22 12:52 신고

      아 역시 내장 그거 말고는 방법이 없나보군요 ㅠ opencv 카메라로 규모가 좀 큰 프로그램을 만들어놔서 다 뜯어고쳐야 하는게 슬프네요....허헛.. 빠른 답변 감사합니다!

  14. thumbnail
    Favicon of https://meqos.tistory.com BlogIcon lookso

    이글 하고 231번 글보고 그대로 따라해봤는데
    mainactivity에서 동일하게 에러(총4부분)가 떠서요.
    물론 코드는 패키지만 다르고 다 같게 했고요.

    1) private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
    -> this 부분 required & provided Type
    context, mainactivity

    2) mOpenCvCameraView = (CameraBridgeViewBase)findViewById(R.id.activity_surface_view);
    -> inconvertible types; cannot cast'android.view.View' to 'org.opencv.android.CameraBridgeViewBase'

    3) mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
    -> setVisibility Cannot resolve method 'setVisibility' in 'CameraBridgeViewBase'

    4) OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_2_0, this, mLoaderCallback);
    -> 'this' Required type: Context/ Provided:MainActivity

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2021.10.22 17:56 신고

      안드로이드 SDK버전을 포스트와 동일하게 맞춰보세요

    • thumbnail
      Favicon of https://meqos.tistory.com BlogIcon lookso
      2021.10.22 19:04 신고

      android studio는 버전이 별로 차이가 안나 신경 안썼는데 거기서 비롯될 수도 있군요 감사합니다. 한번 해볼게요^^

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2021.10.22 21:31 신고

      우선 CMake 방식에서 문제없음을 확인했습니다. 포스트를 업데이트해두었으니 확인해보세요

    • thumbnail
      Favicon of https://meqos.tistory.com BlogIcon lookso
      2021.10.27 13:31 신고

      에러 있는 상태에서 실행시키니 되네요
      이런 경우는 첨이네요.
      감사합니다

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2021.10.27 21:18 신고

      경고가 아니라 에러라면 문제가 생길텐데 이상하군요..

      실행되서 다행입니다.

  15. thumbnail
    Favicon of https://dalekbeam.tistory.com BlogIcon revonoa

    소중한 포스팅 감사합니다.
    make: *** No rule to make target 'C:\Users\webnautes\AndroidStudioProjects\UseOpenCVwithndkbuild\sdk\native\jni\OpenCV.mk'. Stop.
    오류가 뜨는데 무슨 경로가 잘못된걸까요?

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2021.10.24 12:57 신고

      webnautes 부분은 사용하신 사용자 이름으로 바꿔야 합니다

      빌드전 설정파일에서 프로젝트 경로를 변경하세요. 포스트에 언급이 되어있습니다

  16. thumbnail
    Favicon of https://alquimista.tistory.com BlogIcon Alquimista

    좋은 내용 진심으로 감사드립니다. 대단히 큰 도움이 되었습니다. 한가지 여쭙고 싶은게, 왜 BGRA가 아니라 RGBA 포맷을 선택하셨는지 궁금합니다. GPU에서는 BGRA 포맷이 native인지라 혹시 실수하셨나 해서 로그 찍어보니 RGBA가 맞더군요. OpenCV 내부에서 어떤 식으로 RGB 포맷이 생성되는지 모르지만 혹시 BGRA 포맷도 선택이 가능한가요?

  17. thumbnail
    Favicon of https://levcod.tistory.com BlogIcon levi__

    안녕하세요 적으신 글대로 천천히 작성해보았습니다. 3-9이전까지는 오류가 생기지 않았었는데 끝까지 다하고 나니 executing external native build for ndkBuild C:\Users\Users\AndroidStudioProject\ORD\app\src\main\jni\Android.mk
    Affected Modules:app이라는 오류가 생겼습니다. 몇주간 고민해봐도 해결이 안됩니다 ㅜ

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2022.03.05 06:10 신고

      다음 링크를 참고해보세요

      https://github-wiki-see.page/m/LGLTeam/Android-Studio-Solutions/wiki/ERROR.-Executing-external-native-build-for-ndkBuild-Android.mk


      ndk위치를 인식못해서라고 하는데 이 방법으로 해결 못할 가능성이 있습니다.

  18. thumbnail
    Favicon of https://che0802.tistory.com BlogIcon 못쨍

    main.cpp 단계에서 cvtColor(matInput,matResult,COLOR_RGB2GRAY); 에서 오류가 뜹니다
    어떻게 해야할까요? 아니면 이 함수를 안써도 될까요>?

    이 함수를 안쓰고 실행해보았더니 앱이 들어가지는데 바로 앱 오류가 떠서 다시 나와지네요 어떻게 하면 좋을까요?

  19. thumbnail
    Favicon of https://ksm1.tistory.com BlogIcon KSme

    예제 따라해서 카메라까지 성공적으로 띄웠습니다.

    추가적으로 카메라 화면에 간단한 도형이나 이미지를 오버레이 시키려면 어떻게 해야하는지 궁금합니다 ㅠㅠ

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2022.05.27 15:40 신고

      다음 영상에서 사용한 코드를 참고하세요.

      아마 코드가 python이고 KSme님은 자바 또는 c++ 코드에서 진행해야 하지만 opencv api 사용법은 거의 비슷합니다.

  20. thumbnail
    Favicon of https://yes8438.tistory.com BlogIcon IOZI

    화면은 그래도 유지하면서 세로로 변경하고 싶은데 manifest에서 landscape를 portrait로 바꾸면 화면이 같이 돌아가네요 방법이 없을까요?

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2022.09.05 13:18 신고

      OpenCV의 카메라 인터페이스가 세로에선 전체화면을 지원안해서 가로로 돌렸습니다. 안드로이드의 카메라 인터페이스를 사용하면 가능할 듯합니다. 아래 링크를 참고하세요. 예전 포스트라 현재 안드로이드 os의 퍼미션때문에 바꿔야 할 수 있습니다.

      https://webnautes.tistory.com/822

      https://webnautes.tistory.com/822

  21. thumbnail
    Favicon of https://pardess.tistory.com BlogIcon Pardess

    안녕하세요! 좋은 포스팅 정말 감사드립니다!
    혹시 CMake 방식이랑 사용하지 않는 방식이랑은 어떤 차이가 있나요? 그리고 CMake로 코딩한 영상에서는 핸드폰 카메라로 작동하는데, 혹시 카메라가 달려있는 다른 기기와 Udp 통신을 통해 다른 기기의 카메라가 작동하도록 하려면 어떻게 해야할까요?

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2022.09.24 07:44 신고

      CMake 방식이 정식으로 지원하는 방식이고 ndk-build는 예전에 사용하던 방식입니다.

      소켓 통신하여 영상을 화면에 보여주는 앱을 작성하면 될거 같습니다.