DonJadeRoy commited on
Commit
082e871
·
1 Parent(s): 3e0b398

updatevgfdhf

Browse files
controllers/main.py CHANGED
@@ -805,7 +805,7 @@ class CCCDBackSimpleExtractor:
805
 
806
  @staticmethod
807
  def _parse_mrz(lines):
808
- """Parse 3 dòng MRZ thành dict (giữ logic )"""
809
  result = {
810
  "mrz_raw": "", "mrz_doc_type": "", "mrz_country": "",
811
  "mrz_id": "", "mrz_dob": "", "mrz_gender": "",
@@ -814,17 +814,44 @@ class CCCDBackSimpleExtractor:
814
  if len(lines) < 3:
815
  return result
816
 
817
- def _clean_mrz_line(raw):
818
- cleaned = raw.upper()
819
- result["mrz_dob"] = f"{dd}/{mm}/{cc}{yy}"
820
-
821
- result["mrz_gender"] = {"M": "Nam", "F": "Nữ", "<": "Không xác định"}.get(gender_raw, gender_raw)
822
-
823
- if re.fullmatch(r'\d{6}', exp_raw):
824
- yy, mm, dd = exp_raw[0:2], exp_raw[2:4], exp_raw[4:6]
825
- cc = "19" if int(yy) >= 30 else "20"
826
- result["mrz_expiry"] = f"{dd}/{mm}/{cc}{yy}"
827
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
828
  name_field = l3.strip("<")
829
  if "<<" in name_field:
830
  parts = name_field.split("<<", 1)
@@ -837,6 +864,7 @@ class CCCDBackSimpleExtractor:
837
  return result
838
 
839
 
 
840
  # ═══════════════════════════════════════════════════════════════════════════
841
  # KHỞI TẠO AI CHẠY NGẦM
842
  # ═══════════════════════════════════════════════════════════════════════════
@@ -1100,7 +1128,7 @@ def _load_embeddings_to_ram():
1100
  conn = get_db_connection()
1101
  cursor = conn.cursor(dictionary=True)
1102
  cursor.execute("""
1103
- SELECT e.person_id, p.name, p.role, p.img_path,
1104
  p.work_expiry_date, e.embedding_vector
1105
  FROM face_embeddings e
1106
  JOIN persons p ON e.person_id = p.id
@@ -1114,7 +1142,7 @@ def _load_embeddings_to_ram():
1114
  "person_id": row["person_id"],
1115
  "name": row["name"],
1116
  "role": row.get("role", ""),
1117
- "img_path": row.get("img_path", ""),
1118
  "work_expiry_date": str(row["work_expiry_date"]) if row.get("work_expiry_date") else None,
1119
  "embedding_vector": json.loads(row["embedding_vector"]),
1120
  })
@@ -1199,12 +1227,8 @@ async def extract_ocr_local(file: UploadFile = File(...), side: str = Form(...))
1199
  }
1200
 
1201
  else:
1202
- if back_simple_extractor is not None:
1203
- raw = back_simple_extractor.extract(temp_path)
1204
- elif read_back is not None:
1205
- raw = read_back.get_back_info(temp_path)
1206
- else:
1207
- raw = read_info.get_back_info(temp_path)
1208
 
1209
  logger.info(f"[OCR] Mặt sau raw: {raw}")
1210
  mapped_data = {
@@ -1348,28 +1372,28 @@ async def register(
1348
  descriptor = detections[0]["descriptor"]
1349
  emb_id = str(uuid.uuid4())
1350
 
1351
- if i == 0:
1352
- user_descriptor = descriptor
1353
-
1354
- saved_path = face_ai_service.save_image(img_bytes, person_id, index=i)
1355
- saved_files.append(saved_path)
1356
 
1357
  if i == 0:
1358
- avatar_path = saved_path
 
 
 
1359
  cursor.execute(
1360
  """INSERT INTO persons
1361
- (id, name, role, department, status, img_path, work_expiry_date)
1362
- VALUES (%s, %s, %s, %s, 'active', %s, %s)""",
1363
- (person_id, name, role, department, avatar_path, expiry_val),
1364
  )
1365
 
1366
  cursor.execute(
1367
- "INSERT INTO face_embeddings (id, person_id, embedding_vector) VALUES (%s, %s, %s)",
1368
- (emb_id, person_id, json.dumps(descriptor)),
1369
  )
1370
- new_encodings.append((person_id, name, role, avatar_path, expiry_val, descriptor))
1371
 
1372
  front_path, back_path = "", ""
 
1373
 
1374
  if cccd_front:
1375
  fb_bytes = await cccd_front.read()
@@ -1387,24 +1411,22 @@ async def register(
1387
  logger.warning(f"Cảnh báo giả mạo: Score {score} < {COSINE_THRESHOLD}")
1388
  raise Exception("Cảnh báo: Khuôn mặt trên thẻ CCCD KHÔNG KHỚP với ảnh chụp trực tiếp!")
1389
 
1390
- front_path = face_ai_service.save_image(fb_bytes, f"cccd_front_{person_id}", index=0)
1391
- saved_files.append(front_path)
1392
 
1393
  if cccd_back:
1394
  bb_bytes = await cccd_back.read()
1395
  if bb_bytes:
1396
- back_path = face_ai_service.save_image(bb_bytes, f"cccd_back_{person_id}", index=0)
1397
- saved_files.append(back_path)
1398
 
1399
  cursor.execute("""
1400
  INSERT INTO citizen_ids
1401
- (id, person_id, front_img_path, back_img_path,
1402
  id_number, full_name, dob, gender, nationality,
1403
  hometown, address, expiry_date, issue_date, special_features)
1404
- VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
1405
  """, (
1406
  str(uuid.uuid4()), person_id,
1407
- front_path or None, back_path or None,
1408
  cccd.get("id_number"), cccd.get("full_name"),
1409
  cccd.get("dob"), cccd.get("gender"),
1410
  cccd.get("nationality", "Việt Nam"),
 
805
 
806
  @staticmethod
807
  def _parse_mrz(lines):
808
+ """Parse 3 dòng MRZ thành dict (ICAO TD1 format)"""
809
  result = {
810
  "mrz_raw": "", "mrz_doc_type": "", "mrz_country": "",
811
  "mrz_id": "", "mrz_dob": "", "mrz_gender": "",
 
814
  if len(lines) < 3:
815
  return result
816
 
817
+ def _clean(raw):
818
+ c = raw.upper().replace(' ', '<').replace('|', '<')
819
+ c = re.sub(r'[^A-Z0-9<]', '<', c)
820
+ return (c + '<' * 30)[:30]
821
+
822
+ l1 = _clean(lines[0])
823
+ l2 = _clean(lines[1])
824
+ l3 = _clean(lines[2])
825
+ result["mrz_raw"] = f"{l1}\n{l2}\n{l3}"
826
+
827
+ # Line 1: doc_type(2) + country(3) + id(9) + check + optional
828
+ if len(l1) >= 5:
829
+ result["mrz_doc_type"] = l1[0:2].replace('<', '').strip()
830
+ result["mrz_country"] = l1[2:5].replace('<', '').strip()
831
+ if len(l1) >= 14:
832
+ id_raw = l1[5:14].replace('<', '')
833
+ if re.match(r'\d{9}', id_raw):
834
+ result["mrz_id"] = id_raw[:9]
835
+
836
+ # Line 2: dob(6) + check(1) + sex(1) + expiry(6) + ...
837
+ if len(l2) >= 14:
838
+ dob_raw = l2[0:6]
839
+ gender_raw = l2[7] if len(l2) > 7 else ''
840
+ exp_raw = l2[8:14]
841
+
842
+ if re.fullmatch(r'\d{6}', dob_raw):
843
+ yy, mm, dd = dob_raw[0:2], dob_raw[2:4], dob_raw[4:6]
844
+ cc = "19" if int(yy) >= 30 else "20"
845
+ result["mrz_dob"] = f"{dd}/{mm}/{cc}{yy}"
846
+
847
+ result["mrz_gender"] = {"M": "Nam", "F": "Nữ"}.get(gender_raw, "")
848
+
849
+ if re.fullmatch(r'\d{6}', exp_raw):
850
+ yy, mm, dd = exp_raw[0:2], exp_raw[2:4], exp_raw[4:6]
851
+ cc = "19" if int(yy) >= 30 else "20"
852
+ result["mrz_expiry"] = f"{dd}/{mm}/{cc}{yy}"
853
+
854
+ # Line 3: name (LAST<<FIRST<MIDDLE)
855
  name_field = l3.strip("<")
856
  if "<<" in name_field:
857
  parts = name_field.split("<<", 1)
 
864
  return result
865
 
866
 
867
+
868
  # ═══════════════════════════════════════════════════════════════════════════
869
  # KHỞI TẠO AI CHẠY NGẦM
870
  # ═══════════════════════════════════════════════════════════════════════════
 
1128
  conn = get_db_connection()
1129
  cursor = conn.cursor(dictionary=True)
1130
  cursor.execute("""
1131
+ SELECT e.person_id, p.name, p.role, p.img_url,
1132
  p.work_expiry_date, e.embedding_vector
1133
  FROM face_embeddings e
1134
  JOIN persons p ON e.person_id = p.id
 
1142
  "person_id": row["person_id"],
1143
  "name": row["name"],
1144
  "role": row.get("role", ""),
1145
+ "img_path": row.get("img_url", ""),
1146
  "work_expiry_date": str(row["work_expiry_date"]) if row.get("work_expiry_date") else None,
1147
  "embedding_vector": json.loads(row["embedding_vector"]),
1148
  })
 
1227
  }
1228
 
1229
  else:
1230
+ # Dùng scan-based approach (không cần YOLO cho mặt sau)
1231
+ raw = read_info.get_back_info(temp_path)
 
 
 
 
1232
 
1233
  logger.info(f"[OCR] Mặt sau raw: {raw}")
1234
  mapped_data = {
 
1372
  descriptor = detections[0]["descriptor"]
1373
  emb_id = str(uuid.uuid4())
1374
 
1375
+ img_b64 = face_ai_service.bytes_to_base64(img_bytes)
 
 
 
 
1376
 
1377
  if i == 0:
1378
+ user_descriptor = descriptor
1379
+ avatar_path = "" # keeping this variable so API responses don't break immediately
1380
+ avatar_b64 = img_b64
1381
+
1382
  cursor.execute(
1383
  """INSERT INTO persons
1384
+ (id, name, role, department, status, img_url, img_path, work_expiry_date)
1385
+ VALUES (%s, %s, %s, %s, 'active', %s, '', %s)""",
1386
+ (person_id, name, role, department, avatar_b64, expiry_val),
1387
  )
1388
 
1389
  cursor.execute(
1390
+ "INSERT INTO face_embeddings (id, person_id, embedding_vector, img_base64) VALUES (%s, %s, %s, %s)",
1391
+ (emb_id, person_id, json.dumps(descriptor), img_b64),
1392
  )
1393
+ new_encodings.append((person_id, name, role, avatar_b64, expiry_val, descriptor))
1394
 
1395
  front_path, back_path = "", ""
1396
+ front_b64, back_b64 = "", ""
1397
 
1398
  if cccd_front:
1399
  fb_bytes = await cccd_front.read()
 
1411
  logger.warning(f"Cảnh báo giả mạo: Score {score} < {COSINE_THRESHOLD}")
1412
  raise Exception("Cảnh báo: Khuôn mặt trên thẻ CCCD KHÔNG KHỚP với ảnh chụp trực tiếp!")
1413
 
1414
+ front_b64 = face_ai_service.bytes_to_base64(fb_bytes)
 
1415
 
1416
  if cccd_back:
1417
  bb_bytes = await cccd_back.read()
1418
  if bb_bytes:
1419
+ back_b64 = face_ai_service.bytes_to_base64(bb_bytes)
 
1420
 
1421
  cursor.execute("""
1422
  INSERT INTO citizen_ids
1423
+ (id, person_id, front_img_path, back_img_path, front_img_base64, back_img_base64,
1424
  id_number, full_name, dob, gender, nationality,
1425
  hometown, address, expiry_date, issue_date, special_features)
1426
+ VALUES (%s,%s,'','',%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
1427
  """, (
1428
  str(uuid.uuid4()), person_id,
1429
+ front_b64 or None, back_b64 or None,
1430
  cccd.get("id_number"), cccd.get("full_name"),
1431
  cccd.get("dob"), cccd.get("gender"),
1432
  cccd.get("nationality", "Việt Nam"),
database/database.py CHANGED
@@ -54,6 +54,8 @@ def init_database():
54
  person_id VARCHAR(36) NOT NULL,
55
  front_img_path VARCHAR(255),
56
  back_img_path VARCHAR(255),
 
 
57
  id_number VARCHAR(20),
58
  full_name VARCHAR(255),
59
  dob VARCHAR(20),
@@ -77,6 +79,7 @@ def init_database():
77
  id VARCHAR(36) PRIMARY KEY,
78
  person_id VARCHAR(36) NOT NULL,
79
  embedding_vector LONGTEXT NOT NULL,
 
80
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
81
  FOREIGN KEY (person_id) REFERENCES persons(id) ON DELETE CASCADE,
82
  KEY idx_person_id (person_id)
 
54
  person_id VARCHAR(36) NOT NULL,
55
  front_img_path VARCHAR(255),
56
  back_img_path VARCHAR(255),
57
+ front_img_base64 LONGTEXT COMMENT 'Base64 of front CCCD',
58
+ back_img_base64 LONGTEXT COMMENT 'Base64 of back CCCD',
59
  id_number VARCHAR(20),
60
  full_name VARCHAR(255),
61
  dob VARCHAR(20),
 
79
  id VARCHAR(36) PRIMARY KEY,
80
  person_id VARCHAR(36) NOT NULL,
81
  embedding_vector LONGTEXT NOT NULL,
82
+ img_base64 LONGTEXT COMMENT 'Base64 of this face angle',
83
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
84
  FOREIGN KEY (person_id) REFERENCES persons(id) ON DELETE CASCADE,
85
  KEY idx_person_id (person_id)
database/migrate_db_v2.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ sys.path.append('.')
3
+ from database import get_db_connection
4
+
5
+ def migrate():
6
+ conn = get_db_connection()
7
+ cursor = conn.cursor()
8
+
9
+ print("Checking schema for new base64 columns...")
10
+
11
+ # Check and add front_img_base64 to citizen_ids
12
+ cursor.execute("""
13
+ SELECT COUNT(*) as cnt FROM information_schema.COLUMNS
14
+ WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'citizen_ids' AND COLUMN_NAME = 'front_img_base64'
15
+ """)
16
+ if not cursor.fetchone()[0]:
17
+ print(" Adding front_img_base64 to citizen_ids...")
18
+ cursor.execute("ALTER TABLE citizen_ids ADD COLUMN front_img_base64 LONGTEXT")
19
+
20
+ # Check and add back_img_base64 to citizen_ids
21
+ cursor.execute("""
22
+ SELECT COUNT(*) as cnt FROM information_schema.COLUMNS
23
+ WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'citizen_ids' AND COLUMN_NAME = 'back_img_base64'
24
+ """)
25
+ if not cursor.fetchone()[0]:
26
+ print(" Adding back_img_base64 to citizen_ids...")
27
+ cursor.execute("ALTER TABLE citizen_ids ADD COLUMN back_img_base64 LONGTEXT")
28
+
29
+ # Check and add img_base64 to face_embeddings
30
+ cursor.execute("""
31
+ SELECT COUNT(*) as cnt FROM information_schema.COLUMNS
32
+ WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'face_embeddings' AND COLUMN_NAME = 'img_base64'
33
+ """)
34
+ if not cursor.fetchone()[0]:
35
+ print(" Adding img_base64 to face_embeddings...")
36
+ cursor.execute("ALTER TABLE face_embeddings ADD COLUMN img_base64 LONGTEXT")
37
+
38
+ conn.commit()
39
+ cursor.close()
40
+ conn.close()
41
+ print("Migration complete!")
42
+
43
+ if __name__ == "__main__":
44
+ migrate()
service/face_service.py CHANGED
@@ -429,6 +429,13 @@ class FaceAiService:
429
  with open(filepath, "wb") as f: f.write(file_bytes)
430
  return filepath
431
 
 
 
 
 
 
 
 
432
 
433
  face_ai_service = FaceAiService()
434
  face_memory_store = FaceMemoryStore()
 
429
  with open(filepath, "wb") as f: f.write(file_bytes)
430
  return filepath
431
 
432
+ @staticmethod
433
+ def bytes_to_base64(file_bytes: bytes) -> str:
434
+ import base64
435
+ # Return a standard base64 data URI format for images
436
+ encoded = base64.b64encode(file_bytes).decode('utf-8')
437
+ return f"data:image/jpeg;base64,{encoded}"
438
+
439
 
440
  face_ai_service = FaceAiService()
441
  face_memory_store = FaceMemoryStore()