2010년 5월 28일 금요일

안드로이드에서 JNI(Java Native Interface)를 통한 C/C++ 라이브러리와 연동하기

출처 : http://www.itwizard.ro/interfacing-cc-libraries-via-jni-example-tesseract-163.html

JNI(Java Native Interface)를 통한 C/C++ 라이브러리와 연동하기, 예제: tesseract
April 26th, 2009

(Android phone – how-to/example)

여러분이 Android Java 코드에 C/C++ 라이브러리를 사용하고자 한다면,

Android 1.5NDK, Release 1에 대해서 약간 알아 두어야 할 것들이 있다.

먼저 Android SDK와 NDK 둘다 설치해야만 한다.

이 부분에 대해서 여러분은 공식적인 android-developer website에서 지시하는 설치과정을 따라하면 된다.

NDK는 기본적인 C 라이브러리만을 가지고 있다는 것을 염두해 두자:

- standard C library: stdlib, stdio etc
- math library
- C++ library: cstddef, new, utility, stl_pair.h
- log library
- zlib compression library

이와 같이 어떤 c/c++ 코드를 Android로 포팅하는 것은 그리 쉬운일이 아니다.

NDK를 설치하면, 설치된 디렉토리에 두 개의 데모가 있을 것이다:

hello-jni와 two-libs. 다음 과정으로 넘어가기전에 누구든 이 두개의 기초적인 데모를 먼저 이해해야만 한다.

/docs 디렉토리에 있는 참고문서도 읽어야만 한다.

특히나 ANDROID-MK.TXT, APPLICATION-MK.TXT 그리고 OVERVIEW.TXT.

여러분 자신의 코드를 만들기 위해서 다음의 과정을 거쳐야한다.

1. 여러분의 c/c++소스코드를 sources/…디렉토리밑에 두어야 한다.
2. NDK 빌드 시스템에게 여러분의 소스코드에 대한 정보를 알려주기 위해서 sources/Android.mk를 작성하자. Android.mk는 개발자들을 위해서 Makefile 형식과 유사하다. 하지만 이것은 한 부분일 뿐이고 모든 사용가능한 flags에 대해선 docs/ANDROID-MK.TXT를 살펴보자.
3. 여러분의 application와 그에 필요한 소스코드들에 대한 정보를 NDK 빌드 시스템에 알려주기 위해서 apps/Application.mk를 작성하자. Application.mk는 여러분의 application이 필요로하는 (static 혹은 shared 라이브러리) native ‘modules’에 대해 알려주는 용도이다. 일반적인 Application.mk 파일은 다음과 같은 내용을 가지고 있다. APP_MODULES := APP_PROJECT_PATH := 첫 번째 줄은 java 소스파일을 어디서 찾아야하는지 알려주고, 두 번째 줄은 application을 실행하는데 필요한 라이브러리(들)이 무엇인지 알려준다. 다른 사용가능한 flags에 대해서는 docs/APPLICATION-MK.TXT를 보라.
4. 가장 상위 NDK 디렉토리에서 “make APP=”를 실행시켜서 여러분의 native 코드를 빌드하자. 여기서 ‘make’는 GNU make를 지칭하고, ‘$NDK/apps/’의 서브디렉토리중 하나의 이름이 된다.

빌드과정에 대한 정보를 보려면 “V=1”을 사용하고, 여러분의 모든 소스코드를 강제적으로다시 빌드하기 위해선 “-B”를 사용하자.

참고사항: (알아두면 좋은 유용한 트릭) NDK의 권고사항에 따라서 여러분의 Java 파일과 C 파일들이 서로 분리되어 관리되기를 원치 않는다면, 여러분의 디렉토리의 symbolic link를 만들도록 “ln” 명령어를 사용할 수 있다. 형식: ln –s

그리고 여기서 예제로 Google’s의 문자인식 프로젝트 Tesseract (http://en.wikipedia.org/wiki/Tesseract_%28software%29)에 대해 고려해보자. 여러분은 http://android.git.kernel.org/?p=platform/external/tesseract.git;a=summary 에서 소스코드를 다운로드할 수 있다. 그럼 여러분은 위에서 이야기한 과정데로 코드를 컴파일해야만 한다. 개발자들이 이미 Android.mk를 포함시켰다. 하지만 NDK로 컴파일하기 위해서 여러분이 약간 수정해야하는 부분들이 있다. Android.mk의 수정된 버전은 여기(http://code.google.com/p/mezzofanti/source/checkout)에 있다.

또한 Android의 프로세서로 Tesseract를 실행시키는 것은 약간의 시간이 걸릴지도 모른다. 그래서 각 단계에서 OCR 프로세스를 분리시키는 것이 훨씬 낳다. 그리고 좀더 속도가 필요하다면 각 단계를 더 실행하도록 하자. Tesseract는 이미 이러한 작업을 하는 방법들을 제공한다. 불행하게도 “core”에서 만 제공하지, JNI 에서는 지원하지 않는다. OCR step-by-step을 가능하게 하기 위해서 우리의 code repository (http://code.google.com/p/mezzofanti/source/checkout) 에서 jni.cpp를 고려해보자. 작업할 jni.c에 대해선 여러분은 baseapi.cpp파일을 업데이트할 필요가 있을 것이다.

Tesseract의 수정에 대해서 이야기한 모든것들은 step 4에서 봤듯이 여러분의 navtive 코드를 빌드하는데 필요할 것이다. 그리고 나면 여러분은 java 코드에 이것을 취합해야 한다.

Tesseract의 jave-wrapper는 OCR.java의 형태로 encapsulate되어 있다. 모든 native method declarations은 class의 마지막에 있다.

// general initialization/cleanup/setup functions
public native void classInitNative(); // init the lib (1st to be called at startup)
public native void initializeNativeDataNative(); // init allocate lib buffers (2nd to be called)
public native boolean openNative(String sLanguage); // init the api with a language
public native void cleanupNativeDataNative(); // delete the lib buffers
public native void clearResultsNative(); // api clear
public native void closeNative(); // api.end, called by the destructor automatically
public native void setVariableNative( // set a lib variable
String var, String value);
// language functions
public static native String[] getLanguagesNative(); // get the language list
public native int getShardsNative( // get the shard of the language
String lang);
public native boolean isValidWord(String word); // is the word valid according to the installed language
// aux functions before OCR
public native void setImageNative( // copy the image to the internal api buffers
byte[] image, int width, int height, int bpp);
public native void setImageNative( // copy the image to the internal api buffers
int[] image, int width, int height,
boolean bBWFilter, boolean bHorizDisp);
public native void releaseImageNative(); // release the internal api buffers
public native void setRectangleNative( // set the rectangle where OCR will focus
int left, int top, int width, int height);
public native void setPageSegModeNative( // set the page segmentation mode
int mode);
// OCR
public native String recognizeNative( // do OCR over the parameter image
byte[] image, int width, int height, int bpp);
public native String recognizeNative(); // do OCR over the image in the api buffers (all ocr)
public native String recognizeNative(int nopass); // do OCR over the image in the api buffers
// (options: 0=all, 1 or 2 passes)
// aux functions, to be run after OCR
public native int meanConfidenceNative(); // mean confidence (last OCR)
public native int[] wordConfidencesNative(); // confidence for each word (last OCR)
public native String getBoxText(); // get the box for each letter
// debug functions
public native void closeDebug(); // clean close the debug (if any)
public native String libVer(); // get the lib version
public int mNativeData; // storage space for the library’s internal buffers
/* this is used to load the ‘ocr’ library on application
* startup. The library has already been unpacked into
* /data/data/com…/lib/libocr.so at installation time by the package manager.
*/
static
{
System.loadLibrary(”ocr”);
}
코드 예제: Mezzofanti application – google code (http://code.google.com/p/mezzofanti/source/checkout)에 대한 이 모든 구현 사항들을 살펴볼 고 싶어 할 것이다. 코드는 Apache License, ver 2.0으로 배포되기 때문에 여러분 자신의 코드에서 자유롭게 사용할 수 있다.

댓글 없음:

댓글 쓰기