OpenCV3 識別圖中表格-JAVA 實現

2019獨角獸企業重金招聘Python工程師標準>>> hot3.png

關于 JAVA 學習 OpenCV 的內容,函數講解。內容我均整理在 GitHubd的OpenCV3-Study-JAVA

OpenCV 3 識別圖中表格-Java 實現

1. 說明

網上大部分資料,都是針對 C++的,python、java 的例子太少了。所以最近在做這個的時候,把他記錄下來,也可以幫助一些人少走彎路。

OpenCV 確實強大,強大到每一個方法,都能 google 到一篇專題文章,在寫的過程中,參考了許多資料,最終完成了實現和注釋。

但是這僅僅是入門,找到表格后的利用才是后面的核心。比如:

  1. 表格的 OCR 識別,識別表頭,內容數據,形成結構化數據。
  2. 圖片按照順序,轉 Word文檔或者保存為 html,這樣就可以完成格式的轉化,方便在 web 端查看,用戶下載。
  3. 其他利用...

本文僅針對效果較好的,無傾斜,背景干凈的圖片進行識別。復雜的情況會可能無法滿足,需要進一步處理。僅僅是個入門。

2. 開發環境

  • macOS Sierra 10.12.4
  • IntelliJ IDEA 2017
  • Junit 4.12
  • JDK 1.8

因為在 mac 下通過 brew 安裝的 opencv ,所以包都是跟當前系統匹配的,安裝目錄也是一致的。

Windows 下需要根據自己的系統環境,位數,修改代碼的loadLibraries,決定加載的動態庫文件。

3. 代碼實現

import org.junit.Test;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;import java.io.File;
import java.util.*;/*** @Author : alexliu* @Description : opencv 測試* @Date : Create at 下午3:12 2018/1/26*/
public class TestOpenCV {String test_file_path = System.getProperty("user.dir") + File.separator + "testFiles";static {//加載動態鏈接庫時,不使用System.loadLibrary(xxx);。 而是使用 絕對路徑加載:System.load(xxx);/** 加載動態庫** 第一種方式 --------------System.loadLibrary(Core.NATIVE_LIBRARY_NAME);* loadLibrary(Core.NATIVE_LIBRARY_NAME); //使用這種方式加載,需要在 IDE 中配置參數.* Eclipse 配置:http://opencv-java-tutorials.readthedocs.io/en/latest/01-installing-opencv-for-java.html#set-up-opencv-for-java-in-eclipse* IDEA 配置 :http://opencv-java-tutorials.readthedocs.io/en/latest/01-installing-opencv-for-java.html#set-up-opencv-for-java-in-other-ides-experimental** 第二種方式 --------------System.load(path of lib);* System.load(your path of lib) ,方式比較靈活,可根據環境的系統,位數,決定加載內容*/loadLibraries();}/*** 讀取 table*/@Testpublic void readTable(){Mat source_image = Imgcodecs.imread(test_file_path + "/table-3.jpg");//灰度處理Mat gray_image = new Mat(source_image.height(), source_image.width(), CvType.CV_8UC1);Imgproc.cvtColor(source_image,gray_image,Imgproc.COLOR_RGB2GRAY);//二值化Mat thresh_image = new Mat(source_image.height(), source_image.width(), CvType.CV_8UC1);// C 負數,取反色,超過閾值的為黑色,其他為白色Imgproc.adaptiveThreshold(gray_image, thresh_image,255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY,7,-2);this.saveImage("out-table/1-thresh.png",thresh_image);//克隆一個 Mat,用于提取水平線Mat horizontal_image = thresh_image.clone();//克隆一個 Mat,用于提取垂直線Mat vertical_image = thresh_image.clone();/** 求水平線* 1. 根據頁面的列數(可以理解為寬度),將頁面化成若干的掃描區域* 2. 根據掃描區域的寬度,創建一根水平線* 3. 通過腐蝕、膨脹,將滿足條件的區域,用水平線勾畫出來** scale 越大,識別的線越多,因為,越大,頁面劃定的區域越小,在腐蝕后,多行文字會形成一個塊,那么就會有一條線* 在識別表格時,我們可以理解線是從頁面左邊 到 頁面右邊的,那么劃定的區域越小,滿足的條件越少,線條也更準確*/int scale = 10;int horizontalsize = horizontal_image.cols() / scale;// 為了獲取橫向的表格線,設置腐蝕和膨脹的操作區域為一個比較大的橫向直條Mat horizontalStructure = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(horizontalsize, 1));// 先腐蝕再膨脹 new Point(-1, -1) 以中心原點開始// iterations 最后一個參數,迭代次數,越多,線越多。在頁面清晰的情況下1次即可。Imgproc.erode(horizontal_image, horizontal_image, horizontalStructure, new Point(-1, -1),1);Imgproc.dilate(horizontal_image, horizontal_image, horizontalStructure, new Point(-1, -1),1);this.saveImage("out-table/2-horizontal.png",horizontal_image);// 求垂直線scale = 30;int verticalsize = vertical_image.rows() / scale;Mat verticalStructure = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(1, verticalsize));Imgproc.erode(vertical_image, vertical_image, verticalStructure, new Point(-1, -1),1);Imgproc.dilate(vertical_image, vertical_image, verticalStructure, new Point(-1, -1),1);this.saveImage("out-table/3-vertical.png",vertical_image);/** 合并線條* 將垂直線,水平線合并為一張圖*/Mat mask_image = new Mat();Core.add(horizontal_image,vertical_image,mask_image);this.saveImage("out-table/4-mask.png",mask_image);/** 通過 bitwise_and 定位橫線、垂直線交匯的點*/Mat points_image = new Mat();Core.bitwise_and(horizontal_image, vertical_image, points_image);this.saveImage("out-table/5-points.png",points_image);/** 通過 findContours 找輪廓** 第一個參數,是輸入圖像,圖像的格式是8位單通道的圖像,并且被解析為二值圖像(即圖中的所有非零像素之間都是相等的)。* 第二個參數,是一個 MatOfPoint 數組,在多數實際的操作中即是STL vectors的STL vector,這里將使用找到的輪廓的列表進行填充(即,這將是一個contours的vector,其中contours[i]表示一個特定的輪廓,這樣,contours[i][j]將表示contour[i]的一個特定的端點)。* 第三個參數,hierarchy,這個參數可以指定,也可以不指定。如果指定的話,輸出hierarchy,將會描述輸出輪廓樹的結構信息。0號元素表示下一個輪廓(同一層級);1號元素表示前一個輪廓(同一層級);2號元素表示第一個子輪廓(下一層級);3號元素表示父輪廓(上一層級)* 第四個參數,輪廓的模式,將會告訴OpenCV你想用何種方式來對輪廓進行提取,有四個可選的值:*      CV_RETR_EXTERNAL (0):表示只提取最外面的輪廓;*      CV_RETR_LIST (1):表示提取所有輪廓并將其放入列表;*      CV_RETR_CCOMP (2):表示提取所有輪廓并將組織成一個兩層結構,其中頂層輪廓是外部輪廓,第二層輪廓是“洞”的輪廓;*      CV_RETR_TREE (3):表示提取所有輪廓并組織成輪廓嵌套的完整層級結構。* 第五個參數,見識方法,即輪廓如何呈現的方法,有三種可選的方法:*      CV_CHAIN_APPROX_NONE (1):將輪廓中的所有點的編碼轉換成點;*      CV_CHAIN_APPROX_SIMPLE (2):壓縮水平、垂直和對角直線段,僅保留它們的端點;*      CV_CHAIN_APPROX_TC89_L1  (3)or CV_CHAIN_APPROX_TC89_KCOS(4):應用Teh-Chin鏈近似算法中的一種風格* 第六個參數,偏移,可選,如果是定,那么返回的輪廓中的所有點均作指定量的偏移*/List<MatOfPoint> contours = new ArrayList<MatOfPoint>();Mat hierarchy = new Mat();Imgproc.findContours(mask_image,contours,hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE,new Point(0,0));List<MatOfPoint> contours_poly = contours;Rect[] boundRect = new Rect[contours.size()];LinkedList<Mat> tables = new LinkedList<Mat>();//循環所有找到的輪廓-點for(int i=0 ; i< contours.size(); i++){MatOfPoint point = contours.get(i);MatOfPoint contours_poly_point = contours_poly.get(i);/** 獲取區域的面積* 第一個參數,InputArray contour:輸入的點,一般是圖像的輪廓點* 第二個參數,bool oriented = false:表示某一個方向上輪廓的的面積值,順時針或者逆時針,一般選擇默認false*/double area = Imgproc.contourArea(contours.get(i));//如果小于某個值就忽略,代表是雜線不是表格if(area < 100){continue;}/** approxPolyDP 函數用來逼近區域成為一個形狀,true值表示產生的區域為閉合區域。比如一個帶點幅度的曲線,變成折線** MatOfPoint2f curve:像素點的數組數據。* MatOfPoint2f approxCurve:輸出像素點轉換后數組數據。* double epsilon:判斷點到相對應的line segment 的距離的閾值。(距離大于此閾值則舍棄,小于此閾值則保留,epsilon越小,折線的形狀越“接近”曲線。)* bool closed:曲線是否閉合的標志位。*/Imgproc.approxPolyDP(new MatOfPoint2f(point.toArray()),new MatOfPoint2f(contours_poly_point.toArray()),3,true);//為將這片區域轉化為矩形,此矩形包含輸入的形狀boundRect[i] = Imgproc.boundingRect(contours_poly.get(i));// 找到交匯處的的表區域對象Mat table_image = points_image.submat(boundRect[i]);List<MatOfPoint> table_contours = new ArrayList<MatOfPoint>();Mat joint_mat = new Mat();Imgproc.findContours(table_image, table_contours,joint_mat, Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_SIMPLE);//從表格的特性看,如果這片區域的點數小于4,那就代表沒有一個完整的表格,忽略掉if (table_contours.size() < 4)continue;//保存圖片tables.addFirst(source_image.submat(boundRect[i]).clone());//將矩形畫在原圖上Imgproc.rectangle(source_image, boundRect[i].tl(), boundRect[i].br(), new Scalar(0, 255, 0), 1, 8, 0);}for(int i=0; i< tables.size(); i++ ){//拿到表格后,可以對表格再次處理,比如 OCR 識別等this.saveImage("out-table/6-table-"+(i+1)+".png",tables.get(i));}this.saveImage("out-table/7-source.png",source_image);}private void saveImage(String path,Mat image){String outPath = this.test_file_path + File.separator + path;File file = new File(outPath);//目錄是否存在this.dirIsExist(file.getParent());Imgcodecs.imwrite(outPath,image);}private void dirIsExist(String dirPath){File dir = new File(dirPath);if(!dir.exists()){dir.mkdirs();}}/*** 加載動態庫*/private static void loadLibraries() {try {String osName = System.getProperty("os.name");String opencvpath = System.getProperty("user.dir");//windowsif(osName.startsWith("Windows")) {int bitness = Integer.parseInt(System.getProperty("sun.arch.data.model"));//32位系統if(bitness == 32) {opencvpath=opencvpath+"\\opencv\\x86\\Your path to .dll";}//64位系統else if (bitness == 64) {opencvpath=opencvpath+"\\opencv\\x64\\Your path to .dll";} else {opencvpath=opencvpath+"\\opencv\\x86\\Your path to .dll";}}// mac oselse if(osName.equals("Mac OS X")){opencvpath = "/usr/local/Cellar/opencv/3.4.0_1/share/OpenCV/java/libopencv_java340.dylib";}System.out.println(opencvpath);System.load(opencvpath);} catch (Exception e) {throw new RuntimeException("Failed to load opencv native library", e);}}

4. 實現效果

輸入圖片說明

5. 參考資料

OpenCV處理拍照表格 此文是一個專題,有多篇

OpenCV-檢測并提取表格

廣告欄: 歡迎關注我的 個人博客

轉載于:https://my.oschina.net/u/3767256/blog/1615720

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/278505.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/278505.shtml
英文地址,請注明出處:http://en.pswp.cn/news/278505.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

內存泄露 體現在哪個數字上_Microsoft剛剛泄漏了一個新的開始菜單。 你喜歡哪個?...

內存泄露 體現在哪個數字上NTAuthority on TwitterTwitter上的NTAuthorityMicrosoft messed up today, releasing an internal build of Windows 10 to Windows Insiders. This build was never meant to see the light of day, but it features a new Start menu design—with…

簡述 Spring Cloud 是什么

很多同學都了解了Spring &#xff0c;了解了 Spring Boot, 但對于 Spring Cloud 是什么還是比較懵逼的。 本文帶你簡單的了解下&#xff0c;什么是Spring Cloud。 Spring Cloud 是什么 從字面理解&#xff0c;Spring Cloud 就是致力于分布式系統、云服務的框架。 Spring Cloud …

js DOM節點

元素節點 4種方式獲取 var oDiv document.getElementById("box");        //通過Id獲取元素var oDiv document.getElementsByClassName("div")[0];   //通過類名獲取元素  --》[ 0 ] 必須寫var oDiv document.getElementsByTagName("…

python web scraping

2019獨角獸企業重金招聘Python工程師標準>>> 最近在看《Web Scraping with Python》&#xff0c;借此來熟悉Python2.7如何開始編程。 發現書上主要使用的 http://example.webscraping.com/ 網站有部分變化&#xff0c;書中的代碼有點無法對照使用&#xff0c;因此稍…

傅里葉變換的物理意義

用三角函數表示周期函數 傅里葉的相關理論始于下面假設&#xff1a;對于周期為1的信號$f(t)$&#xff0c;可以由不同頻率的三角函數組成&#xff0c; $f(t) \frac{a_0}{2}\displaystyle{\sum^{\infty}_{k1}}(a_kcos(2\pi kt)b_ksin(2\pi kt))$ 組成的基礎波形為一個信號對&…

天貓年度總結

2019獨角獸企業重金招聘Python工程師標準>>> 魯大師天貓工作總結 時間&#xff1a;2017年10月22日-1月30日 1、對代理商進行6大區域的劃分管理&#xff0c;有專門的客服指導。 2、加班費申請和車費報銷制度。 3、簡化了特權訂金2階段改成1階段&#xff0c;極大的方便…

因特網使用期限_Internet死亡時使用PC的其他方式

因特網使用期限Nothing is more annoying than getting your Internet connection shut down, due to weather, or perhaps forgetting to pay your bill. Let’s take a look at some ways you can be productive and entertained without the Internet. 沒有什么比由于天氣原…

【基礎操作】線性基詳解

線性基是一個奇妙的集合&#xff08;我摘的原話&#xff09; 這里以非 $OI$ 的角度介紹了線性基 基礎部分 模板題 給你 $n$ 個數的集合&#xff0c;讓你選出任意多個不重復的數&#xff0c;使得它們的異或和最大。 線性基是什么 我們稱集合 $B$ 是集合 $S$ 的線性基&#xff0c…

節省大量教科書的三種潛在風險方法

Photo by Sultry 攝影&#xff1a; Sultry You can always save money on textbooks by buying online, going ebook, or renting what you need. But there are riskier ways to save a buck that just may yield even greater payoff, such as getting the international or …

解決內網搭建本地yum倉庫。

2019獨角獸企業重金招聘Python工程師標準>>> 一、使用iso鏡像搭建本地yum倉庫&#xff1b; 1、掛載鏡像到/mnt目錄下&#xff1a; [rootDasoncheng ~]# mount /dev/cdrom /mnt mount: /dev/sr0 is write-protected, mounting read-only2、備份配置文件&#xff0c;并…

通過用 .NET 生成自定義窗體設計器來定制應用程序

本文討論&#xff1a; ? 設計時環境基本原理 ? 窗體設計器體系結構 ? Visual Studio .NET 中窗體設計器的實現 ? 為自己的應用程序編寫窗體設計器而需要實現的服務 在很多年中&#xff0c;MFC 一直是生成基于 Windows? 的應用程序的流行框架。MFC 包含一個可以使窗體生成、…

airdrop 是 藍牙嗎_您可以在Windows PC或Android手機上使用AirDrop嗎?

airdrop 是 藍牙嗎Aleksey Khilko/Shutterstock.comAleksey Khilko / Shutterstock.comApple’s AirDrop is a convenient way to send photos, files, links, and other data between devices. AirDrop only works on Macs, iPhones, and iPads, but similar solutions are av…

vue加百度統計代碼(親測有效)

申請百度統計后&#xff0c;會得到一段JS代碼&#xff0c;需要插入到每個網頁中去&#xff0c;在Vue.js項目首先想到的可能就是&#xff0c;把統計代碼插入到index.html入口文件中&#xff0c;這樣就全局插入&#xff0c;每個頁面就都有了;這樣做就涉及到一個問題&#xff0c;V…

如何將Rant變成生產力電動工具

Ranting doesn’t have to be a waste of breathe and time. You can turn a rant into a powerful tool for productivity. Learn how to transform your sense of victim hood and irritability to self-empowerment and mental clarity. 狂歡不必浪費呼吸和時間。 您可以將r…

linux 下使用 curl post

命令&#xff1a; curl -X POST -d /etc/lazada/lazada_tracking.txt http://localhost:8080/booking/rs/LazadaService/post --header "Content-Type:application/json" -d 后臺 / &#xff1a; post 的 body 體 &#xff45;&#xff47;&#xff1a; {"a…

服務治理·理論篇(一)

0、故事主角 呱呱樂 是一家互聯網金融公司。主營現金貸、p2p理財、消費分期業務。 公司現有技術人員800名&#xff0c;系統極其龐雜&#xff0c;每日穩定處理25w左右的訂單量&#xff0c;有搶購活動時&#xff0c;系統的QPS(Query Per Second)峰值達到了3w。 系統雖然龐雜&…

2019-1-92.4G射頻芯片培訓資料

2019-1-92.4G射頻芯片培訓資料 培訓 RF 小書匠 歡迎走進zozo的學習之旅。 2.4G芯片選型2.4G芯片開發Q&A2.4G芯片選型 芯片類型 soc防盜標簽2.4G無線芯片選型發射器收發器LSD2RF-1600-V1.1 調制方式射頻基礎 2.4G芯片開發 原理圖 發射優先收發均衡PCB topbottomlayout規…

在Outlook 2010中使用對話視圖

One of the new features in Outlook 2010 is the ability to use Conversation View for easier management of your email conversations. Here we will take a quick look at how to use the new feature. Outlook 2010中的新功能之一是可以使用“對話視圖”來更輕松地管理電…

openresty capture

local args {} args["name"] "張三" args["sex"] "男"local captureRes; if ngx.var.request_method "POST" thencaptureRes ngx.location.capture(/dsideal_yy/test, {method ngx.HTTP_POST, headers { ["Cont…

Day10:html和css

Day10:html和css <html> <body> <h1>標題</h1> <p>段落</p> </body> </html>HTML 是用來描述網頁的一種語言&#xff0c;超文本標記語言&#xff0c;不是一種編程語言&#xff0c;而是一種標記語言&#xff0c;是一套標記標簽…