快速入門開發實現訂單類圖片識別結果抽象解析
一、背景
面對訂單資料紙質檔案或圖片,僅靠人眼識別的話效率很低,需引入機器學習來識別和解析圖片以提高效率。當前市面上已有收費的圖片識別服務,包括阿里、百度等,識別效果較好,但針對訂單類圖片,不僅要關注圖片上的文字,還要關注文字所在的行列,來分出每條資料和資料詳細欄位。
本文主要介紹一種針對訂單類圖片識別結果進行行列解析的抽象流程和方案,幫助提高開發效率。
注:本文只提供思路,不提供原始碼。另外,本文不介紹人工智慧圖片識別,感興趣的同學可以上網查詢相關資料。
二、解析流程
對於影像處理,opencv算是比較優秀的工具,因此將其選做本文影像處理首選軟體。
- 為了使圖片識別率更高,需要先做圖片矯正,這裡採用較為簡單的霍夫變換加去噪聲點演算法矯正圖片。
- 圖片矯正後,呼叫圖片識別服務獲取結果,一般結果格式包括響應碼、錯誤描述、文字塊列表(文字和四點座標)等。
- 然後使用抽象的俄羅斯方塊法根據識別結果獲取行列資訊。
- 最後根據行列資訊組裝每一行資料並顯示。
三、細節處理
3.1 opencv安裝概要
opencv安裝,本文只做簡單提示,不展開介紹,以後有時間單獨發文。
1)windows
- 下載編譯好的包,
- 解壓縮到自定義資料夾。
2)linux
- 推薦使用ubuntu,並且最好是全新的系統,因為opencv會依賴很多包,對版本要求也高,解決衝突會很麻煩。
- 下載原始碼
- 安裝依賴包
- 編譯安裝
我們使用java呼叫opencv,這裡需要安裝獲取到開發包,windows為opencv_javaxxx.dll,linux為libopencv_javaxxx.so,程式初始化時需要載入到jvm。詳細程式碼如下:
System.load(PropertieUtil.getPropertie("這裡是dll或so的完整路徑");
3.2 圖片矯正
3.2.1 矯正探索
圖片矯正探索之路較為艱辛,起初我們想了一個比較簡單的方案:
- 先呼叫圖片識別服務,獲取到結果。
- 然後根據每一個字塊的四角座標判斷出每個字塊的傾斜角。
- 再根據去燥演算法算出平均的傾斜角。
理論上這個方案是可行的,但實踐證明我們錯了,因為圖片識別服務返回的座標圖片不準確,多數圖片算出的結果都是錯誤的。
經查發現霍夫變換有可能解決這個問題,於是開始嘗試學習霍夫變換和去燥演算法,最終發現可行,並抽象出公共方法,僅需簡單配置一些引數就能完成矯正。
圖片矯正分為兩步:
- 第一步:正反矯正,判斷圖片傾斜角度是90°、180°、270°、0°,這個透過數學方法是無法判斷的,需要引用機器學習。
- 第二步:角度微調,一般為確定圖片是正的,且傾斜角度在+-30°左右。
需要注意的是,上面說的辦法不可能透過一套引數來對所有圖片進行微調,但線上資料證明,針對一類圖片,一套引數基本能讓大多數圖片都矯正正確。
3.2.2 霍夫變換概要
霍夫變換是數學界經典空間變換演算法,用於檢測直線,透過大量檢測到的直線的斜率就能計算出圖片傾斜角度。先進行二值化和邊緣檢測再進行霍夫變換效果更佳,詳細演算法內容請自行搜尋,本文不展開。
3.2.3 去噪聲點演算法
基本公式:
上限=均值+n*標準差
下限=均值-n*標準差
其中n取值一般為1-4,數值越大表示篩選率越高。
最後再將符合的資料求均值。
核心程式碼如下:
/** * 利用標準差篩選 * @param values * @return */ private static double[] calcBestCornList(double[] values) { // 計算標準差 StandardDeviation variance = new StandardDeviation(); double evaluate = variance.evaluate(values); Mean mean = new Mean(); double meanValue = mean.evaluate(values); double biggerValue = meanValue + CHOOSE_POWER * evaluate; double smallerValue = meanValue - CHOOSE_POWER * evaluate; List<Double> selected = Lists.newArrayList(); for (double value : values) { if (value >= smallerValue && value <= biggerValue) { selected.add(value); } } double[] selectedValue = new double[selected.size()]; for (int i = 0; i < selected.size(); i++) { selectedValue[i] = selected.get(i); } logger.info("佔比:{}%,篩選後角度陣列:{}", (selectedValue.length / (float)values.length) * 100F, selected); return selectedValue; }
3.2.4 霍夫變化抽象封裝
基本流程:
- 定義相關引數
- 讀取圖片
- 灰度二值化處理
- 使用opencv畫出輪廓
- 根據引數要求多次畫霍夫變換線,直到線數量滿足引數為止
- 遍歷畫出的線,分出橫線和豎線,根據配置計算出每條線的角度
- 使用去噪聲演算法(需要根據非0數自動重複計算)算出平均傾斜角度
- 使用opencv旋轉圖片
核心程式碼如下:
/** * 矯正圖片,透過霍夫變換矯正 * @param oldImg 原始圖片 * @param rotateParam 旋轉引數 * @return */ public static String rotateHoughLines(File oldFile, String oldImg, RotateParam rotateParam, String cid, String bankCode) throws Exception { Mat src= Imgcodecs.imread(oldFile.getAbsolutePath()); //讀取影像到矩陣中 if(src.empty()){ throw new Exception("no file " + oldFile.getAbsolutePath()); } // 用於計算的圖片矩陣 Mat mathImg = src.clone(); // 灰度化 Imgproc.cvtColor(src, mathImg, Imgproc.COLOR_BGR2GRAY); logger.info("二值化完成"); // 獲取輪廓 Imgproc.Canny(src, mathImg, rotateParam.getCvtThreshould1(), rotateParam.getCvtThreshould2()); logger.info("輪廓完成"); // 霍夫變換獲取角度,詳細程式碼略 double corn = houghLines(mathImg, rotateParam, cid); logger.info("霍夫變換完成,角度:{}", corn); if(corn == 0) { return oldImg; } return rotateOpenv(oldFile, corn, cid, bankCode); }
3.3 常用圖片識別方案
阿里、百度都有提供圖片識別服務,如果有實力也可以自己實現,不過不建議自研,因為樣本需求量巨大,時間成本過高。
3.4 識別結果解析
3.4.1 探索之路
本章節為本文重點內容,因為前文所提到的都是較為基礎的服務和演算法,大量開發內容都在本章。前期要開發的訂單圖片型別巨量(大於100種),每一類圖片區別很大,我們有幾個人分型別開發,但每個人所用的方法都不同,且張三開發出來的李四看不懂,不過畢竟面對的是圖片,比較抽象,這是可以理解的。
開發一段時間後我們發現了問題:每種型別最快也要一週才能開發完成,而且解析成功率極低。開發出一套抽象的方法來把行列資料提取出來迫在眉睫。
透過調研發現,大家常用兩種方法來提取行列資料,分別為座標法和標題法,但這兩種方法解析率都不高。經過幾周思考,終於想出了一套較好的方法,命名為俄羅斯方塊法,最終解決了問題。
3.4.2 俄羅斯方塊法
思路概要:
- 拿到識別結果資料。
- 先把所有資料的y座標進行排序。
- 遍歷排序結果,先把第一條放入第一列結果集中。
- 從第二條開始和第一列結果集對比。
- 對比方法:如果在第一列結果集其中一條資料的右側,則認為是新列;如果在y軸方法和第一列結果集中某些資料重疊了,則認為是新列。
- 如果以上兩條都不是,則認為本條資料還在當前列中,放入第一列結果集。
- 以此類推,繼續對比,直到對比到最後一列最後一條資料。
- 按照上述方法,反過來,以x軸為標準,能夠得到行結果集。
思路圖如下:
概要程式碼如下:
// 按照最左上角的x座標排序 OcrWordInfo[] sortL = NoTableParseResult.ParseUtil.bubbleSortX(ocrResponse.getPrism_wordsInfo(), false); NoTableParseResult ntpr = new NoTableParseResult(param); ntpr.setHeight(converImg.height()); ntpr.setWight(converImg.width()); for (int i = 0; i < sortL.length; i++) { // 當前要比較的資料 OcrWordInfo ocrWordInfo = sortL[i]; // 處理當前列資料 ntpr.getUtil().testCurColData(ocrWordInfo); } // 處理最後一列 ntpr.lastCol(); /** * 判斷是否為下一列,並處理 * @param ocrWordInfo * @return */ public void testCurColData(OcrWordInfo ocrWordInfo) { // 遍歷當前列已存在的所有資料 int size = this.test.getCol().size(); if(size == 0) { this.test.addCol(ocrWordInfo); return; } for (int i = 0; i < size; i++) { OcrWordInfo temp = this.test.getCol().get(i); // 最右邊的資料 int x1 = temp.getPos().get(1).getX(); int x2 = temp.getPos().get(2).getX(); // 當前資料最左邊 int xx0 = ocrWordInfo.getPos().get(0).getX(); int xx3 = ocrWordInfo.getPos().get(3).getX(); int threholdx = this.test.param == null ? 0 : this.test.param.getCoverColXThrehold(); if(xx0 >= (x1 - threholdx) && xx0 >= (x2 - threholdx) && xx3 >= (x1 - threholdx) && xx3 >= (x2 - threholdx)) { // 當前資料在右邊,說明換列了!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! this.test.colAdd(); this.test.addCol(ocrWordInfo); return; } else { // 判斷是否覆蓋座標 int y0 = temp.getPos().get(0).getY(); int y3 = temp.getPos().get(3).getY(); int yy0 = ocrWordInfo.getPos().get(0).getY(); int yy3 = ocrWordInfo.getPos().get(3).getY(); int threhold = (int)Math.round((y3 - y0) * (this.test.param == null ? 0.25 : this.test.param.getCoverThrehold())); if(!(yy3 <= (y0 + threhold) || yy0 >= (y3 - threhold))) { // 當前列表資料重疊,說明換列了!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! this.test.colAdd(); this.test.addCol(ocrWordInfo); return; } } } // 執行到這說明沒覆蓋 this.test.addCol(ocrWordInfo); }
3.4.3 解析行資料技巧
技巧總結:
1)俄羅斯方塊法提供去除干擾項的引數,可以根據圖片特點去除上下左右干擾資料來減少序列列現象。
2)解析資料大致有兩種方法
- 根據標題列號來判斷資料,這種方法不通用,簡單、規範的圖片識別率高,但無法適配亂的圖。
- 把每一行資料以間隔符號分割拼到一起,使用正規表示式來‘扣’資料。因為一般同型別訂單圖片,關鍵欄位的位置是有特點的,例如金額格式、借貸方向、日期等,這種方法通用,但識別率不高。
具體使用哪種方法,還需要根據圖片特點進行取捨。
3)俄羅斯方塊法提供一些微調引數,用於適配一些特殊場景,例如換行列閥值之類的。
4)中間需要儲存一些過程圖片,例如矯正過程的若干張圖、俄羅斯方塊法識別結果的連線圖等。畢竟這種專案在查問題時靠日誌是沒用的,還得靠這些中間圖才能更快查到問題。
四、總結
本文提到的方案不能完全解決所有訂單類圖片解析問題,可以做到新手快速入門快速開發,如果您有更好思路歡迎交流。
作者:劉鵬飛
來源:宜信技術學院
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69918724/viewspace-2663645/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- React 實現圖片識別AppReactAPP
- 圖片文字識別怎麼實現
- AI大模型實現圖片OCR識別AI大模型
- C++快速入門--12--抽象類和介面模擬C++抽象
- Java 實現OCR掃描/識別圖片文字Java
- 實現圖片文字識別的方法有哪些
- 手動輸入圖片識別
- Java 簡單實現撲克牌抽象類Java抽象
- 哪個圖片識別文字app能快速轉換圖片成文字?APP
- 如何實現圖片轉換成文字?快速錄入方法
- 開箱即用的JAVA AI平臺AI合集包含旦不僅限於(人臉識別、車牌識別、安全帽識別、開門關門、常用類物識別等) 圖片和影片識別JavaAI
- 前端開發快速入門前端
- HarmonyOS快速開發入門
- Android開發 - UUID 類通用唯一識別符號解析AndroidUI符號
- Tesseract 圖片識別
- web前端入門到實戰:簡單的圖片輪播Web前端
- 圖片懶載入實現
- 實現簡單的輪播圖(單張圖片、多張圖片)
- C#解析Markdown文件,實現替換圖片連結操作C#
- 圖片文字識別工具怎樣進行批次識別圖片?
- 【JAVA】筆記(5)--- final;抽象方法;抽象類;介面;解析繼承,關聯,與實現;Java筆記抽象繼承
- OCR文件識別:圖片快速轉換成電子文件
- 2.CNN圖片多標籤分類(基於TensorFlow實現驗證碼識別OCR)CNN
- 鴻蒙OS前端開發入門指南:網路圖片_Image渲染網路圖片鴻蒙前端
- paddleocr圖片文字識別
- 圖片懶載入js實現JS
- Python 超簡單實現人類面部情緒的識別Python
- 運維和開發知識,Java中的抽象類和介面的兩大區別!運維Java抽象
- RabbitMQ 延遲佇列實現訂單支付結果非同步階梯性通知MQ佇列非同步
- C++ 抽象類快速使用C++抽象
- 13 個示例快速入門 JS 抽象語法樹JS抽象語法樹
- Java集合為什麼設計為:實現類繼承了抽象類,同時實現抽象類實現的介面Java繼承抽象
- GO 語言快速開發入門Go
- Koa2開發快速入門
- Laravel 開發入門課程基礎頁面實現總結Laravel
- 一個簡單快速的OCR表單識別錄入工具
- 利用百度AI OCR圖片識別,Java實現PDF中的圖片轉換成文字AIJava
- 用canvas實現一個自動識別兩張圖片差異(圖片找不同)的功能Canvas