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

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

ただいまの
回答率

88.22%

画像内の文字を認識して分類したい!!

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 2,449

yhori3

score 7

前提・実現したいこと

下のような百人一首の下の句が書かれている札の画像から文字を認識してそれに対応する上の句をDB上に保存することで、実際の下の句が書かれている札の写真(こちらも下記記載)を識別し、それに対応する上の句を表示できるようにしたいと考えています。

まず第一歩目としてtesseract(4.11)の日本語訓練データとpythonで下記画像の文字を読み取れるかを検討したところ、下のような結果となってしまいました。
イメージ説明
イメージ説明

OCR結果

%22 ( わ
れ は か
つ つ こ
つゆ ろ 
 にも   

該当のソースコード

import os
from PIL import Image
import pyocr

tools = pyocr.get_available_tools()
tool = tools[0]

img = Image.open("torifuda_F_1.jpg")


builder = pyocr.builders.TextBuilder(tesseract_layout=6)
text = tool.image_to_string(img, lang="jpn", builder=builder)

print(text)

lang=jpn_vertという縦書き用の訓練データでは1割程度しか正しく読み取ることが出来なかったためlang=jpnで読み取ったのですが正確に読み取ることは出来ていないようです。
文字認識の精度をあげるにはどうしたらよいでしょうか?

読み取りの精度を上げることが難しい場合、下の句の札100枚の画像を文字ではなく模様として教師あり機械学習?(機械学習は素人でよくわかっていませんが)を行って分類するという感じになるのでしょうか?

目標の分類を実現するために、どういった手順で取り組むのが良いかも教えていただけると非常に助かります。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • fourteenlength

    2020/08/30 12:43

    Q.1 OCRする対象は常に百人一首の画像限定でしょうか?それとももっと不特定多数の(極端に言えば小説や雑誌などの)本になるのでしょうか?将来的にもっと汎用性の高いことをやろうとしているか、トイプロブレムの百人一首がしのげる解決策でよいのか、というイメージです。
    Q.2 OCR対象の画像の撮影位置や角度は決まっておらず、バックリと「人間が認識できるような向きと大きさ」くらいの縛りでしょうか?

    キャンセル

  • yhori3

    2020/08/30 13:10

    ご質問ありがとうございます。
    Q1についてですが、今のところ百人一首の画像限定で考えています。下の句が書かれた実物の札の写真から上の句を返すものを作りたいです。
    Q2については、真上から撮った写真のみを使用する予定ですが、可能であれば札の上下が逆さであっても識別することが出来ると嬉しいです。

    キャンセル

回答 2

checkベストアンサー

+6

情報を補足するような質問を書きましたが、暫定での解決策を書きます。

修正1)
質問欄の2点を反映させます。

  • Q1 とりあえず百人一首のトイプロブレムが解ければOK
  • Q2 基本は真上だけれども180°回転も可能なら対応したい

文字認識の精度をあげるにはどうしたらよいでしょうか?

文字の認識面)
恐らく、百人一首のフォントが通常のフォント(明朝・ゴシック)から離れている(行書寄り?)なことが一因だと思います。OCRで攻めるのであれば、百人一首の文字は全てひらがななので、ひらがなで自前データセットを作るのもありだと思います。

画像の前処理)
元の画像は人間が読むには十分正面の画像ですが、もう少し下準備をしてOCRに放り込んだ方が認識精度が上がりそうな気がします。具体的には、解析対象がカードですので、緑色の枠線で認識させて、領域を抽出、写っている画像を射影変換して台形を長方形に修正させる、です。

物体認識によるカード位置の推定手法もありかと思いますが、枠の内側の内容は100種類あるので、これをOCRの下準備で使うのは(それだけ特徴があればそのまま100種類の画像として認識できそうですので)もったいないですね。

模様として教師あり機械学習?(機械学習は素人でよくわかっていませんが)を行って分類する

1) 物体認識
これが上記の「物体認識によるカード位置の推定手法」にあたります。たぶんAKAZEなどのアルゴリズムを使うことになると思いますが、文字や図形の角やでっぱり、曲がり具合のようなもの特徴を表す点として認識する方法です。回転や拡大縮小があったとしても、それぞれの対応する点は同じように回転や拡大縮小しながら(イメージ的には起点となる一点を中心に一直線に)つながるよね、という原理を使います。その一対一でつながる誤差が一番小さいものが推定するカードの内容となります。が、この物体認識は写真のような濃淡の勾配があるようなものに対して相当うまく働く(恐らく膨大な数の特徴点を抽出できる)はずですが、文字のような濃淡の極端なもの、線に近いもの、ではうまく行かなさそうな気もします。

深層学習を使って物体認識(セマンティックセグメンテーションだとかインスタンスセグメンテーションと呼びます)することもできますが、それができるのであれば直接100クラスの画像認識問題にした方が筋が良さそうですね…

2) 画像認識
画像を物体認識ではなく、特定のカードと限定して認識させようとすると、100種類あるカードの模様(文字)を機会が認識できる解像度まで上げないといけません。すると、現実的な(機械が認識できる)解像度で処理させようとするとハードが追い付かない(計算が遅い?)が起きそうな気がします。特に深層学習の場合がそうです。できなくはないと思いますが、もっとスマートなやり方(例えばOCR)がありそうな気がします。


文字列としての認識精度)
OCRで解決しようとした場合、仮に独自データセットを作ったとしても「しるもしらぬもあふさかのせき」が「しろもしらめもあふきかのせさ」として認識されることもあるかと思います。こういった場合には、文字列全体の一致度をデータベースの中身と比較させると精度が挙げられそうですね。


上下反転してもいける?)
やり方は3種類あると思います。

  • OCRする際に、画像をそのままのものと反転させたものを突っ込み、最後の文章としての認識具合をチェック:恐らく正攻法
  • カード左下領域の情報を使う(左下にある四角部分をマーカーに使う):汎用性低
  • 画像として認識させる:お勧めしません(先の画像認識の下りの部分を参照ください)が、深層学習で対応するのであれば、データ増強の際に画像をゴリゴリ回転させたり反転させたりすることで対応できます。

照明の照りこみが激しい?)
鏡面反射分が多いと画像処理(緑色領域の抽出、OCR精度)で不利になります。照りこみが減るような工夫(すりガラスのような濁ったものを証明の前に挟む、直接照らさずに、白い紙のようなものに一度反射させて間接照明にする、のような工夫をすることでだいぶ良くなると思います。


もし私がやるのであれば、

  1. 色情報を使ってカード領域を抽出
    1.1 色情報での抽出がうまく行かない場合は、まずカードを物体認識で荒く抽出させた上で、限定した領域に対して色情報を使ってカード領域を抽出)

  2. 抽出した領域を射影変換して長方形に修正

  3. OCRで文字認識(可能であれば独自データセットを作成)
    3.1 画像が上下反転する可能性があるのであれば、画像を上下反転(180°回転)させて3-5の処理を実施

  4. 文字を繋げて文章化

  5. 認識した文章と、データベース上の文章の一致度を調べる(文字の誤認識対策です)

  6. 一番一致度の高い句を返す

とやります。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/08/30 16:45

    ご丁寧な回答ありがとうございます。
    なかなか骨が折れそうですが、ひらがなの50音でデータセットを作るところから取り組んでいこうと思います。
    実装までの具体的な手順もわかりやすく示していただいて本当にありがたいです!!

    キャンセル

+3

実際にDeepLearningを使用して日本語OCRを掛けた結果は下記のとおりになりました。エッジ抽出などいっさい行わずに画像をピュアに投げ込んだだけですので認識率は最悪の結果になりました。
1
2

そこで別の方法は無いかなぁ、と思案していたところ、Google Docs のAPIなどを活用して認識率高めの結果が取れそうでしたので、ご参考になるかどうか分かりませんが念の為共有させていただきます。一文字も取りこぼさずに認識しているようです。あれこれ複雑な実装を考えるより簡単にできそうな雰囲気ですが、時間がかかりそうでしたので実装までは試せていません。
JPEG -> Google Drive -> Google Docs で開くと自動的にOCR処理される
3

Google Docs Python APIチュートリアル

from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/documents.readonly']

# The ID of a sample document.
DOCUMENT_ID = '195j9eDD3ccgjQRttHhJPymLJUCOUjs-jmwTrekvdjFE'

def main():
    """Shows basic usage of the Docs API.
    Prints the title of a sample document.
    """
    creds = None
    # The file token.pickle stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    if os.path.exists('token.pickle'):
        with open('token.pickle', 'rb') as token:
            creds = pickle.load(token)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                'credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open('token.pickle', 'wb') as token:
            pickle.dump(creds, token)

    service = build('docs', 'v1', credentials=creds)

    # Retrieve the documents contents from the Docs service.
    document = service.documents().get(documentId=DOCUMENT_ID).execute()

    print('The title of the document is: {}'.format(document.get('title')))


if __name__ == '__main__':
    main()

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/08/30 16:51

    とても参考になりました。
    やはりgoogleのAPIの方が精度は高いのですね。費用などは抑えたいので、札のフォントのひらがなに関するデータセットを作る方向で実装してみたいと思います。
    ご丁寧な回答ありがとうございました。

    キャンセル

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

  • ただいまの回答率 88.22%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る