2019独角兽企业重金招聘Python工程师标准>>>

关于 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

OpenCV3 识别图中表格-JAVA 实现相关推荐

  1. JAVA tess 识别图中文字 已训练好 识别精准

    JAVA tess 识别图中文字 已训练好 识别精准 文章目录 JAVA tess 识别图中文字 已训练好 识别精准 一.添加依赖 二.下载更新好的中文语言包 chi_sim.traineddata, ...

  2. IDEA无法识别项目中的Java类

    一.现象 在切换代码分支时,IDEA有时候会出现无法识别项目中的Java类,提示:无法识别class. 可以成功执行本地命令mvn clean compile,无报错,但IDEA却无法识别生成的Jav ...

  3. 识别图中文字软件哪个好?推荐这四个软件给大家

    随着计算机视觉和人工智能技术的发展,现在可以使用图像文字识别技术来自动将图像中的文本转换为可编辑和可搜索的文本.这种技术被称为OCR技术,它在数字化图书馆.自动化数据录入.自动化文档管理等领域中得到了 ...

  4. label里面的文字换行_批量识别图中文字自动命名,让你1秒找到骚图!太强大了!...

    每次做视频,找图就相当的费劲,因为图片太多,每次想要找到固定的图的话.就像大海捞针一样.因为上个版本的代码保存图片是截取部分图片链接进行命名的,所以名称是随机的. 所以今天我准备对这些图片重新命名.按 ...

  5. 微信二维码图片长按没有出现“识别图中的二维码”

    2019独角兽企业重金招聘Python工程师标准>>> 问题:页面中显示二维码图片,但是长按没有出现"识别图中二维码"选项. 1有说是style 的问题,然而修改 ...

  6. python批量循环图片识别_批量识别图中文字自动命名,让你1秒找到骚图

    自从上次批量的保存了半佛老师的各种骚图之后:我用Python一键保存了半佛老师所有的骚气表情包,每次做视频,找图就相当的费劲,因为图片太多,每次想要找到固定的图的话.就像大海捞针一样.因为上个版本的代 ...

  7. horizon client 无法识别域_iText for Mac(OCR识别图中文字工具)

    itext mac中文特别版是一款从图片中识别文字的OCR(光学字符识别)工具.通过截图.拖拽图片,即可以从扫描版的PDF等任意图片中识字,并且可以很好的解决摘抄和批注需求.而且itext mac版使 ...

  8. java 屏幕识别_Java课程设计:捕获图片以及识别图中的文字

    package屏幕捕捉以及识别;importjava.awt.Color;importjava.awt.FlowLayout;import java.awt.event.*;importjava.aw ...

  9. python3识别图中的文字_Python3.x:如何识别图片上的文字

    Python3.x:如何识别图片上的文字 安装pytesseract库,必须先安装其依赖的PIL及tesseract-ocr,其中PIL为图像处理库,而后面的tesseract-ocr则为google ...

最新文章

  1. VMware三种网络模式根本区别
  2. C# Excel数据有效性
  3. Spark安装与学习
  4. linux中用户的分类
  5. [原]NYOJ-开灯问题-77
  6. IdentityServer4之JWT签名(RSA加密证书)及验签
  7. OAuth2.0详解
  8. idea部署tomcat并实现简单的web项目
  9. class react 获取_「前端进阶」React系列九 - 受控非受控组件
  10. python判断音频是问句_Gamma Lab:让机器回答一个自然语言问题需要几步?
  11. 完全java实现一款开源的报表工具简表(JOR)
  12. 微积分:2.1导数中的中值定理
  13. shimano 型号详解 (zz)
  14. 关于NX/UG使用KF二次开发的常用方法
  15. 失传万年的PS致富经典(一)
  16. linux i2c模型 rtc模型 详细分析,Linux RTC驱动分析(一)----pcf8563驱动和class.c
  17. uniapp vue百度人脸实名认证(V4)接口
  18. 数说故事车企数字化渠道管理创新方法——精准进行消费者洞察
  19. Win10更改用户名
  20. 下载apk到本地,安装遇到的解析包时出现错误的分析

热门文章

  1. 素性测试的Miller-Rabin算法完全解析 (C语言实现、Python实现)
  2. 2019.08.08学习整理
  3. Rapidmind计算库性能测试
  4. java jconsole rmi 连接不上
  5. 【BZOJ3160】万径人踪灭 Manacher+FFT
  6. 32位linux 内存占用,LINUX内存高,触发OOM-KILLER问题解决
  7. eclipse 导入maven项目_手把手的Spring Boot Web 项目教程,Hello Spring Boot
  8. mysql中使用HAVING 筛选分组后的数据
  9. Mybatis框架中SqlSessionFactory
  10. 计算机系统组装 维护常用工具及其作用,《计算机系统组装维护》课程实用标准.doc...