Spaces:
Runtime error
Runtime error
File size: 5,711 Bytes
25b208e |
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 163 164 165 166 167 |
# main.py
import cv2
import numpy as np
import easyocr
import logging
import re
import datetime
import mediapipe as mp
# ----------------- Logging -----------------
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# ----------------- Settings -----------------
class Settings:
threshold_distance = 0.58
max_image_size = (1600, 1600)
min_depth_variation = 0.001
min_texture_score = 0.5
action_threshold = {
"smile": 0.045,
"blink": 0.20,
"head_turn": 15.0
}
target_size = (160, 160)
settings = Settings()
# ----------------- Utils -----------------
def process_image(image_bytes: bytes) -> np.ndarray:
nparr = np.frombuffer(image_bytes, np.uint8)
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
return img
def cosine_similarity(a, b):
a = np.array(a)
b = np.array(b)
a = a / np.linalg.norm(a)
b = b / np.linalg.norm(b)
return float(np.dot(a, b))
easyocr_reader = easyocr.Reader(['ar', 'en'], gpu=False)
def ocr_text(image: np.ndarray, region: tuple = None) -> str:
"""Run OCR on full image or region. Converts Arabic numerals to English."""
if region:
h, w = image.shape[:2]
y1, y2, x1, x2 = region
image = image[int(h*y1):int(h*y2), int(w*x1):int(w*x2)]
result = easyocr_reader.readtext(image)
arabic_to_western = str.maketrans('٠١٢٣٤٥٦٧٨٩', '0123456789')
return " ".join([item[1].translate(arabic_to_western) for item in result])
# ----------------- Factory Number Extraction -----------------
def extract_factory_number(image: np.ndarray) -> str | None:
"""Extract the factory number robustly from the full image using OCR."""
try:
text = ocr_text(image)
logger.info(f"[OCR] Full image text for factory number: {text}")
# OCR confusion correction
confusion_map = str.maketrans({
'$': 'S', '§': 'S', '5': 'S', 's': 'S',
'1': 'I', 'l': 'I', '|': 'I', '!': 'I',
'0': 'O', 'O': '0', 'Q': '0', 'D': '0',
'8': 'B', 'B': '8', '2': 'Z', 'Z': '2',
'6': 'G', 'G': '6', '9': 'G', 'g': 'G',
'4': 'A', 'A': '4', '7': 'T', 'T': '7',
})
norm_text = text.translate(confusion_map).upper()
# Pattern 1: starts with 1–2 letters + 5–9 digits
pattern_letters = r'([A-Z]{1,2}[0-9]{5,9})'
candidates_letters = re.findall(pattern_letters, norm_text)
# Pattern 2: fallback 7–11 alphanumerics with at least 5 digits
pattern_any = r'([A-Z0-9]{7,11})'
candidates_any = [
c for c in re.findall(pattern_any, norm_text)
if sum(x.isdigit() for x in c) >= 5 and sum(x.isalpha() for x in c) >= 1
]
# Prefer pattern with letters
candidate = None
if candidates_letters:
candidate = candidates_letters[-1]
elif candidates_any:
candidate = candidates_any[-1]
# Extra fallback: first char suspicious correction
suspicious_map = {
'|': 'I', '$': 'S', '§': 'S', '5': 'S', '1': 'I',
'l': 'I', '!': 'I', '0': 'O', '8': 'B', '2': 'Z',
'6': 'G', '9': 'G', '4': 'A', '7': 'T'
}
if candidate:
if candidate[0] in suspicious_map:
candidate = suspicious_map[candidate[0]] + candidate[1:]
return candidate.upper()
# Extra fallback: search full text again
fallback_pattern = r'([\|\$§5sl!0182g46947][A-Z0-9]{6,10})'
fallback_candidates = re.findall(fallback_pattern, text)
for fc in fallback_candidates:
fc_up = fc.upper()
if sum(x.isdigit() for x in fc_up) >= 5:
if fc_up[0] in suspicious_map:
fc_up = suspicious_map[fc_up[0]] + fc_up[1:]
return fc_up
return None
except Exception as e:
logger.error(f"Factory number extraction error: {str(e)}")
return None
# ----------------- Gradio Interface -----------------
import gradio as gr
def verify_id_card_gradio(id_img: np.ndarray) -> dict:
"""Gradio-friendly function to extract factory number from ID card image."""
try:
h, w = id_img.shape[:2]
if w > settings.max_image_size[0] or h > settings.max_image_size[1]:
return {
"verified": False,
"factory_number": None,
"message": f"Image too large. Max allowed: {settings.max_image_size[0]}x{settings.max_image_size[1]} pixels."
}
factory_number = extract_factory_number(id_img)
if factory_number:
return {
"verified": True,
"factory_number": factory_number,
"message": "Factory number extracted successfully."
}
else:
return {
"verified": False,
"factory_number": None,
"message": "Factory number not found in the image."
}
except Exception as e:
logger.error(f"Gradio ID card error: {str(e)}")
return {
"verified": False,
"factory_number": None,
"message": "An error occurred while processing the image."
}
iface = gr.Interface(
fn=verify_id_card_gradio,
inputs=gr.Image(type="numpy", label="Upload Egyptian ID Card"),
outputs="json",
title="Egyptian ID Factory Number Extractor",
description="Upload an Egyptian ID card image to extract the factory number. Only returns: match status and extracted factory number.",
allow_flagging="never",
examples=None
)
if __name__ == "__main__":
iface.launch()
|