Chuyển đến nội dung chính

レッスン 11: OCR とドキュメントの理解

OCR パイプライン: Tesseract、EasyOCR、PaddleOCR。ドキュメントレイアウト分析: LayoutLM、Doughnut。テーブルの抽出。請求書/領収書の処理。手書き認識。ベトナム語の OCR の課題。

🧠 AI と ML — レッスン 10 レッスン 11: OCR とドキュメントの理解

深層学習によるコンピューター ビジョン: CNN から Vision Transformer まで

パート 4: 実際のアプリケーションと展開

xdev.asia

はじめに

OCR (光学式文字認識) = 画像からテキストを読み取ります。単純そうに聞こえますが、実際は非常に複雑です。斜体、ぼかし、複数の言語、手書き、表...この記事では、基本的な OCR から最新のドキュメントの理解までを取り上げます。

🎯 使用例: ナンバー プレートの読み取り、請求書/請求書の処理、文書のデジタル化、フォームの抽出。


1. OCR パイプライン

1.1 基本的な手順

Input Image (ảnh chứa chữ)
    ↓
1. Preprocessing:  Denoise, deskew, binarize
    ↓
2. Text Detection: Tìm vùng có chữ (bounding boxes)
    ↓
3. Text Recognition: Đọc chữ trong mỗi vùng
    ↓
4. Post-processing: Sửa lỗi, format output
    ↓
Output: Text + positions

1.2 一般的な OCR エンジン

エンジンタイプベトナム語スピード精度無料?
テッセラクト伝統的な⭐⭐⚡ 速い⭐⭐⭐✅
EasyOCRディープラーニング⭐⭐⭐⭐🔥 中⭐⭐⭐⭐✅
パドルOCRディープラーニング⭐⭐⭐⭐⭐⚡ 速い⭐⭐⭐⭐⭐✅
Google ビジョンクラウドAPI⭐⭐⭐⭐⭐⚡⭐⭐⭐⭐⭐💰
Azure AI ビジョンクラウドAPI⭐⭐⭐⭐⭐⚡⭐⭐⭐⭐⭐💰

2. 実践: OCR エンジン

2.1 テッセラクト

"""Tesseract OCR — engine classic"""
# pip install pytesseract
# macOS: brew install tesseract
# Ubuntu: sudo apt install tesseract-ocr tesseract-ocr-vie

import pytesseract
from PIL import Image
import cv2

# Basic OCR
image = Image.open("document.png")
text = pytesseract.image_to_string(image, lang="vie")  # Tiếng Việt
print(text)

# OCR với bounding boxes
data = pytesseract.image_to_data(image, lang="vie", output_type=pytesseract.Output.DICT)

img_cv = cv2.imread("document.png")
for i, word in enumerate(data['text']):
    if word.strip() and int(data['conf'][i]) > 60:  # Confidence > 60%
        x, y, w, h = data['left'][i], data['top'][i], data['width'][i], data['height'][i]
        cv2.rectangle(img_cv, (x, y), (x+w, y+h), (0, 255, 0), 2)
        cv2.putText(img_cv, word, (x, y-5),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)

2.2 EasyOCR

"""EasyOCR — Deep Learning, hỗ trợ 80+ ngôn ngữ"""
# pip install easyocr
import easyocr

# Khởi tạo (tải model lần đầu)
reader = easyocr.Reader(['vi', 'en'])  # Tiếng Việt + Anh

# OCR
results = reader.readtext("invoice.jpg")

for (bbox, text, confidence) in results:
    print(f"  [{confidence:.2%}] {text}")
    # bbox = [[x1,y1], [x2,y2], [x3,y3], [x4,y4]] — 4 corners

# OCR trên ảnh OpenCV
import cv2
img = cv2.imread("invoice.jpg")

for (bbox, text, confidence) in results:
    if confidence > 0.5:
        # Vẽ polygon
        pts = [tuple(map(int, pt)) for pt in bbox]
        cv2.polylines(img, [np.array(pts)], True, (0, 255, 0), 2)
        cv2.putText(img, text, pts[0],
                    cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)

2.3 PaddleOCR — ベトナム人に最適

"""PaddleOCR — SOTA accuracy, đặc biệt tốt cho CJK + Vietnamese"""
# pip install paddlepaddle paddleocr
from paddleocr import PaddleOCR

# Khởi tạo
ocr = PaddleOCR(
    use_angle_cls=True,    # Tự phát hiện chữ xoay
    lang='vi',             # Tiếng Việt
    use_gpu=True,
)

# OCR
result = ocr.ocr("document.jpg", cls=True)

for line in result[0]:
    bbox = line[0]           # 4 điểm
    text = line[1][0]        # Text
    confidence = line[1][1]  # Confidence
    print(f"  [{confidence:.2%}] {text}")

3. OCR の前処理

"""Preprocessing cải thiện OCR accuracy đáng kể"""
import cv2
import numpy as np

def preprocess_for_ocr(image_path):
    img = cv2.imread(image_path)

    # 1. Grayscale
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 2. Denoise
    denoised = cv2.fastNlMeansDenoising(gray, h=10)

    # 3. Binarize (Otsu's threshold)
    _, binary = cv2.threshold(denoised, 0, 255,
                               cv2.THRESH_BINARY + cv2.THRESH_OTSU)

    # 4. Deskew (sửa nghiêng)
    coords = np.column_stack(np.where(binary < 128))
    if len(coords) > 0:
        angle = cv2.minAreaRect(coords)[-1]
        if angle < -45:
            angle = -(90 + angle)
        else:
            angle = -angle

        if abs(angle) > 0.5:
            h, w = binary.shape
            center = (w // 2, h // 2)
            M = cv2.getRotationMatrix2D(center, angle, 1.0)
            binary = cv2.warpAffine(binary, M, (w, h),
                                     flags=cv2.INTER_CUBIC,
                                     borderMode=cv2.BORDER_REPLICATE)

    # 5. Remove borders / noise
    kernel = np.ones((2, 2), np.uint8)
    binary = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)

    return binary

# Sử dụng
clean_image = preprocess_for_ocr("noisy_scan.jpg")
text = pytesseract.image_to_string(clean_image, lang="vie")

4. ドキュメントのレイアウト分析

4.1 問題

Trang tài liệu có:
- Tiêu đề (header)
- Đoạn văn (paragraph)
- Bảng (table)
- Hình ảnh (figure)
- Chú thích (caption)
- Header / Footer

→ OCR chỉ đọc CHỮ. Layout Analysis hiểu CẤUTRÚC.

4.2 LayoutLM — ドキュメントのトランスフォーマー

"""LayoutLMv3: hiểu layout + text + image cùng lúc"""
from transformers import LayoutLMv3Processor, LayoutLMv3ForTokenClassification
from PIL import Image

processor = LayoutLMv3Processor.from_pretrained(
    "microsoft/layoutlmv3-base",
    apply_ocr=True,  # Tự chạy OCR
)
model = LayoutLMv3ForTokenClassification.from_pretrained(
    "microsoft/layoutlmv3-base",
    num_labels=7,  # title, text, table, figure, list, ...
)

image = Image.open("paper_page.png")
encoding = processor(image, return_tensors="pt")

with torch.no_grad():
    outputs = model(**encoding)

# Predictions cho mỗi token
predictions = outputs.logits.argmax(-1).squeeze()
# Label: 0=other, 1=title, 2=text, 3=table, 4=figure, 5=list, 6=footer

4.3 テーブルの抽出

"""Trích xuất bảng từ ảnh"""
# pip install img2table
from img2table.document import Image as Img2TableImage
from img2table.ocr import PaddleOCR as Img2TableOCR

# OCR engine
ocr = Img2TableOCR(lang="vie")

# Detect và extract tables
doc = Img2TableImage(src="invoice_with_table.jpg")
tables = doc.extract_tables(ocr=ocr)

for i, table in enumerate(tables):
    print(f"\nTable {i+1}:")
    df = table.df  # Pandas DataFrame!
    print(df.to_string())
    df.to_csv(f"table_{i+1}.csv", index=False)

5. 請求書/領収書の処理

"""Pipeline xử lý hóa đơn"""
import re
from paddleocr import PaddleOCR

ocr = PaddleOCR(lang='vi')

def process_invoice(image_path):
    """Extract thông tin từ hóa đơn"""
    result = ocr.ocr(image_path)

    # Tập hợp tất cả text
    all_text = []
    for line in result[0]:
        text = line[1][0]
        confidence = line[1][1]
        bbox = line[0]
        all_text.append({
            "text": text,
            "confidence": confidence,
            "y_center": (bbox[0][1] + bbox[2][1]) / 2,
        })

    full_text = "\n".join([t["text"] for t in all_text])

    # Extract thông tin bằng regex
    info = {}

    # Số hóa đơn
    invoice_match = re.search(r'(?:Số|No|Invoice)\s*[:#]?\s*(\S+)', full_text, re.IGNORECASE)
    if invoice_match:
        info["invoice_number"] = invoice_match.group(1)

    # Ngày
    date_match = re.search(r'(\d{2}[/-]\d{2}[/-]\d{4})', full_text)
    if date_match:
        info["date"] = date_match.group(1)

    # Tổng tiền
    total_match = re.search(r'(?:Tổng|Total|Thành tiền)\s*[:]?\s*([\d,.]+)', full_text, re.IGNORECASE)
    if total_match:
        info["total"] = total_match.group(1)

    return info

# Test
info = process_invoice("invoice_sample.jpg")
print(f"📄 Invoice: {info}")

6. ベトナム語 OCR — 課題

Thách thức riêng cho tiếng Việt:
1. Dấu: sắc, huyền, hỏi, ngã, nặng → dễ nhầm
2. Ký tự đặc biệt: ă, â, ê, ô, ơ, ư, đ
3. Tone marks nhỏ trên chữ hoa: Ả, Ẵ, Ỗ
4. Font chữ đa dạng (đặc biệt trong tài liệu cũ)
5. Mixed language: Việt + English trong cùng document

Tips:
✅ Dùng PaddleOCR hoặc EasyOCR (tốt cho tiếng Việt)
✅ Preprocessing: denoise + binarize + deskew
✅ Post-processing: spell check tiếng Việt
✅ Test trên data thật trước khi deploy

概要

コンセプト覚えておいてください
テッセラクト従来の OCR、高速、無料、前処理が必要
EasyOCRディープラーニング、80 以上の言語、使いやすさ
パドルOCRSOTA の精度、ベトナム人に最適
前処理ノイズ除去 + 2 値化 + デスキュー → 精度の向上
レイアウト分析LayoutLMv3 はドキュメント構造を理解します
テーブルの抽出img2table → パンダデータフレーム

一般的な演習

  1. OCR の比較: 5 つのベトナム語画像に対して 3 つのエンジン (Tesseract、EasyOCR、PaddleOCR) すべてを実行します。精度を比較します。
  2. 請求書パーサー: 請求書読み取りパイプラインを構築します: 請求書番号、日付、合計金額を抽出します。
  3. 前処理の影響: 前処理の前後に OCR を実行します。精度は何%向上しましたか?
  4. テーブル リーダー: 異なるテーブルを含む 3 つの画像からテーブルを抽出します。 CSVをエクスポートします。

次の記事: マルチモーダル AI — 画像理解のための GPT-4o Vision、Gemini Vision。