지난번에 Android에서 OpenCV를 사용하기 위한 SDK 추가작업을 진행 했었는데 실제로 사용법을 익혀 보도록 하겠습니다.

Canny 함수를 이용해서 외각선 검출을 진행하게 되며 결과는 아래와 같습니다.

 

 

대부분 이미지 작업을 하게되기 때문에 OpenCV에서 카메라 제어를 할 수 있도록 작업을 하면 나머지는 간단하게 함수 호출만 해주면 되는데 카메라에서 이미지를 받아오는 설정 부분이 버전마다 조금씩 달라서 Canny() 프로그램 함수는 한줄이면 되는데 환경 설정하는 부분에 시간이 많이 소요 될 가능성도 있습니다.

 

먼저 카메라를 사용 할 수 있도록 권한을 추가합니다.

 

AndroidManifest.xml 

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

  

 

Activity_main.xml은 내용을 전부 삭제하고 OpenCV 카메라 VIew를 추가해 줍니다. 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
   
xmlns:android="http://schemas.android.com/apk/res/android"
   
xmlns:opencv="http://schemas.android.com/apk/res-auto"
   
xmlns:tools="http://schemas.android.com/tools"
   
android:layout_width="match_parent"
   
android:layout_height="match_parent"
   
android:orientation="vertical"
   
tools:context=".MainActivity">

        <
org.opencv.android.JavaCameraView
           
android:layout_width="match_parent"
           
android:layout_height="match_parent"
           
android:visibility="gone"
           
android:id="@+id/frame_Surface"
           
opencv:show_fps="true"
           
opencv:camera_id="any">

        </
org.opencv.android.JavaCameraView>
</
LinearLayout>

 

 

 

MainActivity.java에 카메라 제어 관련 소스를 추가해 줍니다.

 

Global 변수를 만들어주고 카메라로 부터 Callback을 받아 처리하면 되는 간단한 내용인데 여기서 버전마다 조금씩 다르게 작동을 해서 몇가지 추가적인 작업이 필요할 수도 있습니다.

 

먼저 변수와 callback을 선언 합니다.

private Mat mRgba;
private CameraBridgeViewBase mOpenCvCameraView;
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
   
@Override
   
public void onManagerConnected(int status) {
       
switch (status) {
           
case LoaderCallbackInterface.SUCCESS: {
                Log.d(
Tag_Log, "OpenCv loaded");
               
mOpenCvCameraView.enableView();
            }
           
break;
           
default:{
               
super.onManagerConnected(status);
            }
           
break;
        }
    }
};

 

 

onCreate()에는 화면구성시 idframe_Surface로 추가한 JavaCameraView를 설정 합니다.

 

mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.frame_Surface);
mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
mOpenCvCameraView.setCvCameraViewListener(this);
mOpenCvCameraView.setCameraIndex(0);

  

ActivityCvCameraViewListener2를 상속해주고 이와 관련된 OpenCV 카메라 관련 메소드 3개를 Override로 추가합니다.

 

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


    }

   
@Override
   
public void onCameraViewStopped() {
    }

   
@Override
   
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
       
mRgba = inputFrame.rgba();

       
return mRgba;
    }

 

 

그리고 onCameraFrame()에서 받아온 이미지를 리턴 하면 핸드폰 화면에 이미지가 나오는데 그렇지 않은 경우 아래와 같은 기능을 추가해 줍니다.

 

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

 

 

최신 SDK중 이렇게 getCameraViewList()Override 할 수 있도록 되는 SDK 버전이 있습니다.

 

만약 getCameraViewList()Override로 지원 안되는 SDK 버전이라면

 

Permission에서 함수를 호출 하도록 해야 합니다.

 

@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{
           
       
}
       
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

 

  

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

 

 

여기까지 왔으면

 

 

 

 

 

이런식으로 화면이 나와야 합니다.

 

 

그럼 Canny 함수 하나를 추가해서 테스트를 진행해 봅니다.

 

@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
   
mRgba = inputFrame.rgba();

   
if(mRgba == null) {
        Log.d(
Tag_Log, "onCameraFrame error");

    }
   
Mat edges = new Mat();
    Imgproc.Canny(mRgba, edges, 80,200);


   
return edges;
}

 

  

카메라로 가져온 이미지를 OpenCV Canny()함수에 추가해서 작업하는 방법입니다.

python으로도 간단하게 함수만 호출하면 테스트 할 수 있는것과 같이 android java에서도 함수만 호출하면 됩니다.

 

 

이미지가 90도 회전되어 보이는 부분은 AndroidManifest.xml에 설정을 추가하면 되고

MainActivity.java에는 onStart(), onDestroy() 등을 추가해줍니다.

그러면 처음에 보았던 이미지를 확인할 수 있습니다.

 

AndroidManifest.xml 전체 소스 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   
package="copycoding.tistory.opencvlib">

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

    <
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.OpencvLib">
        <
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>

 

 

 

MainActivity.java 전체 소스입니다. 

package copycoding.tistory.opencvlib;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.annotation.TargetApi;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceView;
import android.view.Window;
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;
import org.opencv.imgproc.Imgproc;

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

import static android.Manifest.permission.CAMERA;

public class MainActivity extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener2 {

   
private static String Tag_Log = "OpenCV Test : ---------------------";
   
private static final int CAMERA_PERMISSION_REQUEST_CODE = 200;

   
private Mat mRgba;
   
private Mat mGrey;
   
private CameraBridgeViewBase mOpenCvCameraView;
   
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
       
@Override
       
public void onManagerConnected(int status) {
           
switch (status) {
               
case LoaderCallbackInterface.SUCCESS: {
                    Log.d(
Tag_Log, "OpenCv loaded");
                   
mOpenCvCameraView.enableView();
                }
               
break;
               
default:{
                   
super.onManagerConnected(status);
                }
                
break;
            }
        }
    };


   
@Override
   
protected void onCreate(Bundle savedInstanceState) {
       
super.onCreate(savedInstanceState);

        requestWindowFeature(Window.
FEATURE_NO_TITLE);
        getSupportActionBar().hide();

       
setContentView(R.layout.activity_main);

       
mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.frame_Surface);
       
mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
       
mOpenCvCameraView.setCvCameraViewListener(this);
       
mOpenCvCameraView.setCameraIndex(0);


   
}

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

   
}

   
@Override
   
public void onCameraViewStopped() {
    }

   
@Override
   
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
       
mRgba = inputFrame.rgba();
        if(mRgba == null) {
            Log.d(
Tag_Log, "onCameraFrame error");

        }
        Mat edges = new Mat();
        Imgproc.Canny(mRgba, edges, 80,200);


        
return edges;
    }

   
@Override
   
protected void onResume() {
       
super.onResume();
       
if(OpenCVLoader.initDebug()) {
            Log.d(
Tag_Log, "Opencv initialized");
           
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
else {
            Log.d(
Tag_Log, "Opencv not loaded");
            OpenCVLoader.initAsync(OpenCVLoader.
OPENCV_VERSION, this, mLoaderCallback);
        }
    }

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

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

   
@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();
        }
    }

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

   
@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{
           
       
}
       
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }


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

}

 

 

  

이전에 올렸던 OpenCV SDK 설치관련 글을 읽으러 가지 않으려면 아래 gradle을 참고 하세요.

 

plugins {
   
id
'com.android.application'
}

android {
   
compileSdkVersion
29
   
buildToolsVersion "29.0.0"

   
defaultConfig {
       
applicationId
"copycoding.tistory.opencvlib"
       
minSdkVersion 26
       
targetSdkVersion 29
       
versionCode 1
       
versionName "1.0"

       
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
   
}

   
buildTypes {
       
release {
           
minifyEnabled
false
           
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
       
}
    }
   
compileOptions {
       
sourceCompatibility JavaVersion.
VERSION_1_8
       
targetCompatibility JavaVersion.VERSION_1_8
   
}
}

dependencies {

   
implementation
'androidx.appcompat:appcompat:1.3.1'
   
implementation 'com.google.android.material:material:1.4.0'
   
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
   
implementation project(path: ':OpenCV')
    testImplementation
'junit:junit:4.+'
   
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
   
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

 

 

 - copy coding -


+ Recent posts