Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unsatisfiedlinkerror OpenALPR test project for android

For a few days I'm trying to build OpenALPR example project for Android. It builds and launches, but after calling native method for recognizing it make exception:

java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:299)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:352)
at java.util.concurrent.FutureTask.setException(FutureTask.java:219)
at java.util.concurrent.FutureTask.run(FutureTask.java:239)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:838)

Caused by: java.lang.UnsatisfiedLinkError: Native method not found: org.openalpr.AlprJNIWrapper.recognizeWithCountryRegionNConfig:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;
at org.openalpr.AlprJNIWrapper.recognizeWithCountryRegionNConfig(Native Method)
at org.openalpr.app.AlprFragment$AlprTask.doInBackground(AlprFragment.java:78)
at org.openalpr.app.AlprFragment$AlprTask.doInBackground(AlprFragment.java:1)
at android.os.AsyncTask$2.call(AsyncTask.java:287)
at java.util.concurrent.FutureTask.run(FutureTask.java:234)
... 4 more

I can't do anything, it appears all the time.

Application.mk

APP_ABI := armeabi-v7a
APP_CPPFLAGS := -frtti -fexceptions
APP_STL := gnustl_static

Android.mk after all of my attempts

LOCAL_PATH := $(call my-dir)
LIB_PATH := $(LOCAL_PATH)/../libs/armeabi-v7a

include $(CLEAR_VARS)

LOCAL_MODULE := leptonica
LOCAL_SRC_FILES := 3rdparty/liblept.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE := tesseract
LOCAL_SRC_FILES := 3rdparty/libtess.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE := simpleini
LOCAL_SRC_FILES := 3rdparty/libsimpleini.a
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE := support
LOCAL_SRC_FILES := 3rdparty/libsupport.a
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE := openalpr
LOCAL_SRC_FILES := 3rdparty/libopenalpr-static.a
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)

OPENCV_INSTALL_MODULES:=on
OPENCV_CAMERA_MODULES:=off

include d:\Other\robovisor_mobile\OpenCV-android-sdk\sdk\native\jni\OpenCV.mk

LOCAL_MODULE := openalpr-native
SOURCE_LIST := $(wildcard $(LOCAL_PATH)/*.cpp)
HEADER_LIST := $(wildcard $(LOCAL_PATH)/*.h)
LOCAL_SRC_FILES := AlprJNIWrapper.cpp
LOCAL_SRC_FILES += $(HEADER_LIST:$(LOCAL_PATH)/%=%)
LOCAL_SRC_FILES += $(SOURCE_LIST:$(LOCAL_PATH)/%=%)
LOCAL_EXPORT_C_INCLUDES += /home/sujay/builds/src/openalpr/src/openalpr
LOCAL_EXPORT_C_INCLUDES += /home/sujay/builds/src/OpenCV-2.4.9-android-sdk/sdk/native/include
FILE_LIST := $(foreach dir, $(LOCAL_EXPORT_C_INCLUDES), $(wildcard $(dir)/*.cpp))
LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%)

LOCAL_C_INCLUDES += /home/sujay/builds/src/openalpr/src/openalpr
LOCAL_C_INCLUDES += /home/sujay/builds/src/OpenCV-2.4.9-android-sdk/sdk/native/include
LOCAL_C_INCLUDES += /home/sujay/tools/android-ndk-r10/platforms/android-19/arch-arm/usr/include
LOCAL_SHARED_LIBRARIES += tesseract leptonica
LOCAL_STATIC_LIBRARIES += openalpr support simpleini
LOCAL_LDLIBS := -llog

include $(BUILD_SHARED_LIBRARY)

AlprJNIWrapper.cpp contains needed for me native functions

/**
 * Created by sujay on 13/11/14.
 */
#include <string>
#include <sstream>
#include <cstdio>
#include <iostream>

// openCV includes
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

// open alpr includes
#include "support/filesystem.h"
#include "support/timing.h"
#include "alpr.h"
#include "cjson.h"

#include "AlprJNIWrapper.h"
#include "AlprNative.h"

using namespace alpr;

JNIEXPORT jstring JNICALL Java_org_openalpr_AlprJNIWrapper_recognize(JNIEnv *env,
        jobject object, jstring jimgFilePath, jint jtopN)
{
    jstring defaultCountry = env->NewStringUTF("us");
    jstring defaultRegion = env->NewStringUTF("");
    jstring defaultConfigFilePath = env->NewStringUTF(CONFIG_FILE);
    return _recognize(env, object, defaultCountry, defaultRegion, jimgFilePath, defaultConfigFilePath, jtopN);
}

JNIEXPORT jstring JNICALL Java_org_openalpr_AlprJNIWrapper_recognizeWithCountryNRegion(
        JNIEnv *env, jobject object, jstring jcountry,
        jstring jregion, jstring jimgFilePath, jint jtopN)
{
    jstring defaultConfigFilePath = env->NewStringUTF(CONFIG_FILE);
    return _recognize(env, object, jcountry, jregion, jimgFilePath, defaultConfigFilePath, jtopN);
}

JNIEXPORT jstring JNICALL Java_org_openalpr_AlprJNIWrapper_recognizeWithCountryRegionNConfig
  (JNIEnv *env, jobject object, jstring jcountry, jstring jregion,
          jstring jimgFilePath, jstring jconfigFilePath, jint jtopN)
{
    return _recognize(env, object, jcountry, jregion, jimgFilePath, jconfigFilePath, jtopN);
}

jstring _recognize(JNIEnv *env, jobject object,
        jstring jcountry, jstring jregion, jstring jimgFilePath,
        jstring jconfigFilePath, jint jtopN)
{

    const char* countryChars = env->GetStringUTFChars(jcountry, NULL);

    std::string country(countryChars);

    env->ReleaseStringUTFChars(jcountry, countryChars);

    if(country.empty())
    {
        country = "us";
    }

    const char* configFilePathChars = env->GetStringUTFChars(jconfigFilePath, NULL);

    std::string configFilePath(configFilePathChars);

    env->ReleaseStringUTFChars(jconfigFilePath, configFilePathChars);

    if(configFilePath.empty())
    {
        configFilePath = "/etc/openalpr/openalpr.conf";
    }

    const char* imgFilePath = env->GetStringUTFChars(jimgFilePath, NULL);

    int topN = jtopN;

    std::string response = "";

    cv::Mat frame;
    Alpr alpr(country, configFilePath);

    const char* regionChars = env->GetStringUTFChars(jregion, NULL);

    std::string region(regionChars);

    env->ReleaseStringUTFChars(jregion, regionChars);

    if(region.empty())
    {
        alpr.setDetectRegion(true);
        alpr.setDefaultRegion(region);
    }


    alpr.setTopN(topN);

    if (alpr.isLoaded() == false) {
        env->ReleaseStringUTFChars(jimgFilePath, imgFilePath);
        response = errorJsonString("Error initializing Open Alpr");
        return env->NewStringUTF(response.c_str());
    }

    if(fileExists(imgFilePath))
    {
        frame = cv::imread(imgFilePath);
        response = detectandshow(&alpr, frame, "");
    }
    else
    {
        response = errorJsonString("Image file not found");
    }
    env->ReleaseStringUTFChars(jimgFilePath, imgFilePath);
    return env->NewStringUTF(response.c_str());
}

JNIEXPORT jstring JNICALL Java_org_openalpr_AlprJNIWrapper_version
  (JNIEnv *env, jobject object)
{
    return env->NewStringUTF(Alpr::getVersion().c_str());
}

std::string detectandshow(Alpr* alpr, cv::Mat frame, std::string region) 
{
    std::vector < uchar > buffer;
    std::string resultJson = "";
    cv::imencode(".bmp", frame, buffer);

    std::vector < char > buffer1;

    for(std::vector < uchar >::iterator i = buffer.begin(); i < buffer.end(); i++) {
        buffer1.push_back(*i);
    }

    timespec startTime;
    getTimeMonotonic(&startTime);

    //std::vector < AlprResults > results = alpr->recognize(buffer);
    AlprResults results = alpr->recognize(buffer1);

    timespec endTime;
    getTimeMonotonic(&endTime);
    double totalProcessingTime = diffclock(startTime, endTime);

    //if (results.size() > 0)
    {
        resultJson = alpr->toJson(results/*, totalProcessingTime*/);
    }

    return resultJson;
}

std::string errorJsonString(std::string msg) 
{
    cJSON *root;
    root = cJSON_CreateObject();
    cJSON_AddTrueToObject(root, "error");
    cJSON_AddStringToObject(root, "msg", msg.c_str());

    char *out;
    out = cJSON_PrintUnformatted(root);

    cJSON_Delete(root);

    std::string response(out);

    free(out);
    return response;
}

AlprJNIWrapper.java calls native method

/**
 * 
 */
package org.openalpr;

/**
 * @author sujay
 *
 */
public class AlprJNIWrapper implements Alpr {

        static { 
            System.loadLibrary("lept");
            System.loadLibrary("tess");
            System.loadLibrary("opencv_java");
            System.loadLibrary("openalpr-native");
        }

    /* (non-Javadoc)
     * @see org.openalpr.Alpr#recognize(java.lang.String, int)
     */
    @Override
    public native String recognize(String imgFilePath, int topN);

    /* (non-Javadoc)
     * @see org.openalpr.Alpr#recognizeWithCountryNRegion(java.lang.String, java.lang.String, java.lang.String, int)
     */
    @Override
    public native String recognizeWithCountryNRegion(String country, String region,
            String imgFilePath, int topN);

    /* (non-Javadoc)
     * @see org.openalpr.Alpr#recognizeWithCountryRegionNConfig(java.lang.String, java.lang.String, java.lang.String, java.lang.String, int)
     */
    @Override
    public native String recognizeWithCountryRegionNConfig(String country,
            String region, String imgFilePath, String configFilePath, int topN);

    /*
     * (non-Javadoc)
     * @see org.openalpr.Alpr#version()
     */
    @Override
    public native String version();
}

Edited

There is info about processor on my phone.

Processor       : ARMv7 Processor rev 3 (v7l)
processor       : 0
BogoMIPS        : 1993.93

Features        : swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4
idiva idivt
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x0
CPU part        : 0xc07
CPU revision    : 3

Edited again

I tried to get info about function in result .so file and there is what I get:

Symbol table '.dynsym' contains 6 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_finalize
     2: 00000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_atexit
     3: 00002004     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
     4: 00002004     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
     5: 00002004     0 NOTYPE  GLOBAL DEFAULT  ABS _end

I get it from:

<ndk_path>\toolchains\arm-linux-androideabi-4.8\prebuilt\windows\bin>arm-linux-androideabi-readelf.exe 
-Ws <projects_path>\libs\armeabi-v7a\libopenalpr-native.so

Am I doing something wrong? Or there is really no my functions in .so file?

like image 573
Ircover Avatar asked Apr 08 '15 10:04

Ircover


2 Answers

There are could be several issues.
First, you run the sample on unsupported device, like if lib built for armeabi-v7 (and its definitely so due to APP_ABI := armeabi-v7a setting in make file) and your device is intel x86 or lower than 7 armeabi version, etc.
Could be that sample is outdated while lib project has been updated, so some method names was changed or method was removed as deprecated, etc.
Also compiled NDK lib is package sensitive so if you place JNI class into a different package it wont work either.
Also the native library .so-file has to be placed into the right place of your project and its not a libs folder where you place jar-libs usually. Its a bit different folder like as for android studio:

...\src\main\jniLibs\aremabi-v7a\libYourLib.so (for aremabi-v7a version)
...\src\main\jniLibs\x86\libYourLib.so (for x86 version and so on)

UPDATE:
Don't forget to respect the min API level which is 19. App wont work on devices with lower API level, I mean you could change the min API level in project - don't do it.
The issue here is that libopenalpr-native.so has a dash - char in its name. Dash is a restricted char for resource naming in AOS. So I replaced it with "_" and app works now And replace it here as well:

System.loadLibrary("openalpr_native");

And I didn't use your version of .so but only the included in project one.
UPDATE
Take a look: http://prntscr.com/6w6qfx your lib is just 5kb comparing to original 1.7Mb there is something wrong definitely. And it explains your question in comment:

Why there is no error on System.loadLibrary("openalpr-native");? I just can't understand this situation - creates libopenalpr-native.so file, it loads to program, by there is no method.

Could you just use original lib included in sample project instead? Just to test.

like image 118
Stan Avatar answered Nov 16 '22 16:11

Stan


No correct method found in your library is due to name mismatch, you should wrap your JNI function code with extern "C" in C++ code.

like image 23
jobcrazy Avatar answered Nov 16 '22 17:11

jobcrazy