Reputation: 455
I have made android app with jni C++ code.
To read model file while creating apk on android native code(C++
), I placed the model file in assets folder. (so file path is my_app/src/main/assets/model.tflite
)
Then, I tried to read the model with following code but an error has raised:
Kotlin
code:
private val context: Context
get() = getApplication<Application>().applicationContext
asset_manager = context.assets
C++
code:
AAssetManager* mgr = AAssetManager_fromJava(env, asset_manager);
AAssetDir* assetDir = AAssetManager_openDir(mgr, "");
const char* filename = (const char*)NULL;
while ((filename = AAssetDir_getNextFileName(assetDir)) != NULL) {
AAsset* asset = AAssetManager_open(mgr, filename, AASSET_MODE_STREAMING);
__android_log_print(ANDROID_LOG_INFO, "tag", "file name is: %s", filename); <- file name reading is ok
char buf[BUFSIZ];
int nb_read = 0;
FILE* out = fopen(filename, "w"); <- out has null FILE*
while ((nb_read = AAsset_read(asset, buf, BUFSIZ)) > 0)
fwrite(buf, nb_read, 1, out);
fclose(out);
AAsset_close(asset);
}
AAssetDir_close(assetDir);
logcat
:
2022-06-19 22:45:54.449 12180-12180/com.my.first.app I/tag: file name is: model.tflite
2022-06-19 22:45:54.449 12180-12180/com.my.first.app A/libc: FORTIFY: fwrite: null FILE*
2022-06-19 22:45:54.450 12180-12180/com.my.first.app A/libc: Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 12180 (st.app), pid 12180 (st.app)
Why does fopen(filename, "w")
return null
? It seems that AAssetManager
is work.
Upvotes: 1
Views: 1058
Reputation: 30832
Looks like you want to combine this loading code to load the asset into a memory buffer and then use the BuildFromAllocation
method:
std::unique_ptr<tflite::Allocation> readAsset(std::string target) {
AAssetManager* mgr = AAssetManager_fromJava(env, asset_manager);
AAssetDir* assetDir = AAssetManager_openDir(mgr, "");
const char* filename = nullptr;
while ((filename = AAssetDir_getNextFileName(assetDir)) != nullptr) {
if (target != filename)
continue;
AAsset* asset = AAssetManager_open(mgr, filename, AASSET_MODE_STREAMING);
auto len = AAsset_getLength(asset);
uint8_t *buf = new uint8_t[len];
std::copy_n(AAsset_getBuffer(asset), len, buf);
auto ret = std::make_unique<tflite::Allocation>(buf , len);
AAsset_close(asset);
AAssetDir_close(assetDir);
return ret;
}
AAssetDir_close(assetDir);
return {};
}
and then use it later as:
auto ptr = readAssset("model.tflite");
if (!ptr) {
// do something
}
auto modelPtr = tflite::FlatBufferModel::BuildFromAllocation(ptr, some_error_reporter);
if (!modelPtr) {
// do something
}
Note: this will leak memory, as TFlite takes ownership of the buffer.
Upvotes: 1
Reputation: 455
Thanks to @Botje, I can load the model.tflite
in assets by this way:
AAssetManager* mgr = AAssetManager_fromJava(env, asset_manager);
AAssetDir* assetDir = AAssetManager_openDir(mgr, "");
const char* filename = nullptr;
while ((filename = AAssetDir_getNextFileName(assetDir)) != nullptr) {
AAsset* asset = AAssetManager_open(mgr, filename, AASSET_MODE_STREAMING);
size_t size = AAsset_getLength(asset);
buf = new char[size];
memmove(buf, AAsset_getBuffer(asset), size);
AAsset_close(asset);
}
AAssetDir_close(assetDir);
std::unique_ptr<tflite::FlatBufferModel> model = tflite::FlatBufferModel::BuildFromBuffer((const char*)buf, size);
std::unique_ptr<tflite::Interpreter> interpreter;
tflite::ops::builtin::BuiltinOpResolver op_resolver;
tflite::InterpreterBuilder(*model, op_resolver)(&interpreter);
Upvotes: 1