Android 에서 TensorFlow Lite 사용하기 (C/C++) - ( 2/2 )
1편: JNI 로 C++ 함수 호출하기 https://cppmagister.tistory.com/7
2편: Tensorflow Lite C++ 코드 연결하기
쓰다보니 내용이 상당히 많군요..
이제 얼마 안남았으니 힘 내시길 바랍니다 ㅎㅎ
1. Tensorflow Lite C++ 연결
제가 편의를 위해 Tensorflow Lite C++ 라이브러리와 wrapper 를 미리 빌드해서 만들어놓은 repository가 있습니다(https://github.com/lackhole/Tensorflow-Lite)
CPU, GPU, NNAPI 모두 가능하니 위 레포를 쓰는게 편하실겁니다
제가 주기적으로 업데이트를 하고 있으니, 원본을 빌드하나 저걸 쓰나 차이는 없습니다
여기서는 제가 만든 레포를 이용하여 진행하겠습니다
만약 직접 원본 소스를 빌드하고 싶다면, https://cppmagister.tistory.com/6 글을 참고해주세요
ㄱ. Tensorflow Lite prebuilt libraries & wrapper
터미널을 통해 cpp 디렉터리에 들어간 후, 레포를 받아줍니다
cd app/src/main/cpp
git clone https://github.com/lackhole/Tensorflow-Lite
받고 난 이후에는 레포의 브랜치를 android로 바꿔준 후, 모든 서브모듈들을 다운받아줍니다.
cd Tensorflow-Lite
git checkout android
git submodule update --init --recursive
다운로드가 완료되면, Android Studio를 아무곳이나 클릭해 다운로드한 파일들을 Android Studio 에서 인덱싱을 완료할 때 까지 기다려줍니다.
인덱싱이 완료되고, 아래처럼 파일들이 다 나오면 Tensorflow Lite를 사용할 준비가 끝났습니다.
ㄴ. 새로운 CMakeLists.txt 만들기 및 기존 CMakeLists.txt 수정
그럼 이제 Tensorflow Lite Wrapper (이하 CuteModel)을 사용할 C++ 클래스를 만들고, 이를 연결할 CMakeLists.txt 를 cpp 폴더 안에 다음 처럼 만들어줍니다
Java 에서 JNI 를 이용해 C++ 코드를 호출 할 때는 자원 관리를 잘 해야 합니다.
원래 C++ 코드에서처럼 main 함수가 없고, Java 호출 부분이 main 함수의 역할을 대신 하기 때문에 C++의 모든 리소스를 담는 객체를 하나 만들고, 이 객체를 Java 에서 다루는게 일반적입니다.
예시에서는 그냥 스태틱으로 만들겠습니다..ㅎㅎ
여러분들이 실제로 쓰실 때는 상황에 알맞는 인터페이스들을 잘 설계하셔야 합니다
위에서 만들어준 cpp 디렉토리 안의 CMakeLists.txt 를 아래처럼 수정해서, tflite 를 JustClass.cpp 에 연결해줍니다.
나중에 보시다가 어 저거 뭐야 하고 지우실 수 가 있는데, z 와 log 를 무조건 추가해 주어야 합니다.
안그러면 링크 시 에러가 납니다
CMAKE_MINIMUM_REQUIRED(VERSION 3.3)
PROJECT(lackhole)
SET(CMAKE_CXX_STANDARD 14)
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/Tensorflow-Lite")
add_library(lackhole SHARED
JustClass.cpp)
target_link_libraries(lackhole PUBLIC tflite z log)
그리고 1편에서 만든 app 디렉토리 안의 CMakeLists.txt 에
-
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp")
-
target_link_libraries(native-lib lackhole tflite)
두 줄을 아래처럼 추가해줍니다.
cmake_minimum_required(VERSION 3.3)
project(native-lib)
set(CMAKE_CXX_STANDARD 14)
set(JNI_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/jni)
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/src/main/jni")
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp")
add_library(
native-lib
SHARED
${JNI_DIR}/native-lib.cpp
)
target_link_libraries(native-lib lackhole tflite)
target_include_directories(native-lib PUBLIC)
ㄷ. build.gradle(app) 수정
미리 빌드된 라이브러리가 android 버전에 따라 armeabi-v7 와 arm64-v8a 두 종류로 나뉘어져 있는데, 이것을 build.gradle 에 명시해 주어야 합니다.
ndk 부분을 각각 release 와 debug 안에 아래 처럼 붙여넣어줍니다. (debug 가 없으면 만드시면 됩니다)
그리고 sourceSets 안에 빌드된 라이브러리가 포함된 경로를 넣어줍니다.
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.example.myapplication"
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'
ndk {
abiFilters "armeabi-v7a", "arm64-v8a"
}
}
debug {
ndk {
abiFilters "armeabi-v7a", "arm64-v8a"
}
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
sourceSets {
main {
jniLibs.srcDirs = ['src/main/cpp/Tensorflow-Lite/lib']
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
ㄹ. 빌드 해보기
아래 처럼 gradle sync, refresh C++, make project 를 차례차례 해 줍니다.
보통 make project 만 하면 되는데, 가끔 안 될 수가 있어서 3개 모두 해 줍니다.
모두 에러가 나지 않으시면 성공입니다
2. CuteModel 사용하기
JustClass.hpp
#ifndef MY_APPLICATION_JUSTCLASS_H
#define MY_APPLICATION_JUSTCLASS_H
#include "cutemodel/CuteModel.hpp"
class JustClass {
public:
static ct::CuteModel model;
};
#endif //MY_APPLICATION_JUSTCLASS_H
CuteModel 헤더를 넣고, CuteModel 객체를 하나 만들어줍니다
JustClass.cpp
#include "JustClass.h"
ct::CuteModel JustClass::model;
스태틱이니까 정의해주는것도 까먹지 맙시다 ㅎㅎ
기본 생성자가 있어서 저렇게만 써 줘도 됩니다.
예시로 사용할 모델은 Google 에서 만든 Object Detection 모델입니다.
원래 tflite 파일로 되어 있던 것을 순수 바이너리 헤더로 바꾸었습니다.
이처럼 바이트 버퍼를 이용하여 Tensorflow Lite Interpreter 을 빌드하는 것도 가능합니다.
파일로도 빌드할 수 있습니다.
Interpreter 빌드, 인풋 넣기, 추론, 아웃풋을 뱉는 함수를 native 함수에서 만들어줍니다.
C++ 부분은 여기를 참고하셔서 따라하시면 됩니다 https://cppmagister.tistory.com/18
예전 코드가 날아가서 예시로 들 Java 메서드들을 만들고 하려니 시간이 오래 걸리네요.
정말 필수적인 부분들은 다 들어갔으니 CuteModel 사용법만 익히시면 금방 가능할겁니다! ㅠㅠ
1, 2편 작성하는데 벌써 5시간이 넘게 지났군요..
이번주 안에 글 작성을 마치도록 하겠습니다
+ 궁금한 점이나 간단한 내용이라도 댓글로 남겨주시면 글쓴이에게 정말 큰 힘이 됩니다! +
'TensorFlow > Tensorflow Lite' 카테고리의 다른 글
Tensorflow Lite 실행하기(PC/C++) (2) | 2021.08.30 |
---|---|
Tensorflow Lite 를 C/C++로 빌드하기(bazel) (1) | 2021.08.29 |
Android 에서 TensorFlow Lite 사용하기 (C/C++) - ( 1/2 ) (0) | 2020.04.05 |
TensorFlow Lite 빌드하기(bazel & C/C++) (0) | 2020.04.05 |
TensorFlow Lite로 모델 변환하기 (Python) (1) | 2020.04.05 |