• Tesseract识别图片提取文字&字库训练


       文中测试了3.0和4.0两个版本。发现3.0识别效率不准确,需要训练词库。4.0识别效率就比较高了,而且支持结果生成pdf、txt等格式。所以推荐使用4.0版本。

      这个工具可以用在爬虫的时候获取验证码进行识别且自动输入验证码的功能。

      git地址:https://github.com/tesseract-ocr/tesseract

      下载地址:https://digi.bib.uni-mannheim.de/tesseract/

    1.下载安装

      我下载的是 3.05.01,自带了中文词库。

    下载完成后目录结构:

    2.测试识别

    0.准备一张文字图片

    1.添加环境变量到path中,可以直接使用tesseract命令。检查是否配置成功

    C:UsersAdministratorDesktop新建文件夹>tesseract -v
    tesseract 3.05.01
     leptonica-1.74.1
      libgif 4.1.6(?) : libjpeg 8d (libjpeg-turbo 1.5.0) : libpng 1.6.20 : libtiff 4.0.6 : zlib 1.2.8 : libwebp 0.4.3 : libopenjp2 2.1.0

    查看命令可以携带的参数:

    PS C:UsersAdministratorDesktop新建文件夹> tesseract
    Usage:
      E:	esseractTesseract-OCR	esseract.exe --help | --help-psm | --help-oem | --version
      E:	esseractTesseract-OCR	esseract.exe --list-langs [--tessdata-dir PATH]
      E:	esseractTesseract-OCR	esseract.exe --print-parameters [options...] [configfile...]
      E:	esseractTesseract-OCR	esseract.exe imagename|stdin outputbase|stdout [options...] [configfile...]
    
    OCR options:
      --tessdata-dir PATH   Specify the location of tessdata path.
      --user-words PATH     Specify the location of user words file.
      --user-patterns PATH  Specify the location of user patterns file.
      -l LANG[+LANG]        Specify language(s) used for OCR.
      -c VAR=VALUE          Set value for config variables.
                            Multiple -c arguments are allowed.
      --psm NUM             Specify page segmentation mode.
      --oem NUM             Specify OCR Engine mode.
    NOTE: These options must occur before any configfile.
    
    Page segmentation modes:
      0    Orientation and script detection (OSD) only.
      1    Automatic page segmentation with OSD.
      2    Automatic page segmentation, but no OSD, or OCR.
      3    Fully automatic page segmentation, but no OSD. (Default)
      4    Assume a single column of text of variable sizes.
      5    Assume a single uniform block of vertically aligned text.
      6    Assume a single uniform block of text.
      7    Treat the image as a single text line.
      8    Treat the image as a single word.
      9    Treat the image as a single word in a circle.
     10    Treat the image as a single character.
     11    Sparse text. Find as much text as possible in no particular order.
     12    Sparse text with OSD.
     13    Raw line. Treat the image as a single text line,
                            bypassing hacks that are Tesseract-specific.
    OCR Engine modes:
      0    Original Tesseract only.
      1    Cube only.
      2    Tesseract + cube.
      3    Default, based on what is available.
    
    Single options:
      -h, --help            Show this help message.
      --help-psm            Show page segmentation modes.
      --help-oem            Show OCR Engine modes.
      -v, --version         Show version information.
      --list-langs          List available languages for tesseract engine.
      --print-parameters    Print tesseract parameters to stdout.

     2.进入cmd,进入到要识别的图片的路径下。

    C:UsersAdministratorDesktop新建文件夹>tesseract ./1.jpg re
    Error opening data file 	esseractTesseract-OCR	essdata/eng.traineddata
    Please make sure the TESSDATA_PREFIX environment variable is set to the parent directory of your "tessdata" directory.
    Failed loading language 'eng'
    Tesseract couldn't load any languages!
    Could not initialize tesseract.

    发现报错没有语言,解决办法: 将  tesseract 安装目录下的  tessdata 文件夹配置到环境变量  TESSDATA_PREFIX  

    C:UsersAdministrator>set TESSDATA_PREFIX
    TESSDATA_PREFIX=E:	esseractTesseract-OCR	essdata

    3.再次测试

    C:UsersAdministratorDesktop新建文件夹>tesseract ./1.png re
    Tesseract Open Source OCR Engine v3.05.01 with Leptonica

       会生成一个re.txt文件,内容如下:(发现中文乱码)

     4.解决中文乱码问题:加 -l 参数指定语言即可

    (1)查看支持的语言

    C:UsersAdministratorDesktop新建文件夹>tesseract --list-langs
    List of available languages (107):
    afr
    amh
    ara
    。。。

    (2)使用  chi_sim 识别图片

    C:UsersAdministratorDesktop新建文件夹>tesseract -l chi_sim ./1.png re
    Tesseract Open Source OCR Engine v3.05.01 with Leptonica

     3.测试复杂的中文识别---词库训练

     1.原来图片如下

     

    2. 识别之后的内容如下

    3.解决上面的问题---利用jTessBoxEditor工具进行Tesseract3.02.02样本训练

      此工具基于java运行,所以需要安装java环境。

    1.下载   jTessBoxEditor :http://tenet.dl.sourceforge.net/project/vietocr/jTessBoxEditor/jTessBoxEditor-1.5.zip

    2.解压运行

    $ java -jar jTessBoxEditor.jar

     

    3. 将上面的图片转换成tif格式,用于后面生成box文件。可以通过画图,然后另存为tif即可

    tif文面命名格式[lang].[fontname].exp[num].tif
    lang是语言 fontname是字体 
    比如我们要训练自定义字库 mylan 字体名normal
    那么我们把图片文件重命名 mylan.normal.exp0.jpg在转tif。(画图工具中另存为就可以)

    4.生成box文件。

    tesseract mylan.normal.exp0.jpg mylan.normal.exp0 -l chi_sim batch.nochop makebox

    box文件和对应的tif一定要在相同的目录下,不然后面打不开。会生产一个box文件。

     

    5.打开jTessBoxEditor矫正错误并训练

     打开train.bat

     找到tif图,打开,并校正。(校正完保存即可)

     6、训练。

    (1) 只要在命令行输入命令即可。(执行第一条命令会生产.tr文件,也可以先执行一次然后删掉tr测试是否可以训练)

    tesseract  mylan.normal.exp0.jpg mylan.normal.exp0  nobatch box.train
    
    
    unicharset_extractor mylan.normal.exp0.box

    或者输入两个合并执行的命令:

    tesseract  mylan.normal.exp0.jpg mylan.normal.exp0  nobatch box.train && unicharset_extractor mylan.normal.exp0.box

     如下证明全部识别完毕:

    (2)新建一个font_properties文件

    里面内容写入 normal 0 0 0 0 0 表示默认普通字体 (要求normal与上面的字体名称必须一致),下面是命令直接重定向(注意重定向>和>>的区别是>是覆盖模式,>>是追加模式):

    echo normal 0 0 0 0 0 >> font_properties

    (3)继续敲命令:

    shapeclustering -F font_properties -U unicharset mylan.normal.exp0.tr

    mftraining -F font_properties -U unicharset -O unicharset mylan.normal.exp0.tr

    cntraining mylan.normal.exp0.tr

    或者一次性执行下面命令:

    shapeclustering -F font_properties -U unicharset mylan.normal.exp0.tr && mftraining -F font_properties -U unicharset -O unicharset mylan.normal.exp0.tr && cntraining mylan.normal.exp0.tr

    最后会生成五个文件,把目录下的unicharset、inttemp、pffmtable、shapetable、normproto这五个文件前面都加上normal.

    如图:

    或者执行如下命令进行重命名:

    mv ./unicharset ./normal.unicharset && mv ./inttemp ./normal.inttemp && mv ./pffmtable ./normal.pffmtable && mv ./shapetable ./normal.shapetable && mv ./normproto ./normal.normproto

    (4)命令行输入,合并五个文件:

    combine_tessdata normal.

    得到训练好的字库。

    (5)把 normal.traineddata 复制到Tesseract-OCR 安装目录下的tessdata文件夹中

    或者执行以下命令即可:( TESSDATA_PREFIX 是配置的环境变量)

    mv ./normal.traineddata $TESSDATA_PREFIX

    或者执行复制命令:

    cp ./normal.traineddata $TESSDATA_PREFIX

     7.测试:

    识别命令:(可以使用两种语言,也可以使用一种语言)

    Administrator@MicroWin10-1535 MINGW64 ~/Desktop/新建文件夹
    $ tesseract mylan.normal.exp0.jpg result -l normal
    Tesseract Open Source OCR Engine v3.05.01 with Leptonica
    
    Administrator@MicroWin10-1535 MINGW64 ~/Desktop/新建文件夹
    $ tesseract mylan.normal.exp0.jpg result2 -l normal+chi_sim
    Tesseract Open Source OCR Engine v3.05.01 with Leptonica

    结果:(上面的   result   是指定生成的txt文件的前缀   )

    补充:用notepad++打开之后会发现有换行:

      这个需要大量的训练词库。不知道是否有更好的解决办法。

    补充:一个Java程序调用runtime执行本地命令,并读取生成的文件内容:

    package cn.xm.exam.test;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    
    import org.apache.commons.io.FileUtils;
    import org.apache.xmlbeans.impl.common.IOUtil;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import com.ibm.wsdl.util.IOUtils;
    
    @SuppressWarnings("all")
    public class PlainTest {
        private static final Logger logger = LoggerFactory.getLogger(Test.class);
    
        public static void main(String[] args) throws IOException {
            // 执行命令生成文件
            String cmd = "E:/tesseract/Tesseract-OCR/tesseract.exe G://test.jpg G://tmp -l normal+chi_sim";
            boolean exec = exec(cmd);
            if (exec) {
                String readFileToString = FileUtils.readFileToString(new File("G://tmp.txt"));
                System.out.println(readFileToString);
            }
        }
    
        public static boolean exec(String command) {
            Process process;// Process可以控制该子进程的执行或获取该子进程的信息
            try {
                logger.debug("exec cmd : {}", command);
                process = Runtime.getRuntime().exec(command);// exec()方法指示Java虚拟机创建一个子进程执行指定的可执行程序,并返回与该子进程对应的Process对象实例。
                // 下面两个可以获取输入输出流
                InputStream errorStream = process.getErrorStream();
                InputStream inputStream = process.getInputStream();
            } catch (IOException e) {
                logger.error(" exec {} error", command, e);
                return false;
            }
    
            int exitStatus = 0;
            try {
                exitStatus = process.waitFor();// 等待子进程完成再往下执行,返回值是子线程执行完毕的返回值
                // 第二种接受返回值的方法
                int i = process.exitValue(); // 接收执行完毕的返回值
                logger.debug("i----" + i);
            } catch (InterruptedException e) {
                logger.error("InterruptedException  exec {}", command, e);
                return false;
            }
    
            if (exitStatus != 0) {
                logger.error("exec cmd exitStatus {}", exitStatus);
            } else {
                logger.debug("exec cmd exitStatus {}", exitStatus);
            }
    
            process.destroy(); // 销毁子进程
            process = null;
    
            return true;
        }
    }

    结果:(会将换行也读取出来)

    1 23456
    这是一告测试中文
    不能告诉@154856.text
    电话:1 8434391 711

    =====下面研究 Tesseract  4.0版本的使用(推荐使用这个版本)=========

      官方对  Tesseract4.0 版本做了很多的改进,而且自带的语言库也增加了很多。下面研究在4.0版本的识别。经过研究,4.0版本是可以直接拿来就用的,都不用训练词。识别为pdf的准确率更加高,所以在实际中可以识别为pdf然后用apache-tika提取pdf的内容。这样识别率更加的高效。

    1. 下载安装

    2.修改环境变量为最新的4.0版本并测试

    $ tesseract --version
    tesseract v4.0.0.20181030
     leptonica-1.76.0
      libgif 5.1.4 : libjpeg 8d (libjpeg-turbo 1.5.3) : libpng 1.6.34 : libtiff 4.0.9 : zlib 1.2.11 : libwebp 0.6.1 : libopenjp2 2.2.0

    3.接下来还是对上面的带中文的进行识别

    tesseract ./1.jpg result -l chi_sim

    结果:(识别率已经非常准确了)

     4.测试将识别结果直接提取为字符串、存储为pdf(双层pdf)或者xml

    (1)直接提取为文本

    $ tesseract ./1.jpg stdout -l chi_sim
    123456
    这是一个测试中文
    
    不能告诉@154856 .text
    电话:18434391711

    (2)提取为pdf

       在4.0之后可以自动将识别结果提取为pdf xtxml等格式的数据。

    Administrator@MicroWin10-1535 MINGW64 ~/Desktop/新建文件夹
    $ tesseract ./1.jpg result -l chi_sim pdf
    Tesseract Open Source OCR Engine v4.0.0.20181030 with Leptonica

     结果:

    (3)当然可以一次性提取为多个文件,只需要在后面空格分割就可以了

    tesseract ./0101.jpg result -l chi_sim pdf txt

    结果会生成三个文件

    5.测试识别复杂的中文图片

    图片内容如下:

    (1)直接提取内容到输出控制台

    $ tesseract ./0101.jpg stdout -l chi_sim
    
    
    一个车者对禅师说: “我放不下一-些事放不下一些人。”
    
    禅师说没有什么东西是真正放不下的。苦者说: 可我就信信放不下.
    
    禅师递给他一个水杯然后就入里面侠     -直例到水溢出来。贡者被通到马_上松开了手,水杯掉在
    地上摔坏了。
    
    禅师说:其实,这个世界_上没有什么事是放不下的,痛了,你自然就会放下
    
    土者说到“我能换个水杯吗?
    
    禅师微微一笑道:“可以”他从包里拿出一一个水杯 ,说到再试试吧 , 禅师又往水杯里倒水,水溢出
    来 , 这次他没有放手
    
    祥岳问道:“不汤吗”?苦者说“和”
    
    禅师又问“为何不放手?”葫者说道“这水杯是她送的。禅师回头叹“番狗是真的牛通

    (2)查看提取tx和pdf的效率

    $ tesseract ./0101.jpg result -l chi_sim pdf txt

    txt和控制台的结果一样:

    pdf识别结果更加准确:(接近百分之百)

       经过上面的实验发现啊,pdf的识别是识别的双层pdf,我自己的一种思路就是:将识别结果存到pdf,然后用ApacheTika提取pdf的内容,这样识别效率应该会更加准确。

    6. java中调用runtime获取识别结果和提取为pdf之后用ApacheTika提取pdf内容

    (1)读取内容到字符串中

    package zd.dms.utils.ebuy;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    
    import org.apache.tika.exception.TikaException;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.xml.sax.SAXException;
    
    @SuppressWarnings("all")
    public class PlainTest {
        private static final Logger logger = LoggerFactory.getLogger(PlainTest.class);
    
        public static void main(String[] args) throws IOException, SAXException, TikaException {
            // 执行命令生成文件
            String cmd = "E:/tesseract4/Tesseract-OCR/tesseract.exe G://0101.jpg stdout -l chi_sim";
            boolean exec = exec(cmd);
        }
    
        public static boolean exec(String command) {
            Process process;// Process可以控制该子进程的执行或获取该子进程的信息
            String result = "";
            try {
                logger.debug("exec cmd : {}", command);
                process = Runtime.getRuntime().exec(command);// exec()方法指示Java虚拟机创建一个子进程执行指定的可执行程序,并返回与该子进程对应的Process对象实例。
                // 下面两个可以获取输入输出流
                InputStream errorStream = process.getErrorStream();
    
                // 获取其正常的输出流
                InputStream inputStream = process.getInputStream();
                InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
                BufferedReader br = new BufferedReader(inputStreamReader);
                String line = null;
                while ((line = br.readLine()) != null) {
                    result += line;
                }
    
            } catch (IOException e) {
                logger.error(" exec {} error", command, e);
                return false;
            }
    
            int exitStatus = 0;
            try {
                exitStatus = process.waitFor();// 等待子进程完成再往下执行,返回值是子线程执行完毕的返回值
                // 第二种接受返回值的方法
                int i = process.exitValue(); // 接收执行完毕的返回值
                logger.debug("i----" + i);
            } catch (InterruptedException e) {
                logger.error("InterruptedException  exec {}", command, e);
                return false;
            }
    
            if (exitStatus != 0) {
                logger.error("exec cmd exitStatus {}", exitStatus);
            } else {
                logger.debug("exec cmd exitStatus {}", exitStatus);
            }
    
            process.destroy(); // 销毁子进程
            process = null;
    
            // result就是获取到的结果
            System.out.println(result);
            return true;
        }
    }

    结果:

      一个车者对禅师说: “我放不下一-些事放不下一些人。”禅师说没有什么东西是真正放不下的。苦者说: 可我就信信放不下.禅师递给他一个水杯然后就入里面侠     -直例到水溢出来。贡者被通到马_上松开了手,水杯掉在地上摔坏了。禅师说:其实,这个世界_上没有什么事是放不下的,痛了,你自然就会放下土者说到“我能换个水杯吗?禅师微微一笑道:“可以”他从包里拿出一一个水杯 ,说到再试试吧 , 禅师又往水杯里倒水,水溢出来 , 这次他没有放手祥岳问道:“不汤吗”?苦者说“和”禅师又问“为何不放手?”葫者说道“这水杯是她送的。禅师回头叹“番狗是真的牛通     

    (2)读取内容到pdf中然后tika读取pdf

    package zd.dms.utils.ebuy;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    import org.apache.commons.io.FileUtils;
    import org.apache.tika.exception.TikaException;
    import org.apache.tika.metadata.Metadata;
    import org.apache.tika.parser.ParseContext;
    import org.apache.tika.parser.pdf.PDFParser;
    import org.apache.tika.sax.BodyContentHandler;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.xml.sax.SAXException;
    
    @SuppressWarnings("all")
    public class PlainTest {
        private static final Logger logger = LoggerFactory.getLogger(PlainTest.class);
    
        public static void main(String[] args) throws IOException, SAXException, TikaException {
            // 执行命令生成文件
            String cmd = "E:/tesseract4/Tesseract-OCR/tesseract.exe G://0101.jpg G://result -l chi_sim pdf";
            boolean exec = exec(cmd);
            if (exec) {
                BodyContentHandler handler = new BodyContentHandler();
                Metadata metadata = new Metadata();
                FileInputStream inputstream = new FileInputStream(new File("G://result.pdf"));
                ParseContext pcontext = new ParseContext();
    
                // parsing the document using PDF parser
                PDFParser pdfparser = new PDFParser();
                pdfparser.parse(inputstream, handler, metadata, pcontext);
    
                // getting the content of the document
                System.out.println("Contents of the PDF :" + handler.toString());
            }
        }
    
        public static boolean exec(String command) {
            Process process;// Process可以控制该子进程的执行或获取该子进程的信息
            try {
                logger.debug("exec cmd : {}", command);
                process = Runtime.getRuntime().exec(command);// exec()方法指示Java虚拟机创建一个子进程执行指定的可执行程序,并返回与该子进程对应的Process对象实例。
                // 下面两个可以获取输入输出流
                InputStream errorStream = process.getErrorStream();
                InputStream inputStream = process.getInputStream();
            } catch (IOException e) {
                logger.error(" exec {} error", command, e);
                return false;
            }
    
            int exitStatus = 0;
            try {
                exitStatus = process.waitFor();// 等待子进程完成再往下执行,返回值是子线程执行完毕的返回值
                // 第二种接受返回值的方法
                int i = process.exitValue(); // 接收执行完毕的返回值
                logger.debug("i----" + i);
            } catch (InterruptedException e) {
                logger.error("InterruptedException  exec {}", command, e);
                return false;
            }
    
            if (exitStatus != 0) {
                logger.error("exec cmd exitStatus {}", exitStatus);
            } else {
                logger.debug("exec cmd exitStatus {}", exitStatus);
            }
    
            process.destroy(); // 销毁子进程
            process = null;
    
            return true;
        }
    }

    结果:

    个对禅师说 “我放 不 一-些事放不一 。”
    禅师说没有什么东西是真正不下的 者可我信不.
    禅师递给一水杯然后入里面侠 -直例到水溢出来 者通到马上松了手,水掉
    摔了。
    禅师 :其,这个世界上没有什么事是不下 , 痛了 , 你自然就放下
    者到 “我能换个水杯?
    禅师微微笑 :“可以”他里拿出一水,说到再试试吧 , 禅师又往水杯里倒水 ,水溢出

    来 , 这次他没有放手
    祥岳 问 不汤”? 者 “和”
    禅师又 何不放手?”者 “这水杯她送的 禅师回头 “番真牛通

      git上面的文档对4.0最新的每个命令都有介绍,参照git上的doc文件夹下面对每个文件的介绍:https://github.com/tesseract-ocr/tesseract/tree/master/doc

  • 相关阅读:
    函数防抖与函数节流 封装好的debounce和throttle函数
    机顶盒
    getchar() putchar()
    【整】char、varchar、nchar、nvarchar的区别
    主机名
    主机
    java中的匿名内部类总结
    智能路由器又多一个玩家——乐视TV
    乐视开始折腾路由器,小米与极路由还会好过吗?
    带你认识什么是路由器
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/10273543.html
Copyright © 2020-2023  润新知