Giới thiệu dự án

Trong kỷ nguyên số hóa y tế và chăm sóc sức khỏe thông minh (Smart Healthcare), việc quản lý và số hóa hồ sơ bệnh án, đặc biệt là đơn thuốc, đóng vai trò then chốt nhằm giảm thiểu rủi ro y khoa. Theo thống kê từ Tổ chức Y tế Thế giới (WHO) và các báo cáo y tế cộng đồng, sai sót trong việc đọc và sử dụng sai đơn thuốc chiếm tới 15-20% các sự cố y khoa ngoại trú, chủ yếu xuất phát từ chữ viết tay bác sĩ khó đọc, tên biệt dược phức tạp hoặc người bệnh thiếu kiến thức chuyên môn.

Tại Việt Nam, hàng triệu đơn thuốc được phát hành mỗi ngày dưới dạng giấy in hoặc viết tay tại các bệnh viện, phòng khám. Người bệnh thường gặp khó khăn trong việc tra cứu công dụng, liều dùng, thành phần hoạt chất và tái đặt thuốc. Các giải pháp hiện nay đòi hỏi nhập liệu thủ công tốn thời gian (trung bình 3-5 phút/đơn) và dễ sai sót chính tả đối với các tên thuốc biệt dược như Clarithromycin, Betamethasone, Loxoprofen.

[Ảnh chụp đơn thuốc thô] 
         │
         ▼
[Giai đoạn 1: YOLOv8-seg] ──► [Cắt & nắn chỉnh vùng đơn thuốc]
         │
         ▼
[Giai đoạn 2: YOLOv8 Text Detection] ──► [225k+ Bounding Boxes xác định từ]
         │
         ▼
[Giai đoạn 3: Text Recognition & Sắp xếp tọa độ] ──► [Chuỗi văn bản theo dòng]
         │
         ▼
[Giai đoạn 4: Đối soát CSDL 28.000+ Tên thuốc] ──► [Bộ lọc thực thể y tế]
         │
         ▼
[FastAPI RESTful Response / Lưu trữ MongoDB]

1. Vấn đề nghiên cứu và Pain Points

  • Cấu trúc đơn thuốc không đồng nhất: Mỗi cơ sở y tế áp dụng một biểu mẫu khác nhau (bệnh viện công lập, phòng khám tư nhân, nhà thuốc bán lẻ).
  • Điều kiện chụp ảnh thực tế phức tạp: Ảnh chụp từ camera điện thoại thường bị nghiêng, xoay góc, nhăn giấy, thiếu sáng hoặc dính background phức tạp (mặt bàn, ngón tay cầm).
  • Hạn chế của OCR truyền thống: Các công cụ OCR mã nguồn mở như Tesseract thường thất bại hoặc nhận diện sai nghiêm trọng khi gặp phông chữ in kim, độ phân giải thấp hoặc từ vựng chuyên ngành y khoa.

2. Mục tiêu cụ thể của đề tài

  1. Xây dựng module tiền xử lý & phân vùng (Image Segmentation): Tự động phát hiện và cắt chính xác vùng chứa đơn thuốc từ ảnh chụp thực tế bằng mô hình học sâu.
  2. Phát triển pipeline nhận diện ký tự quang học (OCR): Tách biệt hai giai đoạn phát hiện vùng chữ (Text Detection) và nhận dạng ký tự (Text Recognition) đạt độ chính xác cao.
  3. Xây dựng cơ sở dữ liệu đối soát chuẩn hóa: Khai thác và chuẩn hóa hơn 28.000 danh mục tên thuốc được cấp phép bởi Bộ Y tế và DrugBank Vietnam.
  4. Đóng gói hệ thống Backend API & Database: Triển khai kiến trúc Client-Server hiệu năng cao với FastAPI và cơ sở dữ liệu NoSQL MongoDB, sẵn sàng tích hợp vào ứng dụng di động.

3. Phương pháp tiếp cận và Phạm vi

Đề tài tiếp cận bằng mô hình thị giác máy tính dựa trên kiến trúc YOLOv8 (You Only Look Once phiên bản 8) kết hợp thuật toán xử lý hình học không gian và đối soát cơ sở dữ liệu từ điển. Phạm vi nghiên cứu bao gồm các đơn thuốc điện tử in sẵn và đơn thuốc chụp tay thu thập từ các cơ sở y tế tại Việt Nam.


Phân tích và thiết kế giải pháp

Phân tích hiện trạng

Tiêu chí Tesseract OCR EAST + CRNN PaddleOCR VAIPE (VinUni) Giải pháp đề xuất (YOLOv8 + CSDL)
Độ chính xác từ y tế Thấp (~52%) Trung bình (~71%) Khá (~82%) Cao (chỉ nhận diện viên thuốc) Rất cao (>91.5%)
Xử lý ảnh nhiễu/nghiêng Rất kém Trung bình Tốt Tốt Rất tốt (nhờ YOLOv8-seg)
Tốc độ xử lý (GPU/ảnh) ~1200ms ~450ms ~320ms ~280ms < 180ms (NVIDIA P40)
Khả năng mở rộng API Thấp Phức tạp Trung bình Khép kín Rất cao (FastAPI + Async)
Lọc tên thuốc tự động Không Không Không Phân loại dạng thuốc Tự động đối soát 28k thuốc

Yêu cầu người dùng theo mô hình MoSCoW

  • Must have: Nhận diện và trích xuất đúng tên thuốc; Cắt biên đơn thuốc tự động; Cung cấp RESTful API CRUD đơn thuốc; Lưu trữ lịch sử trên MongoDB.
  • Should have: Hỗ trợ đơn thuốc chụp nghiêng/xoay góc; Tìm kiếm thuốc theo cả tên có dấu và không dấu; Thời gian phản hồi < 2 giây.
  • Could have: Nhận diện liều lượng (mg/ml) và số lượng viên; Gợi ý thuốc tương đương khi không tìm thấy chính xác.
  • Won't have (giai đoạn này): Nhận diện đơn thuốc viết tay nghệch ngoạc hoàn toàn (Cursive handwriting level 5); Tự động kê đơn thay bác sĩ.

Thiết kế hệ thống

graph TD
    Client[Client: Mobile App / Web App] -->|POST multipart/form-data Image| API[FastAPI Gateway v0.109]
    API --> PreProc[Module Tiền xử lý & Phân vùng: YOLOv8n-seg]
    PreProc -->|Ảnh đơn thuốc đã nắn phẳng| TextDet[Module Text Detection: YOLOv8m]
    TextDet -->|Bounding Boxes Tọa độ từ| Sorter[Thuật toán Sắp xếp Không gian dòng/cột]
    Sorter --> TextRec[Module Text Recognition: YOLOv8 OCR]
    TextRec -->|Chuỗi Text thô| Matcher[Engine Đối soát & So khớp Danh mục 28k Tên Thuốc]
    Matcher --> DB[(MongoDB v7.0 Database)]
    DB --> API
    API -->|JSON Response| Client

Technology Stack và Phiên bản

- Ngôn ngữ lập trình: Python 3.10.12
- Framework Deep Learning: PyTorch 2.1.2 + CUDA 11.8 / 12.1
- Vision Framework: Ultralytics YOLOv8 (v8.1.0)
- Web Server Framework: FastAPI 0.109.0, Uvicorn 0.27.0 (ASGI, uvloop)
- Data Validation: Pydantic v2.6.0
- Cơ sở dữ liệu: MongoDB 7.0.5 NoSQL (Motor Async Driver / PyMongo 4.6)
- Xử lý ảnh: OpenCV 4.9.0, Albumentations 1.3.1, NumPy 1.26.3
- Phần cứng huấn luyện & Triển khai: GPU NVIDIA Tesla P40 (24GB VRAM, 24 Cores CPU, 48GB RAM)

Thiết kế Database Schema (MongoDB Collection)

{
  "_id": "ObjectId('65d8a9f1b2c3d4e5f6a7b8c9')",
  "user_id": "usr_998124",
  "image_url": "https://storage.domain.vn/prescriptions/img_20240715_01.jpg",
  "processed_at": "2024-07-15T08:30:00Z",
  "detected_medicines": [
    {
      "raw_text": "Loxoprofen (Mezafen) 60mg",
      "matched_drug_name": "Loxoprofen",
      "brand_name": "Mezafen",
      "dosage": "60mg",
      "quantity": 30,
      "unit": "Viên",
      "confidence_score": 0.948
    },
    {
      "raw_text": "Hoat huyet duong nao 150mg",
      "matched_drug_name": "Đinh lăng, bạch quả",
      "brand_name": "Hoạt huyết dưỡng não",
      "dosage": "150mg+20mg",
      "quantity": 60,
      "unit": "Viên",
      "confidence_score": 0.912
    }
  ],
  "status": "SUCCESS"
}

Thiết kế API Endpoints

POST   /api/v1/prescriptions/extract   # Upload ảnh đơn thuốc, thực thi pipeline AI và trả về danh sách thuốc
GET    /api/v1/prescriptions/{id}       # Lấy thông tin chi tiết đơn thuốc đã trích xuất
PUT    /api/v1/prescriptions/{id}       # Chỉnh sửa thông tin thuốc sau khi người dùng xác nhận
DELETE /api/v1/prescriptions/{id}       # Xóa đơn thuốc khỏi hồ sơ cá nhân
GET    /api/v1/drugs/search?keyword=    # Tra cứu danh mục 28.000+ thuốc chuẩn Bộ Y tế

Implementation và kết quả

Quy trình phát triển (Development Process)

Dự án triển khai theo mô hình Agile/Scrum qua 4 Sprint chính:

  • Sprint 1 (Thu thập & Chuẩn bị dữ liệu): Thu thập 1.400 ảnh hóa đơn/đơn thuốc cho tác vụ phân vùng; Xây dựng bộ dữ liệu 2.481 ảnh toa thuốc (1.353 ảnh chụp tay, 1.128 ảnh điện tử). Gán nhãn 225.524 bounding boxes bằng Roboflow theo format YOLO.
  • Sprint 2 (Huấn luyện Model Seg & Detection): Huấn luyện mô hình phân vùng yolov8n-seg và mô hình phát hiện vùng chữ yolov8m trên Google Colab Pro và hệ thống GPU chuyên dụng.
  • Sprint 3 (Nhận dạng Text & Chuẩn hóa CSDL): Xây dựng module Text Recognition; Phát triển engine đối soát chuỗi ký tự với 28.000+ tên biệt dược/hoạt chất (hỗ trợ cả tiếng Việt có dấu và không dấu).
  • Sprint 4 (Backend Server & Tích hợp hệ thống): Xây dựng RESTful API với FastAPI, kết nối MongoDB, Dockerize ứng dụng và benchmark hiệu năng trên GPU Tesla P40.
from fastapi import FastAPI, UploadFile, File, HTTPException
from ultralytics import YOLO
import cv2
import numpy as np
from typing import List
import motor.motor_asyncio

app = FastAPI(title="Prescription Information Extractor API", version="1.0.0")

# Khởi tạo mô hình YOLOv8
seg_model = YOLO("weights/yolov8n-seg-prescription.pt")
det_model = YOLO("weights/yolov8m-text-det.pt")

@app.post("/api/v1/prescriptions/extract")
async def extract_prescription_endpoint(file: UploadFile = File(...)):
    # 1. Đọc ảnh từ stream
    contents = await file.read()
    nparr = np.frombuffer(contents, np.uint8)
    image = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
    
    if image is None:
        raise HTTPException(status_code=400, detail="Invalid image file format.")

    # 2. Bước 1: Segmentation - Phân vùng đơn thuốc
    seg_results = seg_model.predict(source=image, conf=0.5, imgsz=640)
    cropped_prescription = crop_and_warp_polygon(image, seg_results[0])

    # 3. Bước 2: Text Detection - Nhận diện vùng chữ
    det_results = det_model.predict(source=cropped_prescription, conf=0.4, imgsz=640)
    
    # 4. Bước 3: Sắp xếp tọa độ & Text Recognition
    sorted_boxes = sort_reading_order(det_results[0].boxes.xyxy.cpu().numpy())
    extracted_text_lines = recognize_text_from_boxes(cropped_prescription, sorted_boxes)

    # 5. Bước 4: Đối soát CSDL 28.000 tên thuốc
    matched_medicines = match_with_drug_database(extracted_text_lines)

    return {
        "status": "success",
        "total_drugs_found": len(matched_medicines),
        "data": matched_medicines
    }
def sort_reading_order(boxes: np.ndarray, y_threshold: float = 12.0) -> List[np.ndarray]:
    """
    Thuật toán sắp xếp thứ tự đọc văn bản: Từ trên xuống dưới, từ trái qua phải.
    Xử lý trường hợp chữ trên đơn thuốc bị lệch góc hoặc nghiêng nhẹ.
    """
    # boxes format: [x1, y1, x2, y2]
    sorted_by_y = sorted(boxes, key=lambda b: b[1])
    lines = []
    current_line = []

    for box in sorted_by_y:
        if not current_line:
            current_line.append(box)
        else:
            # Kiểm tra độ lệch theo trục Y so với phần tử đầu dòng
            if abs(box[1] - current_line[0][1]) < y_threshold:
                current_line.append(box)
            else:
                lines.append(sorted(current_line, key=lambda b: b[0]))
                current_line = [box]
    if current_line:
        lines.append(sorted(current_line, key=lambda b: b[0]))
        
    return [box for line in lines for box in line]

Kiểm thử và Đánh giá thực nghiệm

Mô hình được kiểm thử trên tập dữ liệu độc lập gồm 100 ảnh đơn thuốc thực tế (50 ảnh chụp điện thoại cầm tay, 50 ảnh đơn thuốc in điện tử).

Thống kê hiệu năng mô hình AI:
- YOLOv8n-seg (Vùng đơn thuốc): mAP@50 đạt 98.4%, mAP@50-95 đạt 88.2%
- YOLOv8m (Text Detection):    mAP@50 đạt 94.6%, mAP@50-95 đạt 76.8%
- Text Recognition Accuracy:    Ký tự in máy: 96.2% | Ký tự viết tay rõ nét: 84.5%
- Đối soát từ điển tên thuốc:  Độ chính xác (Precision): 93.8%, Độ bao phủ (Recall): 91.2%
Thống kê tải và độ trễ hệ thống (Server Benchmark trên Tesla P40 GPU):
- Tiền xử lý & YOLOv8-seg inference:      32 ms
- YOLOv8m Text Detection inference:      78 ms
- Text Recognition & Sorting:            45 ms
- Database Query & String Match (28k):   18 ms
- Tổng thời gian đáp ứng API (Latency):  173 ms / request
- Throughput đồng thời:                  55 requests/giây (với 4 Uvicorn workers)

Đổi mới và đóng góp

  1. Pipeline 2 tầng thích ứng cao: Thay vì áp dụng OCR trực tiếp lên toàn bộ ảnh gốc chứa nhiều tạp nhiễu, hệ thống kết hợp mô hình YOLOv8n-seg để crop chính xác biên dạng hóa đơn/đơn thuốc, giúp tăng 24% độ chính xác cho tầng OCR tiếp theo.
  2. Thuật toán sắp xếp hộp giới hạn thông minh: Khắc phục triệt để hiện tượng xáo trộn thứ tự từ ngữ khi ảnh chụp bị xoay hoặc góc nghiêng, đảm bảo ngữ cảnh tên thuốc đi kèm hàm lượng và đơn vị tính (60mg, 01 Lọ, 30 Viên).
  3. Engine lọc thực thể y tế dựa trên từ điển chuyên dụng: Tự động loại bỏ các thông tin rác (tiêu đề bệnh viện, tên bác sĩ, lời dặn) mà không cần xây dựng mô hình NLP Named Entity Recognition (NER) phức tạp, giảm tải tài nguyên tính toán gấp 3 lần.
Mức độ cải thiện so với giải pháp truyền thống:
- Độ chính xác trích xuất tên thuốc: Tăng 38.5% so với Tesseract OCR độc lập.
- Thời gian xử lý: Giảm 65% so với pipeline kết hợp EAST + Tesseract.
- Tỷ lệ nhận diện đúng tên thuốc hiếm/biệt dược: Đạt 93.8% nhờ bộ từ điển 28.000 mục.

Ứng dụng thực tế và triển khai

Kịch bản ứng dụng (Use Cases)

  • Ứng dụng Nhà thuốc thông minh & E-Commerce: Khách hàng chỉ cần chụp đơn thuốc tải lên app di động, hệ thống tự động nhận diện danh sách thuốc, kiểm tra tồn kho và thêm vào giỏ hàng chỉ trong 3 giây.
  • Sổ tay y tế điện tử cá nhân: Tự động trích xuất tên thuốc, lập lịch nhắc nhở uống thuốc thông minh theo liều lượng ghi trên đơn.
  • Hỗ trợ Dược sĩ & Nhân viên y tế: Giảm áp lực nhập liệu đơn thuốc vào hệ thống quản lý bệnh viện (HIS/LIS).
graph LR
    subgraph Client Tier
        A[Mobile Application iOS/Android]
    end
    subgraph Gateway Tier
        B[Nginx Reverse Proxy / SSL Termination]
    end
    subgraph Compute Tier
        C[FastAPI Worker 1]
        D[FastAPI Worker 2]
        E[NVIDIA Tesla P40 GPU Engine]
    end
    subgraph Data Tier
        F[(MongoDB Cluster - Drug & History DB)]
    end
    A --> B
    B --> C
    B --> D
    C --> E
    D --> E
    C --> F
    D --> F

Đánh giá Chi phí - Hiệu quả (Cost-Benefit Analysis)

  • Chi phí hạ tầng: Thuê máy chủ GPU Tesla P40 (24GB VRAM) với chi phí ước tính 8.000 VNĐ/giờ (~5.760.000 VNĐ/tháng khi vận hành full-time) hoặc triển khai serverless GPU (chỉ trả phí theo lượt gọi, khoảng 15-20 VNĐ/lần trích xuất).
  • Lợi ích kinh tế: Tiết kiệm 80% nhân lực nhập liệu tại các chuỗi nhà thuốc lớn; Tăng tốc độ phục vụ khách hàng từ 4 phút xuống còn dưới 30 giây mỗi đơn hàng.

Hạn chế và hướng phát triển

Hạn chế kỹ thuật

  • Chưa hỗ trợ xử lý triệt để các đơn thuốc có chữ viết tay bác sĩ quá ẩu (bác sĩ viết dạng tốc ký/nét móc không rõ chữ cái).
  • Chất lượng trích xuất suy giảm nếu ảnh chụp bị lóa đèn flash trực tiếp tại vị trí tên thuốc hoặc giấy bị rách/mất góc nghiêm trọng.

Hướng phát triển

  • Tích hợp Vision-Language Models (VLM): Ứng dụng các mô hình đa phương thức nhẹ (như Donut, LayoutLMv3 hoặc Gemini Nano) để hiểu sâu ngữ nghĩa bảng biểu và trích xuất quan hệ phức tạp: Tên thuốc -> Hàm lượng -> Cách dùng -> Số lần/ngày.
  • Mở rộng nhận diện viên thuốc: Kết hợp công nghệ phân loại hình ảnh viên thuốc thực tế để đối chiếu chéo giữa đơn thuốc giấy và thuốc bệnh nhân đã mua.

Đối tượng hưởng lợi

  • Sinh viên & Nghiên cứu sinh: Bộ tài liệu tham khảo hoàn chỉnh về quy trình xây dựng dataset tùy chỉnh (225k+ labels), huấn luyện YOLOv8-seg và tối ưu hóa inference thời gian thực trên GPU.
  • Lập trình viên (Developers): Cung cấp kiến trúc mẫu chuẩn kết hợp FastAPI bất đồng bộ, Pydantic và MongoDB cho các bài toán Computer Vision thực tế.
  • Doanh nghiệp & Chuỗi nhà thuốc: Giải pháp số hóa đơn thuốc tự động có thể tích hợp tức thì qua RESTful API, tiết kiệm hàng trăm giờ lao động thủ công mỗi tháng.
  • Người dùng & Bệnh nhân: Công cụ hỗ trợ tra cứu thông tin y khoa chính xác, chủ động hiểu rõ đơn thuốc và tuân thủ phác đồ điều trị.

Câu hỏi thường gặp

1. Cấu hình phần cứng tối thiểu để triển khai hệ thống là gì?

Hệ thống yêu cầu máy chủ Linux (Ubuntu 20.04/22.04 LTS), tối thiểu 4 Cores CPU, 8GB RAM. Để đạt độ trễ thời gian thực (<200ms/ảnh), khuyến nghị trang bị GPU NVIDIA có từ 8GB VRAM trở lên (hỗ trợ CUDA Core như GTX 1660Ti, RTX 3060 hoặc Tesla T4/P40). Trên môi trường thuần CPU, thời gian xử lý khoảng 1.2 - 1.8 giây/đơn thuốc.

2. Hệ thống xử lý đơn thuốc viết tay như thế nào?

Đối với đơn thuốc viết tay rõ nét theo từng ký tự, mô hình Text Detection và Recognition vẫn đạt độ chính xác trên 84%. Sau đó, thuật toán Levenshtein Distance sẽ tính toán khoảng cách chỉnh sửa giữa từ nhận dạng và 28.000 tên thuốc trong cơ sở dữ liệu để tự động sửa lỗi chính tả nhỏ do nhận dạng ký tự viết tay gây ra.

3. Làm thế nào để tích hợp API này vào ứng dụng Flutter/React Native có sẵn?

Lập trình viên chỉ cần gọi phương thức POST /api/v1/prescriptions/extract dưới dạng multipart/form-data chứa file ảnh vừa chụp. Phản hồi JSON trả về trực tiếp mảng danh sách tên thuốc, hàm lượng, số lượng và độ tin cậy để render lên giao diện ứng dụng di động.

4. Dữ liệu hình ảnh người dùng có được bảo mật không?

Hệ thống hỗ trợ cấu hình mã hóa đường truyền HTTPS/TLS 1.3, cơ chế xác thực JWT (JSON Web Token) cho mỗi request và hỗ trợ IP Whitelisting trên MongoDB. Ảnh sau khi xử lý trích xuất có thể cấu hình tự động xóa khỏi bộ nhớ tạm sau 24 giờ để đảm bảo quyền riêng tư y tế theo tiêu chuẩn HIPAA/GDPR.

5. Cơ sở dữ liệu 28.000 tên thuốc được cập nhật như thế nào?

Hệ sinh thái đi kèm script tự động đồng bộ định kỳ hàng tháng từ nguồn mở DrugBank Vietnam và Cục Quản lý Dược (Bộ Y tế). Quản trị viên cũng có thể bổ sung các loại biệt dược mới trực tiếp thông qua hệ thống API quản trị nội bộ.


Kết luận

Đồ án tốt nghiệp "Ứng dụng kỹ thuật máy học để trích xuất thông tin toa thuốc từ ảnh chụp" của nhóm tác giả sinh viên Nguyễn Quốc Vương và Võ Quốc Việt (Khoa Kỹ thuật Máy tính, Trường Đại học Công nghệ Thông tin - ĐHQG TPHCM), dưới sự hướng dẫn của TS. Đoàn Duy, đã giải quyết trọn vẹn và hiệu quả bài toán số hóa đơn thuốc y tế tại Việt Nam.

Bằng việc kết hợp sáng tạo giữa kiến trúc thị giác máy tính tiên tiến YOLOv8-seg, mô hình phát hiện chữ YOLOv8m, backend hiệu năng cao FastAPI và kho dữ liệu đối soát 28.000+ tên thuốc, hệ thống đã đạt độ chính xác trích xuất thực tế vượt trội (>91.5%) cùng thời gian xử lý ấn tượng dưới 200ms. Đây là tiền đề công nghệ vững chắc, mở ra tiềm năng ứng dụng to lớn trong việc nâng cao chất lượng dịch vụ y tế số, hỗ trợ cộng đồng tiếp cận thông tin y khoa an toàn, minh bạch và chủ động.