使用tess4j完成身份证和营业执照图片的文字识别 - Mr.Simm - 博客园

标签: | 发表时间:2020-08-25 21:27 | 作者:
出处:https://www.cnblogs.com

   这两天研究了一下关于OCR图文解析的技术。当然市场上已经有开源服务,比如百度的AI开放平台,就有OCR相关的API接口。我这里选用的是Tesseract开源框架,java封装版本是tess4j。结合网上公布的一些开源项目提供的demo,完成了身份证与营业执照的相关文字识别的处理。总体上来讲Tesseract其实还不错,简单应用其实还挺简单的(提供的图片质量可以靠前端做好限制,比如身份证识别,加上头像或国徽的框图限定,能提高识别率)。

  示例项目地址: https://github.com/git-simm/simm-framework

一、技术介绍

OCR(Optical Character Recognition):光学字符识别,是指电子设备(例如扫描仪或数码相机)检查纸上打印的字符,通过检测暗、亮的模式确定其形状,然后用字符识别方法将形状翻译成计算机文字的过程。
Tesseract:开源的OCR识别引擎,初期Tesseract引擎由HP实验室研发,后来贡献给了开源软件业,后由Google进行改进、修改bug、优化,重新发布。
  Tess4J:是对Tesseract OCR API.的Java JNA 封装。使java能够通过调用Tess4J的API来使用Tesseract OCR。支持的格式:TIFF,JPEG,GIF,PNG,BMP,JPEG,and PDF
Tess4J API 提供的功能:
1、直接识别支持的文件
2、识别图片流
3、识别图片的某块区域
4、将识别结果保存为 TEXT/ HOCR/ PDF/ UNLV/ BOX
5、通过设置取词的等级,提取识别出来的文字
6、获得每一个识别区域的具体坐标范围
7、调整倾斜的图片
8、裁剪图片
9、调整图片分辨率
10、从粘贴板获得图像
11、克隆一个图像(目的:创建一份一模一样的图片,与原图在操作修改上,不相 互影响)
12、图片转换为二进制、黑白图像、灰度图像
13、反转图片颜色

、环境准备( https://www.jianshu.com/p/ef60ef5395c5)

  2.1、我们需要安装tessdata语言包,用于图文识别。 tesseract-ocr语言包的下载地址,用于识别文字时进行匹配。链接:  https://pan.baidu.com/s/1XAvPkTdUXuFq-q2InDREhQ 提取码: 6vjp  

     

  2.2、项目引入maven依赖

<dependency>
            <groupId>net.sourceforge.tess4j</groupId>
            <artifactId>tess4j</artifactId>
            <version>4.5.2</version>
        </dependency>

、简单描述下图文解析的过程

   

、服务端关键代码的展示

private static int targetBrightness = 260;
    private static int targetDifferenceValue = 15;

    /**
     * 解析身份证信息
     *
     * @param inputStream
     * @return
     * @throws Exception
     */
    @Override
    public BizLicenseInfo getInfo(InputStream inputStream) throws Exception {
        BizLicenseInfo bizLicenseInfo = new BizLicenseInfo();
        String rootPath = ClassUtils.getDefaultClassLoader().getResource("").getPath()+"/tmp";
        Tesseract tesseract = new Tesseract();
        tesseract.setLanguage("chi_sim");
        //读取网络图片
        BufferedImage bufferedImage = ImageFilter.cloneImage(ImageIO.read(inputStream));
        //不过滤部分颜色
        //bufferedImage = ImageFilter.imageRGBDifferenceFilter(bufferedImage, targetDifferenceValue, null);
        bufferedImage = ImageFilter.convertImageToGrayScale(bufferedImage);
        //缩放到真实身份证大小
        bufferedImage = ImageFilter.imageScale(bufferedImage, 3150, 1920);
        try (OutputStream outputStream = new FileOutputStream(rootPath+"/bg.jpg")) {
            saveImg(bufferedImage,outputStream);
            getBufferedNameImage(tesseract, bufferedImage, bizLicenseInfo,rootPath+"/nameImageBefore.jpg");
            getBufferedCapitalImage(tesseract, bufferedImage, bizLicenseInfo,rootPath+"/capitalImageBefore.jpg");
            getBufferedBizTypeImage(tesseract, bufferedImage, bizLicenseInfo,rootPath+"/bizTypeImageBefore.jpg");
            getBufferedBuildOnImage(tesseract, bufferedImage, bizLicenseInfo,rootPath+"/buildOnImageBefore.jpg");
            getBufferedJuridicalImage(tesseract, bufferedImage, bizLicenseInfo,rootPath+"/juridicalImageBefore.jpg");
            getBufferedBizLimitImage(tesseract, bufferedImage, bizLicenseInfo,rootPath+"/bizLimitImageBefore.jpg");
            getBufferedBizScopeImage(tesseract, bufferedImage, bizLicenseInfo,rootPath+"/bizScopeImageBefore.jpg");
            getBufferedAddressImage(tesseract, bufferedImage, bizLicenseInfo,rootPath+"/addressImageBefore.jpg");
            getBufferedCreditCodeImage(tesseract, bufferedImage, bizLicenseInfo,rootPath+"/creditCodeImageBefore.jpg");
            return bizLicenseInfo;
        }catch (Exception e){
            e.printStackTrace();
            throw e;
        }
    }
/**
     * 获取统一社会信用代码
     * @param tesseract
     * @param bufferedImage
     * @param bizLicenseInfo
     * @param path
     * @throws IOException
     * @throws TesseractException
     */
    private void getBufferedCreditCodeImage(Tesseract tesseract, BufferedImage bufferedImage, BizLicenseInfo bizLicenseInfo, String path) throws IOException, TesseractException {
        try (OutputStream outputStream = new FileOutputStream(path)) {
            BufferedImage idImage = ImageFilter.subImage(bufferedImage, bufferedImage.getMinX() + 200
                    , 250, 550, 300);
            System.out.println("creditCodeImage 辉度处理");
            handBrightness(idImage, targetBrightness);
            saveImg(idImage, outputStream);
//            tesseract.setLanguage("eng");
            tesseract.setLanguage("chi_sim");
            // \W 可以配置 非字母和数字,等价于 [^a-zA-Z0-9] (\d \D 小写表示匹配数字,大写表示匹配非数字)
            String idCardNumber = tesseract.doOCR(idImage).replaceAll("[\\W]", "");
            bizLicenseInfo.setCreditCode(idCardNumber);
        }catch (Exception e){
            e.printStackTrace();
            throw e;
        }
    }

    /**
     * 获取名称
     * @param tesseract
     * @param bufferedImage
     * @param bizLicenseInfo
     * @param path
     */
    private void getBufferedNameImage(Tesseract tesseract, BufferedImage bufferedImage, BizLicenseInfo bizLicenseInfo, String path) throws IOException, TesseractException {
        BufferedImage buffered = ImageFilter.subImage(bufferedImage, 520, 700, 1200, 120);
        getBufferedImage(tesseract,buffered,path,(img,content)->{
            System.out.println("setName 辉度处理");
            bizLicenseInfo.setName(content);
        });
    }
    /**
     * 获取类型
     * @param tesseract
     * @param bufferedImage
     * @param bizLicenseInfo
     * @param path
     * @throws IOException
     * @throws TesseractException
     */
    private void getBufferedBizTypeImage(Tesseract tesseract, BufferedImage bufferedImage, BizLicenseInfo bizLicenseInfo, String path) throws IOException, TesseractException {
        BufferedImage buffered = ImageFilter.subImage(bufferedImage, 520, 820, 1200, 130);
        getBufferedImage(tesseract,buffered,path,(img,content)->{
            System.out.println("setBizType 辉度处理");
            bizLicenseInfo.setBizType(content);
        });
    }
    /**
     * 获取法人信息
     * @param tesseract
     * @param bufferedImage
     * @param bizLicenseInfo
     * @param path
     * @throws IOException
     * @throws TesseractException
     */
    private void getBufferedJuridicalImage(Tesseract tesseract, BufferedImage bufferedImage, BizLicenseInfo bizLicenseInfo, String path) throws IOException, TesseractException {
        BufferedImage buffered = ImageFilter.subImage(bufferedImage, 520, 950, 1200, 120);
        getBufferedImage(tesseract,buffered,path,(img,content)->{
            System.out.println("setJuridical 辉度处理");
            bizLicenseInfo.setJuridical(content);
        });
    }

    /**
     * 获取经营范围
     * @param tesseract
     * @param bufferedImage
     * @param bizLicenseInfo
     * @param path
     * @throws IOException
     * @throws TesseractException
     */
    private void getBufferedBizScopeImage(Tesseract tesseract, BufferedImage bufferedImage, BizLicenseInfo bizLicenseInfo, String path) throws IOException, TesseractException {
        BufferedImage buffered = ImageFilter.subImage(bufferedImage, 520, 1070, 1330, bufferedImage.getHeight() - 1200);
        getBufferedImage(tesseract,buffered,path,(img,content)->{
            System.out.println("setBizScope 辉度处理");
            bizLicenseInfo.setBizScope(content);
        });
    }

    /**
     * 获取注册资本
     * @param tesseract
     * @param bufferedImage
     * @param bizLicenseInfo
     * @param path
     * @throws IOException
     * @throws TesseractException
     */
    private void getBufferedCapitalImage(Tesseract tesseract, BufferedImage bufferedImage, BizLicenseInfo bizLicenseInfo, String path) throws IOException, TesseractException {
        BufferedImage buffered = ImageFilter.subImage(bufferedImage, 2170, 720, bufferedImage.getWidth()-2400, 120);
        getBufferedImage(tesseract,buffered,path,(img,content)->{
            System.out.println("setCapital 辉度处理");
            bizLicenseInfo.setCapital(content);
        });
    }

    /**
     * 获取成立日期
     * @param tesseract
     * @param bufferedImage
     * @param bizLicenseInfo
     * @param path
     * @throws IOException
     * @throws TesseractException
     */
    private void getBufferedBuildOnImage(Tesseract tesseract, BufferedImage bufferedImage, BizLicenseInfo bizLicenseInfo, String path) throws IOException, TesseractException {
        BufferedImage buffered = ImageFilter.subImage(bufferedImage, 2170, 850, bufferedImage.getWidth()-2400, 100);
        getBufferedImage(tesseract,buffered,path,(img,content)->{
            System.out.println("setBuildOn 辉度处理");
            bizLicenseInfo.setBuildOn(content);
        });
    }

    /**
     * 获取营业期限
     * @param tesseract
     * @param bufferedImage
     * @param bizLicenseInfo
     * @param path
     * @throws IOException
     * @throws TesseractException
     */
    private void getBufferedBizLimitImage(Tesseract tesseract, BufferedImage bufferedImage, BizLicenseInfo bizLicenseInfo, String path) throws IOException, TesseractException {
        BufferedImage buffered = ImageFilter.subImage(bufferedImage, 2170, 970, bufferedImage.getWidth()-2400, 100);
        getBufferedImage(tesseract,buffered,path,(img,content)->{
            System.out.println("setBizLimit 辉度处理");
            bizLicenseInfo.setBizLimit(content);
        });
    }

    /**
     * 获取住所
     * @param tesseract
     * @param bufferedImage
     * @param bizLicenseInfo
     * @param path
     * @throws IOException
     * @throws TesseractException
     */
    private void getBufferedAddressImage(Tesseract tesseract, BufferedImage bufferedImage, BizLicenseInfo bizLicenseInfo, String path) throws IOException, TesseractException {
        BufferedImage buffered = ImageFilter.subImage(bufferedImage, 2170, 1070, bufferedImage.getWidth()-2240, 270);
        getBufferedImage(tesseract,buffered,path,(img,content)->{
            System.out.println("setAddress 辉度处理");
            bizLicenseInfo.setAddress(content);
        });
    }
    /**
     * 获取名称
     * @param tesseract
     * @param buffered
     * @param path
     * @param consumer
     */
    private void getBufferedImage(Tesseract tesseract, BufferedImage buffered, String path, BiConsumer<BufferedImage,String> consumer) throws IOException, TesseractException {
        try (OutputStream outputStream = new FileOutputStream(path)) {
//            addressImage = ImageFilter.imageScale(addressImage, ((int) (addressImage.getWidth() * 2.4) + 1), ((int) (addressImage.getHeight() * 2.4) + 1));
            handBrightness(buffered, targetBrightness);
            saveImg(buffered, outputStream);
            tesseract.setLanguage("chi_sim");
            String result = tesseract.doOCR(buffered);
            //留下中文字符、中文标点符号()【】、
            String regexStr = "[^\\s\\u4e00-\\u9fa5\\(\\)\\uff08\\uff09\\u3001\\u3010\\u3011\\-0-9]+";
            String content = result.replaceAll(regexStr, "")
                    .replaceAll("\\n", "")
                    .replaceAll(" ", "");
            if(consumer!=null){
                consumer.accept(buffered,content);
            }
        }catch (Exception e){
            e.printStackTrace();
            throw e;
        }
    }
    /**
     * 保存图片
     * @param image
     * @param outputStream
     * @throws IOException
     */
    private void saveImg(BufferedImage image,OutputStream outputStream) throws IOException {
        ImageIO.write(image, "jpg", outputStream);
    }
    /**
     * 处理图片辉度
     *
     * @param subImage
     */
    private void handBrightness(BufferedImage subImage, int targetBrightness) {
        int fixedBrightness;
        int birthBrightness = ImageFilter.imageBrightness(subImage);
        System.out.println("brightness = " + birthBrightness);
        fixedBrightness = targetBrightness - birthBrightness;
        //辉度处理
        if (fixedBrightness != 0) {
            subImage = ImageFilter.imageBrightness(subImage, fixedBrightness);
        }
        System.out.println("after brightness = " + ImageFilter.imageBrightness(subImage));
    }

、解析效果展示

  5.1、身份证信息识别示例:

  

   5.2、营业执照信息识别示例:

  

 

 参考资料:

https://github.com/firefoxmmx2/IDCardIDentify

https://www.jianshu.com/p/e7915ba6f0e7

https://kefeng.wang/2017/04/22/tess4j/

相关 [tess4j 身份证 营业执照] 推荐:

使用tess4j完成身份证和营业执照图片的文字识别 - Mr.Simm - 博客园

- -
   这两天研究了一下关于OCR图文解析的技术. 当然市场上已经有开源服务,比如百度的AI开放平台,就有OCR相关的API接口. 我这里选用的是Tesseract开源框架,java封装版本是tess4j. 结合网上公布的一些开源项目提供的demo,完成了身份证与营业执照的相关文字识别的处理. 总体上来讲Tesseract其实还不错,简单应用其实还挺简单的(提供的图片质量可以靠前端做好限制,比如身份证识别,加上头像或国徽的框图限定,能提高识别率).

关于身份证号的那些事

- - 标点符
居民身份证号码,根据〖中华人民共和国国家标准 GB 11643-1999〗中有关公民身份号码的规定,公民身份号码是特征组合码,由十七位数字本体码和一位数字校验码组成. 排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码. 1、地址码(身份证号码前六位). 表示编码对象常住户口所在县(市、镇、区)的行政区划代码.

失效身份证系统应与现身份证信息系统完全打通

- - 付亮的竞争情报应用
黄明指出,建立失效居民身份证信息系统,是公安部坚持问题导向、回应社会关切,推出的又一项服务广大群众、确保居民身份证使用安全的改革措施,将为社会用证部门和单位落实居民身份证核查责任提供重要的辅助手段. 该信息系统 具备数据实时更新和动态维护功能,通过社会各用证部门和单位联网核查,实现所有丢失被盗居民身份证即时失效,无法在社会上继续使用.

二代身份证被指有四处语病

- richweller - Solidot
3秒 写道 "第二代身份证至少存在四个值得商榷的语病:1.“二代证”印有照片的一面有“公民身份”字样,而另一面则印有“居民身份证”五个大字. 那么,持证人的身份到底是“公民”还是“居民”. 须知,这是完全不同的两个法律概念. 2.“公民身份号码”表达不妥,因为“身份”不具有数字性,只有“公民身份证”才能被编成一个个号码.

天朝第二代身份证号码的验证机制

- shakhand - I am LAZY bones ?
今天,在盛大某网站注册的时候,身份证必填,但我又不想填真实身份证号码,于是随便编了串自认为合法的身份证号码,但是却马上被提示号码错误,由于响应速度极快,可以肯定不是联机校验正确性的,那也就是说第二代身份证除了大家都知道的几位表示生日和性别的规则以外,还有另外的自我校验规则. 于是翻开页面源码查看,发现这段js没有被压缩,所以规则也很好懂.

复印黑人身份证请用高档复印机

- lzhi - Lzhi&#39;s Views
鸡、牛、草,哪两个是一类. 怎样巧妙地回答让你难堪的问题. 为什么中国穷人的路越走越窄. 本文网址:http://www.lzhi.org/views/700024. 欢迎加入500人超级QQ群:108869281,交流最新好文章.

中国考虑在身份证中登记指纹信息

- 我要发芽 - Solidot
全国人大常委会24日审议居民身份证法修正案草案. 草案规定:公民申请领取、换领、补领居民身份证,应当登记指纹信息. 公安部副部长杨焕宁表示,在居民身份证中加入指纹信息,国家机关以及金融、电信、交通、教育、医疗等单位可以通过机读快速、准确地进行人证同一性认定,有助于维护国家安全和社会稳定,有利于提高工作效率,有效防范冒用他人居民身份证以及伪造、变更居民身份证等违法犯罪行为的发生,并在金融机构清理问题账户、落实存款实名制等方面发挥重要作用.

第一代身份证明年起停用

- applelen - Solidot
lot 写道 "据联合早报网报道, 中国居民身份证法草案规定, 一代身份证自2013年1月1日起停止使用.本次草案由十一届全国人大常委会第二十三次会议第一次全体会议审议, 草案规定, 在居民身份证登记中要加入指纹信息, 已有二代身份证的在领取、换领、补领居民身份证时应当登记指纹信息. 部分城市对尚未换领第二代身份证的市民有便利民措施.".

身份证法通过 居民办证将登记指纹

- Woooon - 网易头条新闻
中新网北京10月29日电  十一届全国人大常委会第二十三次会议29日上午经表决,通过了关于修改居民身份证法的决定. 修改后的居民身份证法规定:公民申请领取、换领、补领居民身份证,应当登记指纹信息. 中国公安部副部长杨焕宁此前在向会议作说明时表示,在居民身份证中加入指纹信息,国家机关以及金融、电信、交通、.

小心倾家荡产 身份证复印一定要加上这些

- - 博客园_新闻
近日,网上传言称办理信用卡、基金、手机、申请书业务,只要附身份证复印件的,或填写和身份证有类似作用的表格,为保障自己的权益,一定要写签注,否则就会被不法分子挪作他用. 这则传言被迅速转发并引发网友热议,不少网友表示自己长了知识,再使用身份证复印件时会多个心眼. 身份证复印件如果保护不善会有什么后果.