OCR tiếng Việt với Claude Vision — Trích xuất text từ hình ảnh giữ nguyên dấu
Điểm nổi bật
Nhấn để đến mục tương ứng
- 1 Bước tiếp theo Claude Vision mang đến giải pháp OCR tiếng Việt vượt trội so với các công cụ truyền thống, đặc biệt trong việc giữ nguyên dấu thanh và hiểu ngữ cảnh.
- 2 Batch API: Giảm 50% chi phí khi không cần kết quả ngay lập tức.
- 3 Claude Vision giúp trích xuất các điểm chính từ hợp đồng một cách hiệu quả.
- 4 Claude Vision có thể trích xuất thông tin từ CMND và CCCD với độ chính xác cao: Hãy trích xuất thông tin từ CMND/CCCD trong hình ảnh.
- 5 Hãy trích xuất các thông tin quan trọng từ trang hợp đồng này: 1.
Tiếng Việt là ngôn ngữ có hệ thống dấu phức tạp bậc nhất thế giới. Một chữ cái như "ô" có thể mang thêm dấu thanh thành "ổ", "ỗ", "ố", "ồ", "ộ" — tổng cộng 5 biến thể chỉ từ một ký tự gốc. Hệ thống này tạo ra thách thức lớn cho bất kỳ công nghệ OCR (Optical Character Recognition) nào khi cần nhận dạng văn bản tiếng Việt từ hình ảnh. Trong bài viết này, chúng ta sẽ khám phá cách Claude Vision giải quyết bài toán OCR tiếng Việt, so sánh với các giải pháp truyền thống, và xây dựng pipeline trích xuất văn bản hoàn chỉnh.
Thách thức OCR tiếng Việt: Tại sao dấu quan trọng?
Tiếng Việt sử dụng bảng chữ cái Latin mở rộng với 12 nguyên âm đặc biệt (ă, â, ê, ô, ơ, ư, và các biến thể viết hoa) kết hợp với 5 dấu thanh (sắc, huyền, hỏi, ngã, nặng). Điều này tạo ra hơn 130 ký tự có dấu khác nhau. Sai một dấu có thể thay đổi hoàn toàn nghĩa của từ:
- ma (con ma) vs má (mẹ) vs mà (liên từ) vs mả (mộ) vs mã (ngựa) vs mạ (lúa non)
- bán (sell) vs bàn (table) vs bản (version)
- cải (rau cải) vs cãi (cãi nhau) vs cài (cài đặt)
Với các hệ thống OCR truyền thống, việc phân biệt chính xác giữa dấu hỏi và dấu ngã — hai dấu thanh có hình dạng gần giống nhau — là thách thức lớn nhất. Sai sót này không chỉ gây khó chịu mà còn có thể dẫn đến hậu quả nghiêm trọng trong các tài liệu pháp lý, hóa đơn tài chính, và giấy tờ hành chính.
So sánh Claude Vision với Tesseract và EasyOCR
Trước khi đi vào chi tiết, hãy so sánh ba giải pháp OCR phổ biến khi xử lý tiếng Việt:
Tesseract OCR
Tesseract là engine OCR mã nguồn mở phổ biến nhất, được Google phát triển. Với tiếng Việt, Tesseract yêu cầu cài đặt gói ngôn ngữ riêng (vie.traineddata) và thường gặp vấn đề sau:
- Nhầm lẫn dấu hỏi/ngã với tỷ lệ lỗi 15-25% tùy chất lượng ảnh
- Khó xử lý font chữ viết tay hoặc font trang trí
- Cần tiền xử lý ảnh (threshold, deskew) để đạt độ chính xác chấp nhận được
- Không hiểu ngữ cảnh — không thể tự sửa lỗi dựa trên ngữ nghĩa
EasyOCR
EasyOCR sử dụng deep learning và hỗ trợ tiếng Việt tốt hơn Tesseract trong nhiều trường hợp. Tuy nhiên vẫn tồn tại hạn chế:
- Tỷ lệ lỗi dấu thanh giảm xuống 8-15% nhưng vẫn chưa đủ tốt cho tài liệu quan trọng
- Tốc độ chậm hơn Tesseract đáng kể khi chạy trên CPU
- Yêu cầu GPU để đạt tốc độ xử lý chấp nhận được
- Không có khả năng hiểu bối cảnh hoặc cấu trúc tài liệu
Claude Vision
Claude Vision tiếp cận bài toán OCR từ góc nhìn khác biệt hoàn toàn. Thay vì nhận dạng từng ký tự rồi ghép lại, Claude hiểu toàn bộ ngữ cảnh của tài liệu:
- Tỷ lệ lỗi dấu thanh dưới 2% nhờ hiểu ngữ cảnh và ngữ nghĩa
- Tự động sửa lỗi dựa trên ngữ cảnh — nếu "hỏi" hay "ngã" hợp lý hơn trong câu
- Xử lý được nhiều loại font, bao gồm chữ viết tay
- Hiểu cấu trúc tài liệu: bảng, danh sách, tiêu đề, chú thích
- Có thể trích xuất và tổ chức thông tin theo format mong muốn
Xây dựng pipeline OCR tiếng Việt với Claude Vision
Pipeline cơ bản gồm ba bước: nhận ảnh đầu vào, gửi đến Claude Vision qua API, và xử lý kết quả đầu ra. Dưới đây là cách triển khai từng bước.
Bước 1: Chuẩn bị và gửi ảnh đến Claude API
Claude Vision chấp nhận ảnh ở các định dạng JPEG, PNG, GIF và WebP. Kích thước tối đa là 20MB. Bạn có thể gửi ảnh dạng base64 hoặc URL trực tiếp.
import anthropic
import base64
from pathlib import Path
client = anthropic.Anthropic()
def ocr_vietnamese(image_path: str, output_format: str = "text") -> str:
"""
Trích xuất văn bản tiếng Việt từ hình ảnh.
Args:
image_path: Đường dẫn đến file ảnh
output_format: "text" (plain text), "markdown", hoặc "json"
Returns:
Văn bản trích xuất từ ảnh
"""
image_data = Path(image_path).read_bytes()
base64_image = base64.standard_b64encode(image_data).decode("utf-8")
media_type = "image/jpeg"
if image_path.endswith(".png"):
media_type = "image/png"
elif image_path.endswith(".webp"):
media_type = "image/webp"
format_instruction = {
"text": "Trả về plain text, giữ nguyên cấu trúc đoạn văn.",
"markdown": "Trả về dạng Markdown với heading, list, table nếu có.",
"json": "Trả về JSON với các trường phù hợp với loại tài liệu."
}
message = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=4096,
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": media_type,
"data": base64_image,
},
},
{
"type": "text",
"text": f"""Hãy trích xuất toàn bộ văn bản tiếng Việt từ hình ảnh này.
Yêu cầu quan trọng:
- Giữ nguyên chính xác tất cả dấu thanh và dấu phụ tiếng Việt
- Không tự ý thay đổi hoặc sửa chữa nội dung
- Nếu không đọc rõ ký tự nào, đánh dấu bằng [?]
- {format_instruction[output_format]}"""
}
],
}
],
)
return message.content[0].text
Bước 2: Xử lý kết quả và xác thực
Sau khi nhận kết quả từ Claude, bạn nên thêm bước xác thực để đảm bảo chất lượng:
import re
def validate_vietnamese_text(text: str) -> dict:
"""
Kiểm tra chất lượng văn bản tiếng Việt trích xuất.
Returns:
Dict chứa các chỉ số chất lượng
"""
total_chars = len(text)
# Đếm ký tự có dấu tiếng Việt
vn_diacritics = re.findall(
r'[àáảãạăắằẳẵặâấầẩẫậèéẻẽẹêếềểễệìíỉĩịòóỏõọôốồổỗộơớờởỡợùúủũụưứừửữựỳýỷỹỵđ]',
text.lower()
)
diacritic_ratio = len(vn_diacritics) / total_chars if total_chars > 0 else 0
# Đếm ký tự không đọc được
uncertain_count = text.count('[?]')
# Văn bản tiếng Việt thông thường có khoảng 25-40% ký tự có dấu
quality = "cao"
if diacritic_ratio < 0.15:
quality = "thấp - có thể thiếu dấu"
elif uncertain_count > 5:
quality = "trung bình - nhiều ký tự không rõ"
return {
"total_chars": total_chars,
"diacritic_chars": len(vn_diacritics),
"diacritic_ratio": round(diacritic_ratio, 3),
"uncertain_chars": uncertain_count,
"quality": quality
}
Trích xuất dữ liệu từ các loại tài liệu phổ biến
Trích xuất hóa đơn (Invoice)
Hóa đơn VAT, hóa đơn điện tử, và các chứng từ tài chính là loại tài liệu được OCR nhiều nhất tại Việt Nam. Claude Vision có thể trích xuất dữ liệu có cấu trúc từ hóa đơn:
Hãy trích xuất thông tin từ hóa đơn trong hình ảnh này thành JSON với cấu trúc sau:
{
"so_hoa_don": "",
"ngay_lap": "",
"don_vi_ban": {
"ten": "",
"ma_so_thue": "",
"dia_chi": ""
},
"don_vi_mua": {
"ten": "",
"ma_so_thue": "",
"dia_chi": ""
},
"hang_hoa": [
{
"ten": "",
"don_vi": "",
"so_luong": 0,
"don_gia": 0,
"thanh_tien": 0
}
],
"tong_tien_truoc_thue": 0,
"thue_vat": 0,
"tong_tien_thanh_toan": 0,
"so_tien_bang_chu": ""
}
Lưu ý:
- Giữ nguyên chính xác dấu tiếng Việt trong tên, địa chỉ
- Số tiền trả về dạng số nguyên (đơn vị VND)
- Nếu trường nào không có trên hóa đơn, để giá trị null
Prompt này yêu cầu Claude trả về dữ liệu có cấu trúc, giúp bạn tích hợp trực tiếp vào hệ thống kế toán hoặc ERP mà không cần xử lý thêm. Điểm mạnh của Claude so với OCR truyền thống là khả năng hiểu ngữ cảnh — ví dụ, phân biệt được "đơn vị bán" và "đơn vị mua" dù layout hóa đơn có thể khác nhau giữa các nhà cung cấp.
Trích xuất CMND/CCCD
Xử lý giấy tờ tùy thân là nhu cầu lớn trong lĩnh vực eKYC (electronic Know Your Customer). Claude Vision có thể trích xuất thông tin từ CMND và CCCD với độ chính xác cao:
Hãy trích xuất thông tin từ CMND/CCCD trong hình ảnh.
Trả về JSON:
{
"loai_giay_to": "CMND hoặc CCCD",
"so": "",
"ho_ten": "",
"ngay_sinh": "",
"gioi_tinh": "",
"quoc_tich": "",
"que_quan": "",
"noi_thuong_tru": "",
"ngay_cap": "",
"noi_cap": "",
"co_het_han": true/false,
"ngay_het_han": ""
}
Quan trọng:
- Họ tên phải giữ nguyên chính xác dấu tiếng Việt
- Địa chỉ ghi đầy đủ như trên giấy tờ
- Nếu ảnh mờ hoặc bị che một phần, ghi rõ trường nào không đọc được
- KHÔNG bịa thông tin nếu không đọc rõ
Trích xuất hợp đồng
Hợp đồng thường dài nhiều trang, chứa nhiều thuật ngữ pháp lý và thông tin quan trọng. Claude Vision giúp trích xuất các điểm chính từ hợp đồng một cách hiệu quả. Lưu ý rằng với tài liệu nhiều trang, bạn cần gửi từng trang hoặc sử dụng PDF reader trước rồi kết hợp với Claude.
Hãy trích xuất các thông tin quan trọng từ trang hợp đồng này:
1. Các bên tham gia (tên, địa chỉ, người đại diện, chức vụ)
2. Đối tượng hợp đồng (dịch vụ/sản phẩm gì)
3. Giá trị hợp đồng và phương thức thanh toán
4. Thời hạn hợp đồng (ngày bắt đầu, ngày kết thúc)
5. Các điều khoản đặc biệt (phạt vi phạm, bảo hành, bảo mật)
6. Điều kiện chấm dứt hợp đồng
Giữ nguyên chính xác dấu tiếng Việt.
Nếu trang này không chứa thông tin nào trong các mục trên, ghi "Không có trên trang này".
Xử lý hình ảnh chất lượng thấp
Trong thực tế, nhiều ảnh chụp tài liệu có chất lượng không tốt: mờ, nghiêng, thiếu sáng, hoặc bị che khuất một phần. Claude Vision xử lý được nhiều trường hợp khó, nhưng bạn nên có chiến lược error handling phù hợp.
Phát hiện và xử lý ảnh mờ
Trước khi gửi ảnh đến Claude, bạn có thể kiểm tra độ nét bằng Laplacian variance:
import cv2
import numpy as np
def check_image_quality(image_path: str) -> dict:
"""
Kiểm tra chất lượng ảnh trước khi OCR.
"""
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Laplacian variance - chỉ số độ nét
laplacian_var = cv2.Laplacian(gray, cv2.CV_64F).var()
# Kiểm tra độ sáng
brightness = np.mean(gray)
# Kiểm tra độ tương phản
contrast = np.std(gray)
issues = []
if laplacian_var < 100:
issues.append("Ảnh mờ - kết quả OCR có thể không chính xác")
if brightness < 50:
issues.append("Ảnh quá tối - cần tăng sáng")
elif brightness > 200:
issues.append("Ảnh quá sáng - có thể mất chi tiết")
if contrast < 30:
issues.append("Độ tương phản thấp - khó phân biệt chữ và nền")
return {
"sharpness": round(laplacian_var, 2),
"brightness": round(brightness, 2),
"contrast": round(contrast, 2),
"issues": issues,
"recommendation": "Chụp lại ảnh" if len(issues) > 1 else "Có thể xử lý"
}
Chiến lược retry thông minh
Khi ảnh có chất lượng không tốt, bạn có thể yêu cầu Claude cố gắng đọc với mức độ chắc chắn khác nhau:
def ocr_with_confidence(image_path: str) -> dict:
"""
OCR với mức độ tin cậy cho từng phần văn bản.
"""
image_data = Path(image_path).read_bytes()
base64_image = base64.standard_b64encode(image_data).decode("utf-8")
message = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=4096,
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/jpeg",
"data": base64_image,
},
},
{
"type": "text",
"text": """Trích xuất văn bản tiếng Việt từ ảnh này.
Với mỗi đoạn văn bản, đánh giá mức độ tin cậy:
- CAO: đọc rõ ràng, chắc chắn đúng
- TRUNG BINH: đọc được nhưng có thể sai 1-2 ký tự
- THAP: khó đọc, nhiều ký tự không chắc chắn
Trả về JSON:
[
{"text": "nội dung", "confidence": "CAO/TRUNG BINH/THAP", "notes": "ghi chú nếu có"}
]"""
}
],
}
],
)
return message.content[0].text
Xử lý hàng loạt (Batch Processing)
Khi cần OCR số lượng lớn tài liệu, bạn nên sử dụng Anthropic Batch API để tối ưu chi phí (giảm 50%) và throughput:
import anthropic
import json
def create_batch_ocr(image_paths: list[str]) -> str:
"""
Tạo batch request để OCR nhiều ảnh cùng lúc.
"""
client = anthropic.Anthropic()
requests = []
for i, path in enumerate(image_paths):
image_data = Path(path).read_bytes()
base64_image = base64.standard_b64encode(image_data).decode("utf-8")
media_type = "image/jpeg"
if path.endswith(".png"):
media_type = "image/png"
requests.append({
"custom_id": f"ocr_{i}_{Path(path).stem}",
"params": {
"model": "claude-sonnet-4-20250514",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": media_type,
"data": base64_image,
},
},
{
"type": "text",
"text": "Trích xuất toàn bộ văn bản tiếng Việt, giữ nguyên dấu."
}
],
}
],
}
})
batch = client.messages.batches.create(requests=requests)
return batch.id
Tối ưu chi phí và hiệu suất
Claude Vision tính phí dựa trên kích thước ảnh (token). Một số mẹo giúp tối ưu chi phí:
- Resize ảnh: Claude tự động resize, nhưng gửi ảnh nhỏ hơn giúp giảm token. Với tài liệu A4, kích thước 1500x2100 pixel là đủ.
- Crop vùng cần thiết: Nếu chỉ cần OCR một phần tài liệu, crop trước khi gửi.
- Dùng Haiku cho ảnh rõ: Với ảnh chất lượng cao, chữ in rõ ràng, Claude Haiku cho kết quả tốt với chi phí thấp hơn nhiều.
- Batch API: Giảm 50% chi phí khi không cần kết quả ngay lập tức.
So sánh kết quả thực tế
Dưới đây là kết quả OCR một đoạn văn bản tiếng Việt từ hình chụp tài liệu in chất lượng trung bình. Văn bản gốc chứa nhiều dấu thanh và thuật ngữ chuyên ngành:
Văn bản gốc: "Theo Nghị định số 123/2020/NĐ-CP ngày 19 tháng 10 năm 2020 của Chính phủ quy định về hóa đơn, chứng từ, các tổ chức kinh doanh phải chuyển đổi sang hóa đơn điện tử trước ngày 01/07/2022."
- Tesseract: "Theo Nghi dinh so 123/2020/ND-CP ngay 19 thang 10 nam 2020 cua Chinh phu quy dinh ve hoa don, chung tu..." — mất gần hết dấu
- EasyOCR: "Theo Nghị định số 123/2020/NĐ-CP ngày 19 tháng 10 năm 2020 của Chính phủ quy định về hóa đơn, chưng từ..." — sai "chứng" thành "chưng"
- Claude Vision: Trích xuất chính xác 100%, bao gồm cả số hiệu văn bản và dấu thanh
Tích hợp vào quy trình thực tế
Dưới đây là một pipeline hoàn chỉnh kết hợp kiểm tra chất lượng, OCR, và xác thực kết quả:
import anthropic
import base64
import json
from pathlib import Path
class VietnameseOCRPipeline:
def __init__(self):
self.client = anthropic.Anthropic()
def process_document(self, image_path: str, doc_type: str = "general") -> dict:
"""
Pipeline xử lý tài liệu tiếng Việt hoàn chỉnh.
Args:
image_path: Đường dẫn ảnh
doc_type: "general", "invoice", "id_card", "contract"
"""
# Bước 1: Kiểm tra chất lượng ảnh
quality = check_image_quality(image_path)
if quality["recommendation"] == "Chụp lại ảnh":
return {
"success": False,
"error": "Chất lượng ảnh quá thấp",
"issues": quality["issues"]
}
# Bước 2: Chọn prompt phù hợp theo loại tài liệu
prompts = {
"general": "Trích xuất toàn bộ văn bản tiếng Việt, giữ nguyên dấu và cấu trúc.",
"invoice": self._get_invoice_prompt(),
"id_card": self._get_id_card_prompt(),
"contract": self._get_contract_prompt()
}
# Bước 3: Gửi đến Claude Vision
image_data = Path(image_path).read_bytes()
base64_image = base64.standard_b64encode(image_data).decode("utf-8")
message = self.client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=4096,
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/jpeg",
"data": base64_image,
},
},
{"type": "text", "text": prompts[doc_type]}
],
}
],
)
result = message.content[0].text
# Bước 4: Xác thực kết quả
validation = validate_vietnamese_text(result)
return {
"success": True,
"text": result,
"quality": validation,
"image_quality": quality,
"tokens_used": message.usage.input_tokens + message.usage.output_tokens
}
def _get_invoice_prompt(self):
return "Trích xuất thông tin hóa đơn thành JSON có cấu trúc..."
def _get_id_card_prompt(self):
return "Trích xuất thông tin CMND/CCCD thành JSON..."
def _get_contract_prompt(self):
return "Trích xuất các điều khoản quan trọng từ hợp đồng..."
Lưu ý quan trọng khi sử dụng
- Bảo mật dữ liệu: Khi xử lý CMND, CCCD, hoặc tài liệu nhạy cảm, hãy đảm bảo tuân thủ quy định bảo vệ dữ liệu cá nhân theo Nghị định 13/2023/NĐ-CP.
- Không dùng cho xác thực pháp lý: Kết quả OCR nên được kiểm tra lại bởi con người trước khi sử dụng trong các quy trình pháp lý.
- Giới hạn kích thước: Mỗi request chỉ nên gửi 1-2 ảnh để đảm bảo chất lượng và tránh vượt giới hạn token.
- Encoding UTF-8: Luôn đảm bảo output được lưu trữ và truyền tải với encoding UTF-8 để tránh mất dấu tiếng Việt.
Bước tiếp theo
Claude Vision mang đến giải pháp OCR tiếng Việt vượt trội so với các công cụ truyền thống, đặc biệt trong việc giữ nguyên dấu thanh và hiểu ngữ cảnh. Để tìm hiểu thêm về cách sử dụng Claude cho các tác vụ xử lý ngôn ngữ tiếng Việt khác, hãy khám phá thêm tại Thư viện Nền tảng Claude.
Bai viet co huu ich khong?
Bản quyền thuộc về tác giả. Vui lòng dẫn nguồn khi chia sẻ.







