hiroi-sora / Umi-OCR_plugins

Umi-OCR 插件库
MIT License
315 stars 32 forks source link

【tesseractOCR插件实现中】请求复选框,输入框,以及box数据处理相关问题 #2

Closed qwedc001 closed 10 months ago

qwedc001 commented 10 months ago

如标题。 两者均针对tesseract可以支持多种语言同时识别的处理情况。tesseract在github上给出的可支持语言太多,全部写入可以但未免选择太过繁琐,所以我想在复选框里提供中英日,其他选项提供对应短码让用户自行选择填写。但是目前config里的optionsList只能做到单选。

hiroi-sora commented 10 months ago

在开始之前,请先更新一个Bug补丁:

UmiOCR-data\qt_res\qml\Configs\Configs.qml更新到这个版本。其它文件可以不用动。

目前没有专门的下拉复选框组件,你可以用“可折叠配置项组”+开关,来模拟一组复选框。

    "language": {
        "title": "语言",
        "type": "group",
        "enabledFold": True,  # 启用折叠
        "fold": False,  # 默认非折叠状态。(折叠状态会保存)
        "cn": {
            "title": "中",
            "default": True,
        },
        "en": {
            "title": "English",
            "default": True,
        },
        "jp": {
            "title": "日",
            "default": False,
        },
        "~enabledOther": {
            "title": "启用自定义语言短码",
            "default": False,
        },
        "~other": {
            "title": "自定义语言短码",
            "toolTip":  “请查看tesseract官方文档”,
            "default": "",
        },
    },

请注意:当在python定义配置字典时,配置项组group内元素是按key的字符进行排序,而不是你编写的顺序。为了指定排序(比如让“自定义语言”固定在最后),需要调整key的字符,上述例子中"~other"~就是这个目的。

所有配置类型的定义可以在UmiOCR-data\qt_res\qml\Configs\Configs.qml 开头的注释中查看。Python中不支持定义热键和按钮组(只能在qml中定义)。

hiroi-sora commented 10 months ago

还有,tesseract应该是首次运行时下载模型库的。咱需要将常用模型库(中英日)预先放置在插件包中,以便开箱即用,避免用户打开还要等半天。

如果允许用户下载新模型的话,下载目录看看能不能也改为存到插件目录中(默认应该是下载到C盘吧)。

qwedc001 commented 10 months ago

好的,已了解。 我在官网上找下的tesseract好像是自带模型的,全部语言模型加起来约为300M,中英日约17M,我并不能确认该模型的版本是tesseract-best还是tesseract-fast,但是目前来看识别简中准确度有点低,我调试一下

qwedc001 commented 10 months ago

按照上述方法处理数据以后,获取到的传参dict非常的 难以使用非硬编码形式处理。

525f2f17db5793a6077fede1fd9aab78

还有其他传参的可能方式吗,要是没有的话我就硬编码了( 问了一下,朋友说Qt里面用QCheckbox来搞复选框,但具体怎么实现我就没接触过了。

hiroi-sora commented 10 months ago

动态解析示例:

# 解析参数字典argd,返回语言码列表
def get_select_languages(argd):
    selects = [] # 存储当前选出的语言码
    for k,flag in argd.items():
        if k.startswith("language."):
            language = k[9:] # 删除"language."
            if language is tesseract的合法语言码:
                if flag: # 传入True
                    selects.append(language)
    return selects
hiroi-sora commented 10 months ago

另外,本项目用的是Qt Qml,跟传统的Qt QWidget不同。界面上几乎所有的组件,按钮啥的,都是我自己手写的。我暂时没写复选框(觉得没必要用这组件,懒得写)😂。如果确实有必要的话我可以写

qwedc001 commented 10 months ago

动态解析示例:

# 解析参数字典argd,返回语言码列表
def get_select_languages(argd):
    selects = [] # 存储当前选出的语言码
    for k,flag in argd.items():
        if k.startswith("language."):
            language = k[9:] # 删除"language."
            if language is tesseract的合法语言码:
                if flag: # 传入True
                    selects.append(language)
    return selects

嗷,合理 看来写代码经验还是少了点啊哈哈哈,行

手写也太强了,那就先用着这个解决方案吧,以后有更新再说

qwedc001 commented 10 months ago

插件已经初步成型。 但是我这里遇到一个很棘手的问题,tesseractOCR给出的box数据无法正确被umi_ocr使用。直接导致无法正确重组识别出来的文字。在开启竖版识别的情况下尤为严重。该处核心代码如下

top,left,width,height = int(item[6]), int(item[7]), int(item[8]), int(item[9]) # 此处的magic number来源于tesseractOCR本身在调用OCR后返回的table,参数固定。
topLeft = [top,left]
topRight = [top,left+width]
bottomLeft = [top+height,left]
bottomRight = [top+height,left+width]
box = [topLeft,topRight,bottomLeft,bottomRight]

当前全部代码因为代理抽风问题还没push到repo。理论上来讲解决完box的问题就可以用了 在和学长讨论这个box的问题

hiroi-sora commented 10 months ago

box列表的排序错了。四个元素应该是:从左上角开始,顺时针遍历四个角的坐标,即:

[topLeft,topRight, bottomRight, bottomLeft]

你下次遇到这类问题时,可以尝试跟已知正常的部分进行比较。比如先使用PaddleOCR插件,打印正确的box。然后同一张图片,使用tesseract插件打印box。将两个数据进行对比,很容易就能看出问题所在。

hiroi-sora commented 10 months ago

tesseract返回的最小单位似乎是单词,但Umi中是以行为最小单位的。你检查一下tesseract官方有没有提供返回整行的方法。

如果没有,要手动将tesseract的结果合并回行。即,同一行的子块( line_num 相同的结果),合并为一个。

这涉及到box、text、score的合并。score取本行所有子块的平均值即可。box的合并,也许可以用所有子块的x、y的最大/最小值。text的合并,要考虑两个单词是不是英文;英文单词间要添加空格,而中文没有空格。

合并俩单词可参考:

    # 获取两个连续单词的分隔符。letter1为单词1结尾字母,letter2为单词2结尾字母
    def _word_separator(self, letter1, letter2):
        # 判断结尾和开头,是否属于汉藏语族
        # 汉藏语族:行间无需分割符。印欧语族:则两行之间需加空格。
        ranges = [
            (0x4E00, 0x9FFF),  # 汉字
            (0x3040, 0x30FF),  # 日文
            (0xAC00, 0xD7AF),  # 韩文
            (0xFF01, 0xFF5E),  # 全角字符
        ]
        fa, fb = False, False
        for l, r in ranges:
            if l <= ord(letter1) <= r:
                fa = True
            if l <= ord(letter2) <= r:
                fb = True
        if fa and fb: # 两个字符都是汉藏语族,才没有空格
            return ""

        # 特殊情况:字母2为缩写,如 n't。或者字母2为结尾符号,意味着OCR错误分割。
        if letter2 in {r"'", ",", ".", "!", "?", ";", ":"}:
            return ""
        # 其它正常情况,如 俩单词 或 一单词一汉字,加空格
        return " "
qwedc001 commented 10 months ago

box排序问题解决了,不过程序行为仍然不符合预期。当前代码已提交。 经过排查,我们发现tesseractOCR识别文字的box并不稳定,最常出现的情况是标点符号的box比文字的box小非常多。导致umi-ocr不能正确对其进行拼接。 我们研究了一下tesseract的传出数据,现在有两种解决方案,一种是在插件里根据tesseract提供的行号或段落号(参见api_tesseractorc.py第53行)在插件内进行合并字符串并计算新的大box,这样操作后umi_ocr不再需要(应该也不能)进行box合并操作。 第二种方法是给umi

我刚刚就在写这个,笑死了()

hiroi-sora commented 10 months ago

哈哈哈~~

另外,tesseract本身是个命令行程序,pytesseract是它的一个第三方python接口。

tesseract有不止一个第三方接口,如这也是一个

如果觉得 pytesseract 这个接口太难用,自己解析结果较复杂。也可以测试一下别的接口,看看会不会封装得更好用。

pytesseract 会将每次交互的文本缓存到本地文件中,依我看……emmm……太不优雅了

qwedc001 commented 10 months ago

已经依据pytesseract完成tesseract接口实现。目前正在测试中。已经推送repo 新的第三方接口我还没细看,如果确实省很多事就换一下,不过现在可用性还不错。 分词直接使用了您的函数,然后分词依据没再使用line num而是选择了level 5进行依据,目前测试会出现偶尔丢整行识别的情况,一会跑个batch测一下。可能是哪里逻辑还有点小问题。(目前测试是关掉自动识别排版按钮可以规避这个问题,好奇怪的临时解决方案)

hiroi-sora commented 10 months ago

你把整个插件打个压缩包扔release里

qwedc001 commented 10 months ago

你把整个插件打个压缩包扔release里

已发。

Edit:淦,忘了在release里面挂i18n.csv了,那个文件我还没编写,网有点差希望不会影响主要问题,i18n我起来在写(?)

hiroi-sora commented 10 months ago

没事,先不用写i18n。现在还不确定有没有文本要改。差不多搞完了,快发布了在搞文本翻译。

hiroi-sora commented 10 months ago

丢失整行识别,是因为 101行 ,你根据if level != 5 and len(scores) != 0: 来执行datas.append。

所以最后几次for循环的文本块没有被添加到datas中(最后几次循环,level肯定是5)。


然后关于自定义语言码。既然咱的插件不支持自动下载新语言库,就意味着用户要手动下载语言库、放到engine/tessdata目录中、还要手动填写语言短码。应该将“填写语言短码”这一步优化掉,改为程序自动搜索可以使用的语言库,在UI上显示出来。

具体来说,插件初始化时你可以搜索engine/tessdata目录中.traineddata后缀的文件,提取文件名,与tesseract语言列表进行对比,识别出它是什么语言库。然后,动态构建localOptions的"language"属性,添加对应的配置项。

本仓库现有的PaddleOCR-json等插件,均使用动态搜索语言配置文件、自动生成配置项的方式,可参考

qwedc001 commented 10 months ago

我考虑了一下,决定采用的方式是常见语言进行标识,其他语言统一显示为英文的动态生成方式。 然后我在翻阅所有可支持语言的时候发现两个特殊模块,一个是equ,是专门的数学识别模块,这个我会在更新中添加到常驻识别。另一个osd似乎和排版有关,我再研究一下。

qwedc001 commented 10 months ago

已经实现。请检查,没问题我就添加git submodule并且提交pr了。

hiroi-sora commented 10 months ago

你现在可以先PR过来,后续继续优化。

以下我发现的一些问题和建议,请一条条核对:


  1. “置信度下限”转移到globalOptions中。因为这是个全局设置,应该仅设定一次、对所有标签页生效。

  1. 不开启“自动识别排版”时,双栏排版的图片,会产生严重的误合并问题(左右两栏的文本块被合并为一个,且行高与实际不符)。你检查一下,能否在调用代码中优化。如果不能解决,那么把“自动识别排版”默认True。

image


  1. 将tesseract本身的识别排版功能与Umi进行整合。具体来说,刚发布的 2.0.2 alpha 版 支持在文本块中额外定义一个"end"属性,标记这个文本块结尾的间隔符。如:
textBlock = {"text":"文本", "box":包围盒, "score ":置信度,
"end": "\n", # 如果这个block为自然段的末尾行,那么结尾间隔符为"\n"。否则,为空格" "或者空""
 }

在新版软件面板中,也会通过小回车图标,标出结尾为\n的文本块。

image

你需要从tesseract的结果中,判断哪些文本块属于自然段的结尾=,end为\n。其余文本块,end为" "或""(根据上下文是汉字还是字母)。

这时,软件面板上设置里的”排版解析方案“换成”不做处理“,Umi就会优先采用tesseract的排版结果。


  1. 空文本块问题。有一些文本块是空的。尝试在调用代码中将它们过滤掉。

image


  1. TESSERACT_SUPPORTED 中,语言名称用相同语言书写,如下。(至少大部分常用的改为相同语言书写,不常用的保留英文名也可)。然后,在 localOptions 中,不要硬编码任何语言(包括中英日数学),所有语言库都动态搜索。
TESSERACT_SUPPORTED = {
    "equ": tr("数学公式"), # 这个使用tr翻译
    "chi_sim": "简体中文",
    "eng": "English",
    "chi_tra": "繁體中文",
    "jpn": "日本語",
    "korean": "한국어",
    "cyrillic": "Русский",
    # .....补充更多语言
}

  1. 重新打包release,把常用的模型库附上(目前缺数学库)。
qwedc001 commented 10 months ago

收到,这样我就把这里的issue关掉了,在tesseract处开一个新的todo issue,感谢建议。