EfficientDetによるドキュメントレイアウトを意識したOCR解析

R&D チームの徳田(@dakuton)です。
過去記事にて紹介したLayoutParserがバージョンアップ(v0.3.0以降)により、EfficientDetによるドキュメントレイアウト解析がサポートされたので動かしてみました。

技術解説

EfficientDetとLayoutParserの技術的な説明については、それぞれ過去記事をご確認ください。

tech-blog.optim.co.jp tech-blog.optim.co.jp

v0.3.0から追加された機能の1つとして、レイアウト解析(物体検出)のマルチバックエンド利用に対応しています。
前回のLayoutParser記事時点(v0.2.0)だとレイアウト解析は下記1のみサポートでしたが、2021/10/07時点で2,3が追加利用可能になっています。

  1. Detectron2
  2. EfficientDet(effdet)
  3. PaddlePaddle
    • 3を利用する場合はInstallation記載とは別途pip install "layoutparser[paddledetection]"が必要です。

サンプル

テストデータ

前回記事と同様の、夏季インターンシップ募集ページの部分スクリーンショット(コース概要)を使用しました。
(2021年度の募集は締め切りました。多数応募・参加いただきありがとうございます。)

サンプルコード(parse_table_layout.py)

前回のLayoutParser記事時点(v0.2.0)では、Model Zooの記載に従い、config_path(利用したいモデル)に応じてlabel_mapを指定する必要がありましたが、マルチバックエンド対応により指定不要(AutoLayoutModelによる自動選択)になりました。
ただし、検出しきい値をデフォルト値以外に変更したい場合はextra_configでの指定方法が利用モデルによって異なる点に注意してください。

import argparse

import cv2
import layoutparser as lp


def main():
    args = parse_args()

    image = cv2.imread(args.src)

    """以下と同じ
    > model = lp.EfficientDetLayoutModel(
    >     'lp://PubLayNet/tf_efficientdet_d1/config',
    >     extra_config={"output_confidence_threshold": 0.25},
    >     label_map={1:"Text", 2:"Title", 3:"List", 4:"Table", 5:"Figure"}
    > )
    Model/Config Full Path Formats: lp://<backend-name>/<dataset-name>/<model-architecture-name>
    https://github.com/Layout-Parser/layout-parser/blob/v0.3.0/src/layoutparser/models/model_config.py#L23
    """
    model = lp.AutoLayoutModel('lp://efficientdet/PubLayNet/tf_efficientdet_d1')
    layout = model.detect(image)

    ocr_agent = lp.GCVAgent.with_credential(args.gcp_credential_json, languages=['ja'])
    res = ocr_agent.detect(image, return_response=True)
    ocr_layout = ocr_agent.gather_full_text_annotation(res, agg_level=lp.GCVFeatureType.BLOCK)

    print('### レイアウト解析なし')
    print('\n'.join(ocr_layout.get_texts()).replace(' ', ''))

    print('### レイアウト解析あり(図表エリアごとにテキスト抽出)')
    for l in detectron2_layout:
        if l.type in ['Table' ,'Figure']:
            texts = ocr_layout.filter_by(l.block, center=True).get_texts()
            print('\n'.join(texts).replace(' ', ''))


def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument('--gcp_credential_json', type=str)
    parser.add_argument('--src', type=str)
    args = parser.parse_args()
    return args


if __name__ == '__main__':
    main()

元画像

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20210803/20210803214441.png

解析結果

OCRとレイアウトそれぞれの検出位置を可視化してみたところ、今回用いたサンプルデータについては、残念ながらEfficientDetよりも前回利用したMask R-CNN(Detectron2)のほうが適切にレイアウト分離できています。

  • 赤枠: OCR(Google Cloud Vision ブロック検出)
  • 青色透過: ドキュメントレイアウト(Detectron2またはEfficientDet)
Detectron2(mask_rcnn_X_101_32x8d_FPN_3x)

テーブルレイアウト分離成功 https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20210803/20210803211656.png

EfficientDet(tf_efficientdet_d1)

テーブルレイアウト分離失敗 https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20210803/20210803211656.png

所感

前回のLayoutParser記事(LayoutParserを利用するメリット・デメリットは?)にてメリットとして述べた「OCR部分とレイアウト解析部分のモジュール分離による再利用性向上」が、実際にバージョンアップにより強化されている点は好感が持てます。
利用方法のドキュメント更新についてはまだ追いついていない部分もありますが、1行(AutoLayoutModelで指定するモデル)変更で試せるようになったので、今後のアップデートに期待です。

オプティムでは技術をいい感じに使ってデジタライゼーションに貢献できるエンジニアを募集しています。

www.optim.co.jp