ページ

2021-07-04

openCV での漢字テキストの表示

 

OpenCVを使うと、簡単に動画再生ができる(スクリプトが書ける)のですが、基本的にはキーによる制御しかサポートされていません。というか、キー入力待ちの期間に表示、イベント受け取りが行なわれる様な構造になっています。

動画からフレームを抽出して、再生タイミングに合わせた表示(+キー待ち)を行なう事で動画が再生されます。フレーム画像に手を加えることができるので、動画に対する学習結果を見せるのに使われているようです(検出結果をマークしたりね)。

■フレームへの追記、テキスト描画について


フレームに書き込むためのAPIが用意されていて、基本的な図形と文字を書き込むことができます。ただ、この文字描画が曲者でして、独自のベクターフォントを使っているとかで、US-ASCII の文字しか表示できないのです。

漢字テキストも描画したいという要望が多いようで、ネットを探すと日本語表示するための工夫が見つかります(「openCV 漢字」で探すといくつも出てきます)。基本は、pillow の描画空間を用意して、そこに描画されたテキストを OpenCV の画像に取り込む、ですね。ただ注意点として

  • OpenCV/PILで画像データの持ち方が異なっているので変換が必要になります。
  • カラーも BGR/RGBでデータ順序が異なっているのでこれについても変換が必要になります。

この変換、内部データ構造参照しているので、少々気持ち悪い処理になっていました。

PillowのAPIを見ていたら、pillow 側の
ピクセルデータを参照する機能がありました。Pillow側の描画空間を二値画像にすれば、描画された文字ピクセルのオン・オフが判ります。なので、pillow 側の画像データをスキャンラインの集合に展開すると、OpenCV側では直線描画だけで、文字を描画できます。

Pillowで文字列を描画すると、その文字列画像のデータ(ピクセル)が書き込まれた範囲、エクステント(外接矩形)を求めることができます。これを使うと、効率的にスキャンライン展開が可能になります。

  • pspc=Image.new('1', (幅,高さ),color=0)
モノクロームのイメージ空間を用意します。
  • draw=ImageDraw.Draw(pspc)
イメージに対する描画空間(ハンドル)を取得します。draw経由でpsrcへの描画が行なわれます。
  • font=ImageFont.truetype(フォントパス, サイズ)
TrueTypeフォントを用意します。
  • draw.text((0,0),  テキスト, 1, font)
PILイメージへのテキスト描画
  • bbox=pspc.getbbox()
描画されたテキスト領域(外接矩形)を取得します。画像はこの矩形内だけに存在しています。ああ、難点として描画したテキストが空白だとこれがエラーになる様です。空白の場合はスキップする必要がありました。
  • for y in range(bbox[1], bbox[3])
外接矩形の各スキャンラインについて
inline=False     フラグ初期値
sp = 基準位置
ep = 基準位置
  • for  x in range(bbox[0], bbox[2])
スキャンラインの水平方向について
if pspc.getpixel((x, y )) :
    if not inline :
        sp =  (base[0]  x, base[1 + y])
        inline=True
else:
    if inline:
        ep =  (base[0]  x, base[1 + y])
        cv2.line(画像, sp, ep, 表示色, 1)
                inline=False

ネットで見つけた言い回し

昔のMediumの記事見てていたら、すごい言い回しがありました。Knuthの「The Art of Computer Programming」が発しているメッセージだとのことですが「Step aside, Muggles, because you’re in the presence of a Real Programmer.」。訳すと'控えよ、マグルども。君たちは本物のプログラマーの前にいるのだ。’。ムスカさんの声で演ると雰囲気が出そうです。