当前位置: 首页 > news >正文

Java使用Tesseract进行OCR图片文字识别

前言

在当前的文字识别技术应用中,除了采用现有的API服务之外,常见的解决方案包括利用Tessdata、Canvas或OCRAD等工具。以下是对几种技术的简要分析:

  1. 百度API的使用体验表明,虽然其识别率令人满意,但并非完美无误。此外,该服务对使用次数有所限制,尽管对于一般需求而言足够,但存在超额使用导致服务受限的风险。

  2. Canvas技术要求对图像执行预处理步骤,如旋转、灰度化以及调整字符间距等,这些处理能显著提升识别成功率。然而,由于公司产品中的验证码类型多样,难以通过统一方法处理,因此该方案被暂时搁置。

  3. OCRAD的JavaScript版本已进行尝试,但识别准确率相对较低。有关此工具的具体应用将在未来的文章中进一步探讨。

尽管互联网上关于Tessdata技术的介绍文章众多,但实际应用过程中仍面临一定挑战。操作流程概述如下:首先截取全屏图像,接着提取特定元素图片,然后进行图像预处理,最后执行文字识别并输出结果。

通过这一流程,可以有效地实现对图像中文字的准确识别和解读。

注意:图片截取格式统一为.jpg,用png会出问题。 

Tessdata介绍

Tessdata是用于光学字符识别(OCR)的软件包,专为与Tesseract OCR引擎配合使用,提供必要的语言数据文件以支持文本识别的多种语言。它包含了预先训练好的模型,这些模型帮助Tesseract识别和转换不同语言的文本图像为可编辑和可搜索的文件格式

在Tesseract中,Tessdata的作用不可小觑,它是让Tesseract能够识别特定语言文本的关键。Tessdata文件夹通常存放在Tesseract安装目录下,每个语言模型都以“.traineddata”为扩展名的文件形式存在。例如,英语的模型文件是“eng.traineddata”,中文简体是“chi_sim.traineddata”。这些文件包含了每种语言特定的字符集和相关的训练信息,这些信息使得Tesseract能够准确地将图像中的文本转换为文本文件。

Tessdata不仅提供了官方支持的语言包,还允许用户生成自定义的traineddata文件,从而训练出适应特定需求的OCR模型。例如,如果现有的语言包无法满足某些特殊场景的需求,用户可以自行收集数据并按照Tesseract的训练流程创建新的语言模型。这一过程可能包括图像预处理、地面实况标注、模型训练和测试等步骤。通过这种方式,Tesseract可以不断扩展其应用范围,适应更多样的应用场景。

总的来说,Tessdata是Tesseract OCR引擎的核心部分,提供了使Tesseract能够识别并处理多种语言文本的能力。通过合理地使用和配置Tessdata文件,用户可以显著提高文字识别的准确率和效率。同时,Tesseract社区持续对Tessdata进行更新和优化,以适应不断变化的应用需求和提升用户体验。

实践案例 

 1、添加项目依赖 在项目的pom.xml文件中,添加以下依赖

<!--<tess4j图片识别>-->
<dependency><groupId>net.java.dev.jna</groupId><artifactId>jna</artifactId><version>4.1.0</version>
</dependency>
<dependency><groupId>net.sourceforge.tess4j</groupId><artifactId>tess4j</artifactId><version>2.0.1</version><exclusions><exclusion><groupId>com.sun.jna</groupId><artifactId>jna</artifactId></exclusion></exclusions>
</dependency>

2、从全图中截取元素图片

该部分路径存了两个,用来将处理后的图片覆盖原图片。

// 元素截图public static String[] elementscreenShot(WebElement element )throws Exception {WrapsDriver wrapsDriver = (WrapsDriver) element;long time = System.currentTimeMillis();// 截图整个页面File screen = ((TakesScreenshot) wrapsDriver.getWrappedDriver()).getScreenshotAs(OutputType.FILE);BufferedImage img = ImageIO.read(screen);// 获得元素的高度和宽度int width = element.getSize().getWidth();int height = element.getSize().getHeight();// 创建一个矩形使用上面的高度,和宽度Rectangle rect = new Rectangle(width, height);// 得到元素的坐标Point p = element.getLocation();BufferedImage dest = img.getSubimage(p.getX(), p.getY(),(int) rect.getWidth(), (int) rect.getHeight());// 存为png格式ImageIO.write(dest, "png", screen);DateFormat dateFormat = new SimpleDateFormat("yyyyMMddhhmmss");FileSystemView fsv = FileSystemView.getFileSystemView();File com = fsv.getHomeDirectory(); // 这便是读取桌面路径的方法了String url = com.getPath() + "/test";File location = new File(url);if (!location.exists()) {location.mkdirs();}String imgPath = location.getAbsolutePath() + File.separator + "pic_"+ time + ".jpg";String cleanPath = location.getAbsolutePath();//存了原图片和清楚后图片的地址String[] imgpath = { imgPath, cleanPath };File targetFile = new File(imgPath);try {FileUtils.copyFile(screen, targetFile);} catch (IOException e1) {e1.printStackTrace();}//元素图片路径return imgpath;
}

3、对截取图片进行处理:灰度化、二值化、去除干扰线等

以下是图像处理的类,其中对于去除干扰线的操作还是慎用,可能会把文字也剔除掉。

public class CleanElementImage {/**** @param sfile*            需要去噪的图像* @param destDir*            去噪后的图像保存地址* @throws IOException*/public static void handlImage(File sfile, String destDir)  throws IOException {File destF = new File(destDir);if (!destF.exists()){destF.mkdirs();}BufferedImage bufferedImage = ImageIO.read(sfile);int h = bufferedImage.getHeight();int w = bufferedImage.getWidth();// 灰度化int[][] gray = new int[w][h];for (int x = 0; x < w; x++){for (int y = 0; y < h; y++){int argb = bufferedImage.getRGB(x, y);// 图像加亮(调整亮度识别率非常高)int r = (int) (((argb >> 16) & 0xFF) * 1.1 + 30);int g = (int) (((argb >> 8) & 0xFF) * 1.1 + 30);int b = (int) (((argb >> 0) & 0xFF) * 1.1 + 30);if (r >= 255){r = 255;}if (g >= 255){g = 255;}if (b >= 255){b = 255;}gray[x][y] = (int) Math.pow((Math.pow(r, 2.2) * 0.2973 + Math.pow(g, 2.2)* 0.6274 + Math.pow(b, 2.2) * 0.0753), 1 / 2.2);}}// 二值化int threshold = ostu(gray, w, h);BufferedImage binaryBufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_BINARY);for (int x = 0; x < w; x++){for (int y = 0; y < h; y++){if (gray[x][y] > threshold){gray[x][y] |= 0x00FFFF;} else{gray[x][y] &= 0xFF0000;}binaryBufferedImage.setRGB(x, y, gray[x][y]);}}ImageIO.write(binaryBufferedImage, "jpg", new File(destDir, sfile.getName()));}public static boolean isBlack(int colorInt)
{Color color = new Color(colorInt);if (color.getRed() + color.getGreen() + color.getBlue() <= 300){return true;}return false;
}public static boolean isWhite(int colorInt)
{Color color = new Color(colorInt);if (color.getRed() + color.getGreen() + color.getBlue() > 300){return true;}return false;
}public static int isBlackOrWhite(int colorInt)
{if (getColorBright(colorInt) < 30 || getColorBright(colorInt) > 730){return 1;}return 0;
}public static int getColorBright(int colorInt)
{Color color = new Color(colorInt);return color.getRed() + color.getGreen() + color.getBlue();
}public static int ostu(int[][] gray, int w, int h)
{int[] histData = new int[w * h];// Calculate histogramfor (int x = 0; x < w; x++){for (int y = 0; y < h; y++){int red = 0xFF & gray[x][y];histData[red]++;}}// Total number of pixelsint total = w * h;float sum = 0;for (int t = 0; t < 256; t++){sum += t * histData[t];}float sumB = 0;int wB = 0;int wF = 0;float varMax = 0;int threshold = 0;for (int t = 0; t < 256; t++){wB += histData[t]; // Weight Backgroundif (wB == 0) {continue;}wF = total - wB; // Weight Foregroundif (wF == 0) {break;}sumB += (float) (t * histData[t]);float mB = sumB / wB; // Mean Backgroundfloat mF = (sum - sumB) / wF; // Mean Foreground// Calculate Between Class Variancefloat varBetween = (float) wB * (float) wF * (mB - mF) * (mB - mF);// Check if new maximum foundif (varBetween > varMax){varMax = varBetween;threshold = t;}}return threshold;
}
}

4、准备识别的语言包

默认是英文(识别字母和数字),如果要识别中文(数字 + 中文),需要制定语言包。\
语言包可以指定一个路径,有就可以了。
源码下载地址
可以下载源码,然后到下面这个路径找到语言包,把语言包放到一个路径:\
例如:XXX/tessdata/下面。

tesseract.js-master.zip\tesseract.js-master\tests\assets\traineddata 

 

 5、对图片进行识别

```
/*** 图片识别* @author wangy* @date 2019-08-26* @param parameter*/public static  String  ocrResult(WebElement element ) throws Exception {FileSystemView fsv = FileSystemView.getFileSystemView();File com=fsv.getHomeDirectory();    //这便是读取桌面路径的方法了String url = "";String os = System.getProperty("os.name");//识别系统,找不同的语言包路径if (os.indexOf("Windows") == -1) {url = "/opt/google/";} else {url = com.getPath();}//获取元素截图的路径String path[]=Screenshot.elementscreenShot(element);//获取未处理的截图路径String imgpath=path[0];String result = null;File imageFile = new File(imgpath);//要对图片处理CleanElementImage.handlImage(imageFile,path[1]);ITesseract instance = new Tesseract();//读取语言包的路径地址instance.setDatapath(url + File.separator + "test" + File.separator+ "tessdata");// 默认是英文(识别字母和数字),如果要识别中文(数字 + 中文),需要制定语言包,这里是数字,所以没用语言包
//        instance.setLanguage("chi_sim");
//为了防止没截完图片就识别,做了一个简单的循环try{String ocrResult=instance.doOCR(imageFile);if(imageFile.exists()&&ocrResult!=""){result=ocrResult;}else {while(true){Thread.sleep(1000);if(imageFile.exists()&&ocrResult!=""){result=ocrResult;break;}}}}catch(TesseractException e){System.out.println(e.getMessage());}return result;}

这一部分由于项目问题,贴在这里做了特殊处理,原码有一点点区别。大家使用,如果有什么问题,欢迎反馈!

6、成果

这里简单放个对照,图片将就看一下效果,识别结果大概90%以上吧:

 

相关文章:

  • 老师是怎么分班的?用什么工具比较好?
  • 实战OpenCV之绘制图形
  • JVM 在GC 时的根对象都有那些
  • day_49
  • 代码断点调试
  • LLM 直接偏好优化(DPO)的一些研究
  • springboot框架中filter过滤器的urlPatterns的匹配源码
  • Oracle(81)如何生成AWR报告?
  • 链动 2+1 模式小程序 AI 智能名片商城源码培训邀约策略研究
  • Springsecurity 自定义AuthenticationManager
  • RocketMQ Dashboard
  • 【大数据】什么是数据中台?
  • 【HarmonyOS 4.0】基础组件
  • 海山数据库(He3DB)源码详解:He3DB-XLogWrite函数
  • 揭秘CAAC、AOPA、ALPA、ASFC和UTC无人机执照的差别及实用价值
  • 《Javascript高级程序设计 (第三版)》第五章 引用类型
  • 【笔记】你不知道的JS读书笔记——Promise
  • JavaScript设计模式与开发实践系列之策略模式
  • java中具有继承关系的类及其对象初始化顺序
  • JSDuck 与 AngularJS 融合技巧
  • Linux CTF 逆向入门
  • mysql中InnoDB引擎中页的概念
  • node 版本过低
  • vue和cordova项目整合打包,并实现vue调用android的相机的demo
  • 服务器之间,相同帐号,实现免密钥登录
  • 规范化安全开发 KOA 手脚架
  • 浅析微信支付:申请退款、退款回调接口、查询退款
  • 如何使用Mybatis第三方插件--PageHelper实现分页操作
  • 吴恩达Deep Learning课程练习题参考答案——R语言版
  • No resource identifier found for attribute,RxJava之zip操作符
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • ​​​​​​​Installing ROS on the Raspberry Pi
  • ​1:1公有云能力整体输出,腾讯云“七剑”下云端
  • # 服务治理中间件详解:Spring Cloud与Dubbo
  • #、%和$符号在OGNL表达式中经常出现
  • #NOIP 2014#Day.2 T3 解方程
  • #考研#计算机文化知识1(局域网及网络互联)
  • (03)光刻——半导体电路的绘制
  • (2)MFC+openGL单文档框架glFrame
  • (zt)最盛行的警世狂言(爆笑)
  • (接口自动化)Python3操作MySQL数据库
  • (顺序)容器的好伴侣 --- 容器适配器
  • .NET 4.0中使用内存映射文件实现进程通讯
  • .NET 自定义中间件 判断是否存在 AllowAnonymousAttribute 特性 来判断是否需要身份验证
  • /dev/VolGroup00/LogVol00:unexpected inconsistency;run fsck manually
  • @AutoConfigurationPackage的使用
  • @Autowired注解的实现原理
  • @entity 不限字节长度的类型_一文读懂Redis常见对象类型的底层数据结构
  • [\u4e00-\u9fa5] //匹配中文字符
  • [AI Embedchain] 开始使用 - 全栈
  • [Angularjs]asp.net mvc+angularjs+web api单页应用之CRUD操作
  • [BJDCTF2020]The mystery of ip1
  • [c++] 什么是平凡类型,标准布局类型,POD类型,聚合体
  • [C++11 多线程同步] --- 条件变量的那些坑【条件变量信号丢失和条件变量虚假唤醒(spurious wakeup)】
  • [DP 训练] Longest Run on a Snowboard, UVa 10285