File size: 6,767 Bytes
6498fe6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
import cv2
import time
import re
from PIL import Image
from DetecInfoBoxes.GetBoxes import Detect
from util import correct_skew
from config import opt

get_dictionary = Detect(opt)

class ReadInfo:
    def __init__(self, imgsz, stride, device, half, model, names, ocr_predictor):
        self.imgsz = imgsz
        self.stride = stride
        self.device = device
        self.half = half
        self.model = model
        self.names = names
        self.opt = opt
        self.ocrPredictor = ocr_predictor

    @staticmethod
    def get_the_most_confident_bbox(page_boxes: dict):
        for key in page_boxes:
            value = page_boxes.get(key)
            if value:
                value = sorted(value, key=lambda item: item[4])
                page_boxes.update({key: [value[-1]]})
        return page_boxes

    @staticmethod
    def arrange_info(infos: list):
        sorted_infos = sorted(infos, key=lambda item: item[1])
        return sorted_infos

    def ocr_info(self, img, info: list):
        x_min = info[0] - int(int(info[2]) / 2)
        y_min = info[1] - int(int(info[3]) / 2)
        w = info[2]
        h = info[3]

        crop_img = img[max(0, y_min):y_min + h, max(0, x_min):x_min + w]
        crop_img_rgb = cv2.cvtColor(crop_img, cv2.COLOR_BGR2RGB)
        image_pill = Image.fromarray(crop_img_rgb)
        text = self.ocrPredictor.predict(image_pill)
        return text

    def get_all_info(self, img_path):
        """
        Đọc MẶT TRƯỚC CCCD sử dụng YOLO cắt ô + VietOCR đọc chữ.
        """
        st = time.time()
        img = cv2.imread(img_path)
        
        if img is None:
            return {}

        img = correct_skew(img)

        page_boxes = get_dictionary.prediction(img, self.imgsz, self.stride, self.device, self.half, self.model, self.names)
        page_boxes = get_dictionary.dict_processing(page_boxes)
        
        print("\n--- [YOLO - MẶT TRƯỚC] TỌA ĐỘ CÁC Ô TÌM THẤY ---")
        for key, val in page_boxes.items():
            if val:
                print(f" > {key}: {len(val)} ô")

        fields = ["id", "full_name", "date_of_birth", "sex", "nationality", "place_of_origin", "place_of_residence", "date_of_expiry"]

        user_info_dict = {}
        
        print("\n--- [VietOCR - MẶT TRƯỚC] KẾT QUẢ DỊCH CHỮ ---")
        
        for field in fields:
            infos = page_boxes.get(field)
            if infos:
                if len(infos) != 1:
                    infos = self.arrange_info(infos)
                    all_text = ''
                    for info in infos:
                        text = self.ocr_info(img, info)
                        all_text += text + ' '
                else:
                    all_text = self.ocr_info(img, infos[0])
                
                extracted_str = all_text.strip()
                user_info_dict.update({field: extracted_str})
                print(f" [*] {field.upper():<20}: {extracted_str}")
            else:
                user_info_dict.update({field: ''})
                print(f" [!] {field.upper():<20}: (Không tìm thấy ô)")

        print("----------------------------------\n")
        print(f'[ReadInfo] Total Process Time: {time.time() - st:.2f}s')
        return user_info_dict


    # ══════════════════════════════════════════════════════════════════
    # MẶT SAU: KHÔNG DÙNG YOLO (VÌ CHƯA CÓ MODEL), DÙNG VIETOCR QUÉT FULL
    # ══════════════════════════════════════════════════════════════════
    def get_back_info(self, img_path):
        """
        Đọc MẶT SAU CCCD: Đẩy toàn bộ ảnh vào VietOCR, sau đó dùng Regex
        và từ khóa để lọc ra thông tin thay vì dùng YOLO cắt ô.
        """
        st = time.time()
        img = cv2.imread(img_path)

        if img is None:
            return {}

        # Xoay ảnh cho thẳng
        img = correct_skew(img)
        
        print("\n--- [VietOCR - MẶT SAU (Quét toàn ảnh)] KẾT QUẢ DỊCH CHỮ ---")
        
        # Chuyển thẳng toàn bộ ảnh sang RGB và đẩy vào VietOCR
        img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        full_text = self.ocrPredictor.predict(Image.fromarray(img_rgb))
        full_text_upper = full_text.upper()
        
        result = {
            "issue_date": "",
            "issued_by": "",
            "special_features": ""
        }

        # 1. Tìm Ngày Cấp (Issue Date) bằng Regex
        # Tìm chuỗi có dạng dd/mm/yyyy, có thể bị lỗi khoảng trắng hoặc dấu gạch nối
        date_match = re.search(r'(\d{2}[\s/\-\.]+\d{2}[\s/\-\.]+\d{4})', full_text)
        if date_match:
            result["issue_date"] = re.sub(r'[\s\-\.]+', '/', date_match.group(1))

        # 2. Tìm Nơi Cấp (Issued By) bằng Từ khóa
        # Lấy từ các chức danh người ký trở về sau
        keywords = ["CỤC TRƯỞNG", "GIÁM ĐỐC", "NƠI CẤP"]
        for kw in keywords:
            idx = full_text_upper.find(kw)
            if idx != -1:
                # Cắt chuỗi từ khóa đó trở về cuối
                result["issued_by"] = full_text[idx:].strip()
                break

        # 3. Tìm Đặc điểm nhận dạng (Special Features)
        # Nằm giữa cụm từ "nhận dạng" và cụm "Ngày, tháng, năm" hoặc con dấu
        feat_match = re.search(r'(ĐẶC ĐIỂM NHẬN DẠNG|NHẬN DẠNG)[:\s]*(.*?)(?=NGÀY|CỤC|GIÁM|DẤU|$)', full_text_upper, re.DOTALL)
        if feat_match:
            # Dùng vị trí match ở text thường để giữ nguyên chữ hoa/chữ thường
            start_idx = feat_match.start(2)
            end_idx = feat_match.end(2)
            extracted_feat = full_text[start_idx:end_idx].strip().replace('\n', ' ')
            # Lọc bỏ các ký tự rác nếu có
            result["special_features"] = extracted_feat

        # Log kết quả ra terminal
        print(f" [*] ISSUE_DATE      : {result['issue_date'] if result['issue_date'] else '(Không tìm thấy)'}")
        print(f" [*] ISSUED_BY       : {result['issued_by'] if result['issued_by'] else '(Không tìm thấy)'}")
        print(f" [*] SPECIAL_FEATURES: {result['special_features'] if result['special_features'] else '(Không tìm thấy)'}")
        print("----------------------------------\n")
        print(f'[ReadInfo - Back] Total Process Time: {time.time() - st:.2f}s')
        
        return result