SKILL.md
readonlyread-only
name
smart-ocr
description
>
version
1.0
Smart OCR 技能
概述
本技能使用 PaddleOCR(支援 100 種以上語言的頂尖 OCR 引擎),從圖片與掃描文件中智慧擷取文字。可從照片、螢幕截圖、掃描 PDF 及手寫文件中準確提取文字。
使用方式
- 提供圖片或掃描文件
- 可選擇指定要偵測的語言
- 我會提取文字,並附上位置與信心度資料
範例提示:
- 「從這張螢幕截圖中提取所有文字」
- 「對這份掃描 PDF 文件進行 OCR」
- 「讀取這張名片照片上的文字」
- 「從這張圖片中提取中文與英文文字」
領域知識
PaddleOCR 基礎
from paddleocr import PaddleOCR
# 初始化 OCR 引擎
ocr = PaddleOCR(use_angle_cls=True, lang='en')
# 對圖片執行 OCR
result = ocr.ocr('image.png', cls=True)
# 結果結構:[[box, (text, confidence)], ...]
for line in result[0]:
box = line[0] # [[x1,y1], [x2,y2], [x3,y3], [x4,y4]]
text = line[1][0] # 提取的文字
conf = line[1][1] # 信心度分數
print(f"{text} ({conf:.2f})")
支援的語言
# 常用語言代碼
languages = {
'en': 'English',
'ch': 'Chinese (Simplified)',
'cht': 'Chinese (Traditional)',
'japan': 'Japanese',
'korean': 'Korean',
'french': 'French',
'german': 'German',
'spanish': 'Spanish',
'russian': 'Russian',
'arabic': 'Arabic',
'hindi': 'Hindi',
'vi': 'Vietnamese',
'th': 'Thai',
# ... 支援 100 種以上語言
}
# 使用特定語言
ocr = PaddleOCR(lang='ch') # 中文
ocr = PaddleOCR(lang='japan') # 日文
ocr = PaddleOCR(lang='multilingual') # 自動偵測
設定選項
from paddleocr import PaddleOCR
ocr = PaddleOCR(
# 偵測設定
det_model_dir=None, # 自訂偵測模型
det_limit_side_len=960, # 偵測最大邊長
det_db_thresh=0.3, # 二值化閾值
det_db_box_thresh=0.5, # 邊界框分數閾值
# 辨識設定
rec_model_dir=None, # 自訂辨識模型
rec_char_dict_path=None, # 自訂字元字典
# 角度分類
use_angle_cls=True, # 啟用角度分類
cls_model_dir=None, # 自訂分類模型
# 語言
lang='en', # 語言代碼
# 效能
use_gpu=True, # 使用 GPU(若可用)
gpu_mem=500, # GPU 記憶體限制(MB)
enable_mkldnn=True, # CPU 最佳化
# 輸出
show_log=False, # 隱藏日誌
)
處理不同來源
圖片檔案
# 單張圖片
result = ocr.ocr('image.png')
# 多張圖片
images = ['img1.png', 'img2.png', 'img3.png']
for img in images:
result = ocr.ocr(img)
process_result(result)
PDF 檔案(掃描)
from pdf2image import convert_from_path
def ocr_pdf(pdf_path):
"""對掃描 PDF 進行 OCR。"""
# 將 PDF 頁面轉換為圖片
images = convert_from_path(pdf_path)
all_text = []
for i, img in enumerate(images):
# 儲存暫存圖片
temp_path = f'temp_page_{i}.png'
img.save(temp_path)
# 對圖片進行 OCR
result = ocr.ocr(temp_path)
# 提取文字
page_text = '\n'.join([line[1][0] for line in result[0]])
all_text.append(f"--- 第 {i+1} 頁 ---\n{page_text}")
os.remove(temp_path)
return '\n\n'.join(all_text)
URL 與位元組資料
import requests
from io import BytesIO
# 從 URL
response = requests.get('https://example.com/image.png')
result = ocr.ocr(BytesIO(response.content))
# 從位元組資料
with open('image.png', 'rb') as f:
img_bytes = f.read()
result = ocr.ocr(BytesIO(img_bytes))
結果處理
def process_ocr_result(result):
"""將 OCR 結果處理為結構化資料。"""
lines = []
for line in result[0]:
box = line[0]
text = line[1][0]
confidence = line[1][1]
# 計算邊界框
x_coords = [p[0] for p in box]
y_coords = [p[1] for p in box]
lines.append({
'text': text,
'confidence': confidence,
'bbox': {
'left': min(x_coords),
'top': min(y_coords),
'right': max(x_coords),
'bottom': max(y_coords),
},
'raw_box': box
})
return lines
# 依位置排序(從上到下,從左到右)
def sort_by_position(lines):
return sorted(lines, key=lambda x: (x['bbox']['top'], x['bbox']['left']))
文字版面重建
def reconstruct_layout(result, line_threshold=10):
"""從 OCR 結果重建文字版面。"""
lines = process_ocr_result(result)
lines = sort_by_position(lines)
# 分組為邏輯行
text_lines = []
current_line = []
current_y = None
for line in lines:
y = line['bbox']['top']
if current_y is None or abs(y - current_y) < line_threshold:
current_line.append(line)
current_y = y
else:
# 新行
text_lines.append(' '.join([l['text'] for l in current_line]))
current_line = [line]
current_y = y
# 加入最後一行
if current_line:
text_lines.append(' '.join([l['text'] for l in current_line]))
return '\n'.join(text_lines)
最佳實務
- 預處理圖片:在 OCR 前提升品質
- 選擇正確語言:指定語言以獲得更高準確度
- 處理多欄位:分別處理各欄位
- 過濾低信心度:跳過低於閾值的結果
- 批次處理:有效處理多張圖片
常見模式
圖片預處理
from PIL import Image, ImageEnhance, ImageFilter
def preprocess_image(image_path):
"""預處理圖片以獲得更好的 OCR 效果。"""
img = Image.open(image_path)
# 轉為灰階
img = img.convert('L')
# 增強對比度
enhancer = ImageEnhance.Contrast(img)
img = enhancer.enhance(2.0)
# 銳化
img = img.filter(ImageFilter.SHARPEN)
# 儲存預處理結果
preprocessed_path = 'preprocessed.png'
img.save(preprocessed_path)
return preprocessed_path
批次 OCR 含進度顯示
from tqdm import tqdm
from concurrent.futures import ThreadPoolExecutor
def batch_ocr(image_paths, max_workers=4):
"""並行 OCR 多張圖片。"""
results = {}
def process_single(img_path):
result = ocr.ocr(img_path)
return img_path, result
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = [executor.submit(process_single, p) for p in image_paths]
for future in tqdm(futures, desc="處理 OCR"):
path, result = future.result()
results[path] = result
return results
範例
範例 1:名片讀取器
from paddleocr import PaddleOCR
import re
def read_business_card(image_path):
"""從名片中提取聯絡資訊。"""
ocr = PaddleOCR(use_angle_cls=True, lang='en')
result = ocr.ocr(image_path)
# 提取所有文字
all_text = []
for line in result[0]:
all_text.append(line[1][0])
full_text = '\n'.join(all_text)
# 解析聯絡資訊
contact = {
'name': None,
'email': None,
'phone': None,
'company': None,
'title': None,
'raw_text': full_text
}
# Email 模式
email_match = re.search(r'[\w\.-]+@[\w\.-]+\.\w+', full_text)
if email_match:
contact['email'] = email_match.group()
# 電話模式
phone_match = re.search(r'[\+\d][\d\s\-\(\)]{8,}', full_text)
if phone_match:
contact['phone'] = phone_match.group().strip()
# 姓名通常是最大/第一個文字
if all_text:
contact['name'] = all_text[0]
return contact
card_info = read_business_card('business_card.jpg')
print(f"姓名:{card_info['name']}")
print(f"Email:{card_info['email']}")
print(f"電話:{card_info['phone']}")
範例 2:收據掃描器
from paddleocr import PaddleOCR
import re
def scan_receipt(image_path):
"""從收據中提取品項與總額。"""
ocr = PaddleOCR(use_angle_cls=True, lang='en')
result = ocr.ocr(image_path)
lines = []
for line in result[0]:
text = line[1][0]
y_pos = line[0][0][1]
lines.append({'text': text, 'y': y_pos})
# 依垂直位置排序
lines.sort(key=lambda x: x['y'])
receipt = {
'items': [],
'subtotal': None,
'tax': None,
'total': None
}
for line in lines:
text = line['text']
# 尋找總額
if 'total' in text.lower():
amount = re.search(r'\$?([\d,]+\.?\d*)', text)
if amount:
if 'sub' in text.lower():
receipt['subtotal'] = float(amount.group(1).replace(',', ''))
else:
receipt['total'] = float(amount.group(1).replace(',', ''))
# 尋找稅金
elif 'tax' in text.lower():
amount = re.search(r'\$?([\d,]+\.?\d*)', text)
if amount:
receipt['tax'] = float(amount.group(1).replace(',', ''))
# 尋找品項(含價格的行)
else:
item_match = re.search(r'(.+?)\s+\$?([\d,]+\.?\d+)$', text)
if item_match:
receipt['items'].append({
'name': item_match.group(1).strip(),
'price': float(item_match.group(2).replace(',', ''))
})
return receipt
receipt_data = scan_receipt('receipt.jpg')
print(f"品項數:{len(receipt_data['items'])}")
print(f"總額:${receipt_data['total']}")
範例 3:多語言文件
from paddleocr import PaddleOCR
def ocr_multilingual(image_path, languages=['en', 'ch']):
"""對多語言文件進行 OCR。"""
all_results = {}
for lang in languages:
ocr = PaddleOCR(use_angle_cls=True, lang=lang)
result = ocr.ocr(image_path)
texts = []
for line in result[0]:
texts.append({
'text': line[1][0],
'confidence': line[1][1]
})
all_results[lang] = texts
# 合併結果,保留最高信心度
merged = {}
for lang, texts in all_results.items():
for item in texts:
text = item['text']
conf = item['confidence']
if text not in merged or merged[text]['confidence'] < conf:
merged[text] = {'confidence': conf, 'language': lang}
return merged
result = ocr_multilingual('bilingual_document.png')
for text, info in result.items():
print(f"[{info['language']}] {text} ({info['confidence']:.2f})")
限制
- 手寫文字準確度因情況而異
- 非常小的文字可能無法偵測
- 複雜背景會降低準確度
- 旋轉文字需要角度分類
- 建議使用 GPU 以獲得最佳效能
安裝
# CPU 版本
pip install paddlepaddle paddleocr
# GPU 版本(CUDA 11.x)
pip install paddlepaddle-gpu paddleocr
# 額外相依套件
pip install pdf2image Pillow






