移动端车牌识别OCR-结合OpenCV

需求

最近产品那边说APP上要加个车牌识别的功能,用户不用手动输入,我说没问题啊加就加呗。脑子中第一反应就是第三方SDK,最终用了百度车牌识别SDK,完成了需求,集成方法详见“百度SDK文档”,好了文章到这里在可以结束了。 文章要是真结束了,提刀的兄弟估计又要砍我了,标题党一个,老是做这些***子放屁的事情,哈哈~~~。皮一下很开心。

问题

一开始我们确实用的百度车牌识别,但是识别率不是太高,而且车牌图片要上传到百度那边去,也会受网速影响,最重要的是,百度每天只能调用200次,多于200次要掏钱的,产品那边就说,能不能做成本地识别, 能啊,肯定可以啊,但是我还是个算法小白,怎么搞这个识别算法嘛,最后找了几个识别平台,某泊车平台,开口就要了八万,他们说还有另一种授权方式接入,一台机器400块,20台起售。虽说贵了点,但是识别率确实可以,我倒是想直接接过来,多省事了,但是公司肯定不想掏这个钱的啊,最后还是让开发想办法 。最苦逼的还是开发~~~~。

找方法

在百度上找了一大圈,大多数都是识别平台的广吿,也有几个说到了识别,但是说的比较模糊,还不提供源码,有的只是打着识别名号赚积分,所以我写文章的时候,只会写一些很实用的,真正能帮到大家的东西,废话不多说了,直奔主题,最后找到了两个识别的库:

一,EasyPR EasyPR github上 star 有五千多个了,但是由于长期没有更新了,新能源车牌,也不支持,所以没有使用这个库。

二, HyperLPR
HyperLPR 作者现在还在维护着,不止Android还支持其他平台的识别,最终选择了这个 ,但是作者关于Android方面的文档写的不是太多,以致于在集成过程中会遇到很多问题。下面我们一步一步来做。

实现

一,下载OpenCV : OpenCV官网:opencv.org/ OpenCV Android 3.4.6 版本 下载地址:nchc.dl.sourceforge.net/project/ope… 用3.4.6版本的,直接打开链接可下载,下载过程有点慢,没办法,多等会吧,我也是下了好长时间的。下载完解压。

二,新建项目,依赖OpenCV 在AS新建一个新项目名字随便取啦,然后在 APP 上右键 New-----> Module 在弹窗里选择 Import Eclipse ADT Project 点Next 。 选择你刚才解压的OpenCV目录下的 sdk/java 目录 点击 OK ,然后填下 Module Name ,我填的 "openCV" 然后 点Next -----> Finish。

接下来项目会报错的,打开 刚导入 的OpenCV 的 AndroidManifest.xml 删除 <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="21" /> 这行代码。

再打开 OpenCV 的 build.gradle 文件,把版本改成和APP 的build.gradle 文件想同的版本,如下:

在APP 的 build.gradle 加入 implementation project(path: ‘:openCV‘) 这行代码 OK,现在同步下项目。 还要添加 SO 文件, 在App 的 buidl.gradle 中 defaultConfig 下加入

 ndk   {
            //选择要添加的对应 cpu 类型的 .so 库。
            abiFilters ‘armeabi-v7a‘
            // 还可以添加 ‘x86‘, ‘x86_64‘, ‘mips‘, ‘mips64‘
        }

在main下新建jinLibs 目录 ,把OpenCV SDK 的解压目录下 sdk/native/libs/armeabi-v7a 目录拷到 jniLibs 下 好了OpenCV 现在完成了,检验下是否可用。

在MainActivity中的代码:

public class MainActivity extends AppCompatActivity {

    private final String TAG = getClass().getSimpleName();

    private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
        @SuppressLint("StaticFieldLeak")
        @Override
        public void onManagerConnected(int status) {
            super.onManagerConnected(status);
            if (status == LoaderCallbackInterface.SUCCESS) {
                Log.d(TAG, "OpenCV 加载成功");
            } else {
                Log.d(TAG, "OpenCV 加载失败");
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (!OpenCVLoader.initDebug()) {
            Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_4_0, getApplicationContext(), mLoaderCallback);
        } else {
            Log.d(TAG, "OpenCV library found inside package. Using it!");
            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
    }
}

运行下项目,能看到日志加载成功就没问题,其实OpenCV没加载成功的话,会有个对话框提示的。

配置NDK,编译库文件

现在NDK最新版本应该是 r20 吧,不能用最新的NDK,不然会出现编译过不去的问题,我们去官网下载 NDK-r14b 的版本,作者应该是在这个版本上开发的,附上下载 地址 : NDK 下载地址 下载对应平台的NDK,一定要下载 NDK-r14b版本。 配置好NDK,我们在main目录下面新建 jin 目录 ,然后把DemoLPR 库下载下来,把里面的 app/src/main/jni 目录下的 include 、src、还有javaWarpper.cpp 文件复制到 jni 目录下,把 assets/lpr/ 目录下的文件全部复制到我们项目的 assets目录下的lpr文件夹。 如下图:

我们在 项目 app文件下新建 CMakeLists.txt 文件,内容如下:

# Sets the minimum version of CMake required to build the native
# library. You should either keep the default value or only pass a
# value of 3.4.0 or lower.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds it for you.
# Gradle automatically packages shared libraries with your APK.
include_directories(src/main/jni/include)

include_directories(src/main/jni)
aux_source_directory(src/main/jni SOURCE_FILES)
aux_source_directory(src/main/jni/src SOURCE_FILES_CORE)
list(APPEND SOURCE_FILES ${SOURCE_FILES_CORE})

#修改修改为自己的opencv-android-sdk 的JNI路径
set(OpenCV_DIR /home/aleyn/Android/TestProject/OpenCV-android-sdk/sdk/native/jni)
find_package(OpenCV REQUIRED)

add_library( # Sets the name of the library.
        lpr
        # Sets the library as a shared library.
        SHARED
        # Provides a relative path to your source file(s).
        # Associated headers in the same location as their source
        # file are automatically included.
        ${SOURCE_FILES})

# Searches for a specified prebuilt library and stores the path as a
# variable. Because system libraries are included in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

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)

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in the
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
        lpr
        ${OpenCV_LIBS}
        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

我是用Linux环境开发的,上面的路径是Linux目录的路径,一定要替换成你解压过的OpenCV SDK的 jni 对应本地目录

下面我们修改 app的build.gradle 文件,最终配置如下:

apply plugin: ‘com.android.application‘

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.pcl.lpr"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        ndk {
            //选择要添加的对应 cpu 类型的 .so 库。
            abiFilters ‘armeabi-v7a‘
            // 还可以添加 ‘x86‘, ‘x86_64‘, ‘mips‘, ‘mips64‘
        }

        externalNativeBuild {
            cmake {
                cppFlags "-std=gnu++11"
                // 注意!!!!       注意!!!!!!
                //如果是用 Linux 开发的的用户下面这行代码不用动,如果是 Win 用户请把 下面这行代码注释了
                arguments "-DANDROID_TOOLCHAIN=gcc", "-DANDROID_ARM_NEON=TRUE", "-DANDROID_STL_FORCE_FEATURES=OFF"
            }
        }
    }

    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile(‘proguard-android-optimize.txt‘), ‘proguard-rules.pro‘
        }
    }
}

dependencies {
    implementation fileTree(dir: ‘libs‘, include: [‘*.jar‘])
    implementation ‘com.android.support:appcompat-v7:28.0.0‘
    implementation ‘com.android.support.constraint:constraint-layout:1.1.3‘
    testImplementation ‘junit:junit:4.12‘
    androidTestImplementation ‘com.android.support.test:runner:1.0.2‘
    androidTestImplementation ‘com.android.support.test.espresso:espresso-core:3.0.2‘
    implementation project(path: ‘:openCV‘)
}

OK,我们在项目下新建 PlateRecognition 类和 DeepAssetUtil: PlateRecognition:

package com.pcl.lpr.utils;

/**
 * @auther : Aleyn
 * time   : 2019/04/24
 */
public class PlateRecognition {

    static {
        System.loadLibrary("lpr");
    }

    static native long InitPlateRecognizer(String casacde_detection,
                                           String finemapping_prototxt, String finemapping_caffemodel,
                                           String segmentation_prototxt, String segmentation_caffemodel,
                                           String charRecognization_proto, String charRecognization_caffemodel,
                                           String segmentation_free_prototxt, String segmentation_free_caffemodel);

    static native void ReleasePlateRecognizer(long object);

    public static native String SimpleRecognization(long inputMat, long object);

}

如果你的包名和我的不一样,一定要到javaWartpper.cpp 文件修改成 和你对应的。

DeepAssetUtil:

package com.pcl.lpr.utils;

import android.content.Context;
import android.os.Environment;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class DeepAssetUtil {

    public static final String ApplicationDir = "lpr";
    public static final String CASCADE_FILENAME = "cascade.xml";
    public static final String FINEMAPPING_PROTOTXT = "HorizonalFinemapping.prototxt";
    public static final String FINEMAPPING_CAFFEMODEL = "HorizonalFinemapping.caffemodel";
    public static final String SEGMENTATION_PROTOTXT = "Segmentation.prototxt";
    public static final String SEGMENTATION_CAFFEMODEL = "Segmentation.caffemodel";
    public static final String RECOGNIZATION_PROTOTXT = "CharacterRecognization.prototxt";
    public static final String RECOGNIZATION_CAFFEMODEL = "CharacterRecognization.caffemodel";
    public static final String FREE_INCEPTION_PROTOTXT = "SegmenationFree-Inception.prototxt";
    public static final String FREE_INCEPTION_CAFFEMODEL = "SegmenationFree-Inception.caffemodel";

    public static final String SDCARD_DIR = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + ApplicationDir; //解压文件存放位置

    private static void CopyAssets(Context context, String assetDir, String dir) {
        String[] files;
        try {
            // 获得Assets一共有几多文件
            files = context.getAssets().list(assetDir);
        } catch (IOException e1) {
            return;
        }
        File mWorkingPath = new File(dir);
        // 如果文件路径不存在
        if (!mWorkingPath.exists()) {
            // 创建文件夹
            if (!mWorkingPath.mkdirs()) {
                // 文件夹创建不成功时调用
            }
        }

        for (String file : files) {
            try {
                // 根据路径判断是文件夹还是文件
                if (!file.contains(".")) {
                    if (0 == assetDir.length()) {
                        CopyAssets(context, file, dir + file + "/");
                    } else {
                        CopyAssets(context, assetDir + "/" + file, dir + "/" + file + "/");
                    }
                    continue;
                }
                File outFile = new File(mWorkingPath, file);
                if (outFile.exists())
                    continue;
                InputStream in;
                if (0 != assetDir.length()) {
                    in = context.getAssets().open(assetDir + "/" + file);
                } else {
                    in = context.getAssets().open(file);
                }

                OutputStream out = new FileOutputStream(outFile);
                // Transfer bytes from in to out
                byte[] buf = new byte[1024];
                int len;
                while ((len = in.read(buf)) > 0) {
                    out.write(buf, 0, len);
                }

                in.close();
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private static void copyFilesFromAssets(Context context) {
        DeepAssetUtil.CopyAssets(context, ApplicationDir, SDCARD_DIR);
    }

    //初始化识别资源
    public static long initRecognizer(Context context) {
        String cascade_filename = SDCARD_DIR + File.separator + CASCADE_FILENAME;
        String finemapping_prototxt = SDCARD_DIR + File.separator + FINEMAPPING_PROTOTXT;
        String finemapping_caffemodel = SDCARD_DIR + File.separator + FINEMAPPING_CAFFEMODEL;
        String segmentation_prototxt = SDCARD_DIR + File.separator + SEGMENTATION_PROTOTXT;
        String segmentation_caffemodel = SDCARD_DIR + File.separator + SEGMENTATION_CAFFEMODEL;
        String character_prototxt = SDCARD_DIR + File.separator + RECOGNIZATION_PROTOTXT;
        String character_caffemodel = SDCARD_DIR + File.separator + RECOGNIZATION_CAFFEMODEL;
        String segmentation_free_prototxt = SDCARD_DIR + File.separator + FREE_INCEPTION_PROTOTXT;
        String segmentation_free_caffemodel = SDCARD_DIR + File.separator + FREE_INCEPTION_CAFFEMODEL;
        copyFilesFromAssets(context);
        //调用JNI 加载资源函数
        return PlateRecognition.InitPlateRecognizer(
                cascade_filename,
                finemapping_prototxt, finemapping_caffemodel,
                segmentation_prototxt, segmentation_caffemodel,
                character_prototxt, character_caffemodel,
                segmentation_free_prototxt, segmentation_free_caffemodel);
    }
}

好了现在识别功能可以用了,差个识别界面

识别界面:

识别界面其实就是一个相机界面,我随便写了一个,还做了个识别框不过没有做屏幕适配,本来想把代码都贴到文章里的,但是稍微有一点多,大家还是看下Demo里边吧。结合Demo把识别功能,移植到你们的项目中。

Demo运行说明

Demo地址: LPR 打开项目肯定会报错,做以下修改

  1. 用AS打开项目
  2. 设置项目NDK为 NDK-r14b
  3. 先修改 CMakeLists.txt 文件, 把第19行修改成你本地的 OpenCV SDK 的对应路径。
  4. 跟据自己的开发平台,设置app下的build.gradle 第 23 行代码 是否要注释。 完成以上步骤后再运行项目,就没有问题了。

推荐:Android学习PDF+架构视频+面试文档+源码笔记

就这样,有问题请留言,更多文章,敬请期待。

此外,我还有一份自己收录、整理和分类的资料分享,Android学习PDF+架构视频+面试文档+源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,还有高级进阶架构资料分享,希望能帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也是可以分享给身边好友一起学习的!

可以扫微信二维码加我免费领取

原文地址:https://blog.51cto.com/14573572/2442964

时间: 2024-11-09 02:22:42

移动端车牌识别OCR-结合OpenCV的相关文章

移动端车牌识别ocr系统

移动端车牌识别ocr系统优点: 1.识别速度快:高度优化的车牌定位和识别算法,识别时间≤50毫秒(200万图片). 2.识别率:白天识别率≥99.7%:夜间识别率≥98%. 3.识别速度:单张图片识别时间≤50毫秒(200万图像). 4.像素宽度:60-400像素宽度. 5.特征识别:车牌颜色.车标类型.车身颜色. 6.车牌类型:普通蓝牌.普通黄牌(单层).双层黄牌.新式军车车牌.新式武警车牌.使馆车牌.农用车牌等各种规格汽车号牌. 7.专注于移动端车牌识别ocr软件的研发:文通是专业的OCR产

技术解析: 手机车牌识别软件—移动端车牌识别/OCR算法

一直以来有朋友就<手机车牌识别软件-OCR算法>与我进行讨论,由于个人原因,一直没有予以应答.今天借这个机会,就和大家一起分享一下所谓的:手机车牌识别软件. 第一种应用场景: 车牌识别的应用场景随处可见:高速公路上超速抓拍.小区门口关卡.车库入口关卡,甚至出现在车载设备上.它的工作原理大致这样:使用摄像头充当"眼睛",使用openCV与深度学习充当"大脑".实时车牌识别工作步骤:摄像头抓拍->openCV初步定位车牌位置->二次确认车牌位置的

移动端车牌识别OCR识别的流程介绍

移动端车牌识别背景 越发达的地区,人均保有车辆越多,加上我国的"互联网+"提出,移动业务越来越兴旺发达,智能终端(智能手机及平板电脑)及移动通信(4G)发展迅速,人们用手机的频率比用电脑的多,灵活便捷,随处可用,因此,手机成为生活中必不可少的工具."互联网+"迫使得移动端APP应用火爆,如今警务方面的办事办案,都离不开移动端的支持,收费也是,没有PDA,连锁就不完美.现今易泊将原来应用在电脑端的车牌识别技术转移到了移动端,可谓是移动端车牌应运而生,OCR识别能够减轻

移动端车牌识别OCR技术怎么样?

说到车牌识别,大家第一时间应该想到的是一个大的相机在一边拍照,然后上传到后台进行识别处理,然后在返回数据,这样处理速度特别慢,而且有时候不安全,要是没信号或者一些别的原因,可能就断掉了.易泊时代推出的移动端车牌识别,在原有的数据上,进行了整合,从而提高了大大效率. 首先看下他的一些功能特点: 1.速度快,识别只要0.5s: 2.识别率高.高达99.7%.目前市场上最高的识别率: 3.支持大角度拍摄,准确识别车牌:可能会有环境因素. 4.支持安卓跟iOS俩大平台. 5.视频流识别,只需扫一扫就可识

移动端车牌识别,OCR光学字符识别技术

移动端车牌识别技术,是在OCR光学字符识别技术的基础上研发的用来识别汽车号牌特征信息的图像识别技术.在国内,该项技术由北京易泊时代携手清华大学成功地将"国家863计划"项目成果--"文字图像识别技术TH-OCR"产业化,真正实现了TH-OCR技术与市场应用的完美结合. 一.移动端车牌识别应用背景 随着我国警务通.停车场手持收费机等移动终端的使用越来越普及,车辆越来越多,对车的管理要高效也是必然的,如果在这些终端上能够集成车牌识别功能,必然省时省力,对于维护交通安全和

移动端车牌识别——可以嵌入智能手机系统里的新OCR识别技术

移动端车牌识别技术,是在OCR光学字符识别技术的基础上研发的用来识别汽车号牌特征信息的图像识别技术.在国内,该项技术由北京易泊时代携手清华大学成功地将"国家863计划"项目成果--"文字图像识别技术TH-OCR"产业化,真正实现了TH-OCR技术与市场应用的完美结合. 一.移动端车牌识应用背景 随着我国警务通.停车场手持收费机等移动终端的使用越来越普及,车辆越来越多,对车的管理要高效也是必然的,如果在这些终端上能够集成车牌识别功能,必然省时省力,对于维护交通安全和城

ocr移动端车牌识别技术特点

目前很多地方都会用到移动端前端车牌识别OCR技术,停车场出入口.路边停车.汽车保养维修.尤其是移动警务等,无论是手机的,还是PDA的,甚至的IOS的苹果手机都能用,通过车牌识别这个技术,实现快速对车辆进行管理与服务.本司安卓前端车牌识别走在技术前沿,有大量真实案例支撑,"源于清华.服务全球"是我们的一贯宗旨目前很多地方都会用到移动端前端车牌识别OCR技术,停车场出入口.路边停车.汽车保养维修.尤其是移动警务等,无论是手机的,还是PDA的,甚至的IOS的苹果手机都能用,通过车牌识别这个技

移动端车牌识别技术

移动端车牌识别OCR技术是我公司开发的基于移动平台的车牌识别软件开发包,支持android.ios等多种主流移动操作系统.该产品采用手机.平板电脑摄像头拍摄汽车牌照图像,然后通过OCR软件对车牌颜色.车牌号进行识别.目前很多地方都会用到移动端前端车牌识别OCR技术,停车场出入口.路边停车.汽车保养维修.尤其是移动警务等,无论是手机的,还是PDA的,甚至的IOS的苹果手机都能用,通过车牌识别这个技术,实现快速对车辆进行管理与服务.本司安卓前端车牌识别走在技术前沿,有大量真实案例支撑,"源于清华.服

怎么才能在APP里实现移动端车牌识别功能?

随着移动通信技术和互联网技术的不断发展, 使手机成为了重要的生产和生活工具, 手机已经成为信息时代中最重要的信息来源.企业为了迎合市场需求,也纷纷加入了移动互联大军,到现在基于手机端的APP已经深入到人们生活中的方方面面.那么在APP中集成移动端车牌识别算法需要注意哪些事项呢? 移动端车牌识别OCR技术描述 移动端车牌识别OCR技术是基于移动平台的车牌识别软件开发包,支持android.ios等多种主流移动操作系统.该产品采用手机.平板电脑摄像头拍摄汽车牌照图像,然后通过OCR软件对车牌颜色.车