使用生成的 LaTeX 文件训练 Tesseract

使用生成的 LaTeX 文件训练 Tesseract

我的问题

Tesseract 是一个可以训练的OCR。

我需要训练它考虑一种新的特定字体(ttf 字体)。

我发现这次讨论这表明可以使用 LaTeX 生成训练文件。但是,脚本文件不再可用。

因此我开始尝试创建自己的脚本文件,但现在我只能用 LaTeX 生成正确的文件。


Tesseract 训练集是如何制作的

为了制作 Tesseract 训练集,我需要生成两个文件:一个 .box 文件和一个图像文件(例如 .tif 或 .png)。

这两个文件都是从我从现有的 Tesseract 训练集中提取的训练文本生成的。该训练文本至少包含 4700 个字符。


以下是训练文本的示例:

    « : : :R: : :» Re: û® 03 vue ë ’ Logiciels septembre ¢§ petite numérique ÈQ été Yû Windows “I” ^Z Commentaires forum privé
    depuis Dernière Annuaire une vendeur ° # “au-delà ” est français FAQ «% moment car œ? | ` fois JuG 14 plus HP Culture

现在,我使用此文本生成一个 pdf 文件(使用 OpenOffice)。然后将此 pdf 转换为图像(使用 imagemagick)。

然后我使用 tesseract 生成一个 .box 文件。此 .box 文件应包含每个包含字符的框的坐标。


例子

m 861 2621 893 2644 0
m 895 2617 921 2648 0
e 922 2621 938 2644 0
m 939 2622 961 2644 0
b 963 2621 978 2649 0

对应下图:

框定义不正确


如您所见,这里的问题是sept字符被替换为m并且它们位于同一个框中。

我应该有这个:

    s 861 2621 877 2644 0
    e 877 2621 893 2644 0
    p 895 2617 908 2648 0
    t 908 2617 921 2648 0
    e 922 2621 938 2644 0
    m 939 2622 961 2644 0
    b 963 2621 978 2649 0

这与这里几乎正确的图像相对应:

分体箱

如您所见,每个字母都在其自己的盒子内。

t与此处底部相同,p因为我编辑了 .box 文件并分割了包含两个字符的原始框。

我必须手动更正坐标才能得到这个:

s 861 2621 877 2644 0
e 877 2621 893 2644 0
p 895 2617 910 2644 0
t 910 2622 921 2648 0
e 922 2621 938 2644 0
m 939 2622 961 2644 0
b 963 2621 978 2649 0

与下图相匹配:

靠近字母边缘的方框

所以我必须纠正手动使用特定软件(jTessBoxEditor)对训练集(4700 多个字符!)进行处理,这个过程非常漫长且痛苦。


盒子文件格式

每行匹配一个字符 (文件必须采用 UTF-8 编码但是,使用另一种编码生成文件应该可以轻松转换为 UTF-8)

每行给出一个字符及其在图像文件中的位置,从左下角开始(0;0)。

e 922 2621 938 2644 0给出包含 ae 字符的框的左下角坐标(x=922 和 y=2621)和右上角坐标(x=938 和 y=2644)。

每行的最后一个数字是页码(从 0 开始):这里是第一页(0)。

更多信息可以在这里找到。


为什么不使用 LaTeX?

问题是 OpenOffice 仅生成 PDF(图像),我不知道如何解析 PDF 文件来获取每个字符的位置(也不知道是否可能)。

然后我想起 LaTeX 使用方框将每个字符放入页面中。而且 LaTeX“知道”每个方框在哪里。所以我找到了已经提到讨论并决定尝试一下。

我在讨论中看到他们使用了以下流程,因此我也尝试这样做:

a) 安装 MikTeX 和一些允许 latex 理解卡纳达语的软件包,我知道有一个叫做 itrans 的软件包可以处理这个。但是你需要使用拉丁音译为其提供输入。

b) 您准备训练文本(使用音译)并使用 itrans 和 latex 对其进行处理。在此阶段,您将获得一个 .dvi 文件,该文件以卡纳达语排版,并包含(以隐秘形式)有关文本字符框的所有信息。您可以使用我的 perl 脚本提取此信息

dvitype <file.dvi>| perl script1.pl > file.texbox

c) 你为 Tesseract 制作训练图像。你可以使用 dvips + ghostscript 以电子方式完成此操作

dvips -o <ps.file> <dvi.file>

gs -r300x300 -dNOPAUSE -sOutputFile=<file.tif> -sDEVICE=tiffg4 <ps.file>

或者通过在打印机上打印 dvi 文件,然后扫描它。

你跑tesseract <file.tif> <file.txt> batch.nochop makebox

您将 file.txt 重命名为 file.box。

d) 使用我的第二个 perl 脚本生成用于训练 tesseract 的最终框文件

perl correlatebox.pl file.texbox file.box > result_file.box

不幸的是,.pl 文件现在丢失了...而且我不是 DVI 文件和字体文件方面的专家。


我尝试过

我的猜测是输出一个可解析的 DVI 文件(使用 dvitype)来获取盒子的坐标,然后从 DVI 生成图像。

但是当我使用时lualatex -output-format dvi training.helveticacomp.tex,字体出现一些错误(HelveticaComp.ttf)。

但是当我使用时xelatex -no-pdf training.helveticacomp.tex,我得到了一个 xdv 文件,我无法使用 dvitype 实用程序对其进行解析。

我在基本 TexLive 发行版上安装了缺少的组件:texware 中的 dvitype、dtl 中的 disdiv(用于反汇编 xdv 文件disdvi -x <xdv file>

我发现了有趣的信息:

我尝试跟随这个文件安装我的需要的字体(因为 lualatex 抱怨缺少字体)但我仍然遇到一些错误。我不确定是否要在我的 LaTeX 中安装一个全新的字体,因为这似乎很难,所以我还没有尝试。

这是我的 .tex 文件的一个示例:

\documentclass[fontsize=12,a4paper,headheight=0.5cm,headsepline,parskip=half-]{scrartcl}

\KOMAoptions{BCOR=0mm,DIV=40}

\usepackage{fontspec}
\setmainfont{Helvetica-Compressed}
\begin{document}
‘ kit Contacts Carte Type un forme ç~ avant BYW: EN monde 2001 qu'on plan image ZG 23 À+ niveau femmes
\end{document}

我的问题

我需要一些线索来使用 LaTeX 生成与 .png(或 .tif)文件相关联的正确的 .box 文件。

如果可能的话,这样就可以避免安装新的字体。

如能就如何解析 xdv 或 dvi 文件以获取 .box 以及如何正确管理我的字体提供任何帮助,我将不胜感激。

当然,任何其他产生相同输出(.box +图像文件)的解决方案都是受欢迎的。

我想这会引起任何想要训练 Tesseract 但又不想花费大量时间进行 .box 编辑的人的兴趣。

我的电脑

我在 Mac OS X (10.10) 上使用基本的 TexLive 安装。

答案1

LuaTeX因此我研究了使用的节点处理回调的选项。最适合的是pre_output_filter当页面准备好输出时调用的。我创建了名为 的简单包boxes,它由两个文件组成:LaTeX 包boxes.sty和 Lua 模块boxes.lua

盒子.sty:

\ProvidesPackage{boxes}
\RequirePackage{luacode}
\RequirePackage{kvoptions}
\DeclareStringOption[eng]{lang}
\DeclareStringOption[72]{resolution}
\DeclareStringOption[75pt]{startx}
\DeclareStringOption[67.5pt]{starty}
\ProcessKeyvalOptions*
\luaexec{%
  main_language = "\boxes@lang"
  resolution    = tonumber("\boxes@resolution")
  startx        = tex.sp("\boxes@startx")
  starty        = tex.sp("\boxes@starty")
}

\begin{luacode*}
  print("language", main_language)
  local boxes = require "boxes"
  boxes.resolution = resolution
  boxes.startx = startx
  boxes.starty = starty
    --boxes.set_name()
    luatexbase.add_to_callback("pre_output_filter", 
    function(head,info, size, pack, maxdpth) 
      local f = font.getfont(font.current())
      local fontname = f.psname or f.fullname
      local name = string.format("%s.%s.exp%i.box", main_language, fontname,0)
      local glyphs = boxes.traverse(head)
      if #glyphs > 0  then boxes.save(name, glyphs) end
      return head
    end, "Save node boxes")
\end{luacode*}
\endinput

这个文件很简单,需要注意的是包选项:

  • lang:处理的语言
  • 分辨率:我还没有找到应该以哪种分辨率保存框文件,我认为使其可配置是一个好主意。默认值为 72 ppi。
  • startx 和 starty - 我找不到如何计算文本块左上角开头的方法,这实际上取决于使用的文档类或类似的包geometry,我希望有某种方法可以确定它,但我不知道如何。所以我们必须手动设置这些值,使用一些实验

现在lua模块boxes.lua

local boxes = {}
boxes.resolution =  300 --72
local pt = 2 ^ 16
local uchar = unicode.utf8.char
local total_height = tex.pageheight
local pagebox = tex.pdfpagebox
local baselineskip = tex.baselineskip.width

local function round(num, idp)
  local mult = 10^(idp or 0)
  return math.floor(num * mult + 0.5) / mult
end


local function make_dimensions(glyph, x, y)
  local resolution = boxes.resolution
  local bp = (2 ^ 16) / (resolution / 72.27)
  local lx = round(x / bp)
    local ly = round((total_height - (y + glyph.depth)) / bp)
    local rx = round((x + glyph.width) / bp)
    local ry = round((total_height - (y - glyph.height)) / bp)
    return lx,ly, rx,ry
end
function boxes.traverse(head) 
  --local set = head.glue_set
    --local sign = head.glue_sign
    --local order = head.glue_order
  local resolution = boxes.resolution
  local bp = (2 ^ 16) / (resolution / 72.27)
    local glyphs = {}
    local i = 0
  for n in node.traverse(head) do
      print(n.id, n.subtype)
        if n.id == 0 then
            i = i + 1
            local set =  n.glue_set
            local sign = n.glue_sign
            local order = n.glue_order
            local height = i * baselineskip
            local nhead = n.head
            -- y is distance from page top to the current baseline
            local y = boxes.starty + height or tex.pdfvorigin + height - 4.5 * (2^16)   
            local x = boxes.startx or tex.pageleftoffset + 2.5 * (2^16) 
            for glyph in node.traverse_id(37, nhead) do
              local w, h, d = node.dimensions(set, sign, order, nhead, glyph)
                local glyph_x = x + w
                local lx,ly, rx, ry = make_dimensions(glyph, glyph_x, y)
              glyphs[#glyphs+1]={uchar(glyph.char), lx,ly,rx,ry}
            end
        end
    end
    return glyphs
end

function boxes.save(name, glyphs)
    local f = io.open(name,"w")
    for _, line in ipairs(glyphs) do
        f:write(table.concat(line,", ").. "\n")
    end
    f:close()
end

return boxes

代码非常简单:在函数中boxes.traverse我们处理行节点列表。当我们找到id0 号节点(即水平线)时,我们用 增加行数和垂直位置\baselineskip。只要文本简单且没有更高级的格式(这会导致垂直空间大于 baselineskip),这种方法就会有效。但出于这个特定目的,我们可以假设只使用没有格式化的纯文本。

然后我们处理节点的子列表glyph并使用函数计算水平位置node.dimensions

local w, h, d = node.dimensions(set, sign, order, nhead, glyph)

setsignorder用于计算空间的大小,因为它具有可变的宽度,所以每行可能略有不同。这些值在父hlist节点中设置。w变量是从行首到当前字形的宽度。

然后我们用函数计算角色的尺寸

local function make_dimensions(glyph, x, y)
  local resolution = boxes.resolution
  local bp = (2 ^ 16) / (resolution / 72.27)
  local lx = round(x / bp)
  local ly = round((total_height - (y + glyph.depth)) / bp)
  local rx = round((x + glyph.width) / bp)
  local ry = round((total_height - (y - glyph.height)) / bp)
  return lx, ly, rx, ry
end

变量xy是左下角坐标。因为坐标系从TeX左上角开始,但对于boxes格式,它从左下角开始,所以所有垂直尺寸都必须镜像,只需y从页面的总高度中减去计算值即可。最后,使用变量除法将计算出的尺寸插值到当前分辨率bp

\documentclass[fontsize=12,a4paper,%headheight=0.5cm,headsepline,
parskip=half-]{scrartcl}
%\documentclass{article}
\usepackage[resolution=300]{boxes}
%\KOMAoptions{BCOR=0mm,DIV=40}

\usepackage{fontspec}
%\setmainfont{Helvetica-Compressed}
\begin{document}
\typeout{\the\baselineskip}
‘ kit Contacts Carte Type un forme ç~ avant BYW: EN monde 2001 qu'on plan image ZG 23 À+ niveau femmes příliš žluťoučký kůň úpěl ďábelské ódy. 
\end{document}

我们可以用盒子命令。它需要pbm格式的图像,可以使用pdftoppm命令创建:

lualatex sample
pdftoppm -mono -freetype yes -aa yes -r 300  sample.pdf > sample.pbm
tessboxes sample.pbm eng.LMRoman12-Regular.exp0.box > output.pbm

在此处输入图片描述

相关内容