안드로이드 OpenCV CascadeClassifier Empty 이슈

안녕하세요. 개발자 드리머즈입니다.


안드로이드 스튜디오로 OpenCV(https://opencv.org/)를 공부 중 발생한 이슈 정리합니다.


문제 현상

얼굴 & 눈동자 인식 테스트를 위해

http://romanhosek.cz/android-eye-detection-updated-for-opencv-2-4-6/

의 코드를 따라해보고 있습니다.


이 코드에서 초기화 부분에 CascadeClassifier를 생성하는 부분이 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mJavaDetector = new CascadeClassifier(mCascadeFile.getAbsolutePath());
if (mJavaDetector.empty()) {
    Log.e(TAG, "Failed to load cascade classifier1");
    mJavaDetector = null;
else
    Log.i(TAG, "Loaded cascade classifier from "
            + mCascadeFile.getAbsolutePath());
 
mJavaDetectorEye = new CascadeClassifier(cascadeFileER.getAbsolutePath());
if (mJavaDetectorEye.empty()) {
    Log.e(TAG, "Failed to load cascade classifier2");
    mJavaDetectorEye = null;
else
    Log.i(TAG, "Loaded cascade classifier from "
            + cascadeFileER.getAbsolutePath());
cs

new CascadeClassifier()의 인자로 파일이름이 String으로 들어갑니다.

위의 예제에서 사용된 인자의 경로(파일)은 모두 존재함에도 mJavaDetector.empty()mJacaDetectorEye.empty()true를 반환했습니다.

이 파일을 읽지 못하면 이미지 인식을 못하는 치명적인 문제가 발생합니다.


문제 원인

OpenCV Java API version OpenCV Native API version 서로 달라서 그렇습니다.

저는 OpenCV 공식홈에서 현재(2018-04-6) 최신버전인 opencv-3.4.1-android-sdk.zip를 다운받아 사용하고 있었습니다.

이 압축을 풀었을 때 OpenCV-android-sdk\sdk\java의 코드를 Android Studio에서 OpenCV java module로 import했습니다.

그렇기에 제가 사용한 OpenCV java API version은 3.4.1입니다.


이와달리 구글 플레이 스토어에서 다운 받은 OpenCV native API version은 3.0.0입니다.

두 버전이 일치해야 한다고 합니다.





해결 방법

OpenCV-android-sdk\apk 경로에 있는 OpenCV Java API 버전과 동일한 버전의 OpenCV manager를 설치하면 됩니다.


여러 플랫폼 버전에 대한 apk가 있습니다. 자신이 테스트하는 단말의 플랫폼에 맞는 버전을 설치하면 됩니다.

OpenCV 공식홈(https://docs.opencv.org/3.4.1/da/d2a/tutorial_O4A_SDK.html)의 아래 내용을 참고하세요.


Note

armeabi, armv7a-neon, arm7a-neon-android8, mips and x86 stand for platform targets:

- armeabi is for ARM v5 and ARM v6 architectures with Android API 8+,

- armv7a-neon is for NEON-optimized ARM v7 with Android API 9+,

- arm7a-neon-android8 is for NEON-optimized ARM v7 with Android API 8,

- mips is for MIPS architecture with Android API 9+,

- x86 is for Intel x86 CPUs with Android API 9+.

If using hardware device for testing/debugging, run the following command to learn its CPU architecture: 

adb shell getprop ro.product.cpu.abi
cs

If you're using an AVD emulator, go Window > AVD Manager to see the list of available devices. Click Edit in the context menu of the selected device. In the window, which then pop-ups, find the CPU field.

You may also see section Manager Selection for details.



OpenCV Manager(native API) 3.4.1이 설치됐습니다.

OpenCV Java API도 3.4.1버전으로 이제는 동일한 버전이 설치됐습니다.


동일한 버전 설치 후 CascaseClassifier가 정상적으로 동작함을 확인했습니다~!!


참고로 구글 플레이 스토어에 있는 OpenCV Manager는 문제가 많으니 더이상 사용하지 말라고 합니다.

개발할 때만 공식홈에서 다운받은 sdk내부에 있는 OpenCV Manager를 사용하고,

출시용 apk에는 static initialization을 사용해서 native 코드가 apk내부에 포함되게 하라고 합니다.




해결법은.. 좀 이상하지만 간단합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mJavaDetector = new CascadeClassifier(mCascadeFile.getAbsolutePath());
mJavaDetector.load(mCascadeFile.getAbsolutePath());
if (mJavaDetector.empty()) {
    Log.e(TAG, "Failed to load cascade classifier1");
    mJavaDetector = null;
else
    Log.i(TAG, "Loaded cascade classifier from "
            + mCascadeFile.getAbsolutePath());
 
mJavaDetectorEye = new CascadeClassifier(cascadeFileER.getAbsolutePath());
mJavaDetectorEye.load(cascadeFileER.getAbsolutePath());
if (mJavaDetectorEye.empty()) {
    Log.e(TAG, "Failed to load cascade classifier2");
    mJavaDetectorEye = null;
else
    Log.i(TAG, "Loaded cascade classifier from "
            + cascadeFileER.getAbsolutePath());
cs

위 코드의 하늘색 음영 코드를 추가하면 됩니다.



주의

아래의 코드처럼 CascaseClassifier() 생성자의 인자로 아무것도 주지 않으면 안됩니다.

아래 코드처럼 하면 empty()함수가 여전히 true를 반환합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mJavaDetector = new CascadeClassifier();
mJavaDetector.load(mCascadeFile.getAbsolutePath());
if (mJavaDetector.empty()) {
    Log.e(TAG, "Failed to load cascade classifier1");
    mJavaDetector = null;
else
    Log.i(TAG, "Loaded cascade classifier from "
            + mCascadeFile.getAbsolutePath());
 
mJavaDetectorEye = new CascadeClassifier();
mJavaDetectorEye.load(cascadeFileER.getAbsolutePath());
if (mJavaDetectorEye.empty()) {
    Log.e(TAG, "Failed to load cascade classifier2");
    mJavaDetectorEye = null;
else
    Log.i(TAG, "Loaded cascade classifier from "
            + cascadeFileER.getAbsolutePath());
cs



참고


작성자

Posted by 드리머즈

관련 글

댓글 영역