mohammed-aljafry commited on
Commit
5587234
·
verified ·
1 Parent(s): 36f2bd1

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +269 -0
app.py ADDED
@@ -0,0 +1,269 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ============================================================================
2
+ # app.py - الواجهة التفاعلية لمشروع Interfuser
3
+ # ============================================================================
4
+ # هذا الملف يقوم بإنشاء واجهة ويب باستخدام Gradio لعرض أداء نموذج
5
+ # Interfuser للقيادة الذاتية. يسمح للمستخدمين باختيار نماذج مختلفة
6
+ # وسيناريوهات قيادة ومقارنة النتائج.
7
+ # ============================================================================
8
+
9
+ # --- الجزء الأول: الاستيرادات ---
10
+ import os
11
+ import torch
12
+ import numpy as np
13
+ import cv2
14
+ import json
15
+ import logging
16
+ import traceback
17
+ from PIL import Image
18
+ from typing import Dict
19
+
20
+ # مكتبة الواجهة الرسومية
21
+ import gradio as gr
22
+
23
+ # مكتبات معالجة الصور من Torch
24
+ from torchvision import transforms
25
+
26
+ # استيراد الوحدات الخاصة بنا من الملفات الأخرى
27
+ try:
28
+ from model_definition import load_and_prepare_model, create_model_config
29
+ from simulation_modules import (
30
+ DisplayInterface,
31
+ InterfuserController,
32
+ ControllerConfig,
33
+ render_waypoints,
34
+ render_self_car,
35
+ render
36
+ )
37
+ except ImportError as e:
38
+ print(f"خطأ في الاستيراد: تأكد من وجود ملفات model_definition.py و simulation_modules.py. الخطأ: {e}")
39
+ exit()
40
+
41
+ # --- الجزء الثاني: الإعدادات والثوابت ---
42
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
43
+
44
+ DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
45
+ MODEL_DIR = "model"
46
+ SAMPLE_DATA_DIR = "sample_data"
47
+
48
+ # --- الجزء الثالث: تعريف التحويلات (Transforms) ---
49
+ # هذه التحويلات يجب أن تطابق تمامًا ما تم استخدامه أثناء تدريب النموذج
50
+ RGB_TRANSFORM = transforms.Compose([
51
+ transforms.Resize((224, 224)),
52
+ transforms.ToTensor(),
53
+ transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
54
+ ])
55
+
56
+ LIDAR_TRANSFORM = transforms.Compose([
57
+ transforms.Resize((224, 224)),
58
+ transforms.ToTensor()
59
+ ])
60
+
61
+ # --- الجزء الرابع: الكائنات العامة ومخبأ النماذج ---
62
+ logging.info("تهيئة المكونات العامة...")
63
+ # قاموس لتخزين النماذج التي تم تحميلها لتجنب إعادة التحميل البطيئة
64
+ GLOBAL_MODELS_CACHE: Dict[str, torch.nn.Module] = {}
65
+
66
+ # تهيئة واجهة العرض والمتحكم (يتم تهيئتها مرة واحدة)
67
+ GLOBAL_DISPLAY_INTERFACE = DisplayInterface()
68
+ controller_config = ControllerConfig()
69
+ GLOBAL_CONTROLLER = InterfuserController(controller_config)
70
+ logging.info(f"تم تهيئة المكونات. يعمل التطبيق على الجهاز: {DEVICE}")
71
+
72
+ # --- الجزء الخامس: دوال المساعدة والمعالجة ---
73
+
74
+ def get_model(model_name: str) -> torch.nn.Module:
75
+ """
76
+ تقوم بتحميل النموذج إذا لم يكن في المخبأ، ثم تعيده.
77
+ """
78
+ if model_name in GLOBAL_MODELS_CACHE:
79
+ logging.info(f"إعادة استخدام النموذج '{model_name}' من المخبأ.")
80
+ return GLOBAL_MODELS_CACHE[model_name]
81
+
82
+ logging.info(f"النموذج '{model_name}' غير موجود في المخبأ. بدء التحميل...")
83
+ gr.Info(f"⏳ جاري تحميل نموذج جديد: {model_name}... قد يستغرق هذا بضع ثوانٍ.")
84
+
85
+ model_path = os.path.join(MODEL_DIR, model_name)
86
+ if not os.path.exists(model_path):
87
+ raise FileNotFoundError(f"ملف النموذج '{model_path}' غير موجود!")
88
+
89
+ model_config = create_model_config(model_path=model_path)
90
+ model = load_and_prepare_model(model_config, DEVICE)
91
+
92
+ if model:
93
+ GLOBAL_MODELS_CACHE[model_name] = model
94
+ logging.info(f"✅ تم تحميل وتخزين النموذج '{model_name}' في المخبأ.")
95
+ return model
96
+ else:
97
+ raise gr.Error(f"فشل تحميل النموذج '{model_name}'. يرجى مراجعة السجلات.")
98
+
99
+
100
+ def process_and_run_model(
101
+ model: torch.nn.Module,
102
+ rgb_image_path: str, rgb_left_image_path: str, rgb_right_image_path: str,
103
+ rgb_center_image_path: str, lidar_image_path: str,
104
+ measurements_path: str, target_point_list: list
105
+ ):
106
+ """
107
+ محرك المعالجة: يعالج إطارًا واحدًا من البيانات بناءً على المسارات
108
+ ويقوم بتشغيل النموذج عليه.
109
+ """
110
+ try:
111
+ # 1. قراءة ومعالجة المدخلات
112
+ if not rgb_image_path or not os.path.exists(rgb_image_path):
113
+ raise FileNotFoundError("ملف الصورة الأمامية (RGB) غير موجود.")
114
+
115
+ rgb_image = Image.open(rgb_image_path).convert("RGB")
116
+
117
+ def open_optional_image(path, default_img):
118
+ return Image.open(path).convert("RGB") if path and os.path.exists(path) else default_img
119
+
120
+ rgb_left_image = open_optional_image(rgb_left_image_path, rgb_image)
121
+ rgb_right_image = open_optional_image(rgb_right_image_path, rgb_image)
122
+ rgb_center_image = open_optional_image(rgb_center_image_path, rgb_image)
123
+
124
+ if lidar_image_path and os.path.exists(lidar_image_path):
125
+ lidar_array = np.load(lidar_image_path)
126
+ max_val = lidar_array.max()
127
+ if max_val > 0: lidar_array = (lidar_array / max_val) * 255.0
128
+ lidar_image = Image.fromarray(lidar_array.astype(np.uint8)).convert('RGB')
129
+ else:
130
+ lidar_image = Image.fromarray(np.zeros((224, 224, 3), dtype=np.uint8))
131
+
132
+ # 2. تطبيق التحويلات
133
+ inputs = {
134
+ 'rgb': RGB_TRANSFORM(rgb_image).unsqueeze(0).to(DEVICE),
135
+ 'rgb_left': RGB_TRANSFORM(rgb_left_image).unsqueeze(0).to(DEVICE),
136
+ 'rgb_right': RGB_TRANSFORM(rgb_right_image).unsqueeze(0).to(DEVICE),
137
+ 'rgb_center': RGB_TRANSFORM(rgb_center_image).unsqueeze(0).to(DEVICE),
138
+ 'lidar': LIDAR_TRANSFORM(lidar_image).unsqueeze(0).to(DEVICE),
139
+ 'measurements': torch.tensor([json.load(open(measurements_path))['values']], dtype=torch.float32).to(DEVICE),
140
+ 'target_point': torch.tensor([target_point_list], dtype=torch.float32).to(DEVICE)
141
+ }
142
+
143
+ # 3. تشغيل النموذج
144
+ with torch.no_grad():
145
+ outputs = model(inputs)
146
+
147
+ return outputs
148
+
149
+ except Exception as e:
150
+ logging.error(traceback.format_exc())
151
+ raise gr.Error(f"حدث خطأ أثناء معالجة البيانات: {e}")
152
+
153
+
154
+ def run_simulation(model_name: str, scenario_name: str):
155
+ """
156
+ المنسق الرئيسي: يجهز المدخلات، يستدعي محرك المعالجة، ثم يعرض النتائج.
157
+ """
158
+ logging.info(f"بدء المحاكاة للنموذج: '{model_name}', السيناريو: '{scenario_name}'")
159
+
160
+ # 1. احصل على النموذج (من المخبأ أو قم بتحميله)
161
+ model = get_model(model_name)
162
+
163
+ # 2. جهز المسارات للسيناريو المختار
164
+ scenario_path = os.path.join(SAMPLE_DATA_DIR, scenario_name)
165
+ paths = {
166
+ 'rgb': os.path.join(scenario_path, 'rgb.png'),
167
+ 'left': os.path.join(scenario_path, 'rgb_left.png'),
168
+ 'right': os.path.join(scenario_path, 'rgb_right.png'),
169
+ 'center': os.path.join(scenario_path, 'rgb_center.png'),
170
+ 'lidar': os.path.join(scenario_path, 'lidar.npy'),
171
+ 'measurements': os.path.join(scenario_path, 'measurements.json'),
172
+ 'target_point': os.path.join(scenario_path, 'target_point.json')
173
+ }
174
+
175
+ # 3. استدعاء محرك المعالجة والنموذج
176
+ target_point_list = json.load(open(paths['target_point']))['value']
177
+
178
+ traffic, waypoints, is_junction, traffic_light, stop_sign, _ = process_and_run_model(
179
+ model=model, rgb_image_path=paths['rgb'], rgb_left_image_path=paths['left'],
180
+ rgb_right_image_path=paths['right'], rgb_center_image_path=paths['center'],
181
+ lidar_image_path=paths['lidar'], measurements_path=paths['measurements'],
182
+ target_point_list=target_point_list
183
+ )
184
+
185
+ # 4. معالجة المخرجات
186
+ pred_wp = waypoints[0].cpu().numpy()
187
+ pred_traffic_map = traffic[0].sigmoid().cpu().numpy().reshape(20, 20, 7)
188
+ current_speed = json.load(open(paths['measurements'])).get('values', [0]*7)[3] # Speed is at index 3
189
+
190
+ # 5. استخدام متحكم القيادة للحصول على الأوامر
191
+ steer, throttle, brake, metadata = GLOBAL_CONTROLLER.run_step(
192
+ current_speed=current_speed, waypoints=pred_wp,
193
+ junction=torch.sigmoid(is_junction)[0,1].item(),
194
+ traffic_light_state=torch.sigmoid(traffic_light)[0,0].item(),
195
+ stop_sign=torch.sigmoid(stop_sign)[0,1].item(),
196
+ meta_data={}
197
+ )
198
+
199
+ # 6. إنشاء الخرائط المرئية
200
+ traffic_render, _ = render(pred_traffic_map)
201
+ waypoints_render = render_waypoints(pred_wp)
202
+ combined_map = cv2.addWeighted(traffic_render, 1.0, waypoints_render, 1.0, 0.0)
203
+ final_map = render_self_car(combined_map)
204
+
205
+ # 7. تجهيز البيانات النهائية للعرض
206
+ display_data = {
207
+ 'camera_view': np.array(Image.open(paths['rgb'])),
208
+ 'map_t0': final_map, 'map_t1': np.zeros_like(final_map), 'map_t2': np.zeros_like(final_map),
209
+ 'text_info': {
210
+ "Model": f"Model: {model_name}",
211
+ "Scenario": f"Scenario: {scenario_name}",
212
+ "Controller": metadata,
213
+ "Brake": f"Brake Activated: {'YES' if brake else 'NO'}"
214
+ }
215
+ }
216
+
217
+ dashboard = GLOBAL_DISPLAY_INTERFACE.run_interface(display_data)
218
+ logging.info("انتهت المحاكاة بنجاح.")
219
+
220
+ return dashboard, metadata
221
+
222
+
223
+ # --- الج��ء السادس: بناء واجهة Gradio ---
224
+ with gr.Blocks(title="Interfuser Demo", theme=gr.themes.Soft()) as demo:
225
+ gr.Markdown("# 🚀 Interfuser: واجهة القيادة التفاعلية")
226
+ gr.Markdown("اختر النموذج والسيناريو، ثم اضغط على 'تشغيل' للمقارنة بين أداء النماذج المختلفة.")
227
+
228
+ # قراءة النماذج والسيناريوهات المتاحة من المجلدات
229
+ try:
230
+ available_models = [f for f in os.listdir(MODEL_DIR) if f.endswith(('.pth', '.pt'))]
231
+ available_scenarios = [d for d in os.listdir(SAMPLE_DATA_DIR) if os.path.isdir(os.path.join(SAMPLE_DATA_DIR, d))]
232
+ except FileNotFoundError as e:
233
+ gr.Error(f"خطأ في إعداد الواجهة: {e}. هل قمت بإنشاء مجلدي 'model' و 'sample_data'؟")
234
+ available_models = []
235
+ available_scenarios = []
236
+
237
+ with gr.Row():
238
+ with gr.Column(scale=1, min_width=300):
239
+ # مدخلات المستخدم
240
+ model_selector = gr.Dropdown(
241
+ choices=available_models, label="1. اختر النموذج",
242
+ value=available_models[0] if available_models else None)
243
+
244
+ scenario_selector = gr.Dropdown(
245
+ choices=available_scenarios, label="2. اختر سيناريو القيادة",
246
+ value=available_scenarios[0] if available_scenarios else None)
247
+
248
+ run_button = gr.Button("▶️ تشغيل المحاكاة", variant="primary")
249
+
250
+ # مخرجات نصية
251
+ controller_output = gr.Textbox(label="بيانات متحكم القيادة", interactive=False)
252
+
253
+ with gr.Column(scale=3):
254
+ # مخرجات مرئية
255
+ dashboard_output = gr.Image(label="لوحة المعلومات الحية (Dashboard)", interactive=False)
256
+
257
+ # ربط زر التشغيل بالدالة الرئيسية
258
+ if available_models and available_scenarios:
259
+ run_button.click(
260
+ fn=run_simulation,
261
+ inputs=[model_selector, scenario_selector],
262
+ outputs=[dashboard_output, controller_output]
263
+ )
264
+ else:
265
+ gr.Warning("لا يمكن العثور على نماذج أو سيناريوهات. يرجى التأكد من إعداد المجلدات بشكل صحيح.")
266
+
267
+ # --- الجزء السابع: إطلاق الواجهة ---
268
+ if __name__ == "__main__":
269
+ demo.launch(debug=True) # استخدم debug=True للمساعدة في اكتشاف الأخطاء أثناء التطوير