Skip to content

Instantly share code, notes, and snippets.

@jav974
Last active January 4, 2023 13:11
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save jav974/072425f14927e6ca2c7a4439d8ac5457 to your computer and use it in GitHub Desktop.
Save jav974/072425f14927e6ca2c7a4439d8ac5457 to your computer and use it in GitHub Desktop.
This bash script compiles openalpr for android. You just need to provide the path to Android SDK and Android NDK in the first lines of this code and execute it. Tested on ubuntu 17.04 64bit
#!/bin/bash
# You should tweak this section to adapt the paths to your need
export ANDROID_HOME=/home/jeremy/Android/Sdk
export NDK_ROOT=/home/jeremy/Android/Sdk/ndk-bundle
ANDROID_PLATFORM="android-21"
# In my case, FindJNI.cmake does not find java, so i had to manually specify these
# You could try without it and remove the cmake variable specification at the bottom of this file
JAVA_HOME=/usr/lib/jvm/oracle-java8-jdk-amd64
JAVA_AWT_LIBRARY=$JAVA_HOME/jre/lib/amd64
JAVA_JVM_LIBRARY=$JAVA_HOME/jre/lib/amd64
JAVA_INCLUDE_PATH=$JAVA_HOME/include
JAVA_INCLUDE_PATH2=$JAVA_HOME/include/linux
JAVA_AWT_INCLUDE_PATH=$JAVA_HOME/include
SCRIPT=`realpath $0`
SCRIPTPATH=`dirname $SCRIPT`
####################################################################
# Prepare Tesseract and Leptonica, using rmtheis/tess-two repository
####################################################################
git clone --recursive https://github.com/rmtheis/tess-two.git tess2
cd tess2
echo "sdk.dir=$ANDROID_HOME
ndk.dir=$NDK_ROOT" > local.properties
./gradlew assemble
cd ..
####################################################################
# Download and extract OpenCV4Android
####################################################################
wget -O opencv-3.2.0-android-sdk.zip -- https://sourceforge.net/projects/opencvlibrary/files/opencv-android/3.2.0/opencv-3.2.0-android-sdk.zip/download
unzip opencv-3.2.0-android-sdk.zip
rm opencv-3.2.0-android-sdk.zip
####################################################################
# Download and configure openalpr from jav974/openalpr forked repo
####################################################################
git clone https://github.com/jav974/openalpr.git openalpr
mkdir openalpr/android-build
TESSERACT_SRC_DIR=$SCRIPTPATH/tess2/tess-two/jni/com_googlecode_tesseract_android/src
rm -rf openalpr/src/openalpr/ocr/tesseract
mkdir openalpr/src/openalpr/ocr/tesseract
shopt -s globstar
cd $TESSERACT_SRC_DIR
cp **/*.h $SCRIPTPATH/openalpr/src/openalpr/ocr/tesseract
cd $SCRIPTPATH
declare -a ANDROID_ABIS=("armeabi"
"armeabi-v7a"
"armeabi-v7a with NEON"
"arm64-v8a"
"mips"
"mips64"
"x86"
"x86_64"
)
cd openalpr/android-build
for i in "${ANDROID_ABIS[@]}"
do
if [ "$i" == "armeabi-v7a with NEON" ]; then abi="armeabi-v7a"; else abi="$i"; fi
TESSERACT_LIB_DIR=$SCRIPTPATH/tess2/tess-two/libs/$abi
if [[ "$i" == armeabi* ]];
then
arch="arm"
lib="lib"
elif [[ "$i" == arm64-v8a ]];
then
arch="arm64"
lib="lib"
elif [[ "$i" == mips ]] || [[ "$i" == x86 ]];
then
arch="$i"
lib="lib"
elif [[ "$i" == mips64 ]] || [[ "$i" == x86_64 ]];
then
arch="$i"
lib="lib64"
fi
echo "
######################################
Generating project for arch $i
######################################
"
rm -rf "$i" && mkdir "$i"
cd "$i"
cmake \
-DANDROID_TOOLCHAIN=clang \
-DCMAKE_TOOLCHAIN_FILE=$NDK_ROOT/build/cmake/android.toolchain.cmake \
-DANDROID_NDK=$NDK_ROOT \
-DCMAKE_BUILD_TYPE=Release \
-DANDROID_PLATFORM=$ANDROID_PLATFORM \
-DANDROID_ABI="$i" \
-DANDROID_STL=gnustl_static \
-DANDROID_CPP_FEATURES="rtti exceptions" \
-DTesseract_INCLUDE_BASEAPI_DIR=$TESSERACT_SRC_DIR/api \
-DTesseract_INCLUDE_CCSTRUCT_DIR=$TESSERACT_SRC_DIR/ccstruct \
-DTesseract_INCLUDE_CCMAIN_DIR=$TESSERACT_SRC_DIR/ccmain \
-DTesseract_INCLUDE_CCUTIL_DIR=$TESSERACT_SRC_DIR/ccutil \
-DTesseract_LIB=$TESSERACT_LIB_DIR/libtess.so \
-DLeptonica_LIB=$TESSERACT_LIB_DIR/liblept.so \
-DOpenCV_DIR=$SCRIPTPATH/OpenCV-android-sdk/sdk/native/jni \
-DJAVA_AWT_LIBRARY=$JAVA_AWT_LIBRARY \
-DJAVA_JVM_LIBRARY=$JAVA_JVM_LIBRARY \
-DJAVA_INCLUDE_PATH=$JAVA_INCLUDE_PATH \
-DJAVA_INCLUDE_PATH2=$JAVA_INCLUDE_PATH2 \
-DJAVA_AWT_INCLUDE_PATH=$JAVA_AWT_INCLUDE_PATH \
-DPngt_LIB=$TESSERACT_LIB_DIR/libpngt.so \
-DJpgt_LIB=$TESSERACT_LIB_DIR/libjpgt.so \
-DJnigraphics_LIB=$NDK_ROOT/platforms/$ANDROID_PLATFORM/arch-$arch/usr/$lib/libjnigraphics.so \
-DANDROID_ARM_MODE=arm \
../../src/
cmake --build . -- -j 8
cd ..
done
echo "
All done !!!"
@cefaci
Copy link

cefaci commented Jan 12, 2020

Can you post the image and your configs, then I can maybe test it later? Background is no problem I let my image stream of ~7 FPS 800x600 run against the openalpr lib with many threads, but I had to load a openalpr lib per thread (if I remember right, did it 2 month ago, I will check).

@alaroma
Copy link

alaroma commented Jan 12, 2020

I used this repository first, I also have my own compiled version, I can send it to an email.
Here are the images that I used, for example.
If possible, please send a file with a working example. thanks

@cefaci
Copy link

cefaci commented Jan 12, 2020

@jav974 thanks for the info.
I could build and get it running w/o errors with following settings

  • @jav974 build.sh+CMakeLists.txt for armeabi-v7a with NEON and arm64-v8a
  • c++_static
  • Ubuntu 18.04 64bit
  • java-8-openjdk
  • Android-ndk-r20
  • OpenCV-4.1.2 (had to change all renamed Enums and some imports for some renamed Enums)

I had the same null pointer dereference PreWarp->warpImage() and struggled a lot with it.
In the end I debugged the code with Android logging and found out the PreWarp is initialized with a null pointer and if the config loading fails it won't be initialized, but you don't get an exception.
It failed because the value e.g. ocr_language = leu for finding the runtime_data/ocr/tessdata/leu.traineddata data is not read from the default config file openalpr.conf as expected but from runtime_data/config/eu.conf (if not set it is set to NONE). If you set it there the countries are loaded completely and the PreWarp is initialized.

So the lib works (tested on Android 10 Pixel 2 XL) but my plates are always 0 have to check that now :)

  • I used these settings, compiled the latest version of the community project git clone the project.
  • The images looks good, please be sure to use the configs from the latest community project for US plates or try EU examples (I tested just EU).
  • OpenCV-4.1.2 is my fun test (4.1.2 is now a shared lib you have to change your android project that it will work right), just use the recommended OpenCV-3.x.
  • LOGGING: I extended the JNI openalprjini.cpp and C++ alpr, alpr_c and alpr_impl with #include <android/log.h> to log in logcat the library or exceptions, e.g. __android_log_print(ANDROID_LOG_ERROR, "recognize(): Caught exception in OpenALPR recognize: ", "%s", e.msg.c_str());.
    Add in @jav974 build.sh+CMakeLists.txt following
find_library( # Sets the name of the path variable. 
        log-lib 
        # Specifies the name of the NDK library that 
        # you want CMake to locate. 
       log  
 ) 
target_link_libraries(log) 
  • YUV420 Bytes: I extended the JNI openalprjini.cpp and C++ alpr, alpr_c and alpr_impl and added code, that openCV understand my bytes
  • Your file code looks good ... String path = destination.getAbsolutePath(); try { result = alpr.recognize(path); }...

@alaroma
Copy link

alaroma commented Jan 12, 2020

Okay, thank you. I compiled the library version a couple of weeks ago, used these settings (it didn’t work with the version of OpenCV 3.x.)

@jav974 thanks for the info.
I could build and get it running w/o errors with following settings

* @jav974 build.sh+CMakeLists.txt for armeabi-v7a with NEON and arm64-v8a

* c++_static

* Ubuntu 18.04 64bit

* java-8-openjdk

* Android-ndk-r20

* OpenCV-4.1.2 (had to change all renamed Enums and some imports for some renamed Enums)

I had the same null pointer dereference PreWarp->warpImage() and struggled a lot with it.
In the end I debugged the code with Android logging and found out the PreWarp is initialized with a null pointer and if the config loading fails it won't be initialized, but you don't get an exception.
It failed because the value e.g. ocr_language = leu for finding the runtime_data/ocr/tessdata/leu.traineddata data is not read from the default config file openalpr.conf as expected but from runtime_data/config/eu.conf (if not set it is set to NONE). If you set it there the countries are loaded completely and the PreWarp is initialized.

So the lib works (tested on Android 10 Pixel 2 XL) but my plates are always 0 have to check that now :)

If you successfully use this library in the Android project, could you send me the file with the code or .so files? or check my code for errors?

@cefaci
Copy link

cefaci commented Jan 12, 2020

  • I mailed you the .so files
  • Use OpenCV 4.1.1 for android then you can link it in your project static
  • To check your code doesn't make sense, as everything is happening in the library and the return looks good, as it executes and takes time. I still think it can be your images or your configs are not right.
  • My build.gradle:
...
android {
    compileSdkVersion 28
    buildToolsVersion "28.0.3"

    defaultConfig {
        ...
        minSdkVersion 24
        targetSdkVersion 28
        versionCode 1
        versionName "1.0.02"
        ndk {
            abiFilters "arm64-v8a","armeabi-v7a"
        }
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }
    }
...

@cefaci
Copy link

cefaci commented Jan 12, 2020

BTW, how you init the configs, mine looks like this (you have to copy the openalpr config directory with your openalpr.conf file from your assets when your apps starts the first time to e.g.
String dirData = getActivity().getExternalFilesDir(null).getAbsolutePath() + File.separatorChar + "data"
):

    alpr = new Alpr(openAlprCountry, openAlprConfFile, openAlprRunDir);
    alpr.setTopN(openAlprTopN);
    alpr.setDetectRegion(false);

@alaroma
Copy link

alaroma commented Jan 13, 2020

@cefaci
I changed the function itself a bit

public Alpr(Context context, String androidDataDir, String country, String configFile, String runtimeDir) {
        Utils.copyAssetFolder(context.getAssets(), "runtime_data", androidDataDir + File.separatorChar + "runtime_data");
        initialize(country, configFile, runtimeDir);
    }

and call it as follows:

String runtimeDir = ANDROID_DATA_DIR + File.separatorChar + "runtime_data";
ANDROID_DATA_DIR = this.getApplicationInfo().dataDir;
Alpr alpr = new Alpr(this, ANDROID_DATA_DIR, "us", openAlprConfFile, runtimeDir);

with your files I get the following result:
{"version":2,"data_type":"alpr_results","epoch_time":1578932220551,"img_width":720,"img_height":1280,"processing_time_ms":471.592621,"regions_of_interest":[{"x":0,"y":0,"width":720,"height":1280}],"results":[]}
and in console:

E/C++->AlprImpl->OpenALPR Initialization Time:: 94.849615
I/C++->AlprImpl->recognizeFullDetails(): Analyzing:: us

why use OpenCV 4.1.1?

@cefaci
Copy link

cefaci commented Jan 13, 2020

public Alpr(Context context, String androidDataDir, String country, String configFile, String runtimeDir) {
Utils.copyAssetFolder(context.getAssets(), "runtime_data", androidDataDir + File.separatorChar + "runtime_data");
initialize(country, configFile, runtimeDir);
}

Looks ok, can you please verify with a file browser your copied assets to your set location and your strings openAlprConfFile and runtimeDir are right to the copied location?

{"version":2,"data_type":"alpr_results","epoch_time":1578932220551,"img_width":720,"img_height":1280,"processing_time_ms":471.592621,"regions_of_interest":[{"x":0,"y":0,"width":720,"height":1280}],"results":[]}

Looks good, there is processing_time_ms, could you please post your openalpr.conf too?

E/C++->AlprImpl->OpenALPR Initialization Time:: 94.849615
I/C++->AlprImpl->recognizeFullDetails(): Analyzing:: us

The android logging I added

why use OpenCV 4.1.1?

As I compiled with OpenCV 4.x and OpenCV 4.1.1 is usable as a static or a shared lib whereas OpenCV 4.1.2+ works only as a shared lib and your Android project has to be setup different

My openaplr.conf for country EU:

ocr_img_size_percent = 1.33333333
state_id_img_size_percent = 2.0

; Calibrating your camera improves detection accuracy in cases where vehicle plates are captured at a steep angle
; Use the openalpr-utils-calibrate utility to calibrate your fixed camera to adjust for an angle
; Once done, update the prewarp config with the values obtained from the tool
;prewarp = planar,1280.000000,720.000000,0.000550,0.000750,0.130000,1.000000,1.000000,0.000000,0.000000
prewarp =

; detection will ignore plates that are too large.  This is a good efficiency technique to use if the
; plates are going to be a fixed distance away from the camera (e.g., you will never see plates that fill
; up the entire image
max_plate_width_percent = 100
max_plate_height_percent = 100

; detection_iteration_increase is the percentage that the LBP frame increases each iteration.
; It must be greater than 1.0.  A value of 1.01 means increase by 1%, 1.10 increases it by 10% each time.
; So a 1% increase would be ~10x slower than 10% to process, but it has a higher chance of landing
; directly on the plate and getting a strong detection
detection_iteration_increase = 1.20

; The minimum detection strength determines how sure the detection algorithm must be before signaling that
; a plate region exists.  Technically this corresponds to LBP nearest neighbors (e.g., how many detections
; are clustered around the same area).  For example, 2 = very lenient, 9 = very strict.
detection_strictness = 1

; The detection doesn't necessarily need an extremely high resolution image in order to detect plates
; Using a smaller input image should still find the plates and will do it faster
; Tweaking the max_detection_input values will resize the input image if it is larger than these sizes
; max_detection_input_width/height are specified in pixels
max_detection_input_width = 1280
max_detection_input_height = 960

; detector is the technique used to find license plate regions in an image.  Value can be set to
; lbpcpu    - default LBP-based detector uses the system CPU
; lbpgpu    - LBP-based detector that uses Nvidia GPU to increase recognition speed.
; lbpopencl - LBP-based detector that uses OpenCL GPU to increase recognition speed.  Requires OpenCV 3.0
; morphcpu  - Experimental detector that detects white rectangles in an image.  Does not require training.
detector = lbpcpu

; If set to true, all results must match a postprocess text pattern if a pattern is available.
; If not, the result is disqualified.
must_match_pattern = 1

; Bypasses plate detection.  If this is set to 1, the library assumes that each region provided is a likely plate area.
skip_detection = 0

; Specifies the full path to an image file that constrains the detection area.  Only the plate regions allowed through the mask
; will be analyzed.  The mask image must match the resolution of your image to be analyzed.  The mask is black and white.
; Black areas will be ignored, white areas will be searched.  An empty value means no mask (scan the entire image)
detection_mask_image =

; OpenALPR can scan the same image multiple times with different randomization.  Setting this to a value larger than
; 1 may increase accuracy, but will increase processing time linearly (e.g., analysis_count = 3 is 3x slower)
analysis_count = 2

; OpenALPR detects high-contrast plate crops and uses an alternative edge detection technique.  Setting this to 0.0
; would classify  ALL images as high-contrast, setting it to 1.0 would classify no images as high-contrast.
contrast_detection_threshold = 0.5

max_plate_angle_degrees = 90

ocr_min_font_point = 6

; Minimum OCR confidence percent to consider.
postprocess_min_confidence = 80

; Any OCR character lower than this will also add an equally likely
; chance that the character is incorrect and will be skipped.  Value is a confidence percent
postprocess_confidence_skip_level = 80

@alaroma
Copy link

alaroma commented Jan 13, 2020

@cefaci Okay, thank you. I got the result, but only with the configuration files of the eu. How do I use other countries?

@cefaci
Copy link

cefaci commented Jan 13, 2020

@alaroma

  • Can you please test it w/ your .so files too?
  • Is you runtime_data directory complete? Check mine below.

=> Please post you runtime_data directory and your openalpr.conf file, so others could maybe help.

Nope, never tried other countries as in my opinion the results on mobiles are not worthy to investigate it further as @jav974 mentioned and @ArthurAttout asked the original author in another thread if there will be new development, the commercial code won't be merged to this version the original author said it's a community project up to them.

Try some new state-of-the-art approaches like LPRNet (google it, you will find the paper and on github tensorflow examples, I rewrote one to tensorflow 2 kerass model), for single line plates I like the results (e.g. rotation, angle etc), training takes some days on a CPU. I exported it as TFLite and recognition takes around ~80-100ms on my Pixel 2. New papers have improved CTC approaches for multi-line text recognition, not yet tested.

Really take a look in ML, even I had a really really painful learning curve of 3 weeks, but the results are really really nice and promising, no problems with white spaces or dashes etc.

  • My runtime_data directory:

image

@alaroma
Copy link

alaroma commented Jan 14, 2020

@cefaci

  • my runtime_dir directory is exactly the same as yours.

  • with my configuration files, the application closes:
    Disconnected from the target VM, address: 'localhost: 8626', transport: 'socket'
    or result is null.

I need to add configuration files for a new country (Russia), it is also necessary that the European Union, America and Australia work.

@cefaci
Copy link

cefaci commented Jan 14, 2020

@alaroma
and your compiled .so files?

@alaroma
Copy link

alaroma commented Jan 15, 2020

@cefaci
Only EU is recognized with my .so files, as with yours.
How to recognize with other configuration files?
And with other country .conf in console:
I/Choreographer: Skipped 618 frames! The application may be doing too much work on its main thread.
or with your .so files: E/--(!) Config file does not exist!: /data/user/0/com.example.var8/runtime_data/xx.conf

@cefaci
Copy link

cefaci commented Jan 15, 2020

Config:

  • As I understood the openalpr.conf is for general detection settings.
  • The [COUNTRY_CODE] you pass in the new Aplr() object will look for a file in runtime_data/config/[COUNTRY_CODE].conf where there is a property ocr_language = l[COUNTRY_CODE] e.g. leu.
  • The ocr_language = l[COUNTRY_CODE] value will look for a file in runtime_data/ocr-tessdata/l[COUNTRY_CODE].trainedata you should have trained with openalpr/train-ocr (?)
  • Then you have the config/postprocess/[COUNTRY_CODE].patterns file, in the directory is a readme.txt how to make RU patterns.
  • Don't know where you create/extract the config/region/[COUNTRY_CODE].xml for the plates.

I/Choreographer: Skipped 618 frames! The application may be doing too much work on its main thread.:

You should create background threads for openalpr processing I do this for my image buffering as well. I have a constant flow of 29FPS where I use ~7-8 FPS for asnyc openalpr processing as it takes ~50-300ms per image (if nothing is found it takes longer).

I could show you videos of my results:
openalpr local (android phone) vs. commercial openalpr (cloud) vs. LPRnet local (android phone).

LPRnet performs in my opinion best, but is not multi-line or 2-line ready. Commercial openalpr (cloud) works multiline.

@alaroma
Copy link

alaroma commented Jan 15, 2020

@cefaci thank you for information.
on the other phone (also android 6.0) throws the following exception and the library is not initialized, what should I do?

`E/--(!) Config file does not exist!: /data/user/0/com.example.var8/runtime_data/eu.conf
E/C++->AlprImpl->config->loaded: FAILED: 0`

@cefaci
Copy link

cefaci commented Jan 16, 2020

Really, again? If you don't start posting/trying EVERYTHING in your questions I won't help you anymore...

  • What is the absolute path of your runtime_data directory you init in new Alpr()?
  • What is the absolute path of your config_file you init in new Alpr()?
  • What is the absolute path of your coutry_code you init in new Alpr()?
  • What is the absolute path where you copy your assets e.g. runtime_data directory
  • Did you check out the copied assets runtime_data path after you copied on your phone e.g. w/ Total Commander?

If you would have just checked this message on your own E/C++->AlprImpl->config->loaded: FAILED: 0 then maybe you would have found an if in the file alpr_impl.cpp saying this:

namespace alpr
{
    AlprImpl::AlprImpl(const std::string country, const std::string configFile, const std::string runtimeDir)
    {
    ...
        // Config file or runtime dir not found.  Don't process any further.
        if (config->loaded == false)
        {
            return;
        }
        ...

The comment is not from me and is already saying everything, where I just added the logging (in my files e.g. the .so you have):

namespace alpr
{
    AlprImpl::AlprImpl(const std::string country, const std::string configFile, const std::string runtimeDir)
    {
    ...
        // Config file or runtime dir not found.  Don't process any further.
        if (config->loaded == false)
        {
            __android_log_print(ANDROID_LOG_ERROR, "C++->AlprImpl->config->loaded: FAILED ", "%d", config->loaded);
            return;
        }
        ...

@cefaci
Copy link

cefaci commented Jan 16, 2020

Config.cpp

namespace alpr
{
  Config::Config(const std::string country, const std::string config_file, const std::string runtime_dir)
  {

    ...

    if (fileExists(config_file_path.c_str()) == false && fileExists(CONFIG_FILE_TEMPLATE_LOCATION) == false)
    {
      std::cerr << "--(!) Config file '" << config_file_path << "' does not exist!" << endl;
      std::cerr << "--(!)             You can specify the configuration file location via the command line " << endl;
      std::cerr << "--(!)             or by setting the environment variable '" << ENV_VARIABLE_CONFIG_FILE << "'" << endl;
      __android_log_print(ANDROID_LOG_ERROR, "--(!) Config file does not exist! ", "%s", config_file_path.c_str());
      return;
    }
    else if (DirectoryExists(config_file_path.c_str()))
    {
      std::cerr << "--(!) Config file '" << config_file_path << "' was specified as a directory, rather than a file!" << endl;
      std::cerr << "--(!)             Please specify the full path to the 'openalpr.conf file'" << endl;
      std::cerr << "--(!)             e.g., /etc/openalpr/openalpr.conf" << endl;
      __android_log_print(ANDROID_LOG_ERROR, "--(!) Config file was specified as a directory, rather than a file! ", "%s", config_file_path.c_str());
      return;
    }
	....

@alaroma
Copy link

alaroma commented Jan 16, 2020

Sorry. I did not change the code, I just reinstalled it on my phone and installed it on another, this error appeared.
Here is my code:

@cefaci
I changed the function itself a bit

public Alpr(Context context, String androidDataDir, String country, String configFile, String runtimeDir) {
        Utils.copyAssetFolder(context.getAssets(), "runtime_data", androidDataDir + File.separatorChar + "runtime_data");
        initialize(country, configFile, runtimeDir);
    }

and call it as follows:

String runtimeDir = ANDROID_DATA_DIR + File.separatorChar + "runtime_data";
ANDROID_DATA_DIR = this.getApplicationInfo().dataDir;
Alpr alpr = new Alpr(this, ANDROID_DATA_DIR, "us", openAlprConfFile, runtimeDir);

with your files I get the following result:
{"version":2,"data_type":"alpr_results","epoch_time":1578932220551,"img_width":720,"img_height":1280,"processing_time_ms":471.592621,"regions_of_interest":[{"x":0,"y":0,"width":720,"height":1280}],"results":[]}
and in console:

E/C++->AlprImpl->OpenALPR Initialization Time:: 94.849615
I/C++->AlprImpl->recognizeFullDetails(): Analyzing:: us

why use OpenCV 4.1.1?

@cefaci
Copy link

cefaci commented Jan 16, 2020

Really, again? :/
I understood what you have done.... Your code seems to not work on your other phone as the message already said
E/--(!) Config file does not exist!: /data/user/0/com.example.var8/runtime_data/eu.conf...

So, how we can find out why? Change YOUR code, print and then POST here what I've asked you:

  • What is the absolute path of your runtime_data directory you init in new Alpr()?
  • What is the absolute path of your config_file you init in new Alpr()?
  • What is country_code you init in new Alpr()?
  • What is the absolute path where you copy your assets e.g. runtime_data directory?

Did you check out the copied assets runtime_data path after you copied on your phone e.g. w/ Total Commander?

After you printed all these 4 paths check out all of them on your phone and POST them here too... your OWN investigation w/ everything I have asked you!

Thanks...

@alaroma
Copy link

alaroma commented Jan 16, 2020

@cefaci
Yes, it helped, thank you, the error was that the configuration files were in the config folder, but you just needed in runtime_dir. The library is initializing, but the application crashes (with eu.conf).

@cefaci
Copy link

cefaci commented Jan 16, 2020

Still, again, post ALL your details...

@alaroma
Copy link

alaroma commented Jan 16, 2020

@cefaci
Okay.

  • I use your .so files.
  • And code in previous post.
  • my runtime_data (runtimeDir): /data/user/0/com.example.var8/runtime_data
  • my abs path config (openAlprConfFile): /data/user/0/com.example.var8/runtime_data/eu.conf
  • country code: eu
  • new Alpr(): Alpr alpr = new Alpr(MainActivity.this, this.getApplicationInfo().dataDir, "eu", openAlprConfFile, runtimeDir);
    and I get:
    Disconnected from the target VM, address: 'localhost:8603', transport: 'socket'

@cefaci
Copy link

cefaci commented Jan 16, 2020

  • But the openAlprConfFile is not copied from runtime_data/config/eu.conf? See mine named openalpr.conf
  • Please post your directory structure of runtime_data, is it like mine? You shouldn't change there anything unless you know what you do...
  • Disconnected from the target VM, address: 'localhost:8603', transport: 'socket' is that all from logcat? If no config is found the alpr_impl is not initialized and every call to the API of new Alpr() will fail and kill your app. I posted it in my first post...

My openaplr.conf aka openAlprConfFile (can be anywhere as well outside of runtime_data):

ocr_img_size_percent = 1.33333333
state_id_img_size_percent = 2.0

; Calibrating your camera improves detection accuracy in cases where vehicle plates are captured at a steep angle
; Use the openalpr-utils-calibrate utility to calibrate your fixed camera to adjust for an angle
; Once done, update the prewarp config with the values obtained from the tool
;prewarp = planar,1280.000000,720.000000,0.000550,0.000750,0.130000,1.000000,1.000000,0.000000,0.000000
prewarp =

; detection will ignore plates that are too large.  This is a good efficiency technique to use if the
; plates are going to be a fixed distance away from the camera (e.g., you will never see plates that fill
; up the entire image
max_plate_width_percent = 100
max_plate_height_percent = 100

; detection_iteration_increase is the percentage that the LBP frame increases each iteration.
; It must be greater than 1.0.  A value of 1.01 means increase by 1%, 1.10 increases it by 10% each time.
; So a 1% increase would be ~10x slower than 10% to process, but it has a higher chance of landing
; directly on the plate and getting a strong detection
detection_iteration_increase = 1.20

; The minimum detection strength determines how sure the detection algorithm must be before signaling that
; a plate region exists.  Technically this corresponds to LBP nearest neighbors (e.g., how many detections
; are clustered around the same area).  For example, 2 = very lenient, 9 = very strict.
detection_strictness = 1

; The detection doesn't necessarily need an extremely high resolution image in order to detect plates
; Using a smaller input image should still find the plates and will do it faster
; Tweaking the max_detection_input values will resize the input image if it is larger than these sizes
; max_detection_input_width/height are specified in pixels
max_detection_input_width = 1280
max_detection_input_height = 960

; detector is the technique used to find license plate regions in an image.  Value can be set to
; lbpcpu    - default LBP-based detector uses the system CPU
; lbpgpu    - LBP-based detector that uses Nvidia GPU to increase recognition speed.
; lbpopencl - LBP-based detector that uses OpenCL GPU to increase recognition speed.  Requires OpenCV 3.0
; morphcpu  - Experimental detector that detects white rectangles in an image.  Does not require training.
detector = lbpcpu

; If set to true, all results must match a postprocess text pattern if a pattern is available.
; If not, the result is disqualified.
must_match_pattern = 1

; Bypasses plate detection.  If this is set to 1, the library assumes that each region provided is a likely plate area.
skip_detection = 0

; Specifies the full path to an image file that constrains the detection area.  Only the plate regions allowed through the mask
; will be analyzed.  The mask image must match the resolution of your image to be analyzed.  The mask is black and white.
; Black areas will be ignored, white areas will be searched.  An empty value means no mask (scan the entire image)
detection_mask_image =

; OpenALPR can scan the same image multiple times with different randomization.  Setting this to a value larger than
; 1 may increase accuracy, but will increase processing time linearly (e.g., analysis_count = 3 is 3x slower)
analysis_count = 2

; OpenALPR detects high-contrast plate crops and uses an alternative edge detection technique.  Setting this to 0.0
; would classify  ALL images as high-contrast, setting it to 1.0 would classify no images as high-contrast.
contrast_detection_threshold = 0.5

max_plate_angle_degrees = 90

ocr_min_font_point = 6

; Minimum OCR confidence percent to consider.
postprocess_min_confidence = 80

; Any OCR character lower than this will also add an equally likely
; chance that the character is incorrect and will be skipped.  Value is a confidence percent
postprocess_confidence_skip_level = 80

@alaroma
Copy link

alaroma commented Jan 16, 2020

  • In android studio my eu.conf was in the directory runtime_data / config, moving it to the directory /runtime_data alpr.isLoaded() returned true.
  • I already wrote that the structure is exactly the same as yours
  • No, it is console in debug process on my device:
`
01/16 19:07:58: Launching 'app' on Sony F3111.
$ adb shell am start -n "com.example.var8/com.example.var8.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -D
Waiting for application to come online: com.example.var8.test | com.example.var8
Waiting for application to come online: com.example.var8.test | com.example.var8
Waiting for application to come online: com.example.var8.test | com.example.var8
Connecting to com.example.var8
Connected to the target VM, address: 'localhost:8601', transport: 'socket'
Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page.
I/art: Late-enabling -Xcheck:jni
W/ActivityThread: Application com.example.var8 is waiting for the debugger on port 8100...
I/System.out: Sending WAIT chunk
I/art: Debugger is active
I/System.out: Debugger has connected
    waiting for debugger to settle...
I/System.out: waiting for debugger to settle...
I/System.out: waiting for debugger to settle...
I/System.out: waiting for debugger to settle...
I/System.out: waiting for debugger to settle...
I/System.out: waiting for debugger to settle...
I/System.out: waiting for debugger to settle...
I/System.out: debugger has settled (1428)
W/art: Before Android 4.1, method android.graphics.PorterDuffColorFilter androidx.vectordrawable.graphics.drawable.VectorDrawableCompat.updateTintFilter(android.graphics.PorterDuffColorFilter, android.content.res.ColorStateList, android.graphics.PorterDuff$Mode) would have incorrectly overridden the package-private method in android.graphics.drawable.Drawable
I/art: Rejecting re-init on previously-failed class java.lang.Class<androidx.core.view.ViewCompat$2>
I/art: Rejecting re-init on previously-failed class java.lang.Class<androidx.core.view.ViewCompat$2>
I/System: FinalizerDaemon: finalize objects = 1
I/[MALI][Gralloc]: [+]r_hnd(0x7f79939320), client(35), share_fd(33)
E/GED: Failed to get GED Log Buf, err(0)
I/OpenGLRenderer: Initialized EGL, version 1.4
I/OpenGLRenderer: Get enable program binary service property (1)
    Initializing program atlas...
I/OpenGLRenderer: Program binary detail: Binary length is 147292, program map length is 128.
I/OpenGLRenderer: Succeeded to mmap program binaries. File descriptor is 40, and path is /dev/ashmem.
    No need to use file discriptor anymore, close fd(40).
W/libEGL: [ANDROID_RECORDABLE] format: 1
I/PerfService: PerfServiceNative api init
I/[MALI][Gralloc]: [+]r_hnd(0x7f89060d20), client(35), share_fd(42)
I/Timeline: Timeline: Activity_idle id: android.os.BinderProxy@770160e time:6933542
I/[MALI][Gralloc]: [+]r_hnd(0x7f89060dc0), client(35), share_fd(44)
I/Timeline: Timeline: Activity_idle id: android.os.BinderProxy@770160e time:6937954
I/[MALI][Gralloc]: [+]r_hnd(0x7f890606e0), client(35), share_fd(46)
I/[MALI][Gralloc]: [+]r_hnd(0x7f89060fa0), client(35), share_fd(48)
I/[MALI][Gralloc]: [-]r_hnd(0x7f89060dc0), client(35), share_fd(44)
I/[MALI][Gralloc]: [-]r_hnd(0x7f890606e0), client(35), share_fd(46)
I/[MALI][Gralloc]: [-]r_hnd(0x7f89060fa0), client(35), share_fd(48)
I/[MALI][Gralloc]: [-]r_hnd(0x7f89060d20), client(35), share_fd(42)
E/C++->AlprImpl->OpenALPR Initialization Time:: 107.559384
I/Choreographer: Skipped 310 frames!  The application may be doing too much work on its main thread.
W/libEGL: [ANDROID_RECORDABLE] format: 1
W/libEGL: [ANDROID_RECORDABLE] format: 1
I/[MALI][Gralloc]: [+]r_hnd(0x7f89060d20), client(35), share_fd(44)
I/[MALI][Gralloc]: [+]r_hnd(0x7f89060b40), client(35), share_fd(46)
I/[MALI][Gralloc]: [+]r_hnd(0x7f89060c80), client(35), share_fd(48)
I/Timeline: Timeline: Activity_idle id: android.os.BinderProxy@770160e time:6950058
I/[MALI][Gralloc]: [+]r_hnd(0x7f890610e0), client(35), share_fd(42)
I/[MALI][Gralloc]: [+]r_hnd(0x7f89061180), client(35), share_fd(52)
I/C++->AlprImpl->recognizeFullDetails(): Analyzing:: eu
Disconnected from the target VM, address: 'localhost:8601', transport: 'socket'
`

@cefaci
Copy link

cefaci commented Jan 16, 2020

Did you read what I post?

In android studio my eu.conf was in the directory runtime_data / config, moving it to the directory /runtime_data alpr.isLoaded() returned true.

  • DON'T copy/move/touch ANYTHING in runtime_data, leave it as you have git cloned (unless you know what you do or extend it w/ other countries e.g. RU) and COPY it completly unchanged in your assets and then on your phone.
  • COPY my openaplr.conf aka openAlprConfFile, change it as needed, move it anywhere and load it in the new Alpr()
  • As I wrote 10 times country_code looks for a file in runtime_data/config/[coutry_code].xml and has nothing to do with the openaplr.conf aka openAlprConfFile! Your copied eu.xml is not a valid openAlprConfFile

I/C++->AlprImpl->recognizeFullDetails(): Analyzing:: eu
Disconnected from the target VM, address: 'localhost:8601', transport: 'socket'

openaplr.conf is not the runtime_data/config/[coutry_code].xml file. Just check the C++ code there are the default config values...

@alaroma
Copy link

alaroma commented Jan 16, 2020

Yes, I did exactly as you said. Here is my catalog.
Maybe the problem is how I call the function? (from asynk task)
1

@RobertSasak
Copy link

I started to migrate this gist into a proper repo with a Travis for building. So that we can easy reproduce building every time.

Repository https://github.com/RobertSasak/openalpr-android
Travis: https://travis-ci.com/RobertSasak/openalpr-android

Currently I have two issues:

  1. Error when building Tess-two. I believe this is issue with the latest AndroidX changes. I am quite sure that I need to downgrade something, but I am not sure what and to what version.
error: package android.support.annotation does not exist import android.support.annotation.Size;
  1. In cmake. I think this can also be resolved by downgrading NDK. What version I should use?
CMake Error at /usr/local/android-sdk/ndk-bundle/build/cmake/android.toolchain.cmake:250 (message):
  gnustl_static is no longer supported.  Please switch to either c++_shared
  or c++_static.  See
  https://developer.android.com/ndk/guides/cpp-support.html for more
  information.

We are using OpenAlpr in react-native projects https://github.com/RobertSasak/react-native-openalpr . This work very well. However Google Play requires 64bits libraries in order to publish app. So we are in desperate need to OpenAlpr 64bit libraries.

@karan-patel61
Copy link

I am having trouble generating the library files using this script but I am using:

Windows 10
Ubuntu 18.04

Which compiler do I need to use in order to generate the .so files Windows Visual Studio, Ninja, Unix Makefiles, or another compiler?

@AbhishekHirapara
Copy link

  • I mailed you the .so files
  • Use OpenCV 4.1.1 for android then you can link it in your project static
  • To check your code doesn't make sense, as everything is happening in the library and the return looks good, as it executes and takes time. I still think it can be your images or your configs are not right.
  • My build.gradle:
...
android {
    compileSdkVersion 28
    buildToolsVersion "28.0.3"

    defaultConfig {
        ...
        minSdkVersion 24
        targetSdkVersion 28
        versionCode 1
        versionName "1.0.02"
        ndk {
            abiFilters "arm64-v8a","armeabi-v7a"
        }
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }
    }
...
  • I mailed you the .so files
  • Use OpenCV 4.1.1 for android then you can link it in your project static
  • To check your code doesn't make sense, as everything is happening in the library and the return looks good, as it executes and takes time. I still think it can be your images or your configs are not right.
  • My build.gradle:
...
android {
    compileSdkVersion 28
    buildToolsVersion "28.0.3"

    defaultConfig {
        ...
        minSdkVersion 24
        targetSdkVersion 28
        versionCode 1
        versionName "1.0.02"
        ndk {
            abiFilters "arm64-v8a","armeabi-v7a"
        }
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }
    }
...

@cefaci Can you send me .so file for 64 bit? My mail is : abhijnd@gmail.com

@momodeveloper
Copy link

@AbhishekHirapara can you mail me the .so files please if you managed to get them ?, my mail is nassikmohamed7@gmail.com

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment