YomiTokuによる印刷・手書き文字認識

【概要】Yomitokuを用いた文字認識を行う。7000文字超の日本語文字認識と縦書き・手書き文字に対応する。

目次

概要

主要技術: YomiToku文書画像解析

出典: Kinoshita, K. (2024). YomiToku: AI-powered document image analysis package for Japanese language [Computer software]. GitHub. https://github.com/kotaro-kinoshita/yomitoku

技術特徴: 4つのAIモデル(文字位置検知、文字列認識、レイアウト解析、表構造認識)を備えた日本語文書解析システムである。7000文字超の日本語文字対応、縦書き・横書き混在文書、複雑な表構造(rowspan×colspan:表のセル結合機能)の解析が可能である。リアルタイム処理により、Webカメラで撮影した文書を構造化データとして抽出できる。

応用例: 文書デジタル化、会議議事録自動作成、帳票データ入力自動化、手書き記入読み取り

学習目標: 従来型のOCR(光学文字認識)では困難な複雑な日本語文書の構造理解を体験.AI解析技術の性能特性を理解する。

Python開発環境,ライブラリ類

ここでは、最低限の事前準備について説明する。機械学習や深層学習を行う場合は、NVIDIA CUDA、Visual Studio、Cursorなどを追加でインストールすると便利である。これらについては別ページ https://www.kkaneko.jp/cc/dev/aiassist.htmlで詳しく解説しているので、必要に応じて参照してください。

Python 3.12 のインストール

インストール済みの場合は実行不要。

管理者権限でコマンドプロンプトを起動(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行する。管理者権限は、wingetの--scope machineオプションでシステム全体にソフトウェアをインストールするために必要である。

REM Python をシステム領域にインストール
winget install --scope machine --id Python.Python.3.12 -e --silent
REM Python のパス設定
set "PYTHON_PATH=C:\Program Files\Python312"
set "PYTHON_SCRIPTS_PATH=C:\Program Files\Python312\Scripts"
echo "%PATH%" | find /i "%PYTHON_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%PYTHON_PATH%" /M >nul
echo "%PATH%" | find /i "%PYTHON_SCRIPTS_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%PYTHON_SCRIPTS_PATH%" /M >nul

関連する外部ページ

Python の公式ページ: https://www.python.org/

AI エディタ Windsurf のインストール

Pythonプログラムの編集・実行には、AI エディタの利用を推奨する。ここでは,Windsurfのインストールを説明する。

管理者権限でコマンドプロンプトを起動(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行して、Windsurfをシステム全体にインストールする。管理者権限は、wingetの--scope machineオプションでシステム全体にソフトウェアをインストールするために必要となる。

winget install --scope machine Codeium.Windsurf -e --silent

関連する外部ページ

Windsurf の公式ページ: https://windsurf.com/

必要なライブラリのインストール

コマンドプロンプトを管理者として実行(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行する。


pip install -U torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
pip install yomitoku opencv-python pillow numpy

YomiTokuによる文字認識プログラム

概要

このプログラムは、カメラやビデオから取得した画像内の日本語テキストを認識する。YomiTokuライブラリを使用して、画像内の文字位置を検出し、検出された領域から文字を認識する処理を行う。

主要技術

参考文献

[1] Kinoshita, K. (2024). YomiToku: Japanese Document Intelligence. GitHub repository. https://github.com/kotaro-kinoshita/yomitoku

[2] Kinoshita, K. (2024). YomiToku v0.8.0 Model Card. Hugging Face. https://huggingface.co/yomitoku/yomitoku-v0.8.0


# YomiTokuによる文字認識プログラム
# 特徴技術名: 文字認識(Text Recognition)
# 出典: GitHub: https://github.com/kotaro-kinoshita/yomitoku
# 特徴機能: 7000文字超の日本語文字認識(ひらがな、カタカナ、漢字)による高精度な文字認識
# 学習済みモデル: YomiToku v0.8.0 - 手書き文字認識対応の統合モデル。活字・手書き文字の両方を単一モデルで認識可能。Hugging Face Hubから自動ダウンロード
# 方式設計:
#   関連利用技術: 文字位置検知(Text Detection)、レイアウト解析(Layout Analysis)、表構造認識(Table Structure Recognition)、OpenCV(カメラ入力)、PIL(日本語テキスト描画)
#   入力と出力: 入力: 動画(ユーザは「0:動画ファイル,1:カメラ,2:サンプル動画」のメニューで選択.0:動画ファイルの場合はtkinterでファイル選択.1の場合はOpenCVでカメラが開く.2の場合はhttps://github.com/opencv/opencv/blob/master/samples/data/vtest.aviを使用)、出力: OpenCV画面でリアルタイムに認識結果を表示.1秒間隔でprint()で処理結果を表示.プログラム終了時にprint()で表示した処理結果をresult.txtファイルに保存
#   処理手順: 1.カメラ/動画フレーム取得、2.DocumentAnalyzerによる統合解析、3.文字認識結果の抽出、4.レイアウト解析結果の取得、5.表構造の認識、6.結果の画面表示とコンソール出力
#   前処理、後処理: 前処理: BGR→RGB変換(YomiTokuはRGB形式を期待)、後処理: 日本語フォントによる認識結果の画面描画
#   追加処理: バッファサイズを1に設定してリアルタイム性を確保、認識結果を最大10ブロックに制限して画面の視認性を維持
#   調整を必要とする設定値: FONT_PATH(日本語フォントのパス、環境により要調整)、FONT_SIZE(文字サイズ、画面解像度により調整が必要)
# 将来方策: FONT_SIZE(文字サイズ)の自動調整機能。画面解像度とフレームサイズから最適な文字サイズを自動計算し、視認性を最適化する機能をプログラム内で実装可能
# その他の重要事項: VRAM 8GB以上推奨、CUDA 11.8以上必要、低解像度画像では精度低下(短辺720px以上推奨)
# 前準備:
#   pip install -U torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
#   pip install yomitoku opencv-python pillow numpy

import cv2
import tkinter as tk
from tkinter import filedialog
import os
import numpy as np
from PIL import Image, ImageDraw, ImageFont
from yomitoku import DocumentAnalyzer
import sys
import io
import torch
import logging
import time
import urllib.request

# ログレベル設定(YomiTokuのログを抑制)
logging.getLogger('yomitoku').setLevel(logging.WARNING)
logging.getLogger('yomitoku.base').setLevel(logging.WARNING)

# フォント設定
FONT_PATH = 'C:/Windows/Fonts/msgothic.ttc'  # 日本語フォントパス(環境により要調整)
FONT_SIZE = 30  # 表示文字サイズ(画面解像度により要調整)
TEXT_COLOR_RGB = (0, 255, 0)  # RGB形式での緑色

# 表示設定
WINDOW_NAME = 'Video'  # OpenCVウィンドウ名
TEXT_START_Y = 30      # テキスト表示開始Y座標
TEXT_LINE_HEIGHT = 40  # テキスト行間隔
OUTPUT_INTERVAL = 1.0  # コンソール出力間隔(秒)
MAX_DISPLAY_TEXTS = 10 # 最大表示テキスト数

# ファイル設定
RESULT_FILE = 'result.txt'  # 結果保存ファイル名
SAMPLE_VIDEO_URL = 'https://github.com/opencv/opencv/raw/master/samples/data/vtest.avi'
SAMPLE_VIDEO_FILE = 'vtest.avi'

# カメラ設定
CAMERA_INDEX = 0      # カメラデバイス番号
BUFFER_SIZE = 1       # カメラバッファサイズ

# Windows文字エンコーディング設定
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', line_buffering=True)

# グローバル変数
device = 'cuda' if torch.cuda.is_available() else 'cpu'
analyzer = DocumentAnalyzer(device=device)
font = ImageFont.truetype(FONT_PATH, FONT_SIZE)
all_results = []
last_print_time = time.time()
first_run = True


def video_processing(frame):
    global analyzer, font, all_results, last_print_time, first_run

    rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

    # YomiTokuによる解析
    try:
        result = analyzer(rgb_frame)
    except Exception as e:
        print(f'解析エラー: {e}')
        return frame

    # 検出されたテキストを収集
    detected_texts = []

    if len(result) > 0 and result[0] is not None:
        doc_schema = result[0]

        # 初回実行時のみデバッグ情報
        if first_run:
            print('\n=== DocumentAnalyzerSchemaの詳細構造 ===')
            if hasattr(doc_schema, 'paragraphs') and doc_schema.paragraphs:
                print(f'paragraphs length: {len(doc_schema.paragraphs)}')
            if hasattr(doc_schema, 'words') and doc_schema.words:
                print(f'words length: {len(doc_schema.words)}')
            first_run = False

        # テキスト抽出
        if hasattr(doc_schema, 'paragraphs') and doc_schema.paragraphs:
            for paragraph in doc_schema.paragraphs[:MAX_DISPLAY_TEXTS]:
                if hasattr(paragraph, 'text'):
                    detected_texts.append(paragraph.text)
                elif hasattr(paragraph, 'content'):
                    detected_texts.append(paragraph.content)

        if not detected_texts and hasattr(doc_schema, 'words') and doc_schema.words:
            for word in doc_schema.words[:MAX_DISPLAY_TEXTS]:
                if hasattr(word, 'text'):
                    detected_texts.append(word.text)
                elif hasattr(word, 'content'):
                    detected_texts.append(word.content)

    # 画面表示処理
    display_frame = frame.copy()

    if detected_texts:
        img_pil = Image.fromarray(cv2.cvtColor(display_frame, cv2.COLOR_BGR2RGB))
        draw = ImageDraw.Draw(img_pil)

        for i, text in enumerate(detected_texts):
            y_position = TEXT_START_Y + i * TEXT_LINE_HEIGHT
            draw.text((10, y_position), text, font=font, fill=TEXT_COLOR_RGB)

        display_frame = cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)

    # 1秒間隔でコンソール出力
    current_time = time.time()
    if current_time - last_print_time >= OUTPUT_INTERVAL:
        if detected_texts:
            timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
            print(f'\n[{timestamp}] 検出されたテキスト:')
            for i, text in enumerate(detected_texts):
                print(f'  {i+1}: {text}')
                all_results.append(f'[{timestamp}] {i+1}: {text}')
        last_print_time = current_time

    return display_frame


# プログラム開始
print('=== YomiToku文字認識プログラム ===')
print('概要: 動画や画像から日本語文字(ひらがな・カタカナ・漢字)を認識します')
print('操作方法:')
print('  - \'q\'キー: プログラム終了')
print('  - 認識結果は画面上に緑色で表示されます')
print('  - 1秒ごとに認識結果をコンソールに出力します')
print('  - 終了時にresult.txtに全結果を保存します\n')

print(f'使用デバイス: {device.upper()}')
print('YomiToku DocumentAnalyzerが正常に初期化されました\n')

print('0: 動画ファイル')
print('1: カメラ')
print('2: サンプル動画')

choice = input('選択: ')
temp_file = None

if choice == '0':
    root = tk.Tk()
    root.withdraw()
    path = filedialog.askopenfilename()
    if not path:
        exit()
    cap = cv2.VideoCapture(path)
elif choice == '1':
    cap = cv2.VideoCapture(CAMERA_INDEX, cv2.CAP_DSHOW)
    cap.set(cv2.CAP_PROP_BUFFERSIZE, BUFFER_SIZE)
elif choice == '2':
    # サンプル動画ダウンロード・処理
    try:
        urllib.request.urlretrieve(SAMPLE_VIDEO_URL, SAMPLE_VIDEO_FILE)
        temp_file = SAMPLE_VIDEO_FILE
        cap = cv2.VideoCapture(SAMPLE_VIDEO_FILE)
    except Exception as e:
        print(f'動画のダウンロードに失敗しました: {SAMPLE_VIDEO_URL}')
        print(f'エラー: {e}')
        exit()
else:
    print('無効な選択です')
    exit()

# メイン処理
try:
    while True:
        cap.grab()
        ret, frame = cap.retrieve()
        if not ret:
            break

        processed_frame = video_processing(frame)
        cv2.imshow(WINDOW_NAME, processed_frame)

        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
finally:
    cap.release()
    cv2.destroyAllWindows()
    if temp_file:
        os.remove(temp_file)

    # 結果をファイルに保存
    if all_results:
        with open(RESULT_FILE, 'w', encoding='utf-8') as f:
            f.write('=== YomiToku文字認識結果 ===\n')
            f.write(f"処理日時: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}\n\n")
            for result in all_results:
                f.write(result + '\n')
        print(f'\n結果を{RESULT_FILE}に保存しました')

    print('\nプログラムを終了しました')

使用方法

  1. 上記のプログラムを実行する
  2. パソコンカメラが起動し、リアルタイム文書解析画面が表示される
  3. 解析したい文書(印刷物、手書き文書、表、レイアウト複雑な文書等)をカメラに映す
  4. 画面に文字認識結果が表示される
  5. コンソールにも文字認識結果が出力される
  6. 'q'キーを押すとプログラムが終了する

実験・探求のアイデア

基礎レベル実験

文書種別による精度比較実験

応用レベル実験

複雑レイアウト解析実験

表構造認識の限界探求

発展レベル実験

詳細パラメータ分析

応用可能性の検証