Skip to content

Instantly share code, notes, and snippets.

@jav974
Last active January 4, 2023 13:11
Show Gist options
  • 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 !!!"
@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