Python&Plotlyを使って、OCR結果を画像上でインタラクティブに可視化する

4 min read読了の目安(約3800字

概要

OCRを使った文字読み取りの開発をしていると、読み取られたテキストと画像と見比べて結果の確認をすることがよくあります。読み取られたこの文字は画像のどこから出てきたのかとか、単語の区切りが不自然なときになぜそうなったのかとかといった確認の際には、テキストとその座標から画像中の該当箇所を見つける必要があり、人間が目視で行うにはなかなか大変な作業です。物体認識などのタスクでは、画像上に四角形のバウンディングボックスを物体名のラベルとともに描写して可視化していますが、OCRの読み取りの場合は文字が画像上で密に配置されていたりと、画像上に直接描写するには情報量が過多になってしまいます。なるべくインタラクティブに操作出来る形で情報を表示し、必要な部分だけを確認できるようにしたい場合が多いです。

この記事では、OCR読み取りの結果を画像上に描写して、インタラクティブに結果を確認する方法を紹介します。

デモ

まず実際の動作画面を紹介します。

ここではGoogle Vision APIを使ってOCRを行い、その中のWordの領域を可視化して表示しています。ポップアップで表示されたのはOCRで読み取られた文字です。なお、画像はreceiptlineで作成したレシートを利用しています(生成コード)。

方法

今回はPlotlyというグラフ描写ライブラリを利用します。Plotlyはヒストグラムや散布図を描写しインタラクティブに分析するための可視化ツールですが、グラフの背景に画像を描写する機能があります。これを使うことで、背景にOCR対象の画像を、全面にはオブジェクトの領域とそのメタ情報を描写することで、画像上の対象の情報を表示することができます。

この方法はPlotly公式のレポジトリの一つであるplotly/dash-detrで実装されいる方法を参考にしました。こちらは物体認識に可視化ツールを、Dashというウェブアプリケーション構築パッケージを使って作成しているデモアプリです。

https://github.com/plotly/dash-detr

本来の使い方とは違って少しハックのような方法ですが、自前でGUIのインタラクティブなツールの作るのはかなり大変ですし、特定領域をハイライトしてマウスオーバーで情報を表示するくらいならばこの方法で十分だと思います。

コード

ここからは、上記で紹介したdash-detrレポジトリのコードを引用しながら解説していきます。コード中には定義していない変数等が出現しますが、自身で試される際は適宜置き換えてもらえればと思います。

描写領域を設定する

plotlyのFigureオブジェクトを作成し、add_trace()update_xaxes()などといったメソッドを適用していくことで、グラフの情報を更新していきます。

この記事冒頭のgif動画のようにXY軸に目盛りを表示するには、各軸の設定でvisible=Trueに変更します。

import plotly.graph_objects as go
fig = go.Figure()

fig.add_trace(go.Scatter(
    x=[img_width * 0.05, img_width * 0.95],
    y=[img_height * 0.95, img_height * 0.05],
    showlegend=False, mode="markers", marker_opacity=0, 
    hoverinfo="none", legendgroup='Image'))
fig.update_xaxes(
    showgrid=False, visible=False, constrain="domain", range=[0, img_width])
fig.update_yaxes(
    showgrid=False, visible=False,
    scaleanchor="x", scaleratio=1,
    range=[img_height, 0])    

画像を読み込む

次に画像を読み込んで背面に配置します。ここで画像はPIL形式からBase64形式に変換してsourceの引数としています。画像のURLを直に指定することもできます。

fig.add_layout_image(dict(
    source=pil_to_b64(im), sizing="stretch", opacity=1, layer="below",
    x=0, y=0, xref="x", yref="y", sizex=img_width, sizey=img_height,))

OCR結果を描写する

最後にOCR結果を描写します。add_bbox関数では色々とパラメータを指定していますが、基本的には矩形の座標とメタ情報を渡しているだけです。ポップアップに表示したい内容はgo.Scatter()text引数に指定します。

def add_bbox(fig, x0, y0, x1, y1, 
             showlegend=True, name=None, color=None, 
             opacity=0.5, group=None, text=None):
    fig.add_trace(go.Scatter(
        x=[x0, x1, x1, x0, x0],
        y=[y0, y0, y1, y1, y0],
        mode="lines",
        fill="toself",
        opacity=opacity,
        marker_color=color,
        hoveron="fills",
        name=name,
        hoverlabel_namelength=0,
        text=text,
        legendgroup=group,
        showlegend=showlegend,
    ))
    
add_bbox(
    fig,
    word.bounding_box.top_left.x,
    word.bounding_box.top_left.y,
    word.bounding_box.bottom_right.x,
    word.bounding_box.bottom_right.y,
    text=word.text,
)

描写する

最後にfig.show()をして画像を表示させます。Jupyter Notebookで実行している場合は、画像の上に矩形が表示されたグラフが描写されるはずです。

fig.update_layout(title=title, showlegend=showlegend)
fig.show()

まとめ

今回はOCR結果を画像上でインタラクティブに確認するため、Plotlyを使った可視化手法を紹介しました。座標情報を元に文字と画像を行き来するのはなかなか大変なので、こうしたサポートツールがあると開発がしやすくなりますね。

参考