Spaces:
Sleeping
Sleeping
| import base64 | |
| import datetime | |
| import os | |
| import re | |
| import time | |
| import uuid | |
| import cv2 | |
| from fastapi import FastAPI, File, UploadFile, HTTPException | |
| from pydantic import BaseModel | |
| from typing import Optional | |
| # Import lớp Extractor từ thư mục core | |
| from core.extractor import Extractor | |
| # --- Khởi tạo --- | |
| # Khởi tạo ứng dụng FastAPI | |
| app = FastAPI( | |
| title="CCCD Extraction API", | |
| description="Một microservice để trích xuất thông tin từ Căn cước công dân Việt Nam.", | |
| version="1.0.0" | |
| ) | |
| # Đường dẫn để lưu trữ file upload | |
| UPLOAD_DIR = "uploads" | |
| os.makedirs(UPLOAD_DIR, exist_ok=True) | |
| # Khởi tạo một lần duy nhất đối tượng Extractor để tái sử dụng | |
| # Điều này giúp load model một lần và tăng tốc độ xử lý cho các request sau | |
| try: | |
| idcard_extractor = Extractor() | |
| print("CCCD Extractor loaded successfully.") | |
| except Exception as e: | |
| print(f"Error loading CCCD Extractor: {e}") | |
| idcard_extractor = None | |
| # --- Định nghĩa Model cho Request và Response --- | |
| # Model cho request nếu gửi ảnh dạng base64 | |
| class ImageRequest(BaseModel): | |
| image_base64: str | |
| # Model cho response trả về | |
| class ExtractionResponse(BaseModel): | |
| ID_number: Optional[str] = None | |
| Name: Optional[str] = None | |
| Date_of_birth: Optional[str] = None | |
| Gender: Optional[str] = None | |
| Nationality: Optional[str] = None | |
| Place_of_origin: Optional[str] = None | |
| Place_of_residence: Optional[str] = None | |
| elapsed: float | |
| # --- Xây dựng API Endpoint --- | |
| def read_root(): | |
| return {"message": "Welcome to the CCCD Extraction API. Use the /extract/ endpoint to process an image."} | |
| async def extract_id_card_info(file: UploadFile = File(...)): | |
| """ | |
| Nhận một file ảnh CCCD, trích xuất thông tin và trả về. | |
| """ | |
| if not idcard_extractor: | |
| raise HTTPException(status_code=500, detail="OCR Extractor is not available.") | |
| # --- 1. Lưu file ảnh được upload --- | |
| # Tạo tên file ngẫu nhiên và an toàn để tránh trùng lặp | |
| file_extension = os.path.splitext(file.filename)[1] | |
| random_filename = f"{uuid.uuid4()}{file_extension}" | |
| file_path = os.path.join(UPLOAD_DIR, random_filename) | |
| try: | |
| # Đọc nội dung file và lưu lại | |
| with open(file_path, "wb") as buffer: | |
| buffer.write(await file.read()) | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=f"Could not save uploaded file: {e}") | |
| # --- 2. Xử lý ảnh và trích xuất thông tin (logic từ Django view) --- | |
| start_time = time.time() | |
| try: | |
| frame = cv2.imread(file_path) | |
| if frame is None: | |
| raise HTTPException(status_code=400, detail="Invalid image file.") | |
| # Bước 1: Dùng PaddleOCR để phát hiện các vùng văn bản | |
| annotations = idcard_extractor.Detection(frame) | |
| info = {} | |
| # Tìm số CCCD trước tiên | |
| for box in annotations: | |
| text_detected = box[1][0] | |
| if re.search(r'\d{9,12}', text_detected): | |
| # Tách số ra khỏi chuỗi nhiễu | |
| id_number = re.search(r'\d{9,12}', text_detected).group(0) | |
| info['ID_number'] = id_number | |
| info['ID_number_box'] = box[0] | |
| break | |
| if 'ID_number' not in info: | |
| raise HTTPException(status_code=400, detail="Could not detect an ID number in the image.") | |
| # Bước 2: Dùng VietOCR để nhận dạng các trường thông tin còn lại | |
| extracted_result = [] | |
| for box in annotations: | |
| # Bỏ qua vùng chứa số ID đã xử lý | |
| if re.search(r'\d{9,12}', box[1][0]): | |
| continue | |
| top_left = (int(box[0][0][0]), int(box[0][0][1])) | |
| top_right = (int(box[0][1][0]), int(box[0][1][1])) | |
| bottom_right = (int(box[0][2][0]), int(box[0][2][1])) | |
| bottom_left = (int(box[0][3][0]), int(box[0][3][1])) | |
| # Warp và nhận dạng | |
| result_text, _ = idcard_extractor.WarpAndRec(frame, top_left, top_right, bottom_right, bottom_left) | |
| extracted_result.append((result_text, box[0])) # Lưu cả text và bounding box | |
| # Bước 3: Tổng hợp thông tin | |
| final_info = idcard_extractor.GetInformationAndSave(extracted_result, info['ID_number'], info['ID_number_box']) | |
| elapsed = time.time() - start_time | |
| final_info["elapsed"] = round(elapsed, 2) | |
| # Xóa file tạm sau khi xử lý xong | |
| os.remove(file_path) | |
| return final_info | |
| except Exception as e: | |
| # Nếu có lỗi, cũng xóa file tạm | |
| if os.path.exists(file_path): | |
| os.remove(file_path) | |
| raise HTTPException(status_code=500, detail=f"An error occurred during processing: {str(e)}") |