面對訂單數據紙質文件或圖片,僅靠人眼識別的話效率很低,需引入機器學習來識別和解析圖片以提升效率。當前市面上已有收費的圖片識別服務,包括阿里、百度等,識別效果較好,但針對訂單類圖片,不只要關注圖片上的文字,還要關注文字所在的行列,來分出每條數據和數據詳細字段。java
本文主要介紹一種針對訂單類圖片識別結果進行行列解析的抽象流程和方案,幫助提升開發效率。linux
注:本文只提供思路,不提供源碼。另外,本文不介紹人工智能圖片識別,感興趣的同窗能夠上網查詢相關資料。正則表達式
對於圖像處理,opencv算是比較優秀的工具,所以將其選作本文圖像處理首選軟件。算法
opencv安裝,本文只作簡單提示,不展開介紹,之後有時間單獨發文。ubuntu
1)windowswindows
2)linux數組
咱們使用java調用opencv,這裏須要安裝獲取到開發包,windows爲opencv_javaxxx.dll,linux爲libopencv_javaxxx.so,程序初始化時須要加載到jvm。詳細代碼以下:機器學習
System.load(PropertieUtil.getPropertie("這裏是dll或so的完整路徑");
圖片矯正探索之路較爲艱辛,起初咱們想了一個比較簡單的方案:jvm
理論上這個方案是可行的,但實踐證實咱們錯了,由於圖片識別服務返回的座標圖片不許確,多數圖片算出的結果都是錯誤的。工具
經查發現霍夫變換有可能解決這個問題,因而開始嘗試學習霍夫變換和去燥算法,最終發現可行,並抽象出公共方法,僅需簡單配置一些參數就能完成矯正。
圖片矯正分爲兩步:
須要注意的是,上面說的辦法不可能經過一套參數來對全部圖片進行微調,但線上數據證實,針對一類圖片,一套參數基本能讓大多數圖片都矯正正確。
霍夫變換是數學界經典空間變換算法,用於檢測直線,經過大量檢測到的直線的斜率就能計算出圖片傾斜角度。先進行二值化和邊緣檢測再進行霍夫變換效果更佳,詳細算法內容請自行搜索,本文不展開。
基本公式:
上限=均值+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; }
基本流程:
核心代碼以下:
/** * 矯正圖片,經過霍夫變換矯正 * @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); }
阿里、百度都有提供圖片識別服務,若是有實力也能夠本身實現,不過不建議自研,由於樣本需求量巨大,時間成本太高。
本章節爲本文重點內容,由於前文所提到的都是較爲基礎的服務和算法,大量開發內容都在本章。前期要開發的訂單圖片類型巨量(大於100種),每一類圖片區別很大,咱們有幾我的分類型開發,但每一個人所用的方法都不一樣,且張三開發出來的李四看不懂,不過畢竟面對的是圖片,比較抽象,這是能夠理解的。
開發一段時間後咱們發現了問題:每種類型最快也要一週才能開發完成,並且解析成功率極低。開發出一套抽象的方法來把行列數據提取出來迫在眉睫。
經過調研發現,你們經常使用兩種方法來提取行列數據,分別爲座標法和標題法,但這兩種方法解析率都不高。通過幾周思考,終於想出了一套較好的方法,命名爲俄羅斯方塊法,最終解決了問題。
思路概要:
思路圖以下:
概要代碼以下:
// 按照最左上角的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); }
技巧總結:
1)俄羅斯方塊法提供去除干擾項的參數,能夠根據圖片特色去除上下左右干擾數據來減小串行列現象。
2)解析數據大體有兩種方法
具體使用哪一種方法,還須要根據圖片特色進行取捨。
3)俄羅斯方塊法提供一些微調參數,用於適配一些特殊場景,例如換行列閥值之類的。
4)中間須要保存一些過程圖片,例如矯正過程的若干張圖、俄羅斯方塊法識別結果的連線圖等。畢竟這種項目在查問題時靠日誌是沒用的,還得靠這些中間圖才能更快查到問題。
本文提到的方案不能徹底解決全部訂單類圖片解析問題,能夠作到新手快速入門快速開發,若是您有更好思路歡迎交流。
做者:劉鵬飛
來源:宜信技術學院