前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android通过OpenCV和TesserartOCR实时进行识别

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

作者头像
Vaccae
发布2019-07-30 12:28:32
3.8K0
发布2019-07-30 12:28:32
举报
文章被收录于专栏:微卡智享

前言

最近一系列的文章都是用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-

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-07-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 微卡智享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
文字识别
文字识别(Optical Character Recognition,OCR)基于腾讯优图实验室的深度学习技术,将图片上的文字内容,智能识别成为可编辑的文本。OCR 支持身份证、名片等卡证类和票据类的印刷体识别,也支持运单等手写体识别,支持提供定制化服务,可以有效地代替人工录入信息。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档