zoukankan      html  css  js  c++  java
  • Android ORC文字识别之识别身份证号等(附源码)

    项目地址
    https://github.com/979451341/OrcTest

    我们说说实现这个项目已实现的功能,能够截图手机界面的某一块,将这个某一块图片的Bitmap传给tess-two的代码来获取扫描结果

    我这里在贴出tess-two这个专为Android而创建的文字识别框架的地址
    https://github.com/rmtheis/tess-two

    接下来我就说我如何一步一步的实现项目

    1.实现基础界面,我这里贴出已完成的界面

     

    这样是为了模仿扫描二维码的界面,因为扫描身份证号码或者是手机号那样长条的数字,就将扫描区域也做成长条状,这个扫描区域是有意义的,因为到时候截图会只将扫描区域里的图片信息拿去扫描,这也是为了提高扫描速度和精度。

    首先要实现这个界面,我们需要画出四个灰色长方体的位置大小,上下左右。

    left是扫描区域左边离手机屏幕左边的距离是手机屏幕宽度的1/10,right就是扫描区域右边离手机屏幕左边的距离是手机屏幕宽度的9/10,top是扫描区域顶部离手机屏幕顶部的距离是手机屏幕宽度的1/3,bottom是扫描区域底部离手机屏幕顶部的距离是手机屏幕宽度的4/9

            WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            Display display = manager.getDefaultDisplay();
    
            PMwidth = display.getWidth();
            PMheight = display.getHeight();
    
            left = PMwidth/10;
            top = PMheight/3;
            right = PMwidth*9/10;
            bottom = PMheight*4/9;
            mFrameRect = new Rect(left,top,right,bottom);

    画画

        @Override
        public void onDraw(Canvas canvas) {
            int width = PMwidth;
            int height = PMheight;
            Rect frame = mFrameRect;
    
            // 绘制焦点框外边的暗色背景
            mPaint.setColor(mMaskColor);
            canvas.drawRect(0, 0, width, frame.top, mPaint);
            canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, mPaint);
            canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, mPaint);
            canvas.drawRect(0, frame.bottom + 1, width, height, mPaint);
    
        }

    还没有完,还有布局文件放SurfaceView和按钮,还有刚才做的自定义View

    2.显示Camera预览和Camera拍摄


    这里SurfaceView如何显示Camera我不多说,只说如何把Camera预览变清晰,这里是通过循环自动对焦来完成。

    设置自动对焦接口

    mCamera.autoFocus(autoFocusCallback);

    这个接口初始化传入了Handler

    autoFocusCallback.setHandler(handler,MSG_AUTOFUCS);

    然后这个接口实现类里,当完成自动对焦,会通过handler发送一个消息

        @Override
        public void onAutoFocus(boolean success, Camera camera) {
            Log.v("zzw", "autof focus "+success);
            if (mAutoFocusHandler != null) {
                mAutoFocusHandler.sendEmptyMessageDelayed(mAutoFocusMessage,AUTO_FOCUS_INTERVAL_MS);
    //            mAutoFocusHandler = null;
            } else {
                Log.v(TAG, "Got auto-focus callback, but no handler for it");
            }
        }
    

    然后handler如何执行以下代码,再进行一次自动对焦,这样就完成了循环

                        case MSG_AUTOFUCS:
                            cameraUtil.autoFocus();
                            break;

    然后给按钮赋予拍摄功能,拍摄的还要停止聚焦

                    handler.removeCallbacksAndMessages(null);
                    cameraUtil.takePicture(TwoActivity.this,TwoActivity.this,TwoActivity.this);

    这个函数会被调用,data就是图片数据

        @Override
        public void onPictureTaken(byte[] data, Camera camera) 

    这里要注意一件事,拍摄后Camera预览界面就会停止,因为他停止聚焦了,我们需要重新设置自动对焦,并开启预览

        // 刷新相机
        public void refreshCamera(){
            if (surfaceHolder.getSurface() == null){
                // preview surface does not exist
                return;
            }
    
            // stop preview before making changes
            try {
                mCamera.stopPreview();
            } catch(Exception e){
                // ignore: tried to stop a non-existent preview
            }
    
            // set preview size and make any resize, rotate or
            // reformatting changes here
            // start preview with new settings
    
    
    
    
    
            try {
                mCamera.setPreviewDisplay(surfaceHolder);
                mCamera.startPreview();
                mCamera.autoFocus(autoFocusCallback);
            } catch (Exception e) {
    
            }
            surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        }

    3.处理图片数据,完成局部截图


    继续在onPictureTaken函数的data数据处理

    因为处理图片是耗时任务,所以开启子线程完成

    这里先开启一个等待对话框

            if(!mypDialog.isShowing())
            mypDialog.show();

    然后开启子线程

            if(data != null){
                new Thread(new BitmapThread(bitmap,data,handler,TwoActivity.this)).start();
    
            }

    将data转换为Bitmap数据

            bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);

    将图片旋转90度

            bitmap = rotateBitmap(bitmap,90);

    这是旋转Bitmap的函数

        public static Bitmap rotateBitmap(Bitmap source, float angle) {
            Matrix matrix = new Matrix();
            matrix.postRotate(angle);
            return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);
        }

    切割Bitmap,将扫描区域的图片切割出来

            int PMwidth = bitmap.getWidth(); // 得到图片的宽,高
            int PMheight = bitmap.getHeight();
    
            int left = PMwidth/10;
            int top = PMheight/3;
            int right = PMwidth*9/10;
            int bottom = PMheight*4/9;
            int width = right - left;
            int height = bottom - top;
    
            Log.v("zzw",PMheight+" "+PMwidth);
    
    
            bitmap = Bitmap.createBitmap(bitmap, left, top, width, height, null,
                    false);

    4.扫描出结果


    其实tess-two框架的使用很简单,但是使用这个框架需要依靠训练文件来完成扫描,我在res目录下放了raw文件夹,里面的eng_traineddata文件就是这个用途,但是我们不能直接使用它们,我们需要将他们复制到手机存储里

    下面的代码意思是在应用私有路径里创建tesseract/tessdata/eng.traineddata相关路径的文件并使用输入流将文件的数据读出来,然后使用输出流将数据传入eng.traineddata文件

        public static void initTessTrainedData(Context context){
    
            if(initiated){
                return;
            }
    
            File appFolder = context.getFilesDir();
            File folder = new File(appFolder, tessdir);
            if(!folder.exists()){
                folder.mkdir();
            }
                
            tesseractFolder = folder.getAbsolutePath();
    
            File subfolder = new File(folder, subdir);
            if(!subfolder.exists()){
                subfolder.mkdir();
            }
    
            File file = new File(subfolder, filename);
            trainedDataPath = file.getAbsolutePath();
            Log.d(TAG, "Trained data filepath: " + trainedDataPath);
    
            if(!file.exists()) {
    
                try {
                    FileOutputStream fileOutputStream;
                    byte[] bytes = readRawTrainingData(context);
                    if (bytes == null){
                        return;
                    }
                        
                    fileOutputStream = new FileOutputStream(file);
                    fileOutputStream.write(bytes);
                    fileOutputStream.close();
                    initiated = true;
                    Log.d(TAG, "Prepared training data file");
                } catch (FileNotFoundException e) {
                    Log.e(TAG, "Error opening training data file
    " + e.getMessage());
                } catch (IOException e) {
                    Log.e(TAG, "Error opening training data file
    " + e.getMessage());
                }
            }
            else{
                initiated = true;
            }
        }

    好了再说说tess-two框架的使用

    创建TessBaseAPI

            TessBaseAPI tessBaseAPI = new TessBaseAPI();

    关闭测试

            tessBaseAPI.setDebug(true);

    设置训练数据路径和识别文字是英文

            tessBaseAPI.init(path, "eng");

     设置白名单

            tessBaseAPI.setVariable(TessBaseAPI.VAR_CHAR_WHITELIST, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");

    设置黑名单

            tessBaseAPI.setVariable(TessBaseAPI.VAR_CHAR_BLACKLIST, "!@#$%^&*()_+=-[]}{;:'"\|~`,./<>?"); 

    设置识别模式

            tessBaseAPI.setPageSegMode(TessBaseAPI.PageSegMode.PSM_AUTO_OSD);

    传入bitmap数据

            tessBaseAPI.setImage(bitmap);

    获取扫描结果

            String inspection = tessBaseAPI.getHOCRText(0);

    结束TestBaseAPI的使用

            tessBaseAPI.end();

    实现扫描身份证号码,这里是通过正则表达式来判断扫描出的结果是否有身份证号码,也就是说tess-two其实是只是扫描出Bitmap文件里面有哪些文字,然后使用正则表达式来筛选出我们需要的数据。也就是说我们通过换取正则表达式就能做到扫描手机号等,带有某种规律的数字或者字母

    这是正则表达式的线上工具地址,大家可以自己试试 http://tool.oschina.net/regex/#

        private static Pattern pattern = Pattern.compile("\d{17}[\d|x]|\d{15}");
        public static String getTelNum(String sParam){
            if(TextUtils.isEmpty(sParam)){
                return "";
            }
    
            Matcher matcher = pattern.matcher(sParam);
            StringBuilder bf = new StringBuilder();
            while (matcher.find()) {
                bf.append(matcher.group()).append(",");
            }
            int len = bf.length();
            if (len > 0) {
                bf.deleteCharAt(len - 1);
            }
            return bf.toString();
        }

    然后通过handler返回结果

            Message message = Message.obtain();
            message.what = 1;
            Bundle bundle = new Bundle();
            bundle.putString("decode",strDecode);
            message.setData(bundle);
            message.what = TwoActivity.MSG_BITMAP;
            handler.sendMessage(message);

    取消加载框,并将局部截图的图像和扫描的结果通过DialogFragment显示出来

                            mypDialog.dismiss();
                            String strDecode = msg.getData().getString("decode","扫描失败");
    
                            if(strDecode == null ||strDecode.equals(""))
                                strDecode = "扫描失败";
    
                            imageDialogFragment.setImage(bitmap);
                            imageDialogFragment.setText(strDecode);
                            imageDialogFragment.show(getFragmentManager(), "ImageDialogFragment");

     

    5.结论


    其实还没有结束因为我本想做出一个能够扫描整张身份证的项目,我看一下网上有很多API都能实现这个功能,但都要钱,如果要是能够实现这个功能,并发到github,我岂不是成为大神了。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

     

    博客首发地址
    http://blog.csdn.net/z979451341

  • 相关阅读:
    系统综合实践第一次实践作业
    个人作业——软件工程实践总结作业
    个人作业——软件评测
    软件工程实践第五次作业
    Structured Multimodal Attentions for TextVQA
    文字版三国杀开发
    OO Unit 4 Summary
    TextCaps竞赛总结
    Multi-Source Pointer Network
    Pointer Generator Network
  • 原文地址:https://www.cnblogs.com/jianpanwuzhe/p/8514789.html
Copyright © 2011-2022 走看看