這兩天研究了一下關於OCR圖文解析的技術。當然市場上已經有開源服務,比如百度的AI開放平台,就有OCR相關的API接口。我這里選用的是Tesseract開源框架,java封裝版本是tess4j。結合網上公布的一些開源項目提供的demo,完成了身份證與營業執照的相關文字識別的處理。總體上來講Tesseract其實還不錯,簡單應用其實還挺簡單的(提供的圖片質量可以靠前端做好限制,比如身份證識別,加上頭像或國徽的框圖限定,能提高識別率)。
示例項目地址:https://github.com/git-simm/simm-framework
一、技術介紹
Tesseract:開源的OCR識別引擎,初期Tesseract引擎由HP實驗室研發,后來貢獻給了開源軟件業,后由Google進行改進、修改bug、優化,重新發布。
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>
官方文檔:Tess4J Usage
2.3 Linux 環境
sudo yum -y install tesseract ## 安裝so和英文tessdata sudo yum -y install tesseract-langpack-chi_sim.noarch ## 安裝簡體中文 sudo yum -y install tesseract-langpack-chi_tra.noarch ## 安裝繁體中文 sudo ls -l /usr/share/tesseract/tessdata/*.traineddata ## tessdata 目錄
Windows 版本的 Tesseract 本機庫是用 VS2013(或者VS2012/VS2015) 構建的,所以必須安裝 Microsoft Visual C++ 20XX Redistributable。
已經在 WinXP/Win7/Win10 上驗證通過:安裝 Visual C++ Redistributable for VS2013
32 位版本。
三、簡單描述下圖文解析的過程
四、服務端關鍵代碼的展示
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