草庐IT

基于OCR进行Bert独立语义纠错实践

华为云开发者社区 2023-04-11 原文
摘要:本案例我们利用视频字幕识别中的文字检测与识别模型,增加预训练Bert进行纠错

本文分享自华为云社区《Bert特调OCR》,作者:杜甫盖房子。

做这个项目的初衷是发现图比较糊/检测框比较长的时候,OCR会有一些错误识别,所以想对识别结果进行纠错。一个很自然的想法是利用语义信息进行纠错,其实在OCR训练时加入语义信息也有不少工作,感兴趣的朋友可以了解一下,为了更大程度复用已有的项目,我们决定保留现有OCR单元,在之后加入独立语义纠错模块进行纠错。

本案例我们利用视频字幕识别中的文字检测与识别模型,增加预训练Bert进行纠错,最终效果如下:

我们使用ModelBox Windows SDK进行开发,如果还没有安装SDK,可以参考ModelBox端云协同AI开发套件(Windows)设备注册篇ModelBox端云协同AI开发套件(Windows)SDK安装篇完成设备注册与SDK安装。

技能开发

这个应用对应的ModelBox版本已经做成模板放在华为云OBS中,可以用sdk中的solution.bat工具下载,接下来我们给出该应用在ModelBox中的完整开发过程:

1)下载模板

执行.\solution.bat -l可看到当前公开的技能模板:

███@DESKTOP-UUVFMTP MINGW64 /d/DEMO/modelbox-win10-x64-1.5.1
$ ./solution.bat -l
start download desc.json
 3942.12KB/S, percent=100.00%
Solutions name:
mask_det_yolo3
...
doc_ocr_db_crnn_bert

结果中的doc_ocr_db_crnn_bert即为文档识别应用模板,下载模板:

███@DESKTOP-UUVFMTP MINGW64 /d/DEMO/modelbox-win10-x64-1.5.1
$ ./solution.bat -s doc_ocr_db_crnn_bert
...

solution.bat工具的参数中,-l 代表list,即列出当前已有的模板名称;-s 代表solution-name,即下载对应名称的模板。下载下来的模板资源,将存放在ModelBox核心库的solution目录下。

2)创建工程

ModelBox sdk目录下使用create.bat创建doc_ocr工程

███@DESKTOP-UUVFMTP MINGW64 /d/DEMO/modelbox-win10-x64-1.5.1
$ ./create.bat -t server -n doc_ocr -s doc_ocr_db_crnn_bert
sdk version is modelbox-win10-x64-1.5.1
success: create doc_ocr in D:\DEMO\modelbox-win10-x64-1.5.1\workspace

create.bat工具的参数中,-t 表示创建事务的类别,包括工程(server)、Python功能单元(Python)、推理功能单元(infer)等;-n 代表name,即创建事务的名称;-s 代表solution-name,表示将使用后面参数值代表的模板创建工程,而不是创建空的工程。

workspace目录下将创建出doc_ocr工程,工程内容如下所示:

doc_ocr
|--bin
│  |--main.bat:应用执行入口
│  |--mock_task.toml:应用在本地执行时的输入输出配置,此应用为http服务
|--CMake:存放一些自定义CMake函数
|--data:存放应用运行所需要的图片、视频、文本、配置等数据
│  |--char_meta.txt:字形拆解文件,用来计算字形相似度
│  |--character_keys.txt:OCR算法的字符集合
│  |--GB2312.ttf:中文字体文件
│  |--test_http.py:应用测试脚本
│  |--text.jpg:应用测试图片
│  |--vocab.txt:tokenizer配置文件
|--dependence
│  |--modelbox_requirements.txt:应用运行依赖的外部库在此文件定义,本应用依赖pyclipper、Shapely、pillow等工具包
|--etc
│  |--flowunit:应用所需的功能单元存放在此目录
│  │  |--cpp:存放C++功能单元编译后的动态链接库,此应用没有C++功能单元
│  │  |--bert_preprocess:bert预处理功能单元,条件功能单元,判断是否需要纠错
│  │  |--collapse_position:归拢单句纠错结果
│  │  |--collapse_sentence:归拢全文纠错结果
│  │  |--det_post:文字检测后处理功能单元
│  │  |--draw_ocr:ocr结果绘制功能单元
│  │  |--expand_img:展开功能单元,展开文字检测结果
│  │  |--expand_position:展开功能单元,展开bert预处理结果
│  │  |--match_position:匹配纠错结果
│  │  |--ocr_post:ocr后处理功能单元
|--flowunit_cpp:存放C++功能单元的源代码,此应用没有C++功能单元
|--graph:存放流程图
│  |--doc_ocr.toml:默认流程图,http服务
│  |--modelbox.conf:modelbox相关配置
|--hilens_data_dir:存放应用输出的结果文件、日志、性能统计信息
|--model:推理功能单元目录
│  |--bert:Bert推理功能能单元
│  │  |--bert.toml:语义推理配置文件
│  │  |--bert.onnx:语义推理模型
│  |--det:文字检测推理功能单元
│  │  |--det.toml:文字检测推理功能单元的配置文件
│  │  |--det.onnx:文字检测onnx模型
│  |--ocr:文字识别推理功能单元
│  │  |--ocr.toml:文字识别推理功能单元的配置文件
│  │  |--ocr.onnx:文字识别onnx模型
|--build_project.sh:应用构建脚本
|--CMakeLists.txt
|--rpm:打包rpm时生成的目录,将存放rpm包所需数据
|--rpm_copyothers.sh:rpm打包时的辅助脚本

3)查看流程图

doc_ocr工程graph目录下存放流程图,默认的流程图doc_ocr.toml与工程同名,将流程图可视化:

图示中,灰色部分为预置功能单元,其余颜色为我们实现的功能单元,其中绿色为一般通用功能单元,红色为推理功能单元,蓝色为条件功能单元,黄色为展开归拢功能单元。HTTP接收图解码后做预处理,接着是文字检测,模型后处理得到检测框,经过条件功能判断,检测到文字的图送入展开功能单元,切图进行文字识别,文字识别结果送入bert预处理单元判断是否需要进行纠错,如需纠错则再展开并行进行语义推理,不需要纠错的就直接进行结果绘制并返回。而未检测到文字的帧则直接返回。

4)核心逻辑

本应用核心逻辑中的文字检测与识别可以参考【ModelBox OCR实战营】视频字幕识别中的相关介绍,本文重点介绍文字纠错部分。

首先查看纠错预处理功能单元bert_preprocess

def process(self, data_context):
    in_feat = data_context.input("in_feat")
    out_feat = data_context.output("out_feat")
    out_bert = data_context.output("out_bert")

    for buffer_feat in in_feat:
        ocr_data = json.loads(buffer_feat.as_object())['ocr_result']
        score_data = json.loads(json.loads(buffer_feat.as_object())['result_score'])

        text_to_process = []
        text_to_pass = []
        err_positions = []
        for i, (sent, p) in enumerate(zip(ocr_data, score_data)):
            if not do_correct_filter(sent, self.max_seq_length):
                text_to_pass.append((i, sent))
            else:
                err_pos = find_err_pos_by_prob(p, self.prob_threshold)
                if not err_pos:
                    text_to_pass.append((i, sent))
                else:
                    text_to_process.append(sent)
                    err_positions.append(err_pos)
        if not text_to_process:
            out_feat.push_back(buffer_feat)
        else:
            out_dict = []
            texts_numfree = [self.number.sub(lambda m: self.rep[re.escape(m.group(0))], s) for s in text_to_process]
            err_positions = check_error_positions(texts_numfree, err_positions)
            if err_positions is None:
                err_positions = [range(len(d)) for d in texts_numfree]
            batch_data = BatchData(texts_numfree, err_positions, self.tokenizer, self.max_seq_length)
            input_ids, input_mask, segment_ids, masked_lm_positions = batch_data.data
            ...
    return modelbox.Status.StatusCode.STATUS_SUCCESS

预处理单元对通过do_correct_filter函数对OCR结果进行判断,只对大于3个字的中文字符进行纠错:

def do_correct_filter(text, max_seq_length):
    if re.search(re.compile(r'[a-zA-ZA-Za-z]'), text):
        return False
    if len(re.findall(re.compile(r'[\u4E00-\u9FA5]'), text)) < 3:
        return False
    if len(text) > max_seq_length - 2:
        return False
    return True

通过find_err_pos_by_prob函数定位需要纠错的字符,只对OCR置信度小于阈值的字符进行纠错:

def find_err_pos_by_prob(prob, prob_threshold):
    if not prob:
        return []
    err_pos = [i for i, p in enumerate(prob) if p < prob_threshold]
    return err_pos

如有需要纠错的字符,则将该句编码,进行语义推理。

语义推理后,通过collapse_position对推理结果进行解码,在match_position功能单元中使用shape_similarity函数计算语义推理结果与OCR结果的字符相似度:

def shape_similarity(self, char1, char2):
    decomp1 = self.decompose_text(char1)
    decomp2 = self.decompose_text(char2)
    similarity = 0.0
    ed = edit_distance(safe_encode_string(decomp1), safe_encode_string(decomp2))
    normalized_ed = ed / max(len(decomp1), len(decomp2), 1)
    similarity = max(similarity, 1 - normalized_ed)
    return similarity

其中,decompose_text函数将单个汉字编码为笔划级别的IDS,如:

华: ⿱⿰⿰丿丨⿻乚丿⿻一丨

+----+
           | ⿱  |
           +----+
        化         十
      +----+     +----+
      | ⿰  |    | ⿻  |
      +----+     +----+
   亻       七  一      丨
 +----+    +----+
 | ⿰  |   | ⿻  |
 +----+    +----+
丿     丨  乚      丿

计算语义推理结果字符与原OCR结果字符相似度之后,综合语义推理置信度与相似度判断是否接收纠错结果:

def accept_correct(self, confidence, similarity):
    if confidence + similarity >= self.all_conf \
        and confidence  >= self.confidence_conf \
            and similarity >= self.similarity_conf:
        return True
    return False

5)三方依赖库

本应用依赖pyclipper、Shapely、pillow等工具包,ModelBox应用不需要手动安装三方依赖库,只需要配置在dependence\modelbox_requirements.txt,应用在编译时会自动安装。

技能运行

在项目目录下执行.\bin\main.bat运行应用,为了方便观察纠错结果,我们将日志切换为info:

███@DESKTOP-UUVFMTP MINGW64 /d/DEMO/modelbox-win10-x64-1.5.1/workspace/doc_ocr
$ ./bin/main.bat default info
...
[2022-12-27 15:20:40,043][ INFO][httpserver_sync_receive.cc:188 ] Start server at http://0.0.0.0:8083/v1/ocr_bert

另起终端,进入项目data目录下,运行test_http.py脚本进行测试:

███@DESKTOP-UUVFMTP MINGW64 /d/DEMO/modelbox-win10-x64-1.5.1/workspace/doc_ocr/data
$ python test_http.py

可以在技能运行日志中观察到接受的纠错结果:

[2022-12-27 15:22:40,700][ INFO][match_position\match_position.py:51  ] confidence: 0.99831665, similarity: 0.6470588235294117, 柜 -> 相

同时,在data目录下可以看到应用返回的结果图片:

 

点击关注,第一时间了解华为云新鲜技术~

有关基于OCR进行Bert独立语义纠错实践的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  3. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  4. ruby-on-rails - 独立 ruby​​ 脚本的配置文件 - 2

    我有一个在Linux服务器上运行的ruby​​脚本。它不使用rails或任何东西。它基本上是一个命令行ruby​​脚本,可以像这样传递参数:./ruby_script.rbarg1arg2如何将参数抽象到配置文件(例如yaml文件或其他文件)中?您能否举例说明如何做到这一点?提前谢谢你。 最佳答案 首先,您可以运行一个写入YAML配置文件的独立脚本:require"yaml"File.write("path_to_yaml_file",[arg1,arg2].to_yaml)然后,在您的应用中阅读它:require"yaml"arg

  5. ruby - 如何进行排列以有效地定制输出 - 2

    这是一道面试题,我没有答对,但还是很好奇怎么解。你有N个人的大家庭,分别是1,2,3,...,N岁。你想给你的大家庭拍张照片。所有的家庭成员都排成一排。“我是家里的friend,建议家庭成员安排如下:”1岁的家庭成员坐在这一排的最左边。每两个坐在一起的家庭成员的年龄相差不得超过2岁。输入:整数N,1≤N≤55。输出:摄影师可以拍摄的照片数量。示例->输入:4,输出:4符合条件的数组:[1,2,3,4][1,2,4,3][1,3,2,4][1,3,4,2]另一个例子:输入:5输出:6符合条件的数组:[1,2,3,4,5][1,2,3,5,4][1,2,4,3,5][1,2,4,5,3][

  6. ruby - 即使失败也继续进行多主机测试 - 2

    我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r

  7. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  8. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  9. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  10. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

随机推荐