質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.12%

Google Cloud Vision の OCR で、認識されている文章は正しいが、文字を分割している矩形の位置がズレてしまう。

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,268

namagon

score 5

 発生している問題・エラーメッセージ

Google Cloud Vision (以下GCV) の DOCUMENT_TEXT_DETECTION を利用しています。
OCR にて文字列は正しく検知されているにもかかわらず、文字分割の認識領域がズレているように見られます。
単独文字の confidence も高いため、検知箇所を間違えているということも考えづらいです。
以下の仮説を検証中していますが、詰まってしまいました。

  • 私が書いた描画関数がポンコツ
  • GCV の character segmentation がポンコツ
  • GCV の 内部で画像に何かしらの補正をかけている。
  • その他

 実現したいこと

Cloud Vision の 文字分割の結果を元に、画像中の文字から1文字ずつトリミングした画像郡を生成したいと思いっています。
そのためになぜ矩形の位置がズレているのか、補正する方法があるのかを知りたいです。
よろしくお願いします。

 入力画像

入力画像

 Cloud Vision API から帰ってきた Json ファイル

147475.json

 文字分割の認識領域を描画した画像

文字分割の認識領域を描画した画像

 描画関数

現在試しに書いているソースから描画に関するところだけ抜いてきたので
至らないところは目をつぶっていただけると助かります。

import argparse
import cv2
import glob
import json

def get_coordinate_list(json_data):
    json_list = json_data['responses'][0]['fullTextAnnotation']['pages']
    coordinate_list = []

    for pages in json_list:
        for blocks in pages['blocks']:
            for paragraphs in blocks['paragraphs']:
                for words in paragraphs['words']:
                    for symbols in words['symbols']:
                            coordinate_list.append(symbols['boundingBox'])

    return coordinate_list


def read_image(img_file):
    image_file = img_file
    image = cv2.imread(str(image_file))
    cv2.destroyAllWindows()
    return image


def get_rectangle(img, lists, file_path):
    for box in lists:
        cv2.destroyAllWindows()
        cv2.rectangle(img, (box['vertices'][0]['x'], box['vertices'][0]['y']), (box['vertices'][2]['x'], box['vertices'][2]['y']), (255, 255, 0), 4)

    ext = ("_rectangle" + get_extension(file_path))
    cv2.imwrite(file_path + ext, img)
    return file_path + ext


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument("--input", type=str)
    args = parser.parse_args()
    input_dir = args.input

    # 描画対象のファイルを取得
    file_list = list(glob.glob(input_dir + '/*.jpg') + glob.glob(input_dir + '/*.png'))
    for file_path in file_list:
        json_file = file_path + '.json'
        f = open(json_file, 'r')
        json_data = json.load(f)
        rect_list = get_coordinate_list(json_data)

        # 矩形を描画
        img = read_image(file_path)

        get_rectangle(img, rect_list, file_path)

 補足情報

Python 3.6.5

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+2

確認しましたが、たしかに頂点情報をそのまま描画しても、ご指摘のようにズレますね。

import json
import math
from pprint import pprint

import matplotlib.patches as patches
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image

# 画像 を読み込む。
img_path = 'test.jpg'
img = np.array(Image.open(img_path))

# json を読み込む。
json_path = 'test.json'
with open(json_path, encoding='utf-8') as f:
    j = json.loads(f.read())

# json オブジェクトからデータ抽出
#########################################################
words = j['responses'][0]['fullTextAnnotation']\
        ['pages'][0]["blocks"][0]['paragraphs'][0]['words']

# symbol オブジェクトを解析する。
def parse_symbol(symbol):
    bbox = []
    for vertex in symbol['boundingBox']['vertices']:
        bbox.append([vertex['x'], vertex['y']])

    return bbox, symbol['confidence'], symbol['text']

# json オブジェクトを解析する。
bboxes = []
confs = []
words = []

pages = j['responses'][0]['fullTextAnnotation']['pages']
for page in pages:
    for block in page['blocks']:
        for paragraph in block['paragraphs']:
            for word in paragraph['words']:
                for symbol in word['symbols']:
                    bbox, conf, word = parse_symbol(symbol)
                    bboxes.append(bbox)
                    confs.append(conf)
                    words.append(word)

# 可視化
#########################################################

# 画像表示用の Figure
fig1, axes1 = plt.subplots(figsize=(6, 6))
axes1.imshow(img)

# 文字表示用の Figure
fig2 = plt.figure(figsize=(8, 18))
fig2.subplots_adjust(hspace=0.5)
cols = 4
rows = math.ceil(len(words) / cols)

for i, (bbox, conf, word) in enumerate(zip(bboxes, confs, words)):

    # 矩形を画像に描画する。
    rect = patches.Polygon(bbox, color='g', fill=False)
    axes1.add_patch(rect)

    # 文字の部分を切り出して描画する。
    axes2 = fig2.add_subplot(rows, cols, i + 1)

    # 画像
    tl, br = bbox[0], bbox[2]  # 矩形の左上、右下の点
    word_img = img[tl[1]:br[1], tl[0]:br[0], :]
    axes2.imshow(word_img)
    axes2.set_axis_off()
    # タイトル
    text = '{} {:.1%}'.format(word, conf)
    axes2.set_title(text)

plt.show()

イメージ説明

イメージ説明

 原因の推測

座標値から観察できることは

  • 例えば「中」は矩形が文字と全く被っていないので、文字の切り出しが正確でないが、なんとか認識している線はありえない。
  • 全体に同じ量ずれているわけではない。
  • 画像に写る文字は正面から写っているとは限らない。その文字を長方形で表現するのは不可能 (文字が斜めに写っていたら、矩形は平行四辺形のような形になるはず)。だけど、結果として返ってくる4点の座標は長方形である。

ここから以下のことが推察されます。

  • 文字認識は「1文字単位の切り出し」→「文字の認識」の2段階で行われている。
  • 文字を切り出す前に、斜めから写っている画像をホモグラフィー変換等で正面から写る画像に補正している。
  • 矩形は補正後の画像の座標を返している。
  • 内部でどのような補正が行われているかわからないので、矩形から画像の文字の位置を復元するのは不可能。(例えば、ホモグラフィー変換の行列がわかれば、矩形の補正前の座標値は計算できる。)

同じ質問が Stack Overflow にもありましたが、解決はしていません。
答えが得られるかはわかりませんが、Google に問い合わせてみてはどうでしょうか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/09/20 14:28

    >文字を切り出す前に、斜めから写っている画像をホモグラフィー変換等で正面から写る画像に補正している

    入力画像を真正面の画像(もしくは手動補正して)にして動かすと、切り分けしやすいのでは?コレの結果が正しい座標を返してくるなら結論は以下になると思います。

    >GCV の 内部で画像に何かしらの補正をかけている。

    キャンセル

  • 2018/09/21 15:20

    やはり補正をかけているが有力ですか、一旦問い合わせて返信があり次第追記します。
    ありがとうございます。

    キャンセル

  • 2018/09/21 19:02

    問い合わせ先、Stack Overflowなんですね
    https://cloud.google.com/vision/support?hl=ja

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.12%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる
  • トップ
  • Pythonに関する質問
  • Google Cloud Vision の OCR で、認識されている文章は正しいが、文字を分割している矩形の位置がズレてしまう。