今天想着把之前作过的一个Android的文字检测识别应用好好的回顾一下,由于之前写java程序,目的就是能用就行,不会仔细看每个部分代码,也不会记他们的用法,不回会去查API,借鉴别人的例程,用过就忘了,如今想着要改变,因而就回顾了一番。html
以前检测用到的是Tesseract_OCR,之因此能在Android的上运行,是由于黑暗伯爵大神已经把tess-two(为android写的tesseract-tools)编译好了,而后我直接用的。我仍是小白,彻底不懂编译那些,若是让我本身搞.... 反正最后编译成so文件(这个是linux平台下的动态连接库,能够类比dll),而后我用编译好的so文件,以及jar包导入到工程,照葫芦画瓢把文字识别部分和本身以前看论文写的文字检测部分合到一块儿,而后百度了怎么调用摄像头,而后写了一个摄像头拍照,而后检测文字所在区域,处理,而后识别的应用。之后有时间会详细的从新把应用梳理一遍,而后记录,固然这不是今天的重点。若是对Android上如何作OCR感兴趣,能够参考 这里 。java
回到正题,前几天同窗问我之前作的文字识别是咋搞得,他想用用,我因而就想把程序移回来到java上,结果我百度才发现原来还有另一种思路,那就是执行exe进程。先在pc上安装好tesseract_orc,而后jvm运行命令行跑识别程序,完成识别后结果写入txt,最后读取txt把内容返回到java程序中。这么想一想也行,因而便试了试。(PS:我是后来才发现也有Java的API tess4j,参考 这里)linux
而后就是讲本身具体怎么实现:android
工程也就两部分,一部分是GUI,反正我如今对于Java的基础知识薄弱,而后经常使用类也用的很少,因此借此机会正好熟悉。数组
GUI主要用到javax.swing包和java.awt包,swing主要写组件,awt是事件(其实awt也能够写组件),可是说swing是对awt中组建的优化,因此如今经常使用swing。GUI里面比较重要的概念仍是容器,组件必须放到容器里面才能显示,经常使用frame和dialog。我此次写GUI用到jframe,jbutton,filedialog,JLabel,imageicon这几个类app
界面以下jvm
FileDialog
类显示一个对话框窗口,用户能够从中选择文件。JLabel
对象能够显示文本、图像或同时显示两者。能够经过设置垂直和水平对齐方式,指定标签显示区中标签内容在何处对齐。默认状况下,标签在其显示区内垂直居中对齐。默认状况下,只显示文本的标签是开始边对齐;而只显示图像的标签则水平居中对齐。界面的具体代码我就不贴上来了,值得注意的是图片压缩显示有一个小trick。优化
imagePath = new String(dirPath+fileName); imageico = new ImageIcon(imagePath); int w = imageico.getIconWidth(); int h = imageico.getIconHeight(); double ratio = (double)w/(double)h; if(ratio>4/3){ h = (int)(640*h/w); w = 640; }else{ w = (int)(480*w/h); h = 480; } imageico.setImage(imageico.getImage().getScaledInstance(w,h,imageico.getImage().SCALE_DEFAULT));
另外,就是文字识别的部分,主要仍是一个执行进程的过程,那么首先得下载安装Tesseract_OCR:ui
安装下载的工做参考 这里this
那么这边主要的挑战就是使用java执行进程和作文件io,主要分一下几点:
1.java操做命令行主要用到processbuilder & process 类,出自java.lang
通常都是ProcessBuilder.start() 和 Runtime.exec(ArrayList<String>) 方法建立一个本机进程,并返回 Process 子类的一个实例,该实例可用来控制进程并得到相关信息。Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。
Runtime.getRuntime.exec(ArrayList<String> cmd)
processbuilder pb; pb.command(ArrayList<String> cmd);
这里就是用的processbuilder pb,新建一个实例,而且把运行参数保存到字符串表单cmd里,而后pb.command(ArrayList<String> cmd)执行,结果保存到指定txt中;
2.表示文件的类file类 出自java.io
它是文件和目录路径名的抽象表示形式。
主要方法getName(),getPath(),getParentPath();
在执行命令行时,表示输入的图片,表示输出txt均可以用到file类。
3.读取字节流过程 出自java.io
FileInputStream 从文件系统中的某个文件中得到输入字节。哪些文件可用取决于主机环境,这里能够读取图片。
new FileInputStream(outputFile.getAbsolutePath()) 新建一个文件输入字节流
new InputStreamReader(new FileInputStream(outputFile.getAbsolutePath()),"UTF-8")。文件输入字节流变成文件输入字符流
BufferedReader从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
主要是两种用法还能够这么使用
BufferedReader in = new BufferedReader(new FileReader("foo.in"));
或者
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream("foo.in"),"UTF-8"));
前者不能指定编码,然后者能够。
在最后把txt中文本读取到java程序,进而显示在GUI中用到
识别的代码以下
import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; public class textRecognizer{ private String textResult; /** * 输出的结果 */ private final String EOL = System.getProperty("line.separator"); //回车 private String tessPath = "D:\\Tesseract-OCR"; //tessocr程序所在目录 public textRecognizer(String path) { try { File imagefile = new File(path); textResult = this.recognizeText(imagefile); } catch (Exception e) { e.printStackTrace(); } } public String getResult(){ return textResult; } private String recognizeText(File imageFile) throws Exception { /** * 设置输出文件的保存的文件目录 */ File outputFile = new File(imageFile.getParentFile(),"output"); StringBuffer strB = new StringBuffer(); //设置cmd命令行字符串形式 List<String> cmd = new ArrayList<String>(); cmd.add(tessPath + "\\tesseract"); cmd.add(imageFile.getName()); cmd.add(outputFile.getName()); cmd.add("-l"); cmd.add("eng"); //启动exe进程 ProcessBuilder pb = new ProcessBuilder(); pb.directory(imageFile.getParentFile()); pb.command(cmd); pb.redirectErrorStream(true); Process process = pb.start(); //等待此进程完成 int w = process.waitFor(); if (w == 0){// 0表明正常退出 BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(outputFile.getAbsolutePath()+ ".txt"),"UTF-8")); String str; while ((str = in.readLine()) != null) { strB.append(str).append(EOL); } in.close(); } else{ String msg; switch (w){ case 1: msg = "Errors accessing files. There may be spaces in your image's filename."; break; case 29: msg = "Cannot recognize the image or its selected region."; break; case 31: msg = "Unsupported image format."; break; default: msg = "Errors occurred."; } throw new RuntimeException(msg); } new File(outputFile.getAbsolutePath()+ ".txt").delete(); /** * 若是作验证码 * return strB.toString().replaceAll("\\s*", ""); */ return strB.toString(); } }
惋惜的是最后检测的结果通常。
今天写程序期间还有其余的有意思的地方我也有记录。