PFE / app.py
boulahia's picture
Update app.py
20e53fa verified
raw
history blame
44.5 kB
import gradio as gr
from ultralytics import YOLO
import cv2
import numpy as np
from PIL import Image
import torch
from transformers import TrOCRProcessor, VisionEncoderDecoderModel
from datetime import datetime
from tensorflow.keras.models import load_model
import os
import tempfile
import sqlite3
from sqlite3 import Error
import re
import time
# ------------------------------
# 1. CHARGEMENT DES MODÈLES
# ------------------------------
# Modèle CNN pour reconnaissance des logos
cnn_logo_model = load_model("logo_model_cnn.h5")
# Chargement automatique des classes depuis le dossier train
logo_classes = ['Alfa romeo', 'Audi', 'BMW', 'Chevrolet', 'Citroen', 'Dacia', 'Daewoo', 'Dodge', 'Ferrari', 'Fiat', 'Ford', 'Honda', 'Hyundai', 'Jaguar', 'Jeep', 'Kia', 'Lada', 'Lancia', 'Land rover', 'Lexus', 'Maserati', 'Mazda', 'Mercedes', 'Mitsubishi', 'Nissan', 'Opel', 'Peugeot', 'Porsche', 'Renault', 'Rover', 'Saab', 'Seat', 'Skoda', 'Subaru', 'Suzuki', 'Tata', 'Tesla', 'Toyota', 'Volkswagen', 'Volvo']
# Modèles YOLO
model_color = YOLO("car_color.pt")
model_orientation = YOLO("direction_best.pt")
model_plate_detection = YOLO("plate_detection.pt")
model_logo_detection = YOLO("car_logo_detection.pt")
model_characters = YOLO("character_detetion.pt")
model_vehicle = YOLO("vehicle_detection.pt")
# Modèle TrOCR pour reconnaissance de caractères
trocr_model = VisionEncoderDecoderModel.from_pretrained("microsoft/trocr-base-printed")
trocr_processor = TrOCRProcessor.from_pretrained("microsoft/trocr-base-printed")
# ------------------------------
# 2. DICTIONNAIRES DE RÉFÉRENCE
# ------------------------------
CATEGORIES = {
'1': "Véhicules de tourisme",
'2': "Camions",
'3': "Camionnettes",
'4': "Autocars et autobus",
'5': "Tracteurs routiers",
'6': "Autres tracteurs",
'7': "Véhicules spéciaux",
'8': "Remorques et semi-remorques",
'9': "Motocyclettes"
}
WILAYAS = {
"01": "Adrar", "02": "Chlef", "03": "Laghouat", "04": "Oum El Bouaghi",
"05": "Batna", "06": "Béjaïa", "07": "Biskra", "08": "Béchar",
"09": "Blida", "10": "Bouira", "11": "Tamanrasset", "12": "Tébessa",
"13": "Tlemcen", "14": "Tiaret", "15": "Tizi Ouzou", "16": "Alger",
"17": "Djelfa", "18": "Jijel", "19": "Sétif", "20": "Saïda",
"21": "Skikda", "22": "Sidi Bel Abbès", "23": "Annaba", "24": "Guelma",
"25": "Constantine", "26": "Médéa", "27": "Mostaganem", "28": "MSila",
"29": "Mascara", "30": "Ouargla", "31": "Oran", "32": "El Bayadh",
"33": "Illizi", "34": "Bordj Bou Arreridj", "35": "Boumerdès",
"36": "El Tarf", "37": "Tindouf", "38": "Tissemsilt", "39": "El Oued",
"40": "Khenchela", "41": "Souk Ahras", "42": "Tipaza", "43": "Mila",
"44": "Aïn Defla", "45": "Naâma", "46": "Aïn Témouchent",
"47": "Ghardaïa", "48": "Relizane",
"49": "El M'Ghair", "50": "El Menia",
"51": "Ouled Djellal", "52": "Bordj Badji Mokhtar",
"53": "Béni Abbès", "54": "Timimoun",
"55": "Touggourt", "56": "Djanet",
"57": "In Salah", "58": "In Guezzam"
}
# ------------------------------
# 3. VARIABLES PARTAGÉES
# ------------------------------
shared_results = {
"original_image": None,
"img_rgb": None,
"img_draw": None,
"plate_crop_img": None,
"logo_crop_img": None,
"plate_with_chars_img": None,
"trocr_char_list": [],
"trocr_combined_text": "",
"classification_result": "",
"label_color": "",
"label_orientation": "",
"vehicle_type": "",
"vehicle_model": "",
"vehicle_brand": "",
"logo_recognition_results": [],
"current_frame": None,
"video_path": None,
"video_processing": False,
"frame_count": 0,
"total_frames": 0,
"original_video_dimensions": None,
"corrected_orientation": False,
"vehicle_box": None,
"vehicle_detected": False,
"detection_boxes": {
"plate": None,
"logo": None,
"color": None,
"orientation": None
},
"classified_plate": None
}
# ------------------------------
# 4. FONCTIONS UTILITAIRES
# ------------------------------
def classify_plate(text):
"""Classification des plaques algériennes (10-11 chiffres)"""
try:
if not text:
return None
clean_text = ''.join(c for c in text if c.isdigit())
if len(clean_text) not in {10, 11}:
return None
serie = clean_text[:3]
middle = clean_text[3:9]
wilaya_code = clean_text[9:]
wilaya = WILAYAS.get(wilaya_code, ("", "Wilaya inonnue"))
categorie_code = middle[0]
annee = middle[1:3]
return {
'matricule_complet': clean_text,
'serie': serie,
'wilaya': (wilaya_code, wilaya[1]),
'annee': f"20{annee}",
'categorie': (categorie_code, CATEGORIES.get(categorie_code, ("", "Inconnue"))[1]),
'is_algerian': True,
'length': len(clean_text)
}
except Exception as e:
print(f"Erreur classification plaque: {str(e)}")
return None
def predict_brand(image):
"""Prédire la marque de voiture à partir de l'image"""
try:
img = Image.fromarray(image).resize((224, 224))
img_array = np.array(img) / 255.0
img_array = np.expand_dims(img_array, axis=0)
predictions = cnn_logo_model.predict(img_array)
predicted_class = np.argmax(predictions[0])
confidence = predictions[0][predicted_class]
if confidence < 0.5:
return "Marque non détectée (confiance trop faible)"
brand = logo_classes[predicted_class]
return f"{brand} (confiance: {confidence:.2f})"
except Exception as e:
print(f"Erreur lors de la prédiction de la marque: {str(e)}")
return "Erreur de détection"
def recognize_logo(cropped_logo):
"""Reconnaître la marque à partir d'un logo détecté"""
try:
if cropped_logo.size == 0:
return "Logo trop petit pour analyse"
resized_logo = cv2.resize(np.array(cropped_logo), (128, 128))
rgb_logo = cv2.cvtColor(resized_logo, cv2.COLOR_BGR2RGB)
normalized_logo = rgb_logo / 255.0
input_logo = np.expand_dims(normalized_logo, axis=0)
predictions = cnn_logo_model.predict(input_logo, verbose=0)
pred_index = np.argmax(predictions[0])
pred_label = logo_classes[pred_index]
pred_conf = predictions[0][pred_index]
if pred_conf < 0.5:
return f"Marque incertaine: {pred_label} ({pred_conf:.2f})"
return f"{pred_label} (confiance: {pred_conf:.2f})"
except Exception as e:
print(f"Erreur reconnaissance logo: {str(e)}")
return "Erreur d'analyse"
def draw_detection_boxes(image):
"""Dessiner toutes les boîtes de détection sur l'image"""
img_draw = image.copy()
if shared_results["vehicle_box"]:
x1, y1, x2, y2 = shared_results["vehicle_box"]
cv2.rectangle(img_draw, (x1, y1), (x2, y2), (0, 165, 255), 2)
cv2.putText(img_draw, "VEHICLE", (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 165, 255), 2)
if shared_results["detection_boxes"]["plate"]:
x1, y1, x2, y2 = shared_results["detection_boxes"]["plate"]
cv2.rectangle(img_draw, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.putText(img_draw, "PLATE", (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
if shared_results["detection_boxes"]["logo"]:
x1, y1, x2, y2 = shared_results["detection_boxes"]["logo"]
cv2.rectangle(img_draw, (x1, y1), (x2, y2), (255, 0, 0), 2)
cv2.putText(img_draw, "LOGO", (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 0, 0), 2)
if shared_results["vehicle_model"]:
model_text = shared_results["vehicle_model"].split("(")[0].strip() if "(" in shared_results["vehicle_model"] else shared_results["vehicle_model"]
cv2.putText(img_draw, f"Model: {model_text}", (x1, y2 + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)
if shared_results["detection_boxes"]["color"]:
x1, y1, x2, y2 = shared_results["detection_boxes"]["color"]
cv2.rectangle(img_draw, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv2.putText(img_draw, "COLOR", (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2)
if shared_results["label_color"]:
cv2.putText(img_draw, f"{shared_results['label_color']}", (x1, y2 + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
if shared_results["detection_boxes"]["orientation"]:
x1, y1, x2, y2 = shared_results["detection_boxes"]["orientation"]
cv2.rectangle(img_draw, (x1, y1), (x2, y2), (255, 255, 0), 2)
cv2.putText(img_draw, "ORIENTATION", (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 255, 0), 2)
if shared_results["label_orientation"]:
cv2.putText(img_draw, f"{shared_results['label_orientation']}", (x1, y2 + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 0), 2)
return img_draw
# ------------------------------
# 5. FONCTIONS DE TRAITEMENT VIDÉO
# ------------------------------
def extract_frames(video_path, frame_skip=15):
"""Extraire les frames d'une vidéo avec un intervalle donné"""
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
raise gr.Error("Échec de lecture de la vidéo")
frames = []
count = 0
while True:
ret, frame = cap.read()
if not ret:
break
if count % frame_skip == 0:
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frames.append(frame_rgb)
count += 1
cap.release()
return frames
def process_video_frame(frame):
"""Traiter un seul frame de vidéo"""
# Réinitialiser les résultats partagés pour ce frame
shared_results.update({
"original_image": frame,
"img_rgb": frame.copy(),
"img_draw": frame.copy(),
"current_frame": frame,
"corrected_orientation": False,
"label_color": "",
"label_orientation": "",
"vehicle_type": "",
"vehicle_model": "",
"vehicle_brand": "",
"logo_recognition_results": [],
"trocr_char_list": [],
"trocr_combined_text": "",
"classification_result": "",
"vehicle_box": None,
"vehicle_detected": False,
"detection_boxes": {
"plate": None,
"logo": None,
"color": None,
"orientation": None
},
"plate_crop_img": None,
"logo_crop_img": None,
"plate_with_chars_img": None,
"classified_plate": None
})
# Effectuer toutes les détections
detect_vehicle()
detect_color()
detect_orientation()
detect_logo_and_model()
detect_plate()
classify_plate_number()
# Retourner les résultats
return {
"processed_image": Image.fromarray(shared_results["img_draw"]),
"color": shared_results["label_color"],
"orientation": shared_results["label_orientation"],
"brand": shared_results["vehicle_brand"],
"model": shared_results["vehicle_model"],
"plate_text": shared_results["trocr_combined_text"],
"plate_info": shared_results["classified_plate"],
"vehicle_type": shared_results["vehicle_type"]
}
def process_video(video_path):
"""Traiter une vidéo complète et retourner les meilleurs résultats"""
frames = extract_frames(video_path)
if not frames:
raise gr.Error("Aucun frame valide trouvé dans la vidéo")
# Traiter chaque frame et stocker les résultats
all_results = []
processed_frames = []
for frame in frames:
frame_results = process_video_frame(frame)
all_results.append(frame_results)
processed_frames.append(frame_results["processed_image"])
# Trouver le frame avec la meilleure détection
best_frame = None
best_score = -1
for result in all_results:
score = 0
if result["plate_info"] and result["plate_info"]["is_algerian"]:
score += 2
if result["brand"] and "non détectée" not in result["brand"]:
score += 1
if result["color"]:
score += 1
if score > best_score:
best_score = score
best_frame = result
if not best_frame:
best_frame = all_results[0]
return processed_frames, best_frame
# ------------------------------
# 6. FONCTIONS PRINCIPALES
# ------------------------------
def load_image(image_path):
"""Charger et préparer l'image de base"""
if isinstance(image_path, str):
img = cv2.imread(image_path)
else:
img = cv2.cvtColor(image_path, cv2.COLOR_RGB2BGR)
if img is None:
raise gr.Error("Échec de lecture de l'image")
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img_draw = img_rgb.copy()
shared_results.update({
"original_image": img,
"img_rgb": img_rgb,
"img_draw": img_draw,
"video_processing": False,
"corrected_orientation": False,
"detection_boxes": {
"plate": None,
"logo": None,
"color": None,
"orientation": None
}
})
return Image.fromarray(img_rgb)
def detect_vehicle():
"""Détecter le véhicule principal dans l'image"""
if shared_results["img_rgb"] is None:
return "Veuillez d'abord charger une image/vidéo", None
img_to_process = shared_results["img_rgb"]
if shared_results.get("corrected_orientation", False):
height, width = img_to_process.shape[:2]
if height > width:
img_to_process = cv2.rotate(img_to_process, cv2.ROTATE_90_CLOCKWISE)
results_vehicle = model_vehicle(img_to_process)
img_with_boxes = shared_results["img_rgb"].copy()
vehicle_detected = False
for r in results_vehicle:
if hasattr(r, 'boxes') and r.boxes and hasattr(r.boxes, 'cls') and len(r.boxes.cls) > 0:
largest_box = None
max_area = 0
for box in r.boxes:
x1, y1, x2, y2 = map(int, box.xyxy[0])
area = (x2 - x1) * (y2 - y1)
if area > max_area:
max_area = area
largest_box = (x1, y1, x2, y2)
if largest_box:
x1, y1, x2, y2 = largest_box
shared_results["vehicle_box"] = largest_box
shared_results["vehicle_detected"] = True
vehicle_detected = True
cv2.rectangle(img_with_boxes, (x1, y1), (x2, y2), (0, 165, 255), 2)
cv2.putText(img_with_boxes, "VEHICLE", (x1, y1 - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 165, 255), 2)
shared_results["img_draw"] = img_with_boxes
if vehicle_detected:
return "Véhicule détecté - Vous pouvez maintenant détecter la couleur", Image.fromarray(img_with_boxes)
else:
shared_results["vehicle_box"] = None
shared_results["vehicle_detected"] = False
return "Aucun véhicule détecté - La détection de couleur sera moins précise", Image.fromarray(img_with_boxes)
def detect_color():
"""Détecter la couleur du véhicule"""
if shared_results["img_rgb"] is None:
return "Veuillez d'abord charger une image/vidéo", None
img_to_process = shared_results["img_rgb"]
if shared_results.get("corrected_orientation", False):
height, width = img_to_process.shape[:2]
if height > width:
img_to_process = cv2.rotate(img_to_process, cv2.ROTATE_90_CLOCKWISE)
if shared_results["vehicle_detected"] and shared_results["vehicle_box"]:
x1, y1, x2, y2 = shared_results["vehicle_box"]
vehicle_roi = img_to_process[y1:y2, x1:x2]
results_color = model_color(vehicle_roi)
else:
results_color = model_color(img_to_process)
img_with_boxes = shared_results["img_draw"].copy() if shared_results["img_draw"] is not None else img_to_process.copy()
color_detected = False
for r in results_color:
if hasattr(r, 'boxes') and r.boxes and hasattr(r.boxes, 'cls') and len(r.boxes.cls) > 0:
cls = int(r.boxes.cls[0])
shared_results["label_color"] = r.names[cls]
if shared_results["vehicle_detected"] and shared_results["vehicle_box"]:
vx1, vy1, vx2, vy2 = shared_results["vehicle_box"]
box = r.boxes.xyxy[0].cpu().numpy()
x1, y1, x2, y2 = map(int, box)
abs_x1 = vx1 + x1
abs_y1 = vy1 + y1
abs_x2 = vx1 + x2
abs_y2 = vy1 + y2
shared_results["detection_boxes"]["color"] = (abs_x1, abs_y1, abs_x2, abs_y2)
else:
box = r.boxes.xyxy[0].cpu().numpy()
x1, y1, x2, y2 = map(int, box)
shared_results["detection_boxes"]["color"] = (x1, y1, x2, y2)
color_detected = True
img_with_boxes = draw_detection_boxes(img_with_boxes)
shared_results["img_draw"] = img_with_boxes
if color_detected:
return f"Couleur: {shared_results['label_color']}", Image.fromarray(img_with_boxes)
else:
return "Couleur non détectée", Image.fromarray(img_with_boxes)
def detect_orientation():
"""Détecter l'orientation du véhicule"""
if shared_results["img_rgb"] is None:
return "Veuillez d'abord charger une image/vidéo", None # <-- Ajout de None comme deuxième valeur
img_to_process = shared_results["img_rgb"]
if shared_results["video_processing"]:
height, width = img_to_process.shape[:2]
if height > width:
img_to_process = cv2.rotate(img_to_process, cv2.ROTATE_90_CLOCKWISE)
shared_results["corrected_orientation"] = True
results_orientation = model_orientation(img_to_process)
for r in results_orientation:
if hasattr(r, 'boxes') and r.boxes and hasattr(r.boxes, 'cls') and len(r.boxes.cls) > 0:
cls = int(r.boxes.cls[0])
shared_results["label_orientation"] = r.names[cls]
box = r.boxes.xyxy[0].cpu().numpy()
x1, y1, x2, y2 = map(int, box)
shared_results["detection_boxes"]["orientation"] = (x1, y1, x2, y2)
img_with_boxes = draw_detection_boxes(shared_results["img_rgb"])
shared_results["img_draw"] = img_with_boxes
return (
f"Orientation: {shared_results['label_orientation']}" if shared_results['label_orientation'] else "Orientation non détectée",
Image.fromarray(img_with_boxes) # <-- Deuxième valeur retournée
)
def detect_logo_and_model():
"""Détecter et reconnaître le logo et le modèle du véhicule"""
if shared_results["img_rgb"] is None:
return "Veuillez d'abord charger une image/vidéo", None, None, None, None
shared_results["logo_recognition_results"] = []
img_to_process = shared_results["img_rgb"]
detected_model = "Modèle non détecté"
if shared_results.get("corrected_orientation", False):
height, width = img_to_process.shape[:2]
if height > width:
img_to_process = cv2.rotate(img_to_process, cv2.ROTATE_90_CLOCKWISE)
if shared_results["vehicle_detected"] and shared_results["vehicle_box"]:
vx1, vy1, vx2, vy2 = shared_results["vehicle_box"]
roi = img_to_process[vy1:vy2, vx1:vx2]
results_logo = model_logo_detection(roi)
else:
results_logo = model_logo_detection(img_to_process)
if results_logo and results_logo[0].boxes:
for box in results_logo[0].boxes:
if shared_results["vehicle_detected"] and shared_results["vehicle_box"]:
rx1, ry1, rx2, ry2 = map(int, box.xyxy[0])
abs_x1 = vx1 + rx1
abs_y1 = vy1 + ry1
abs_x2 = vx1 + rx2
abs_y2 = vy1 + ry2
else:
abs_x1, abs_y1, abs_x2, abs_y2 = map(int, box.xyxy[0])
shared_results["detection_boxes"]["logo"] = (abs_x1, abs_y1, abs_x2, abs_y2)
logo_crop = img_to_process[abs_y1:abs_y2, abs_x1:abs_x2]
shared_results["logo_crop_img"] = Image.fromarray(logo_crop)
logo_recognition = recognize_logo(shared_results["logo_crop_img"])
shared_results["logo_recognition_results"].append(logo_recognition)
if not shared_results["vehicle_brand"] or "confiance" not in shared_results["vehicle_brand"]:
shared_results["vehicle_brand"] = logo_recognition
if logo_recognition and shared_results["logo_crop_img"]:
detected_model = predict_car_model(logo_recognition, shared_results["logo_crop_img"])
shared_results["vehicle_model"] = detected_model
img_with_boxes = draw_detection_boxes(shared_results["img_rgb"])
shared_results["img_draw"] = img_with_boxes
if not shared_results["vehicle_brand"] or "incertaine" in shared_results["vehicle_brand"] or "Erreur" in shared_results["vehicle_brand"]:
global_brand = predict_brand(img_to_process)
if global_brand and "non détectée" not in global_brand:
shared_results["vehicle_brand"] = global_brand
logo_results_text = " | ".join(shared_results["logo_recognition_results"]) if shared_results["logo_recognition_results"] else "Aucun logo reconnu"
return (
f"Marque: {shared_results['vehicle_brand']}" if shared_results['vehicle_brand'] else "Marque non détectée",
f"Modèle: {shared_results['vehicle_model']}" if shared_results['vehicle_model'] else "Modèle non détecté",
f"Reconnaissance logo: {logo_results_text}",
Image.fromarray(img_with_boxes),
shared_results["logo_crop_img"]
)
def detect_plate():
"""Détecter la plaque d'immatriculation et reconnaître les caractères"""
if shared_results["img_rgb"] is None:
return "Veuillez d'abord charger une image/vidéo", None, None, None
shared_results["trocr_char_list"] = []
shared_results["trocr_combined_text"] = ""
img_to_process = shared_results["img_rgb"]
if shared_results.get("corrected_orientation", False):
height, width = img_to_process.shape[:2]
if height > width:
img_to_process = cv2.rotate(img_to_process, cv2.ROTATE_90_CLOCKWISE)
if shared_results["vehicle_detected"] and shared_results["vehicle_box"]:
vx1, vy1, vx2, vy2 = shared_results["vehicle_box"]
roi = img_to_process[vy1:vy2, vx1:vx2]
results_plate = model_plate_detection(roi)
else:
results_plate = model_plate_detection(img_to_process)
if results_plate and results_plate[0].boxes:
for box in results_plate[0].boxes:
if shared_results["vehicle_detected"] and shared_results["vehicle_box"]:
rx1, ry1, rx2, ry2 = map(int, box.xyxy[0])
abs_x1 = vx1 + rx1
abs_y1 = vy1 + ry1
abs_x2 = vx1 + rx2
abs_y2 = vy1 + ry2
else:
abs_x1, abs_y1, abs_x2, abs_y2 = map(int, box.xyxy[0])
shared_results["detection_boxes"]["plate"] = (abs_x1, abs_y1, abs_x2, abs_y2)
plate_crop = img_to_process[abs_y1:abs_y2, abs_x1:abs_x2]
shared_results["plate_crop_img"] = Image.fromarray(plate_crop)
plate_for_char_draw = plate_crop.copy()
results_chars = model_characters(plate_crop)
char_boxes = []
for r in results_chars:
if r.boxes:
for box in r.boxes:
x1c, y1c, x2c, y2c = map(int, box.xyxy[0])
char_boxes.append(((x1c, y1c, x2c, y2c), x1c))
char_boxes.sort(key=lambda x: x[1])
for i, (coords, _) in enumerate(char_boxes):
x1c, y1c, x2c, y2c = coords
char_crop = plate_crop[y1c:y2c, x1c:x2c]
char_pil = Image.fromarray(char_crop).convert("RGB")
try:
inputs = trocr_processor(images=char_pil, return_tensors="pt").pixel_values
generated_ids = trocr_model.generate(inputs)
predicted_char = trocr_processor.batch_decode(generated_ids, skip_special_tokens=True)[0].strip()
shared_results["trocr_char_list"].append(predicted_char)
except:
shared_results["trocr_char_list"].append("?")
cv2.rectangle(plate_for_char_draw, (x1c, y1c), (x2c, y2c), (255, 0, 255), 1)
cv2.putText(plate_for_char_draw, predicted_char, (x1c, y1c - 5),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 255), 1)
shared_results["plate_with_chars_img"] = Image.fromarray(plate_for_char_draw)
shared_results["trocr_combined_text"] = ''.join(shared_results["trocr_char_list"])
break
img_with_boxes = draw_detection_boxes(shared_results["img_rgb"])
shared_results["img_draw"] = img_with_boxes
return (
Image.fromarray(img_with_boxes),
shared_results["plate_crop_img"],
shared_results["plate_with_chars_img"],
shared_results["trocr_char_list"]
)
def classify_plate_number():
"""Classifier le numéro de plaque détecté"""
if not shared_results.get("trocr_combined_text"):
return (
"Aucun texte de plaque détecté",
"Type: Non déterminé",
"❌ Aucune plaque valide",
"Action: Vérifier la qualité de l'image"
)
plate_text = shared_results["trocr_combined_text"]
digits = ''.join(c for c in plate_text if c.isdigit())
if len(digits) not in {10, 11}:
return (
f"Texte rejeté: {plate_text} ({len(digits)} chiffres)",
"Type: Non déterminé",
f"❌ NON ALGÉRIEN (format {len(digits)} chiffres)",
"Action: Contrôle requis"
)
if any(c.isalpha() for c in plate_text):
return (
f"Texte rejeté: {plate_text} (contient des lettres)",
"Type: Non déterminé",
"❌ NON ALGÉRIEN (lettres détectées)",
"Action: Contrôle immédiat"
)
plate_info = classify_plate(plate_text)
if not plate_info:
return (
f"Texte non classifiable: {plate_text}",
"Type: Non déterminé",
"❌ Format invalide",
"Action: Vérifier manuellement"
)
result = [
f"Plaque: {plate_info['matricule_complet']}",
f"Longueur: {plate_info['length']} chiffres",
f"Wilaya: {plate_info['wilaya'][1]} ({plate_info['wilaya'][0]})",
f"Année: {plate_info['annee']}",
f"Catégorie: {plate_info['categorie'][1]}",
f"Série: {plate_info['serie']}"
]
shared_results["classified_plate"] = plate_info
shared_results["vehicle_type"] = plate_info['categorie'][1]
# Vérification dans la base de données
db_check = check_vehicle(plate_info['matricule_complet'])
db_status = "\nStatut DB: " + db_check[1] if db_check[0] else ""
return (
'\n'.join(result) + db_status,
f"Type: {plate_info['categorie'][1]}",
"✅ PLAQUE ALGÉRIENNE VALIDE",
"Action: Vérification standard"
)
# ------------------------------
# 7. GESTION BASE DE DONNÉES
# ------------------------------
DB_PATH = "vehicules_database.db"
TIME_PATTERN = re.compile(r'^\d{2}:\d{2}-\d{2}:\d{2}$')
def create_connection():
"""Créer une connexion à la base SQLite"""
conn = None
try:
conn = sqlite3.connect(DB_PATH)
return conn
except Error as e:
print(f"Erreur de connexion à SQLite: {e}")
return conn
def init_database():
"""Initialiser la base de données si elle n'existe pas"""
conn = create_connection()
if conn is not None:
try:
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS vehicules (
id INTEGER PRIMARY KEY AUTOINCREMENT,
plaque TEXT NOT NULL UNIQUE,
marque TEXT,
modele TEXT,
couleur TEXT,
statut TEXT,
plage_horaire TEXT,
date_enregistrement TEXT
)
""")
conn.commit()
except Error as e:
print(f"Erreur création table: {e}")
finally:
conn.close()
def check_vehicle(plate_text):
"""Vérifier si un véhicule existe dans la base"""
conn = create_connection()
if conn is not None:
try:
cursor = conn.cursor()
cursor.execute("SELECT statut, plage_horaire FROM vehicules WHERE plaque = ?", (plate_text,))
result = cursor.fetchone()
if result:
return True, f"Statut: {result[0]} | Accès: {result[1]}"
return False, "Véhicule non enregistré"
except Error as e:
print(f"Erreur lecture base: {e}")
return False, "Erreur base de données"
finally:
conn.close()
return False, "Erreur de connexion"
def save_vehicle(plate_info, color, model, brand, status, time_range):
"""Enregistrer un nouveau véhicule dans la base"""
conn = create_connection()
if conn is not None:
try:
plate_number = str(plate_info['matricule_complet']).strip()
clean_brand = brand.split('(')[0].strip() if '(' in brand else brand
clean_model = model.split('(')[0].strip() if '(' in model else model
cursor = conn.cursor()
cursor.execute("SELECT 1 FROM vehicules WHERE plaque = ?", (plate_number,))
if cursor.fetchone():
return False, "Véhicule déjà existant"
cursor.execute("""
INSERT INTO vehicules (plaque, marque, modele, couleur, statut, plage_horaire, date_enregistrement)
VALUES (?, ?, ?, ?, ?, ?, ?)
""", (
plate_number,
clean_brand,
clean_model,
color,
status,
time_range,
datetime.now().strftime("%Y-%m-%d %H:%M:%S")
))
conn.commit()
return True, "Enregistrement réussi"
except Error as e:
return False, f"Erreur enregistrement: {e}"
finally:
conn.close()
return False, "Erreur de connexion"
def is_access_allowed(plate_text):
"""Vérifier si l'accès est autorisé selon la plage horaire"""
conn = create_connection()
if conn is not None:
try:
cursor = conn.cursor()
cursor.execute("SELECT statut, plage_horaire FROM vehicules WHERE plaque = ?", (plate_text,))
vehicle = cursor.fetchone()
if not vehicle:
return False
if vehicle[0] == "Non Autorisé":
return False
if vehicle[1] == "24/24":
return True
current_time = datetime.now().time()
start_str, end_str = vehicle[1].split('-')
start = time(*map(int, start_str.split(':')))
end = time(*map(int, end_str.split(':')))
return start <= current_time <= end
except Error as e:
print(f"Erreur vérification accès: {e}")
return False
finally:
conn.close()
return False
# ------------------------------
# 8. INTERFACE GRADIO
# ------------------------------
with gr.Blocks(title="🚗 Système de Reconnaissance de Véhicules Algériens", theme=gr.themes.Soft()) as demo:
gr.Markdown("""
# 🚗 Système de Reconnaissance de Véhicules Algériens
*Détection de plaque d'immatriculation, logo, couleur et autres caractéristiques du véhicule*
""")
with gr.Row():
# Colonne de gauche - Contrôles
with gr.Column(scale=1):
# Section de chargement
gr.Markdown("### 📁 Chargement des données")
input_type = gr.Radio(["Image", "Vidéo"], label="Type d'entrée", value="Image", interactive=True)
file_input = gr.File(label="Télécharger un fichier", file_types=["image", "video"])
load_btn = gr.Button("Charger le fichier", variant="primary")
# Bouton pour traiter toute la vidéo
process_video_btn = gr.Button("Traiter toute la vidéo", variant="primary", visible=False)
# Section détection pour frame par frame
gr.Markdown("### 🔍 Analyse Frame par Frame")
with gr.Row():
detect_vehicle_btn = gr.Button("Détection de véhicule", variant="secondary")
detect_color_btn = gr.Button("Détection de couleur", variant="secondary")
with gr.Row():
detect_orientation_btn = gr.Button("Détection de l'orientation", variant="secondary")
detect_logo_btn = gr.Button("Détecter logo", variant="secondary")
with gr.Row():
detect_plate_btn = gr.Button("Détection de plaque", variant="secondary")
classify_plate_btn = gr.Button("Classifier plaque", variant="primary")
next_frame_btn = gr.Button("Frame suivant", visible=False)
# Gestion d'accès
gr.Markdown("### 🔐 Gestion d'Accès")
check_btn = gr.Button("Vérifier véhicule", variant="secondary")
with gr.Row(visible=False) as access_form:
access_status = gr.Radio(
["Autorisé", "Non Autorisé"],
label="Statut d'accès"
)
time_range = gr.Dropdown(
["24/24", "8:00-16:00", "9:00-17:00", "Personnalisé..."],
label="Plage horaire"
)
custom_time = gr.Textbox(
visible=False,
placeholder="HH:MM-HH:MM",
label="Plage horaire personnalisée"
)
save_btn = gr.Button("Enregistrer véhicule", variant="primary")
access_output = gr.Textbox(label="Résultat vérification")
# Colonne de droite - Résultats
with gr.Column(scale=2):
# Visualisation
with gr.Tab("📸 Visualisation"):
original_image = gr.Image(label="Image originale", interactive=False)
processed_image = gr.Image(label="Image annotée", interactive=False)
status_output = gr.Textbox(label="Statut", interactive=False)
# Résultats vidéo
video_output = gr.Gallery(label="Frames traités", visible=False)
video_details = gr.Textbox(label="Détails vidéo", visible=False)
# Résultats complets
with gr.Tab("📊 Résultats complets"):
# Section pour les résultats vidéo
with gr.Accordion("🎥 Résultats de la vidéo", open=True):
video_results = gr.DataFrame(
headers=["Frame", "Véhicule", "Action", "Couleur", "Logo", "Orientation", "Plaque", "Wilaya", "Catégorie", "Année"],
datatype=["str", "str", "str", "str", "str", "str", "str", "str", "str", "str"],
col_count=(10, "fixed"),
visible=False
)
# Section pour les résultats détaillés (frame par frame)
with gr.Accordion("🚗 Caractéristiques du véhicule", open=True):
with gr.Row():
color_output = gr.Textbox(label="Couleur")
orientation_output = gr.Textbox(label="Orientation")
with gr.Row():
logo_output = gr.Textbox(label="Marque détectée")
logo_details = gr.Textbox(label="Détails reconnaissance")
logo_image = gr.Image(label="Logo détecté", visible=False)
with gr.Accordion("🔢 Plaque d'immatriculation", open=False):
with gr.Row():
plate_classification = gr.Textbox(label="Classification", lines=5)
vehicle_type_output = gr.Textbox(label="Type de véhicule")
with gr.Row():
plate_image = gr.Image(label="Plaque détectée")
plate_chars_image = gr.Image(label="Caractères détectés")
plate_chars_list = gr.Textbox(label="Texte détecté")
# Fonctions utilitaires pour l'interface
def update_input_visibility(input_type):
return [
gr.Button(visible=input_type == "Vidéo"), # next_frame_btn
gr.Button(visible=input_type == "Vidéo"), # process_video_btn
gr.Gallery(visible=input_type == "Vidéo"), # video_output
gr.DataFrame(visible=input_type == "Vidéo"), # video_results
gr.Image(visible=input_type != "Vidéo"), # processed_image
gr.Textbox(visible=input_type != "Vidéo") # status_output
]
def toggle_time_range(choice):
"""Afficher/masquer le champ personnalisé"""
if choice == "Personnalisé...":
return gr.Textbox(visible=True)
return gr.Textbox(visible=False)
def verify_vehicle():
"""Vérifier l'existence du véhicule"""
if not shared_results.get("trocr_combined_text"):
raise gr.Error("Aucune plaque détectée")
plate_info = classify_plate(shared_results["trocr_combined_text"])
if not plate_info:
raise gr.Error("Plaque non valide")
exists, message = check_vehicle(plate_info['matricule_complet'])
if exists:
allowed = "✅ ACCÈS AUTORISÉ" if is_access_allowed(plate_info['matricule_complet']) else "❌ ACCÈS REFUSÉ"
return {
access_output: f"{message}\n{allowed}",
access_form: gr.update(visible=False),
save_btn: gr.update(interactive=False)
}
else:
return {
access_output: message,
access_form: gr.update(visible=True),
save_btn: gr.update(interactive=True)
}
def save_vehicle_info(status, time_choice, custom_time_input):
"""Enregistrer les informations du véhicule"""
if not shared_results.get("classified_plate"):
raise gr.Error("Aucune information de plaque disponible")
plate_info = shared_results["classified_plate"]
if time_choice == "Personnalisé...":
if not TIME_PATTERN.match(custom_time_input):
raise gr.Error("Format horaire invalide. Utilisez HH:MM-HH:MM")
time_range = custom_time_input
else:
time_range = time_choice
brand = shared_results.get("vehicle_brand", "Inconnu")
model = shared_results.get("vehicle_model", "Inconnu")
color = shared_results.get("label_color", "Inconnu")
success, message = save_vehicle(
plate_info,
color,
model,
brand,
status,
time_range
)
if not success:
raise gr.Error(message)
return {
access_output: message,
access_form: gr.update(visible=False),
save_btn: gr.update(interactive=False)
}
def process_load(input_type, files):
"""Charger un fichier image ou vidéo"""
if files is None:
raise gr.Error("Veuillez sélectionner un fichier")
file_path = files.name if hasattr(files, 'name') else files
if input_type == "Image" and not file_path.lower().endswith(('.png', '.jpg', '.jpeg')):
raise gr.Error("Veuillez sélectionner une image valide (PNG, JPG, JPEG)")
elif input_type == "Vidéo" and not file_path.lower().endswith(('.mp4', '.avi', '.mov')):
raise gr.Error("Veuillez sélectionner une vidéo valide (MP4, AVI, MOV)")
if input_type == "Vidéo":
cap = cv2.VideoCapture(file_path)
success, frame = cap.read()
cap.release()
if not success:
raise gr.Error("Échec de lecture de la vidéo")
img_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
return [
Image.fromarray(img_rgb),
"Vidéo chargée - Cliquez sur 'Traiter toute la vidéo' ou analysez frame par frame",
gr.Button(visible=True),
gr.Button(visible=True)
]
else:
img = cv2.imread(file_path)
if img is None:
raise gr.Error("Échec de lecture de l'image")
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
return [
Image.fromarray(img_rgb),
"Image chargée - Cliquez sur les boutons pour analyser",
gr.Button(visible=False),
gr.Button(visible=False)
]
def handle_video_processing(video_path):
"""Gérer le traitement de la vidéo complète"""
frames, best_frame = process_video(video_path)
return {
video_output: gr.Gallery(value=frames, visible=True),
processed_image: best_frame["processed_image"],
color_output: f"Couleur: {best_frame['color']}" if best_frame['color'] else "Couleur non détectée",
orientation_output: f"Orientation: {best_frame['orientation']}" if best_frame['orientation'] else "Orientation non détectée",
logo_output: f"Marque: {best_frame['brand']}" if best_frame['brand'] else "Marque non détectée",
model_output: f"Modèle: {best_frame['model']}" if best_frame['model'] else "Modèle non détecté",
plate_classification: best_frame["plate_info"] if best_frame["plate_info"] else "Plaque non classifiée",
vehicle_type_output: f"Type: {best_frame['vehicle_type']}" if best_frame['vehicle_type'] else "Type non détecté"
}
# Connexion des composants
input_type.change(
fn=update_input_visibility,
inputs=input_type,
outputs=[next_frame_btn, process_video_btn, video_output, video_results, processed_image, status_output]
)
time_range.change(
fn=toggle_time_range,
inputs=time_range,
outputs=custom_time
)
load_btn.click(
fn=process_load,
inputs=[input_type, file_input],
outputs=[original_image, status_output, next_frame_btn, process_video_btn]
)
process_video_btn.click(
fn=handle_video_processing,
inputs=file_input,
outputs=[video_output, processed_image, color_output, orientation_output,
logo_output, logo_details, plate_classification, vehicle_type_output]
)
check_btn.click(
fn=verify_vehicle,
outputs=[access_output, access_form, save_btn]
)
save_btn.click(
fn=save_vehicle_info,
inputs=[access_status, time_range, custom_time],
outputs=[access_output, access_form, save_btn]
)
# Connexion des boutons de détection
detect_vehicle_btn.click(
fn=detect_vehicle,
outputs=[status_output, processed_image]
)
detect_color_btn.click(
fn=detect_color,
outputs=[color_output, processed_image]
)
detect_orientation_btn.click(
fn=detect_orientation,
outputs=[orientation_output, processed_image]
)
detect_logo_btn.click(
fn=detect_logo_and_model,
outputs=[logo_output, logo_details, logo_details, processed_image, logo_image]
)
detect_plate_btn.click(
fn=detect_plate,
outputs=[processed_image, plate_image, plate_chars_image, plate_chars_list]
)
classify_plate_btn.click(
fn=classify_plate_number,
outputs=[plate_classification, vehicle_type_output, status_output, access_output]
)
# ------------------------------
# 9. LANCEMENT DE L'APPLICATION
# ------------------------------
if __name__ == "__main__":
init_database() # Initialiser la base de données
demo.launch(share=True)