Android通过OpenCV和TesserartOCR实时进行识别

2019-07-30 12:28:32 浏览数 (1)

前言

最近一系列的文章都是用Android利用OpenCV NDK的方法通过摄像头实时获取图像进行图像处理,在上一篇《Android使用Tesseract-ocr进行文字识别》我们学习了一下TesserartOCR的图像识别功能,这一章主要介绍怎么样通过图像的处理再加上我们OCR的识别获取的想要的东西。

提前说了下,OpenCV我个人还是个小白阶段,原来的数据处理是想提取车牌信息再通过OCR把车牌识别出来,不过确实差强人意,不过我们整个程序的基本框架算是都完成了,只不过最后在OpenCV里的车牌定位什么的可能需要自己研究吧。

视频效果

代码实现

主框架

程序的主框架还是用《Android利用SurfaceView显示Camera图像爬坑记(六) -- 用OpenCV进行Canny边缘检测》里面的那套,我们重新建了一个新的项目,OpenCV还有NDK的设置都是按SurfaceView调用Camera的方式进行处理的。

TesserartOCR配置

《Android使用Tesseract-ocr进行文字识别》中我们通过导入Tess-Two这个Module后进行处理的,但是这个每次重新编译都要十几分钟,原理上它还是用的NDK方式,所以我们直接把Tess-Two编译好的so库用在这里,就不再引入这个Module了,用到的4个so库为

我们直接把这几个动态库放入到和Opencv相关的目录下,对应的不同的arm拷入,如下图

上面对应的so库放到一起后,我们在build.gradle中要加入这个的引入,如下图:

TesseratCallBack

为了不影响程序的流畅度,我们的OCR识别都是在线程中操作,这个接口是用于OCR识别后的文字通过这个回调函数接口传给主进程中。

VaccaeTesserat

这个类用的AsyncTask用于进行OCR的识别。

核心代码

代码语言:javascript复制
    @Override
    protected String doInBackground(Bitmap... bitmaps) {
        TessBaseAPI tessAPI=null;
        try {

            StringBuilder sb=new StringBuilder();
            // 核心预设置代码
            tessAPI=new TessBaseAPI();
            //如果Android的版本大于23,路径取根目录下的tesserart,小于的话是
            //在mnt/sdcard下面
            String path=Environment.getExternalStorageDirectory().getAbsolutePath()   File.separator   "tesserart";
            tessAPI.setDebug(true);
            tessAPI.init(path , "eng");
//            tessAPI.setPageSegMode(TessBaseAPI.PageSegMode.PSM_AUTO);
            tessAPI.setVariable(TessBaseAPI.VAR_CHAR_WHITELIST, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
            tessAPI.setVariable(TessBaseAPI.VAR_CHAR_BLACKLIST, "!@#$%^&*()_ =-[]}{;:'"\|~`,./<>?");

            //第一张为原图不取
            for (int i=1; i < bitmaps.length; i  ) {
                tessAPI.setImage(bitmaps[i]);
                // 获取并显示识别结果
                sb.append(tessAPI.getUTF8Text());
            }

            mCallBack.CallBackOver(sb.toString());
        } catch (Exception e) {
            Log.e("Tess", e.getMessage());
            mCallBack.CallBackOver(e.getMessage());
        } finally {
            tessAPI.clear();
            tessAPI.end();
        }

        return null;
    }

VaccaeOpenCVJNI

jni的方法里面定义了获取Cameraframe实时帧的图像,返回是Bitmap的列表,第一个还是原图用于显示,后面的就是我们截取的判断为车牌的矩形图用于OCR识别

native-lib.cpp

这里是JNI方法中的实现方法,主要是怎么将bitmap转为OpenCV中的Mat,和图像处理结束后怎么再生成List<Bitmap>,下图右边红框中就是图像处理的核心方法,这个我们写在了testcv的C 文件中。

图像处理核心方法

核心方法我们自己新建了一个C 的类,生成了testcv的头文件和源文件。

核心代码

这里面是我们查找类似车牌的处理方法,部分是参考网上的定位方法。

代码语言:javascript复制
//
// Created by 36574 on 2019-06-25.
//

#include "testcv.h"


bool testcv::VerifySize(RotatedRect candidate) {
    float error = 0.2; //20%的误差范围
    float aspect = 4.7272;//宽高比例
    int min = 15 * aspect * 15; //最小像素为15
    int max = 125 * aspect * 125;//最大像素为125
    float rmin = aspect - aspect * error;//最小误差
    float rmax = aspect   aspect * error;//最大误差
    int area = candidate.size.height * candidate.size.width;//求面积
    float r = (float) candidate.size.width / (float) candidate.size.height;//长宽比
    if (r < 1) r = 1 / r;
    if (area < min || area > max || r < rmin || r > rmax
        || abs(candidate.angle) > 10 || candidate.size.width < candidate.size.height) {
        return false;
    } else {
        return true;
    }
}

//获取多个截取的矩形
std::vector<Mat> testcv::getrectdetector(Mat &src) {
    Mat gray, imgsobel, dst;
    //转为灰度图
    cvtColor(src, gray, cv::COLOR_BGRA2GRAY);
    //高斯模糊
    GaussianBlur(gray, gray, Size(5, 5), 0.5, 0.5);
    //利用sobel滤波,对x进行求导,就是强调Y方向
    Sobel(gray, imgsobel, CV_8U, 1, 0, 3);
    //二值化
    threshold(imgsobel, imgsobel, 0, 255, THRESH_BINARY | THRESH_OTSU);
    //闭操作  这个Size很重要
    Mat element = getStructuringElement(MORPH_RECT, Size(21, 5));
    morphologyEx(imgsobel, imgsobel, MORPH_CLOSE, element);

    //提取轮廓
    std::vector<std::vector<cv::Point>> contours;
    findContours(imgsobel, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);

    //用来存放旋转矩形的容器
    std::vector<RotatedRect> Rotatedrects;

    //判断图像
    for (size_t i = 0; i < contours.size(); i  ) {
        //用来存放旋转矩形4个点
        Point2f Vertices[4];
        //寻找最小矩形
        RotatedRect currentrect = minAreaRect(Mat(contours[i]));
        //判断是不是要找的区域,如果是画线
        if (VerifySize(currentrect)) {
            currentrect.points(Vertices);
            //在源图上画四点的线
            for (size_t j = 0; j < 4; j  ) {
                line(src, Vertices[j], Vertices[(j   1) % 4], Scalar(0, 0, 255),
                     3);
            }
            //将符合的矩形存放到容器里
            Rotatedrects.push_back(currentrect);
        }
    }

    //用于存放识别到的图像
    std::vector<Mat>output;
    for (size_t i = 0; i < Rotatedrects.size(); i  ) {
        Mat dst_warp;
        Mat dst_warp_rotate;
        Mat rotMat(2, 3, CV_32FC1);
        dst_warp = Mat::zeros(src.size(), src.type());
        float r = (float)Rotatedrects[i].size.width / (float)Rotatedrects[i].size.height;
        float  angle = Rotatedrects[i].angle;
        if (r < 1)
            angle = angle   90;

        //其中的angle参数,正值表示逆时针旋转,关于旋转矩形的角度,以为哪个是长哪个是宽,在下面会说到
        rotMat = getRotationMatrix2D(Rotatedrects[i].center,angle, 1);
        //将矩形通过仿射变换修正回来
        warpAffine(src, dst_warp_rotate, rotMat, dst_warp.size());

        Size rect_size = Rotatedrects[i].size;
        if (r < 1)
            swap(rect_size.width, rect_size.height);

        //定义输出的图像
        Mat dst(Rotatedrects[i].size, CV_8U);
        //裁剪矩形,下面的函数只支持CV_8U 或者CV_32F格式的图像输入输出。
        //所以要先转换图像将RGBA改为RGB
        cvtColor(dst_warp_rotate, dst_warp_rotate, CV_RGBA2RGB);
        //裁剪矩形
        getRectSubPix(dst_warp_rotate, rect_size, Rotatedrects[i].center, dst);

        //将裁减到的矩形设置为相同大小,并且提高对比度
        Mat resultResized;
        resultResized.create(33, 144, CV_8UC3);
        resize(dst, resultResized, resultResized.size(), 0, 0, INTER_CUBIC);
        Mat grayResult;
        cvtColor(resultResized, grayResult, CV_BGR2GRAY);
        blur(grayResult, grayResult, Size(3, 3));
        //均值化提高对比度
        equalizeHist(grayResult, grayResult);

        //最终生成的矩形存放进vector<Mat>中
        output.push_back(grayResult);
    }

    return output;
}

总结及源码下载地址

项目中定位车牌的效果很一般,主要是自己也是OpenCV的初学者,个项目的主要目的是为了搭建出可以OpenCV及TesserartOCR的整个NDK的框架。

下载地址

GitHub:https://github.com/Vaccae/AndroidOpenCVTesserartOCR.git

-END-

0 人点赞