boulahia commited on
Commit
da991e7
·
verified ·
1 Parent(s): 8d4b1da

Upload 12 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ chevrolet_model_final2.keras filter=lfs diff=lfs merge=lfs -text
37
+ nissan_model_final2.keras filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,1473 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from ultralytics import YOLO
3
+ import cv2
4
+ import numpy as np
5
+ from PIL import Image
6
+ import torch
7
+ from transformers import TrOCRProcessor, VisionEncoderDecoderModel
8
+ from datetime import datetime
9
+ from tensorflow.keras.models import load_model
10
+ import os
11
+ import tempfile
12
+ #ficher.db
13
+ import sqlite3
14
+ from sqlite3 import Error
15
+ import re # Module pour les expressions régulières
16
+
17
+ # ------------------------------
18
+ # 1. CHARGEMENT DES MODÈLES
19
+ # ------------------------------
20
+
21
+ # Modèle CNN pour reconnaissance des logos
22
+ cnn_logo_model = load_model(r"C:\Users\ADMIN\Downloads\VehicleRecognitionApp\best\logo_model_cnn.h5")
23
+ # Chargement automatique des classes depuis le dossier train
24
+ train_dir = r"C:\Users\ADMIN\Downloads\train-20250512T133726Z-1-001\train"
25
+ logo_classes = sorted([d for d in os.listdir(train_dir) if os.path.isdir(os.path.join(train_dir, d))])
26
+ print(f"Classes de logos chargées ({len(logo_classes)}): {logo_classes}")
27
+ # Modèles YOLO
28
+ model_color = YOLO(r"C:\Users\ADMIN\Downloads\VehicleRecognitionApp\best\car_color.pt")
29
+ model_orientation = YOLO(r"C:\Users\ADMIN\Downloads\VehicleRecognitionApp\best\direction_best.pt")
30
+ model_plate_detection = YOLO(r"C:\Users\ADMIN\Downloads\VehicleRecognitionApp\best\plate_detection.pt")
31
+ model_logo_detection = YOLO(r"C:\Users\ADMIN\Downloads\VehicleRecognitionApp\best\car_logo_detection.pt")
32
+ model_characters = YOLO(r"C:\Users\ADMIN\Downloads\VehicleRecognitionApp\best\character_detetion.pt")
33
+ model_vehicle = YOLO(r"C:\Users\ADMIN\Downloads\VehicleRecognitionApp\best\vehicle_detection.pt")
34
+
35
+ # Modèle TrOCR pour reconnaissance de caractères
36
+ trocr_model = VisionEncoderDecoderModel.from_pretrained("microsoft/trocr-base-printed")
37
+ trocr_processor = TrOCRProcessor.from_pretrained("microsoft/trocr-base-printed")
38
+
39
+ # Modèles de reconnaissance de modèle par marque
40
+ model_per_brand = {
41
+ 'nissan': load_model(r"C:\Users\ADMIN\Downloads\VehicleRecognitionApp\best\nissan_model_final2.keras"),
42
+ 'chevrolet': load_model(r"C:\Users\ADMIN\Downloads\VehicleRecognitionApp\best\chevrolet_model_final2.keras"),
43
+ 'honda': load_model(r"C:\Users\ADMIN\Downloads\VehicleRecognitionApp\best\mon_model_cnn_honda.h5"),
44
+ }
45
+
46
+ model_labels = {
47
+ 'nissan': ['nissan-altima', 'nissan-armada', 'nissan-datsun', 'nissan-maxima', 'nissan-navara', 'nissan-patrol', 'nissan-sunny'],
48
+ 'chevrolet': ['chevrolet-aveo', 'chevrolet-impala', 'chevrolet-malibu', 'chevrolet-silverado', 'chevrolet-tahoe', 'chevrolet-traverse'],
49
+ 'honda': ['honda-accord', 'honda-odyssey'],
50
+ }
51
+
52
+ # ------------------------------
53
+ # 2. DICTIONNAIRES DE RÉFÉRENCE
54
+ # ------------------------------
55
+
56
+ CATEGORIES = {
57
+ '1': "Véhicules de tourisme",
58
+ '2': "Camions",
59
+ '3': "Camionnettes",
60
+ '4': "Autocars et autobus",
61
+ '5': "Tracteurs routiers",
62
+ '6': "Autres tracteurs",
63
+ '7': "Véhicules spéciaux",
64
+ '8': "Remorques et semi-remorques",
65
+ '9': "Motocyclettes"
66
+ }
67
+
68
+ WILAYAS = {
69
+ "01": "Adrar", "02": "Chlef", "03": "Laghouat", "04": "Oum El Bouaghi",
70
+ "05": "Batna", "06": "Béjaïa", "07": "Biskra", "08": "Béchar",
71
+ "09": "Blida", "10": "Bouira", "11": "Tamanrasset", "12": "Tébessa",
72
+ "13": "Tlemcen", "14": "Tiaret", "15": "Tizi Ouzou", "16": "Alger",
73
+ "17": "Djelfa", "18": "Jijel", "19": "Sétif", "20": "Saïda",
74
+ "21": "Skikda", "22": "Sidi Bel Abbès", "23": "Annaba", "24": "Guelma",
75
+ "25": "Constantine", "26": "Médéa", "27": "Mostaganem", "28": "MSila",
76
+ "29": "Mascara", "30": "Ouargla", "31": "Oran", "32": "El Bayadh",
77
+ "33": "Illizi", "34": "Bordj Bou Arreridj", "35": "Boumerdès",
78
+ "36": "El Tarf", "37": "Tindouf", "38": "Tissemsilt", "39": "El Oued",
79
+ "40": "Khenchela", "41": "Souk Ahras", "42": "Tipaza", "43": "Mila",
80
+ "44": "Aïn Defla", "45": "Naâma", "46": "Aïn Témouchent",
81
+ "47": "Ghardaïa", "48": "Relizane",
82
+ "49": "El M'Ghair", "50": "El Menia",
83
+ "51": "Ouled Djellal", "52": "Bordj Badji Mokhtar",
84
+ "53": "Béni Abbès", "54": "Timimoun",
85
+ "55": "Touggourt", "56": "Djanet",
86
+ "57": "In Salah", "58": "In Guezzam"
87
+ }
88
+
89
+ # ------------------------------
90
+ # 3. VARIABLES PARTAGÉES
91
+ # ------------------------------
92
+
93
+ shared_results = {
94
+ "original_image": None,
95
+ "img_rgb": None,
96
+ "img_draw": None,
97
+ "plate_crop_img": None,
98
+ "logo_crop_img": None,
99
+ "plate_with_chars_img": None,
100
+ "trocr_char_list": [],
101
+ "trocr_combined_text": "",
102
+ "classification_result": "",
103
+ "label_color": "",
104
+ "label_orientation": "",
105
+ "vehicle_type": "",
106
+ "vehicle_model": "",
107
+ "vehicle_brand": "",
108
+ "logo_recognition_results": [],
109
+ "current_frame": None,
110
+ "video_path": None,
111
+ "video_processing": False,
112
+ "frame_count": 0,
113
+ "total_frames": 0,
114
+ "original_video_dimensions": None,
115
+ "corrected_orientation": False,
116
+ "vehicle_box": None, # Pour stocker les coordonnées du véhicule détecté
117
+ "vehicle_detected": False,
118
+ "detection_boxes": {
119
+ "plate": None,
120
+ "logo": None,
121
+ "color": None,
122
+ "orientation": None
123
+ }
124
+ }
125
+
126
+ # ------------------------------
127
+ # 4. FONCTIONS UTILITAIRES
128
+ # ------------------------------
129
+
130
+ def save_complete_results(plate_info, color, model, orientation, vehicle_type, brand):
131
+ """Sauvegarde toutes les informations dans resultats.txt"""
132
+ with open("/content/drive/MyDrive/resultats.txt", "a", encoding="utf-8") as f:
133
+ f.write("\n" + "="*60 + "\n")
134
+ f.write(f"ANALYSE EFFECTUÉE LE : {datetime.now().strftime('%d/%m/%Y %H:%M:%S')}\n")
135
+ f.write("="*60 + "\n\n")
136
+
137
+ # Section plaque d'immatriculation
138
+ f.write("INFORMATIONS PLAQUE:\n")
139
+ f.write("-"*50 + "\n")
140
+ if plate_info:
141
+ f.write(f"Numéro complet: {plate_info.get('matricule_complet', 'N/A')}\n")
142
+ f.write(f"Wilaya: {plate_info.get('wilaya', ('', 'N/A'))[1]} ({plate_info.get('wilaya', ('', ''))[0]})\n")
143
+ f.write(f"Année: {plate_info.get('annee', 'N/A')}\n")
144
+ f.write(f"Catégorie: {plate_info.get('categorie', ('', 'N/A'))[1]} ({plate_info.get('categorie', ('', ''))[0]})\n")
145
+ f.write(f"Série: {plate_info.get('serie', 'N/A')}\n")
146
+ else:
147
+ f.write("Aucune information de plaque disponible\n")
148
+
149
+ # Section caractéristiques véhicule
150
+ f.write("\nCARACTÉRISTIQUES VÉHICULE:\n")
151
+ f.write("-"*50 + "\n")
152
+ f.write(f"Couleur: {color if color else 'Non détectée'}\n")
153
+ f.write(f"Marque: {brand if brand else 'Non détectée'}\n")
154
+ f.write(f"Modèle: {model if model else 'Non détecté'}\n")
155
+ f.write(f"Orientation: {orientation if orientation else 'Non détectée'}\n")
156
+ f.write(f"Type de véhicule: {vehicle_type if vehicle_type else 'Non détecté'}\n")
157
+ f.write("\n" + "="*60 + "\n\n")
158
+
159
+ def classify_plate(text):
160
+ """Classification des plaques algériennes (10-11 chiffres)"""
161
+ try:
162
+ if not text:
163
+ return None
164
+
165
+ # Nettoyage strict : uniquement les chiffres
166
+ clean_text = ''.join(c for c in text if c.isdigit())
167
+
168
+ # Vérification longueur (10 ou 11 chiffres)
169
+ if len(clean_text) not in {10, 11}:
170
+ return None
171
+
172
+ # Découpage des parties (exemple pour 11 chiffres: 123 456789 01)
173
+ serie = clean_text[:3] # 3 premiers chiffres
174
+ middle = clean_text[3:9] # 6 chiffres centraux
175
+ wilaya_code = clean_text[9:] # 2 derniers chiffres (wilaya)
176
+
177
+ # Vérification wilaya
178
+ wilaya = WILAYAS.get(wilaya_code, ("", "Wilaya inconnue"))
179
+
180
+ # Décodage catégorie (premier chiffre de la partie centrale)
181
+ categorie_code = middle[0]
182
+ annee = middle[1:3] # 2 chiffres pour l'année
183
+
184
+ return {
185
+ 'matricule_complet': clean_text,
186
+ 'serie': serie,
187
+ 'wilaya': (wilaya_code, wilaya[1]),
188
+ 'annee': f"20{annee}",
189
+ 'categorie': (categorie_code, CATEGORIES.get(categorie_code, ("", "Inconnue"))[1]),
190
+ 'is_algerian': True,
191
+ 'length': len(clean_text)
192
+ }
193
+
194
+ except Exception as e:
195
+ print(f"Erreur classification plaque: {str(e)}")
196
+ return None
197
+
198
+
199
+ def predict_brand(image):
200
+ """Prédire la marque de voiture à partir de l'image en utilisant le modèle CNN"""
201
+ try:
202
+ img = Image.fromarray(image).resize((224, 224))
203
+ img_array = np.array(img) / 255.0
204
+ img_array = np.expand_dims(img_array, axis=0)
205
+
206
+ predictions = cnn_logo_model.predict(img_array)
207
+ predicted_class = np.argmax(predictions[0])
208
+ confidence = predictions[0][predicted_class]
209
+
210
+ if confidence < 0.5:
211
+ return "Marque non détectée (confiance trop faible)"
212
+
213
+ brand = logo_classes[predicted_class]
214
+ return f"{brand} (confiance: {confidence:.2f})"
215
+ except Exception as e:
216
+ print(f"Erreur lors de la prédiction de la marque: {str(e)}")
217
+ return "Erreur de détection"
218
+
219
+
220
+ def recognize_logo(cropped_logo):
221
+ """Reconnaître la marque à partir d'un logo détecté"""
222
+ try:
223
+ if cropped_logo.size == 0:
224
+ return "Logo trop petit pour analyse"
225
+
226
+ resized_logo = cv2.resize(np.array(cropped_logo), (128, 128))
227
+ rgb_logo = cv2.cvtColor(resized_logo, cv2.COLOR_BGR2RGB)
228
+ normalized_logo = rgb_logo / 255.0
229
+ input_logo = np.expand_dims(normalized_logo, axis=0)
230
+
231
+ predictions = cnn_logo_model.predict(input_logo, verbose=0)
232
+ pred_index = np.argmax(predictions[0])
233
+ pred_label = logo_classes[pred_index]
234
+ pred_conf = predictions[0][pred_index]
235
+
236
+ if pred_conf < 0.5:
237
+ return f"Marque incertaine: {pred_label} ({pred_conf:.2f})"
238
+
239
+ return f"{pred_label} (confiance: {pred_conf:.2f})"
240
+ except Exception as e:
241
+ print(f"Erreur reconnaissance logo: {str(e)}")
242
+ return "Erreur d'analyse"
243
+
244
+
245
+ #########" recognize modele"
246
+
247
+
248
+ def recognize_model(brand, logo_crop):
249
+ """Reconnaître le modèle spécifique d'une voiture à partir de son logo"""
250
+ try:
251
+ if brand.lower() not in model_per_brand:
252
+ return "Modèle non disponible pour cette marque"
253
+
254
+ if logo_crop.size == 0:
255
+ return "Image trop petite pour analyse"
256
+
257
+ model_recognizer = model_per_brand[brand.lower()]
258
+ model_input_height, model_input_width = model_recognizer.input_shape[1:3]
259
+
260
+ resized_model = cv2.resize(np.array(logo_crop), (model_input_width, model_input_height))
261
+ normalized_model = resized_model / 255.0
262
+ input_model = np.expand_dims(normalized_model, axis=0)
263
+
264
+ model_predictions = model_recognizer.predict(input_model)
265
+ model_index = np.argmax(model_predictions[0])
266
+ model_name = model_labels[brand.lower()][model_index]
267
+
268
+ return model_name
269
+ except Exception as e:
270
+ print(f"Erreur reconnaissance modèle: {str(e)}")
271
+ return "Erreur de détection"
272
+
273
+ def draw_detection_boxes(image):
274
+ """Dessiner toutes les boîtes de détection sur l'image"""
275
+ img_draw = image.copy()
276
+
277
+ # Boîte pour le véhicule (en premier pour qu'elle soit en arrière-plan)
278
+ if shared_results["vehicle_box"]:
279
+ x1, y1, x2, y2 = shared_results["vehicle_box"]
280
+ cv2.rectangle(img_draw, (x1, y1), (x2, y2), (0, 165, 255), 2) # Orange pour véhicule
281
+ cv2.putText(img_draw, "VEHICLE", (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 165, 255), 2)
282
+
283
+ # Boîte pour la plaque
284
+ if shared_results["detection_boxes"]["plate"]:
285
+ x1, y1, x2, y2 = shared_results["detection_boxes"]["plate"]
286
+ cv2.rectangle(img_draw, (x1, y1), (x2, y2), (0, 255, 0), 2) # Vert pour plaque
287
+ cv2.putText(img_draw, "PLATE", (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
288
+
289
+ # Boîte pour le logo
290
+ if shared_results["detection_boxes"]["logo"]:
291
+ x1, y1, x2, y2 = shared_results["detection_boxes"]["logo"]
292
+ cv2.rectangle(img_draw, (x1, y1), (x2, y2), (255, 0, 0), 2) # Bleu pour logo
293
+ cv2.putText(img_draw, "LOGO", (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 0, 0), 2)
294
+
295
+ # Ajouter le modèle si détecté
296
+ if shared_results["vehicle_model"]:
297
+ model_text = shared_results["vehicle_model"].split("(")[0].strip() if "(" in shared_results["vehicle_model"] else shared_results["vehicle_model"]
298
+ cv2.putText(img_draw, f"Model: {model_text}", (x1, y2 + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)
299
+
300
+ # Boîte pour la couleur
301
+ if shared_results["detection_boxes"]["color"]:
302
+ x1, y1, x2, y2 = shared_results["detection_boxes"]["color"]
303
+ cv2.rectangle(img_draw, (x1, y1), (x2, y2), (0, 0, 255), 2) # Rouge pour couleur
304
+ cv2.putText(img_draw, "COLOR", (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2)
305
+
306
+ # Ajouter la couleur détectée
307
+ if shared_results["label_color"]:
308
+ cv2.putText(img_draw, f"{shared_results['label_color']}", (x1, y2 + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
309
+
310
+ # Boîte pour l'orientation
311
+ if shared_results["detection_boxes"]["orientation"]:
312
+ x1, y1, x2, y2 = shared_results["detection_boxes"]["orientation"]
313
+ cv2.rectangle(img_draw, (x1, y1), (x2, y2), (255, 255, 0), 2) # Cyan pour orientation
314
+ cv2.putText(img_draw, "ORIENTATION", (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 255, 0), 2)
315
+
316
+ # Ajouter l'orientation détectée
317
+ if shared_results["label_orientation"]:
318
+ cv2.putText(img_draw, f"{shared_results['label_orientation']}", (x1, y2 + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 0), 2)
319
+
320
+ return img_draw
321
+
322
+ # ------------------------------
323
+ # 5. FONCTIONS PRINCIPALES
324
+ # ------------------------------
325
+
326
+ def load_input(input_data):
327
+ """Charger une image ou une vidéo et préparer le premier frame"""
328
+ if isinstance(input_data, str): # Fichier (vidéo ou image)
329
+ if input_data.lower().endswith(('.png', '.jpg', '.jpeg')):
330
+ # Traitement comme une image
331
+ return load_image(input_data)
332
+ else:
333
+ # Traitement comme une vidéo
334
+ return load_video(input_data)
335
+ else: # Image directe (numpy array)
336
+ return load_image(input_data)
337
+
338
+ def load_image(image_path):
339
+ """Charger et préparer l'image de base"""
340
+ if isinstance(image_path, str):
341
+ img = cv2.imread(image_path)
342
+ else: # Si c'est déjà un numpy array (cas du fichier uploadé)
343
+ img = cv2.cvtColor(image_path, cv2.COLOR_RGB2BGR)
344
+
345
+ if img is None:
346
+ raise gr.Error("Échec de lecture de l'image")
347
+
348
+ img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
349
+ img_draw = img_rgb.copy()
350
+
351
+ shared_results["original_image"] = img
352
+ shared_results["img_rgb"] = img_rgb
353
+ shared_results["img_draw"] = img_draw
354
+ shared_results["video_processing"] = False
355
+ shared_results["corrected_orientation"] = False
356
+
357
+ # Réinitialiser les boîtes de détection
358
+ shared_results["detection_boxes"] = {
359
+ "plate": None,
360
+ "logo": None,
361
+ "color": None,
362
+ "orientation": None
363
+ }
364
+
365
+ return Image.fromarray(img_rgb)
366
+
367
+ def load_video(video_path):
368
+ """Charger une vidéo et préparer le premier frame"""
369
+ cap = cv2.VideoCapture(video_path)
370
+ if not cap.isOpened():
371
+ raise gr.Error("Échec de lecture de la vidéo")
372
+
373
+ # Sauvegarder le chemin de la vidéo et les informations
374
+ shared_results["video_path"] = video_path
375
+ shared_results["video_processing"] = True
376
+ shared_results["frame_count"] = 0
377
+ shared_results["total_frames"] = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
378
+
379
+ # Lire les dimensions originales
380
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
381
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
382
+ shared_results["original_video_dimensions"] = (width, height)
383
+
384
+ # Lire le premier frame
385
+ success, frame = cap.read()
386
+ cap.release()
387
+
388
+ if not success:
389
+ raise gr.Error("Échec de lecture du premier frame de la vidéo")
390
+
391
+ img_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
392
+ img_draw = img_rgb.copy()
393
+
394
+ shared_results["original_image"] = frame
395
+ shared_results["img_rgb"] = img_rgb
396
+ shared_results["img_draw"] = img_draw
397
+ shared_results["current_frame"] = frame
398
+ shared_results["corrected_orientation"] = False
399
+
400
+ # Réinitialiser les boîtes de détection
401
+ shared_results["detection_boxes"] = {
402
+ "plate": None,
403
+ "logo": None,
404
+ "color": None,
405
+ "orientation": None
406
+ }
407
+
408
+ return Image.fromarray(img_rgb)
409
+
410
+ def get_next_video_frame():
411
+ """Obtenir le frame suivant de la vidéo en cours"""
412
+ if not shared_results["video_processing"] or not shared_results["video_path"]:
413
+ return None
414
+
415
+ cap = cv2.VideoCapture(shared_results["video_path"])
416
+ if not cap.isOpened():
417
+ return None
418
+
419
+ # Aller au frame suivant
420
+ shared_results["frame_count"] += 1
421
+ cap.set(cv2.CAP_PROP_POS_FRAMES, shared_results["frame_count"])
422
+
423
+ success, frame = cap.read()
424
+ cap.release()
425
+
426
+ if not success:
427
+ # Fin de la vidéo, réinitialiser
428
+ shared_results["frame_count"] = 0
429
+ cap = cv2.VideoCapture(shared_results["video_path"])
430
+ success, frame = cap.read()
431
+ cap.release()
432
+ if not success:
433
+ return None
434
+
435
+ # Conserver les dimensions originales
436
+ frame = cv2.resize(frame, shared_results["original_video_dimensions"])
437
+
438
+ img_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
439
+ img_draw = img_rgb.copy()
440
+
441
+ shared_results["original_image"] = frame
442
+ shared_results["img_rgb"] = img_rgb
443
+ shared_results["img_draw"] = img_draw
444
+ shared_results["current_frame"] = frame
445
+ shared_results["corrected_orientation"] = False
446
+
447
+ # Réinitialiser les boîtes de détection
448
+ shared_results["detection_boxes"] = {
449
+ "plate": None,
450
+ "logo": None,
451
+ "color": None,
452
+ "orientation": None
453
+ }
454
+
455
+ return Image.fromarray(img_rgb)
456
+
457
+ # 3. Ajouter une fonction pour détecter les véhicules
458
+ def detect_vehicle():
459
+ """Détecter le véhicule principal dans l'image"""
460
+ if shared_results["img_rgb"] is None:
461
+ return "Veuillez d'abord charger une image/vidéo", None
462
+
463
+ img_to_process = shared_results["img_rgb"]
464
+ if shared_results.get("corrected_orientation", False):
465
+ height, width = img_to_process.shape[:2]
466
+ if height > width: # Portrait, besoin de rotation
467
+ img_to_process = cv2.rotate(img_to_process, cv2.ROTATE_90_CLOCKWISE)
468
+
469
+ results_vehicle = model_vehicle(img_to_process)
470
+ img_with_boxes = shared_results["img_rgb"].copy()
471
+ vehicle_detected = False
472
+
473
+ for r in results_vehicle:
474
+ if hasattr(r, 'boxes') and r.boxes and hasattr(r.boxes, 'cls') and len(r.boxes.cls) > 0:
475
+ # Prendre la plus grande détection (supposée être le véhicule principal)
476
+ largest_box = None
477
+ max_area = 0
478
+ for box in r.boxes:
479
+ x1, y1, x2, y2 = map(int, box.xyxy[0])
480
+ area = (x2 - x1) * (y2 - y1)
481
+ if area > max_area:
482
+ max_area = area
483
+ largest_box = (x1, y1, x2, y2)
484
+
485
+ if largest_box:
486
+ x1, y1, x2, y2 = largest_box
487
+ shared_results["vehicle_box"] = largest_box
488
+ shared_results["vehicle_detected"] = True
489
+ vehicle_detected = True
490
+
491
+ # Dessiner le rectangle autour du véhicule
492
+ cv2.rectangle(img_with_boxes, (x1, y1), (x2, y2), (0, 165, 255), 2) # Orange pour véhicule
493
+ cv2.putText(img_with_boxes, "VEHICLE", (x1, y1 - 10),
494
+ cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 165, 255), 2)
495
+
496
+ shared_results["img_draw"] = img_with_boxes
497
+
498
+ if vehicle_detected:
499
+ return "Véhicule détecté - Vous pouvez maintenant détecter la couleur", Image.fromarray(img_with_boxes)
500
+ else:
501
+ shared_results["vehicle_box"] = None
502
+ shared_results["vehicle_detected"] = False
503
+ return "Aucun véhicule détecté - La détection de couleur sera moins précise", Image.fromarray(img_with_boxes)
504
+
505
+ # 4. Modifier la fonction detect_color() pour utiliser la zone du véhicule si disponible
506
+ def detect_color():
507
+ """Détecter la couleur du véhicule dans la zone détectée"""
508
+ if shared_results["img_rgb"] is None:
509
+ return "Veuillez d'abord charger une image/vidéo", None
510
+
511
+ img_to_process = shared_results["img_rgb"]
512
+ if shared_results.get("corrected_orientation", False):
513
+ height, width = img_to_process.shape[:2]
514
+ if height > width: # Portrait, besoin de rotation
515
+ img_to_process = cv2.rotate(img_to_process, cv2.ROTATE_90_CLOCKWISE)
516
+
517
+ # Si un véhicule a été détecté, utiliser cette zone pour la détection de couleur
518
+ if shared_results["vehicle_detected"] and shared_results["vehicle_box"]:
519
+ x1, y1, x2, y2 = shared_results["vehicle_box"]
520
+ vehicle_roi = img_to_process[y1:y2, x1:x2]
521
+ results_color = model_color(vehicle_roi)
522
+ else:
523
+ results_color = model_color(img_to_process)
524
+
525
+ img_with_boxes = shared_results["img_draw"].copy() if shared_results["img_draw"] is not None else img_to_process.copy()
526
+ color_detected = False
527
+
528
+ for r in results_color:
529
+ if hasattr(r, 'boxes') and r.boxes and hasattr(r.boxes, 'cls') and len(r.boxes.cls) > 0:
530
+ cls = int(r.boxes.cls[0])
531
+ shared_results["label_color"] = r.names[cls]
532
+
533
+ # Si on a utilisé la ROI du véhicule, ajuster les coordonnées
534
+ if shared_results["vehicle_detected"] and shared_results["vehicle_box"]:
535
+ vx1, vy1, vx2, vy2 = shared_results["vehicle_box"]
536
+ box = r.boxes.xyxy[0].cpu().numpy()
537
+ x1, y1, x2, y2 = map(int, box)
538
+ # Convertir les coordonnées relatives à la ROI en coordonnées absolues
539
+ abs_x1 = vx1 + x1
540
+ abs_y1 = vy1 + y1
541
+ abs_x2 = vx1 + x2
542
+ abs_y2 = vy1 + y2
543
+ shared_results["detection_boxes"]["color"] = (abs_x1, abs_y1, abs_x2, abs_y2)
544
+ else:
545
+ box = r.boxes.xyxy[0].cpu().numpy()
546
+ x1, y1, x2, y2 = map(int, box)
547
+ shared_results["detection_boxes"]["color"] = (x1, y1, x2, y2)
548
+
549
+ color_detected = True
550
+
551
+ # Mettre à jour l'image avec toutes les détections
552
+ img_with_boxes = draw_detection_boxes(img_with_boxes)
553
+ shared_results["img_draw"] = img_with_boxes
554
+
555
+ if color_detected:
556
+ return f"Couleur: {shared_results['label_color']}", Image.fromarray(img_with_boxes)
557
+ else:
558
+ return "Couleur non détectée", Image.fromarray(img_with_boxes)
559
+
560
+ def detect_orientation():
561
+ """Détecter l'orientation du véhicule"""
562
+ if shared_results["img_rgb"] is None:
563
+ return "Veuillez d'abord charger une image/vidéo"
564
+
565
+ # S'assurer que l'image est dans le bon sens
566
+ img_to_process = shared_results["img_rgb"]
567
+ if shared_results["video_processing"]:
568
+ # Pour les vidéos, vérifier l'orientation et corriger si nécessaire
569
+ height, width = img_to_process.shape[:2]
570
+ if height > width: # Portrait, besoin de rotation
571
+ img_to_process = cv2.rotate(img_to_process, cv2.ROTATE_90_CLOCKWISE)
572
+ shared_results["corrected_orientation"] = True
573
+
574
+ results_orientation = model_orientation(img_to_process)
575
+ for r in results_orientation:
576
+ if hasattr(r, 'boxes') and r.boxes and hasattr(r.boxes, 'cls') and len(r.boxes.cls) > 0:
577
+ cls = int(r.boxes.cls[0])
578
+ shared_results["label_orientation"] = r.names[cls]
579
+
580
+ # Enregistrer la boîte de détection
581
+ box = r.boxes.xyxy[0].cpu().numpy()
582
+ x1, y1, x2, y2 = map(int, box)
583
+ shared_results["detection_boxes"]["orientation"] = (x1, y1, x2, y2)
584
+
585
+ # Mettre à jour l'image avec toutes les détections
586
+ img_with_boxes = draw_detection_boxes(shared_results["img_rgb"])
587
+ shared_results["img_draw"] = img_with_boxes
588
+
589
+ return f"Orientation: {shared_results['label_orientation']}" if shared_results['label_orientation'] else "Orientation non détectée", Image.fromarray(img_with_boxes)
590
+
591
+ def detect_logo_and_model():
592
+ """Détecter et reconnaître le logo et le modèle du véhicule"""
593
+ if shared_results["img_rgb"] is None:
594
+ return "Veuillez d'abord charger une image", None, None, None, None
595
+
596
+ shared_results["logo_recognition_results"] = []
597
+ img_draw = shared_results["img_draw"].copy()
598
+ detected_model = "Modèle non détecté"
599
+
600
+ results_logo = model_logo_detection(shared_results["img_rgb"])
601
+ if results_logo and results_logo[0].boxes:
602
+ for box in results_logo[0].boxes:
603
+ x1, y1, x2, y2 = map(int, box.xyxy[0])
604
+ cv2.rectangle(img_draw, (x1, y1), (x2, y2), (255, 0, 0), 2)
605
+
606
+ logo_crop = shared_results["img_rgb"][y1:y2, x1:x2]
607
+ shared_results["logo_crop_img"] = Image.fromarray(logo_crop)
608
+
609
+ # Reconnaissance du logo (marque)
610
+ logo_recognition = recognize_logo(shared_results["logo_crop_img"])
611
+ shared_results["logo_recognition_results"].append(logo_recognition)
612
+
613
+ cv2.putText(img_draw, "LOGO", (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255,0,0), 2)
614
+
615
+ if not shared_results["vehicle_brand"] or "confiance" not in shared_results["vehicle_brand"]:
616
+ shared_results["vehicle_brand"] = logo_recognition
617
+
618
+ # Reconnaissance du modèle spécifique si la marque est reconnue
619
+ brand = None
620
+ if "(" in logo_recognition: # Format: "Marque (confiance: 0.xx)"
621
+ brand = logo_recognition.split("(")[0].strip().lower()
622
+ else:
623
+ brand = logo_recognition.lower()
624
+
625
+ if brand in model_per_brand:
626
+ try:
627
+ detected_model = recognize_model(brand, shared_results["logo_crop_img"])
628
+
629
+ # Mise à jour du texte sur l'image
630
+ cv2.putText(img_draw, f"Modèle: {detected_model}", (x1, y2 + 20),
631
+ cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)
632
+ except Exception as e:
633
+ print(f"Erreur reconnaissance modèle: {str(e)}")
634
+ detected_model = "Erreur reconnaissance modèle"
635
+
636
+ shared_results["img_draw"] = img_draw
637
+ shared_results["vehicle_model"] = detected_model
638
+
639
+ # Détection globale de la marque si la détection du logo a échoué
640
+ if not shared_results["vehicle_brand"] or "incertaine" in shared_results["vehicle_brand"] or "Erreur" in shared_results["vehicle_brand"]:
641
+ global_brand = predict_brand(shared_results["img_rgb"])
642
+ if global_brand and "non détectée" not in global_brand:
643
+ shared_results["vehicle_brand"] = global_brand
644
+
645
+ logo_results_text = " | ".join(shared_results["logo_recognition_results"]) if shared_results["logo_recognition_results"] else "Aucun logo reconnu"
646
+
647
+ return (
648
+ f"Marque: {shared_results['vehicle_brand']}" if shared_results['vehicle_brand'] else "Marque non détectée",
649
+ f"Modèle: {shared_results['vehicle_model']}" if shared_results['vehicle_model'] else "Modèle non détecté",
650
+ f"Reconnaissance logo: {logo_results_text}",
651
+ Image.fromarray(img_draw),
652
+ shared_results["logo_crop_img"]
653
+ )
654
+
655
+ def detect_plate():
656
+ """Détecter la plaque d'immatriculation et reconnaître les caractères"""
657
+ if shared_results["img_rgb"] is None:
658
+ return "Veuillez d'abord charger une image/vidéo", None, None, None
659
+
660
+ shared_results["trocr_char_list"] = []
661
+ shared_results["trocr_combined_text"] = ""
662
+ img_to_process = shared_results["img_rgb"]
663
+
664
+ # Utiliser l'image corrigée si nécessaire
665
+ if shared_results.get("corrected_orientation", False):
666
+ height, width = img_to_process.shape[:2]
667
+ if height > width: # Portrait, besoin de rotation
668
+ img_to_process = cv2.rotate(img_to_process, cv2.ROTATE_90_CLOCKWISE)
669
+
670
+ # Si un véhicule a été détecté, utiliser cette zone pour la détection
671
+ if shared_results["vehicle_detected"] and shared_results["vehicle_box"]:
672
+ vx1, vy1, vx2, vy2 = shared_results["vehicle_box"]
673
+ roi = img_to_process[vy1:vy2, vx1:vx2]
674
+ results_plate = model_plate_detection(roi)
675
+ else:
676
+ results_plate = model_plate_detection(img_to_process)
677
+
678
+ if results_plate and results_plate[0].boxes:
679
+ for box in results_plate[0].boxes:
680
+ # Ajuster les coordonnées si on a utilisé la ROI du véhicule
681
+ if shared_results["vehicle_detected"] and shared_results["vehicle_box"]:
682
+ vx1, vy1, vx2, vy2 = shared_results["vehicle_box"]
683
+ rx1, ry1, rx2, ry2 = map(int, box.xyxy[0])
684
+ # Convertir en coordonnées absolues
685
+ x1 = vx1 + rx1
686
+ y1 = vy1 + ry1
687
+ x2 = vx1 + rx2
688
+ y2 = vy1 + ry2
689
+ else:
690
+ x1, y1, x2, y2 = map(int, box.xyxy[0])
691
+
692
+ shared_results["detection_boxes"]["plate"] = (x1, y1, x2, y2)
693
+ plate_crop = img_to_process[y1:y2, x1:x2]
694
+ shared_results["plate_crop_img"] = Image.fromarray(plate_crop)
695
+ plate_for_char_draw = plate_crop.copy()
696
+
697
+ # Détection des caractères
698
+ results_chars = model_characters(plate_crop)
699
+ char_boxes = []
700
+ for r in results_chars:
701
+ if r.boxes:
702
+ for box in r.boxes:
703
+ x1c, y1c, x2c, y2c = map(int, box.xyxy[0])
704
+ char_boxes.append(((x1c, y1c, x2c, y2c), x1c))
705
+
706
+ char_boxes.sort(key=lambda x: x[1])
707
+
708
+ for i, (coords, _) in enumerate(char_boxes):
709
+ x1c, y1c, x2c, y2c = coords
710
+ char_crop = plate_crop[y1c:y2c, x1c:x2c]
711
+ char_pil = Image.fromarray(char_crop).convert("RGB")
712
+
713
+ try:
714
+ inputs = trocr_processor(images=char_pil, return_tensors="pt").pixel_values
715
+ generated_ids = trocr_model.generate(inputs)
716
+ predicted_char = trocr_processor.batch_decode(generated_ids, skip_special_tokens=True)[0].strip()
717
+ shared_results["trocr_char_list"].append(predicted_char)
718
+ except Exception as e:
719
+ shared_results["trocr_char_list"].append("?")
720
+
721
+ cv2.rectangle(plate_for_char_draw, (x1c, y1c), (x2c, y2c), (255, 0, 255), 1)
722
+ cv2.putText(plate_for_char_draw, predicted_char, (x1c, y1c - 5),
723
+ cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 255), 1)
724
+
725
+ shared_results["plate_with_chars_img"] = Image.fromarray(plate_for_char_draw)
726
+ shared_results["trocr_combined_text"] = ''.join(shared_results["trocr_char_list"])
727
+ break
728
+
729
+ # Mettre à jour l'image avec toutes les détections
730
+ img_with_boxes = draw_detection_boxes(shared_results["img_rgb"])
731
+ shared_results["img_draw"] = img_with_boxes
732
+
733
+ return (
734
+ Image.fromarray(img_with_boxes),
735
+ shared_results["plate_crop_img"],
736
+ shared_results["plate_with_chars_img"],
737
+ shared_results["trocr_char_list"]
738
+ )
739
+
740
+ def is_empty_plate(cropped_plate_image):
741
+ """Détecte si la plaque est visuellement vide (espace blanc)"""
742
+ if cropped_plate_image is None:
743
+ return True
744
+
745
+ # Convertir en numpy array si c'est une image PIL
746
+ if isinstance(cropped_plate_image, Image.Image):
747
+ plate_img = np.array(cropped_plate_image)
748
+ else:
749
+ plate_img = cropped_plate_image
750
+
751
+ # Convertir en niveaux de gris
752
+ gray = cv2.cvtColor(plate_img, cv2.COLOR_RGB2GRAY)
753
+
754
+ # Seuillage pour détecter les zones non blanches
755
+ _, thresholded = cv2.threshold(gray, 220, 255, cv2.THRESH_BINARY_INV)
756
+
757
+ # Compter les pixels non blancs (potentiels caractères)
758
+ non_white_pixels = cv2.countNonZero(thresholded)
759
+
760
+ # Si moins de 1% de pixels non blancs, considérer comme vide
761
+ total_pixels = gray.shape[0] * gray.shape[1]
762
+ return non_white_pixels < (0.01 * total_pixels)
763
+
764
+
765
+ def classify_plate_number():
766
+ """Fonction complète de classification des plaques algériennes avec gestion des cas spéciaux"""
767
+ # 1. Vérification initiale - Aucune détection de plaque
768
+ if not shared_results.get("plate_crop_img"):
769
+ return (
770
+ "Aucune plaque détectée",
771
+ "Type: Non déterminé",
772
+ "❌ Aucune détection",
773
+ "Action: Ajuster l'angle de vue ou la distance"
774
+ )
775
+
776
+ # 2. Cas spécial - Plaque détectée mais visuellement vide (espace blanc)
777
+ plate_img = shared_results["plate_crop_img"]
778
+ if isinstance(plate_img, Image.Image):
779
+ plate_img = np.array(plate_img)
780
+
781
+ gray = cv2.cvtColor(plate_img, cv2.COLOR_RGB2GRAY)
782
+ _, thresholded = cv2.threshold(gray, 220, 255, cv2.THRESH_BINARY_INV)
783
+ non_white_pixels = cv2.countNonZero(thresholded)
784
+
785
+ if non_white_pixels < (0.01 * gray.size):
786
+ return (
787
+ "Zone de plaque détectée mais vide",
788
+ "Type: Non déterminé",
789
+ "⚠️ Plaque blanche détectée",
790
+ "Action: Vérifier si la plaque est masquée"
791
+ )
792
+
793
+ # 3. Cas où du texte est détecté
794
+ if not shared_results.get("trocr_combined_text"):
795
+ return (
796
+ "Plaque détectée mais aucun caractère reconnu",
797
+ "Type: Non déterminé",
798
+ "⚠️ Plaque illisible",
799
+ "Action: Améliorer la qualité d'image"
800
+ )
801
+
802
+ plate_text = shared_results["trocr_combined_text"].strip()
803
+
804
+ # 4. Vérification présence de lettres (rejet immédiat)
805
+ if any(c.isalpha() for c in plate_text):
806
+ return (
807
+ f"Texte rejeté: {plate_text}",
808
+ "Type: Non déterminé",
809
+ "❌ NON ALGÉRIEN (lettres détectées)",
810
+ "Action: Contrôle immédiat requis"
811
+ )
812
+
813
+ # 5. Extraction des chiffres uniquement
814
+ digits = ''.join(c for c in plate_text if c.isdigit())
815
+
816
+ # 6. Vérification longueur (10-11 chiffres)
817
+ if len(digits) not in {10, 11}:
818
+ return (
819
+ f"Texte rejeté: {plate_text} ({len(digits)} chiffres)",
820
+ "Type: Non déterminé",
821
+ f"❌ NON ALGÉRIEN (format invalide)",
822
+ "Action: Contrôle requis"
823
+ )
824
+
825
+ # 7. Classification algérienne standard
826
+ try:
827
+ # Découpage des parties (ex: 123 456789 01)
828
+ serie = digits[:3]
829
+ middle = digits[3:9]
830
+ wilaya_code = digits[9:]
831
+
832
+ # Validation wilaya
833
+ wilaya = WILAYAS.get(wilaya_code, ("", "Wilaya inconnue"))
834
+ if wilaya_code not in WILAYAS:
835
+ return (
836
+ f"Plaque: {digits}",
837
+ "Type: Non déterminé",
838
+ f"❌ Wilaya {wilaya_code} inconnue",
839
+ "Action: Vérification manuelle"
840
+ )
841
+
842
+ # Décodage catégorie et année
843
+ categorie_code = middle[0]
844
+ annee = f"20{middle[1:3]}"
845
+ categorie = CATEGORIES.get(categorie_code, ("", "Inconnue"))
846
+
847
+ # Construction du résultat
848
+ result = {
849
+ 'matricule_complet': digits,
850
+ 'serie': serie,
851
+ 'wilaya': (wilaya_code, wilaya[1]),
852
+ 'annee': annee,
853
+ 'categorie': (categorie_code, categorie[1]),
854
+ 'length': len(digits)
855
+ }
856
+
857
+ shared_results["classified_plate"] = result
858
+ shared_results["vehicle_type"] = categorie[1]
859
+
860
+ return (
861
+ f"Plaque: {digits}\n"
862
+ f"Wilaya: {wilaya[1]} ({wilaya_code})\n"
863
+ f"Année: {annee}\n"
864
+ f"Catégorie: {categorie[1]}\n"
865
+ f"Série: {serie}",
866
+ f"Type: {categorie[1]}",
867
+ "✅ PLAQUE ALGÉRIENNE VALIDE",
868
+ "Action: Vérification standard"
869
+ )
870
+
871
+ except Exception as e:
872
+ print(f"Erreur classification: {str(e)}")
873
+ return (
874
+ f"Erreur d'analyse: {plate_text}",
875
+ "Type: Non déterminé",
876
+ "❌ Erreur de traitement",
877
+ "Action: Contrôle technique requis"
878
+ )
879
+
880
+ def next_frame():
881
+ """Passer au frame suivant dans une vidéo"""
882
+ if not shared_results["video_processing"] or not shared_results["video_path"]:
883
+ return (
884
+ "Aucune vidéo en cours de traitement",
885
+ None, # original_image
886
+ None, # status_output
887
+ None, # color_output
888
+ None, # orientation_output
889
+ None, # logo_output
890
+ None, # model_output
891
+ None, # plate_classification
892
+ None # vehicle_type_output
893
+ )
894
+
895
+ cap = cv2.VideoCapture(shared_results["video_path"])
896
+ if not cap.isOpened():
897
+ return (
898
+ "Erreur de lecture de la vidéo",
899
+ None, None, None, None, None, None, None, None
900
+ )
901
+
902
+ # Aller au frame suivant
903
+ shared_results["frame_count"] += 1
904
+ cap.set(cv2.CAP_PROP_POS_FRAMES, shared_results["frame_count"])
905
+ success, frame = cap.read()
906
+ cap.release()
907
+
908
+ if not success:
909
+ # Fin de la vidéo atteinte, revenir au début
910
+ shared_results["frame_count"] = 0
911
+ cap = cv2.VideoCapture(shared_results["video_path"])
912
+ success, frame = cap.read()
913
+ cap.release()
914
+ if not success:
915
+ return (
916
+ "Erreur de lecture du premier frame",
917
+ None, None, None, None, None, None, None, None
918
+ )
919
+
920
+ # Conserver les dimensions originales
921
+ frame = cv2.resize(frame, shared_results["original_video_dimensions"])
922
+
923
+ # Convertir et préparer l'image
924
+ img_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
925
+ img_draw = img_rgb.copy()
926
+
927
+ # Mettre à jour les résultats partagés
928
+ shared_results.update({
929
+ "original_image": frame,
930
+ "img_rgb": img_rgb,
931
+ "img_draw": img_draw,
932
+ "current_frame": frame,
933
+ "corrected_orientation": False,
934
+ "label_color": "",
935
+ "label_orientation": "",
936
+ "vehicle_type": "",
937
+ "vehicle_model": "",
938
+ "vehicle_brand": "",
939
+ "logo_recognition_results": [],
940
+ "trocr_char_list": [],
941
+ "trocr_combined_text": "",
942
+ "classification_result": "",
943
+ "vehicle_box": None,
944
+ "vehicle_detected": False,
945
+ "detection_boxes": {
946
+ "plate": None,
947
+ "logo": None,
948
+ "color": None,
949
+ "orientation": None
950
+ },
951
+ "plate_crop_img": None,
952
+ "logo_crop_img": None,
953
+ "plate_with_chars_img": None
954
+ })
955
+
956
+ # Retourner les résultats
957
+ return (
958
+ Image.fromarray(img_rgb), # original_image
959
+ f"Frame {shared_results['frame_count']}/{shared_results['total_frames']} - Prêt pour analyse", # status_output
960
+ None, # color_output (réinitialisé)
961
+ None, # orientation_output (réinitialisé)
962
+ None, # logo_output (réinitialisé)
963
+ None, # model_output (réinitialisé)
964
+ None, # plate_classification (réinitialisé)
965
+ None # vehicle_type_output (réinitialisé)
966
+ )
967
+
968
+
969
+ # ------------------------------
970
+ # 7. GESTION BASE DE DONNÉES VÉHICULES
971
+ # ------------------------------
972
+
973
+ DB_PATH = "/content/drive/MyDrive/vehicules_database.db"
974
+ TIME_PATTERN = re.compile(r'^\d{2}:\d{2}-\d{2}:\d{2}$')
975
+
976
+ def create_connection():
977
+ """Créer une connexion à la base SQLite"""
978
+ conn = None
979
+ try:
980
+ conn = sqlite3.connect(DB_PATH)
981
+ return conn
982
+ except Error as e:
983
+ print(f"Erreur de connexion à SQLite: {e}")
984
+ return conn
985
+
986
+ def init_database():
987
+ """Initialiser la base de données SQLite"""
988
+ conn = create_connection()
989
+ if conn is not None:
990
+ try:
991
+ cursor = conn.cursor()
992
+ cursor.execute("""
993
+ CREATE TABLE IF NOT EXISTS vehicules (
994
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
995
+ plaque TEXT NOT NULL UNIQUE,
996
+ marque TEXT,
997
+ modele TEXT,
998
+ couleur TEXT,
999
+ statut TEXT,
1000
+ plage_horaire TEXT,
1001
+ date_enregistrement TEXT
1002
+ )
1003
+ """)
1004
+ conn.commit()
1005
+ except Error as e:
1006
+ print(f"Erreur création table: {e}")
1007
+ finally:
1008
+ conn.close()
1009
+
1010
+ def check_vehicle(plate_text):
1011
+ """Vérifier si un véhicule existe dans la base"""
1012
+ init_database()
1013
+ conn = create_connection()
1014
+ if conn is not None:
1015
+ try:
1016
+ cursor = conn.cursor()
1017
+ cursor.execute("SELECT statut, plage_horaire FROM vehicules WHERE plaque = ?", (plate_text,))
1018
+ result = cursor.fetchone()
1019
+
1020
+ if result:
1021
+ return True, f"Statut: {result[0]} | Accès: {result[1]}"
1022
+ return False, "Véhicule non enregistré"
1023
+ except Error as e:
1024
+ print(f"Erreur lecture base: {e}")
1025
+ return False, "Erreur base de données"
1026
+ finally:
1027
+ conn.close()
1028
+ return False, "Erreur de connexion"
1029
+
1030
+ def save_vehicle(plate_info, color, model, brand, status, time_range):
1031
+ """Enregistrer un nouveau véhicule"""
1032
+ init_database()
1033
+ conn = create_connection()
1034
+ if conn is not None:
1035
+ try:
1036
+ # Nettoyer les données
1037
+ plate_number = str(plate_info['matricule_complet']).strip()
1038
+ clean_brand = brand.split('(')[0].strip() if '(' in brand else brand
1039
+ clean_model = model.split('(')[0].strip() if '(' in model else model
1040
+
1041
+ cursor = conn.cursor()
1042
+
1043
+ # Vérifier si le véhicule existe déjà
1044
+ cursor.execute("SELECT 1 FROM vehicules WHERE plaque = ?", (plate_number,))
1045
+ if cursor.fetchone():
1046
+ return False, "Véhicule déjà existant"
1047
+
1048
+ # Insérer le nouveau véhicule
1049
+ cursor.execute("""
1050
+ INSERT INTO vehicules (plaque, marque, modele, couleur, statut, plage_horaire, date_enregistrement)
1051
+ VALUES (?, ?, ?, ?, ?, ?, ?)
1052
+ """, (
1053
+ plate_number,
1054
+ clean_brand,
1055
+ clean_model,
1056
+ color,
1057
+ status,
1058
+ time_range,
1059
+ datetime.now().strftime("%Y-%m-%d %H:%M:%S")
1060
+ ))
1061
+
1062
+ conn.commit()
1063
+ return True, "Enregistrement réussi"
1064
+ except Error as e:
1065
+ return False, f"Erreur enregistrement: {e}"
1066
+ finally:
1067
+ conn.close()
1068
+ return False, "Erreur de connexion"
1069
+
1070
+ def is_access_allowed(plate_text):
1071
+ """Vérifier si l'accès est autorisé à l'heure actuelle"""
1072
+ conn = create_connection()
1073
+ if conn is not None:
1074
+ try:
1075
+ cursor = conn.cursor()
1076
+ cursor.execute("SELECT statut, plage_horaire FROM vehicules WHERE plaque = ?", (plate_text,))
1077
+ vehicle = cursor.fetchone()
1078
+
1079
+ if not vehicle:
1080
+ return False
1081
+
1082
+ if vehicle[0] == "Non Autorisé":
1083
+ return False
1084
+
1085
+ if vehicle[1] == "24/24":
1086
+ return True
1087
+
1088
+ current_time = datetime.now().time()
1089
+ start_str, end_str = vehicle[1].split('-')
1090
+ start = time(*map(int, start_str.split(':')))
1091
+ end = time(*map(int, end_str.split(':')))
1092
+
1093
+ return start <= current_time <= end
1094
+ except Error as e:
1095
+ print(f"Erreur vérification accès: {e}")
1096
+ return False
1097
+ finally:
1098
+ conn.close()
1099
+ return False
1100
+
1101
+
1102
+ # ------------------------------
1103
+ # 6. INTERFACE GRADIO
1104
+ # ------------------------------
1105
+
1106
+ with gr.Blocks(title="🚗 Système de Reconnaissance de Véhicules Algériens") as demo:
1107
+ gr.Markdown("# 🚗 Système de Reconnaissance de Véhicules Algériens")
1108
+ gr.Markdown("Détection de plaque d'immatriculation, logo, couleur, modèle et autres caractéristiques du véhicule")
1109
+
1110
+ with gr.Row():
1111
+ with gr.Column():
1112
+ # Section de chargement améliorée
1113
+ input_type = gr.Radio(["Image", "Vidéo"], label="Type d'entrée", value="Image", interactive=True)
1114
+ file_input = gr.File(label="Déposer le fichier ici - ou - Cliquez pour télécharger",
1115
+ file_types=["image", "video"])
1116
+ load_btn = gr.Button("Charger le fichier")
1117
+
1118
+ ###########################""
1119
+ # Nouveaux éléments pour la sélection de frame
1120
+ frame_gallery = gr.Gallery(visible=False, label="Sélectionnez un frame", columns=4)
1121
+ frame_slider = gr.Slider(visible=False, interactive=True, label="Frame sélectionné")
1122
+ load_frame_btn = gr.Button(visible=False, value="Charger le frame sélectionné")
1123
+ #########################
1124
+
1125
+ # Détections
1126
+ with gr.Row():
1127
+ detect_vehicle_btn = gr.Button("Détection de véhicule")
1128
+ detect_color_btn = gr.Button("Détection de couleur")
1129
+
1130
+ with gr.Row():
1131
+ detect_orientation_btn = gr.Button("Détection de l'orientation")
1132
+ detect_logo_btn = gr.Button("Logo et modèle")
1133
+
1134
+ with gr.Row():
1135
+ detect_plate_btn = gr.Button("Détection de plaque")
1136
+ classify_plate_btn = gr.Button("Classifier plaque")
1137
+
1138
+ with gr.Row():
1139
+ next_frame_btn = gr.Button("Frame suivant", visible=False)
1140
+
1141
+ # Nouvelle position pour la gestion d'accès (déplacée ici)
1142
+ with gr.Tab("Gestion Accès"):
1143
+ with gr.Row():
1144
+ check_btn = gr.Button("🔍 Vérifier Véhicule")
1145
+ save_btn = gr.Button("💾 Enregistrer", interactive=False)
1146
+
1147
+ with gr.Row(visible=False) as access_form:
1148
+ with gr.Column():
1149
+ access_status = gr.Radio(
1150
+ ["Autorisé", "Non Autorisé"],
1151
+ label="Statut d'accès"
1152
+ )
1153
+ time_range = gr.Dropdown(
1154
+ ["24/24", "8:00-16:00", "9:00-17:00", "Personnalisé..."],
1155
+ label="Plage horaire"
1156
+ )
1157
+ custom_time = gr.Textbox(
1158
+ visible=False,
1159
+ placeholder="HH:MM-HH:MM",
1160
+ label="Entrez la plage horaire"
1161
+ )
1162
+ save_btn = gr.Button("Confirmer Enregistrement")
1163
+
1164
+ access_output = gr.Textbox(label="Résultat Vérification")
1165
+
1166
+ with gr.Column():
1167
+ original_image = gr.Image(label="Image originale")
1168
+ processed_image = gr.Image(label="Image annotée")
1169
+ status_output = gr.Textbox(label="Statut")
1170
+
1171
+ # Onglets restants (sans Gestion Accès qui a été déplacé)
1172
+ with gr.Tab("Couleur"):
1173
+ color_output = gr.Textbox(label="Détection de couleur")
1174
+ color_image = gr.Image(label="Image avec détection")
1175
+
1176
+ with gr.Tab("Orientation"):
1177
+ orientation_output = gr.Textbox(label="Détection d'orientation")
1178
+ orientation_image = gr.Image(label="Image avec détection")
1179
+
1180
+ with gr.Tab("Logo & Modèle"):
1181
+ with gr.Column():
1182
+ logo_output = gr.Textbox(label="Détection de marque")
1183
+ model_output = gr.Textbox(label="Détection de modèle")
1184
+
1185
+ logo_image = gr.Image(label="Logo détecté")
1186
+
1187
+ with gr.Tab("Plaque"):
1188
+ with gr.Column():
1189
+ plate_image = gr.Image(label="Plaque détectée")
1190
+ plate_chars_image = gr.Image(label="Plaque avec caractères")
1191
+ plate_chars_list = gr.Textbox(label="Caractères détectés")
1192
+
1193
+ with gr.Tab("Classification"):
1194
+ with gr.Column():
1195
+ plate_classification = gr.Textbox(label="Détails de la plaque")
1196
+ vehicle_type_output = gr.Textbox(label="Type de véhicule")
1197
+ with gr.Row():
1198
+ algerian_check_output = gr.Textbox(label="Origine", scale=2)
1199
+ action_output = gr.Textbox(label="Action recommandée", scale=3)
1200
+
1201
+ def update_input_visibility(input_type):
1202
+ if input_type == "Vidéo":
1203
+ return gr.Button(visible=True)
1204
+ else:
1205
+ return gr.Button(visible=False)
1206
+
1207
+ input_type.change(
1208
+ fn=update_input_visibility,
1209
+ inputs=input_type,
1210
+ outputs=next_frame_btn
1211
+ )
1212
+
1213
+
1214
+ ##############################""
1215
+ def extract_video_frames(video_path, num_frames=12):
1216
+ """Extraire plusieurs frames d'une vidéo pour la sélection"""
1217
+ cap = cv2.VideoCapture(video_path)
1218
+ total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
1219
+ frames = []
1220
+
1221
+ # Extraire des frames régulièrement espacées
1222
+ for i in range(num_frames):
1223
+ frame_pos = int(i * (total_frames / num_frames))
1224
+ cap.set(cv2.CAP_PROP_POS_FRAMES, frame_pos)
1225
+ ret, frame = cap.read()
1226
+ if ret:
1227
+ frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
1228
+ frames.append((frame_pos, Image.fromarray(frame_rgb)))
1229
+
1230
+ cap.release()
1231
+ return frames
1232
+
1233
+ ##############
1234
+
1235
+ def process_load(input_type, files):
1236
+ if files is None:
1237
+ raise gr.Error("Veuillez sélectionner un fichier")
1238
+
1239
+ file_path = files.name if hasattr(files, 'name') else files
1240
+
1241
+ if input_type == "Image" and not file_path.lower().endswith(('.png', '.jpg', '.jpeg')):
1242
+ raise gr.Error("Veuillez sélectionner une image valide (PNG, JPG, JPEG)")
1243
+ elif input_type == "Vidéo" and not file_path.lower().endswith(('.mp4', '.avi', '.mov')):
1244
+ raise gr.Error("Veuillez sélectionner une vidéo valide (MP4, AVI, MOV)")
1245
+
1246
+ if input_type == "Image":
1247
+ return (
1248
+ load_image(file_path),
1249
+ "Image chargée - Cliquez sur les boutons pour analyser",
1250
+ gr.Button(visible=False),
1251
+ gr.Gallery(visible=False),
1252
+ gr.Slider(visible=False),
1253
+ gr.Button(visible=False)
1254
+ )
1255
+ else:
1256
+ # Pour les vidéos, extraire les miniatures
1257
+ frames = extract_video_frames(file_path)
1258
+ shared_results["video_path"] = file_path
1259
+ shared_results["video_frames"] = frames
1260
+
1261
+ return (
1262
+ None, # Pas d'image principale initiale
1263
+ f"Vidéo chargée - {len(frames)} frames extraits",
1264
+ gr.Button(visible=True),
1265
+ gr.Gallery(visible=True, value=[(img, f"Frame {pos}") for pos, img in frames]),
1266
+ gr.Slider(visible=True, maximum=len(frames)-1, value=0, step=1, label="Frame sélectionné"),
1267
+ gr.Button(visible=True, value="Charger le frame sélectionné")
1268
+ )
1269
+
1270
+ ######################################
1271
+ def load_selected_frame(selected_frame_idx):
1272
+ if not shared_results.get("video_frames"):
1273
+ raise gr.Error("Aucune vidéo chargée")
1274
+
1275
+ frame_pos, frame_img = shared_results["video_frames"][selected_frame_idx]
1276
+
1277
+ # Mettre à jour le frame courant dans les résultats partagés
1278
+ cap = cv2.VideoCapture(shared_results["video_path"])
1279
+ cap.set(cv2.CAP_PROP_POS_FRAMES, frame_pos)
1280
+ ret, frame = cap.read()
1281
+ cap.release()
1282
+
1283
+ if not ret:
1284
+ raise gr.Error("Erreur de lecture du frame sélectionné")
1285
+
1286
+ # Convertir et préparer l'image
1287
+ img_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
1288
+ img_draw = img_rgb.copy()
1289
+
1290
+ # Mettre à jour les résultats partagés
1291
+ shared_results.update({
1292
+ "original_image": frame,
1293
+ "img_rgb": img_rgb,
1294
+ "img_draw": img_draw,
1295
+ "current_frame": frame,
1296
+ "corrected_orientation": False,
1297
+ "frame_count": frame_pos,
1298
+ "video_processing": True
1299
+ })
1300
+
1301
+ return (
1302
+ Image.fromarray(img_rgb),
1303
+ f"Frame {frame_pos} chargé - Prêt pour analyse",
1304
+ gr.Button(visible=True)
1305
+ )
1306
+
1307
+ ########################
1308
+ # Nouveaux callbacks
1309
+ def toggle_time_range(choice):
1310
+ """Afficher/masquer le champ personnalisé"""
1311
+ if choice == "Personnalisé...":
1312
+ return gr.Textbox(visible=True)
1313
+ return gr.Textbox(visible=False)
1314
+
1315
+ def verify_vehicle():
1316
+ """Vérifier l'existence du véhicule"""
1317
+ if not shared_results["trocr_combined_text"]:
1318
+ raise gr.Error("Aucune plaque détectée")
1319
+
1320
+ plate_info = classify_plate(shared_results["trocr_combined_text"])
1321
+ if not plate_info:
1322
+ raise gr.Error("Plaque non valide")
1323
+
1324
+ exists, message = check_vehicle(plate_info['matricule_complet'])
1325
+
1326
+ if exists:
1327
+ allowed = "✅ ACCÈS AUTORISÉ" if is_access_allowed(plate_info['matricule_complet']) else "❌ ACCÈS REFUSÉ"
1328
+ return {
1329
+ access_output: f"{message}\n{allowed}",
1330
+ access_form: gr.update(visible=False),
1331
+ save_btn: gr.update(interactive=False)
1332
+ }
1333
+ else:
1334
+ return {
1335
+ access_output: message,
1336
+ access_form: gr.update(visible=True),
1337
+ save_btn: gr.update(interactive=True)
1338
+ }
1339
+
1340
+ def save_vehicle_info(status, time_choice, custom_time_input):
1341
+ """Enregistrer les informations du véhicule"""
1342
+ if not shared_results.get("classified_plate"):
1343
+ raise gr.Error("Aucune information de plaque disponible")
1344
+
1345
+ plate_info = shared_results["classified_plate"]
1346
+
1347
+ # Gestion du temps personnalisé
1348
+ if time_choice == "Personnalisé...":
1349
+ if not TIME_PATTERN.match(custom_time_input):
1350
+ raise gr.Error("Format horaire invalide. Utilisez HH:MM-HH:MM")
1351
+ time_range = custom_time_input
1352
+ else:
1353
+ time_range = time_choice
1354
+
1355
+ # Get brand and model, handling cases where they might not be available
1356
+ brand = shared_results.get("vehicle_brand", "Inconnu")
1357
+ model = shared_results.get("vehicle_model", "Inconnu")
1358
+
1359
+ # Sauvegarde
1360
+ success, message = save_vehicle(
1361
+ plate_info,
1362
+ shared_results.get("label_color", "Inconnu"),
1363
+ model,
1364
+ brand,
1365
+ status,
1366
+ time_range
1367
+ )
1368
+
1369
+ if not success:
1370
+ raise gr.Error(message)
1371
+
1372
+ return {
1373
+ access_output: message,
1374
+ access_form: gr.update(visible=False),
1375
+ save_btn: gr.update(interactive=False)
1376
+ }
1377
+
1378
+
1379
+
1380
+
1381
+ # Connexion des boutons aux fonctions
1382
+ load_btn.click(
1383
+ fn=process_load,
1384
+ inputs=[input_type, file_input],
1385
+ outputs=[original_image, status_output, next_frame_btn]
1386
+ )
1387
+ ################
1388
+ # Mettre à jour les connexions
1389
+ load_btn.click(
1390
+ fn=process_load,
1391
+ inputs=[input_type, file_input],
1392
+ outputs=[
1393
+ original_image,
1394
+ status_output,
1395
+ next_frame_btn,
1396
+ frame_gallery,
1397
+ frame_slider,
1398
+ load_frame_btn
1399
+ ]
1400
+ )
1401
+
1402
+ load_frame_btn.click(
1403
+ fn=load_selected_frame,
1404
+ inputs=[frame_slider],
1405
+ outputs=[original_image, status_output, next_frame_btn]
1406
+ )
1407
+ #####################
1408
+
1409
+ detect_vehicle_btn.click(
1410
+ fn=detect_vehicle,
1411
+ outputs=[status_output, processed_image]
1412
+ )
1413
+
1414
+ detect_color_btn.click(
1415
+ fn=detect_color,
1416
+ outputs=[color_output, processed_image]
1417
+ )
1418
+
1419
+ detect_orientation_btn.click(
1420
+ fn=detect_orientation,
1421
+ outputs=[orientation_output, processed_image]
1422
+ )
1423
+
1424
+ detect_logo_btn.click(
1425
+ fn=detect_logo_and_model,
1426
+ outputs=[logo_output, model_output, logo_recognition_output, processed_image, logo_image]
1427
+ )
1428
+
1429
+ detect_plate_btn.click(
1430
+ fn=detect_plate,
1431
+ outputs=[processed_image, plate_image, plate_chars_image, plate_chars_list]
1432
+ )
1433
+
1434
+ classify_plate_btn.click(
1435
+ fn=classify_plate_number,
1436
+ outputs=[
1437
+ plate_classification,
1438
+ vehicle_type_output,
1439
+ algerian_check_output,
1440
+ action_output
1441
+ ]
1442
+ )
1443
+
1444
+ next_frame_btn.click(
1445
+ fn=next_frame,
1446
+ outputs=[original_image, status_output,
1447
+ color_output, orientation_output,
1448
+ logo_output, model_output,
1449
+ plate_classification, vehicle_type_output]
1450
+ )
1451
+
1452
+ # Connecter les nouveaux composants
1453
+ time_range.change(
1454
+ fn=toggle_time_range,
1455
+ inputs=time_range,
1456
+ outputs=custom_time
1457
+ )
1458
+
1459
+ check_btn.click(
1460
+ fn=verify_vehicle,
1461
+ outputs=[access_output, access_form, save_btn]
1462
+ )
1463
+
1464
+ save_btn.click(
1465
+ fn=save_vehicle_info,
1466
+ inputs=[access_status, time_range, custom_time],
1467
+ outputs=[access_output, access_form, save_btn]
1468
+ )
1469
+
1470
+ # Lancer l'interface
1471
+ if __name__ == "__main__":
1472
+ init_database() # Créer la base SQLite si elle n'existe pas
1473
+ demo.launch()
car_color_classifier.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1a9f7df0b7e5e9cbe3412b63f4becfd9ad46182dd5bfad264cce2dac14027909
3
+ size 39722836
car_logo_detection.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c551e8a144a5815126d6ad0ef0402c7447dd1189057332f9f14b477f689863fe
3
+ size 6229923
character_detetion.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a0714aefd9836c39657ab6ffbc3bb2c2c2996859df3751cb2e68db94888cf242
3
+ size 18508694
chevrolet_model_final2.keras ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e9a6d701050d69d9d78b6749c38252581ba055d67c994059231ed19ee3b0b907
3
+ size 228495059
direction_best.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:de9fcc1fc76690223ee822cad480377c56b86ddb2f2d9fc5ea202743ff4d4339
3
+ size 6248867
logo_model_cnn.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0d1fbac8a0b1b6a2072b7f8ab0752632265bec5d7020408de6adf08b26a3bebc
3
+ size 78362984
nissan_model_final2.keras ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2ccb665d88fb6f2a334d77745142756fa1d6a2ec026ff8caa137e1324ff3da83
3
+ size 228501211
plate_detection.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f02c9507b8a156ada378da1ad6b264d700ce58b594e31d39c8b032369748f29a
3
+ size 22498211
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ gradio
2
+ ultralytics
3
+ opencv-python
4
+ numpy
5
+ Pillow
6
+ torch
7
+ transformers
8
+ tensorflow
vehicle_detection.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b3fe9a41930fe10b103cffc95fe06d9ec309888adc16df901e1df9a26a9fe586
3
+ size 6252067
vehicules_database.db ADDED
Binary file (16.4 kB). View file