美文网首页
Android高级 NDK开发图片识别

Android高级 NDK开发图片识别

作者: LiuJP | 来源:发表于2020-04-06 15:06 被阅读0次
    NDK开发
    案例图片上的身份证号码

    alipay 支付中的libidcardtextcut.so
    wechat 微信中libIDCardRecog.so

    身份证识别

    身份证识别

    OpenCV

    OpenCV

    处理流程

    轮廓检测,图片膨胀,轮廓检测


    身份证识别

    图像处理流程

    图像处理流程

    CMakeLists.txt

    
    cmake_minimum_required(VERSION 3.4.1)
    
    #头文件配置
    include_directories(include)
    
    #编译头文件
    file(GLOB my_source_path ${CMAKE_SOURCE_DIR}/*.cpp ${CMAKE_SOURCE_DIR}/*.c)
    
    
    #添加动态连接库
    add_library(
            OpenCV
            SHARED
            ${my_source_path})
    
    add_library(
            lib_opencv
            SHARED
            IMPORTED)
    
    #设置本地so库
    set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}/libopencv_java4.so)
    
    
    #find_library(
    #        log-lib
    #
    #        log)
    
    target_link_libraries(
            OpenCV
            log
            jnigraphics#图像
            ${log-lib}
            lib_opencv)
    

    java代码

        static {
            System.loadLibrary("OpenCV");
        }
    
        private ImageView iv_pic_idcard;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            iv_pic_idcard = (ImageView) findViewById(R.id.pic_idcard);
            findViewById(R.id.pic_prev).setOnClickListener(this);
            findViewById(R.id.pic_next).setOnClickListener(this);
            findViewById(R.id.pic_parse).setOnClickListener(this);
    
        }
    
        public native String stringFromJNI();
    
        @Override
        public void onClick(View v) {
            int id = v.getId();
            if (id == R.id.pic_prev) {
                iv_pic_idcard.setImageResource(R.drawable.idcard0);
            } else if (id == R.id.pic_next) {
                iv_pic_idcard.setImageResource(R.drawable.idcard0);
            } else if (id == R.id.pic_parse) {
                Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.idcard0);
                Bitmap bitmap1 = findInNumber(bitmap,Bitmap.Config.ARGB_8888);
                bitmap.recycle();
                if (bitmap1!=null){
                    iv_pic_idcard.setImageBitmap(bitmap1);
                }else {
                    return;
                }
            }
        }
    
        private native Bitmap findInNumber(Bitmap bitmap, Bitmap.Config argb8888);
    

    layout

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">
    
        <ImageView
            android:id="@+id/pic_idcard"
            android:layout_width="wrap_content"
            android:layout_height="160dp"
            android:scaleType="centerInside" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
    
            <Button
                android:id="@+id/pic_prev"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="上一张" />
    
            <Button
                android:id="@+id/pic_next"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="下一张" />
    
    
        </LinearLayout>
    
        <Button
            android:id="@+id/pic_parse"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="识别" />
    
    </LinearLayout>
    

    native代码

    缺少一段mat2Bitmap 和bitmap2Mat的代码

    extern "C"
    JNIEXPORT jobject JNICALL
    Java_com_example_ljp_idrecognize_MainActivity_findInNumber(JNIEnv *env, jobject instance,
                                                               jobject bitmap, jobject argb8888) {
    
        //1,bitmap转为矩阵
        Mat src_img;
        Mat dst_img;
        BitmapToMat(env, bitmap, src_img);
        //2,归一化
        Mat dst;
        resize(src_img, dst, FIX_IDCARD_SIZE);
        //3,灰度化
        cvtColor(src_img, dst, COLOR_RGB2GRAY);
        //4,二值化  阈值100高于100是255白色
        threshold(dst, dst, 100, 255, THRESH_BINARY);
        //5,膨胀处理
        Mat erodeElement = getStructuringElement(MORPH_RECT, Size(40, 10));
        erode(dst, dst, erodeElement);
        //6,轮廓检测
        vector<vector<Point>> contours;
        vector<Rect> rects;
        findContours(dst, contours, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
        //7,逻辑处理
        for (int i = 0; i < contours.size(); ++i) {
            //获取矩形
            Rect rect = boundingRect(contours.at(i));
            //绘制
    //        rectangle(dst, rect, Scalar(0, 0, 255));
            if (rect.width > rect.height * 8 && rect.width < rect.height * 16) {
                //需要的区域
                rects.push_back(rect);
            }
        }
        //8,获取最终区域
        int lowPoint = 0;
        Rect finalRect;
        for (int i = 0; i < rects.size(); ++i) {
            Rect rect = rects.at(i);
            Point point = rect.tl();
            if (point.y > lowPoint) {
                lowPoint = point.y;
                finalRect = rect;
            }
        }
        //9,裁剪
        dst_img = src_img(finalRect);
        //10,回收
        free(&dst);
        free(&erodeElement);
        free(&contours);
        free(&rects);
        free(&finalRect);
        //11,矩阵转bitmap
        return createBitmap(env,dst_img,argb8888);
    }
    

    Utils.h

    //
    // Created by LJP on 2020/4/6.
    //
    
    #ifndef IDRECOGNIZE_UTILS_H
    #define IDRECOGNIZE_UTILS_H
    
    #include <android/bitmap.h>
    #include <opencv2/opencv.hpp>
    #include <android/log.h>
    #include <jni.h>
    #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "error", __VA_ARGS__))
    #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "debug", __VA_ARGS__))
    
    using namespace std;
    using namespace cv;
    
    extern "C"{
        void MatToBitmap (JNIEnv *env, Mat& mat, jobject& bitmap);
        void BitmapToMat (JNIEnv *env, jobject& bitmap, Mat& mat);
        jobject createBitmap (JNIEnv *env,  Mat& mat,jobject config);
    }
    
    #endif //IDRECOGNIZE_UTILS_H
    
    Utils.cpp
    //
    // Created by LJP on 2020/4/6.
    //
    
    #include "utils.h"
    //#include "opencv2/core/base.hpp"
    
    void BitmapToMat2(JNIEnv *env, jobject& bitmap, Mat& mat, jboolean needUnPremultiplyAlpha) {
        AndroidBitmapInfo info;
        void *pixels = 0;
        Mat &dst = mat;
    
        try {
            LOGD("nBitmapToMat");
            CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
            CV_Assert(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888 ||
                      info.format == ANDROID_BITMAP_FORMAT_RGB_565);
            CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);
            CV_Assert(pixels);
            dst.create(info.height, info.width, CV_8UC4);
            if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
                LOGD("nBitmapToMat: RGBA_8888 -> CV_8UC4");
                Mat tmp(info.height, info.width, CV_8UC4, pixels);
                if (needUnPremultiplyAlpha) cvtColor(tmp, dst, COLOR_mRGBA2RGBA);
                else tmp.copyTo(dst);
            } else {
                // info.format == ANDROID_BITMAP_FORMAT_RGB_565
                LOGD("nBitmapToMat: RGB_565 -> CV_8UC4");
                Mat tmp(info.height, info.width, CV_8UC2, pixels);
                cvtColor(tmp, dst, COLOR_BGR5652RGBA);
            }
            AndroidBitmap_unlockPixels(env, bitmap);
            return;
        } catch (const cv::Exception &e) {
            AndroidBitmap_unlockPixels(env, bitmap);
            LOGE("nBitmapToMat catched cv::Exception: %s", e.what());
            jclass je = env->FindClass("org/opencv/core/CvException");
            if (!je) je = env->FindClass("java/lang/Exception");
            env->ThrowNew(je, e.what());
            return;
        } catch (...) {
            AndroidBitmap_unlockPixels(env, bitmap);
            LOGE("nBitmapToMat catched unknown exception (...)");
            jclass je = env->FindClass("java/lang/Exception");
            env->ThrowNew(je, "Unknown exception in JNI code {nBitmapToMat}");
            return;
        }
    }
    
    void BitmapToMat(JNIEnv *env, jobject& bitmap, Mat& mat) {
        BitmapToMat2(env, bitmap, mat, false);
    }
    
    void MatToBitmap2
            (JNIEnv *env, Mat& mat, jobject& bitmap, jboolean needPremultiplyAlpha) {
        AndroidBitmapInfo info;
        void *pixels = 0;
        Mat &src = mat;
    
        try {
            LOGD("nMatToBitmap");
            CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
            CV_Assert(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888 ||
                      info.format == ANDROID_BITMAP_FORMAT_RGB_565);
            CV_Assert(src.dims == 2 && info.height == (uint32_t) src.rows &&
                      info.width == (uint32_t) src.cols);
            CV_Assert(src.type() == CV_8UC1 || src.type() == CV_8UC3 || src.type() == CV_8UC4);
            CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);
            CV_Assert(pixels);
            if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
                Mat tmp(info.height, info.width, CV_8UC4, pixels);
                if (src.type() == CV_8UC1) {
                    LOGD("nMatToBitmap: CV_8UC1 -> RGBA_8888");
                    cvtColor(src, tmp, COLOR_GRAY2RGBA);
                } else if (src.type() == CV_8UC3) {
                    LOGD("nMatToBitmap: CV_8UC3 -> RGBA_8888");
                    cvtColor(src, tmp, COLOR_RGB2RGBA);
                } else if (src.type() == CV_8UC4) {
                    LOGD("nMatToBitmap: CV_8UC4 -> RGBA_8888");
                    if (needPremultiplyAlpha)
                        cvtColor(src, tmp, COLOR_RGBA2mRGBA);
                    else
                        src.copyTo(tmp);
                }
            } else {
                // info.format == ANDROID_BITMAP_FORMAT_RGB_565
                Mat tmp(info.height, info.width, CV_8UC2, pixels);
                if (src.type() == CV_8UC1) {
                    LOGD("nMatToBitmap: CV_8UC1 -> RGB_565");
                    cvtColor(src, tmp, COLOR_GRAY2BGR565);
                } else if (src.type() == CV_8UC3) {
                    LOGD("nMatToBitmap: CV_8UC3 -> RGB_565");
                    cvtColor(src, tmp, COLOR_RGB2BGR565);
                } else if (src.type() == CV_8UC4) {
                    LOGD("nMatToBitmap: CV_8UC4 -> RGB_565");
                    cvtColor(src, tmp, COLOR_RGBA2BGR565);
                }
            }
            AndroidBitmap_unlockPixels(env, bitmap);
            return;
        } catch (const cv::Exception &e) {
            AndroidBitmap_unlockPixels(env, bitmap);
            LOGE("nMatToBitmap catched cv::Exception: %s", e.what());
            jclass je = env->FindClass("org/opencv/core/CvException");
            if (!je) je = env->FindClass("java/lang/Exception");
            env->ThrowNew(je, e.what());
            return;
        } catch (...) {
            AndroidBitmap_unlockPixels(env, bitmap);
            LOGE("nMatToBitmap catched unknown exception (...)");
            jclass je = env->FindClass("java/lang/Exception");
            env->ThrowNew(je, "Unknown exception in JNI code {nMatToBitmap}");
            return;
        }
    }
    
    void MatToBitmap(JNIEnv *env, Mat& mat, jobject& bitmap) {
        MatToBitmap2(env, mat, bitmap, false);
    }
    
    jobject createBitmap(JNIEnv *env, Mat &mat, jobject config) {
        jclass jclass1 = env->FindClass("android/graphics/Bitmap");
        jmethodID jmethodID1 = env->GetStaticMethodID(jclass1, "createBitmap",
                                                      "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
        jobject jobject1 = env->CallStaticObjectMethod(jclass1, jmethodID1, mat.cols, mat.rows, config);
        MatToBitmap(env, mat, jobject1);
        return jobject1;
    }
    

    问题1undefined reference to 'cv::error(int, std::string const&, char const, char const, int)'

    如果遇到链接错误,一般是lib的路径不对,但是显然这次不是,错误如下:
    error: undefined reference to 'cv::error(int, std::string const&, char const, char const, int)'
    error: undefined reference to 'cv::error(int, std::string const&, char const, char const, int)'
    显然是链上了,但是找不到特定函数的实现,比如error() ,imread(),imwrite()等等
    幸而stackover上opencv4.0.1已经有过这个问题了,见
    https://stackoverflow.com/questions/54376290/opencv-4-0-1-link-failure-in-android
    从NDK r16版本开始,Android NDK 切换到LLVM的libc ++。在新的主要发行版OpenCV4.0中,也从GNU的libstdc ++切换到了libc ++。
    如果您设置“ -DANDRID_STL = gnustl_shared”,则它将无法工作,因为默认的OpenCV二进制文件是使用libc ++而非gnustl构建的。
    您应该在build.gradle文件中设置cmake参数“ -DANDROID_STL = c ++ _ shared”,如下所示:

     externalNativeBuild {
                cmake {
                     //sample cpp flag parameters
                    cppFlags "-std=c++14 -Ofast -Rpass-analysis=loop-vectorize -fsave-optimization-record -fdiagnostics-show-hotness"
                     //sample abi filter parameters
                    abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
                    //set -DANDROID_STL to c++_shared
                    arguments "-DANDROID_STL=c++_shared" 
                }
            }
    
    build.gradle
    android {
        compileSdkVersion 28
        defaultConfig {
            applicationId "com.example.ljp.idcard"
            minSdkVersion 21
            targetSdkVersion 28
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
            externalNativeBuild {
                cmake {
                    cppFlags "-std=c++11 -frtti -fexceptions"
                    abiFilters 'armeabi-v7a' //添加平台架构类型
                    arguments "-DANDROID_STL=c++_shared"
                }
            }
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
        externalNativeBuild {
            cmake {
                path "src/main/cpp/CMakeLists.txt"
            }
        }
    }
    

    ORC 光学字符识别

    traineddata 训练库的制作

    https://sourceforge.net/projects/vietocr/files/jTessBoxEditor/

    相关文章

      网友评论

          本文标题:Android高级 NDK开发图片识别

          本文链接:https://www.haomeiwen.com/subject/ubcbphtx.html