FaizTech commited on
Commit
f659ec7
·
verified ·
1 Parent(s): b2f270d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +381 -93
app.py CHANGED
@@ -1,4 +1,266 @@
1
- # app.py
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  import os
4
  import json
@@ -9,6 +271,8 @@ import numpy as np
9
  from PIL import Image
10
  import cv2
11
  import math
 
 
12
 
13
  # --- استيراد من الملفات المنظمة في مشروعك ---
14
  from model import build_interfuser_model
@@ -18,8 +282,15 @@ from logic import (
18
  ensure_rgb, WAYPOINT_SCALE_FACTOR, T1_FUTURE_TIME, T2_FUTURE_TIME
19
  )
20
 
 
 
 
 
 
 
 
21
  # ==============================================================================
22
- # 1. إعدادات ومسارات النماذج
23
  # ==============================================================================
24
  WEIGHTS_DIR = "model"
25
  EXAMPLES_DIR = "examples"
@@ -35,10 +306,12 @@ def find_available_models():
35
  return [f.replace(".pth", "") for f in os.listdir(WEIGHTS_DIR) if f.endswith(".pth")]
36
 
37
  # ==============================================================================
38
- # 2. الدوال الأساسية
39
  # ==============================================================================
40
 
 
41
  def load_model(model_name: str):
 
42
  if not model_name or "لم يتم" in model_name:
43
  return None, "الرجاء اختيار نموذج صالح."
44
  weights_path = os.path.join(WEIGHTS_DIR, f"{model_name}.pth")
@@ -58,14 +331,12 @@ def load_model(model_name: str):
58
  model.eval()
59
  return model, f"تم تحميل نموذج: {model_name}"
60
 
61
-
62
  def run_single_frame(
63
  model_from_state, rgb_image_path, rgb_left_image_path, rgb_right_image_path,
64
  rgb_center_image_path, lidar_image_path, measurements_path, target_point_list
65
  ):
66
- """
67
- (نسخة أكثر قوة مع معالجة أخطاء مفصلة)
68
- """
69
  if model_from_state is None:
70
  print("API session detected or model not loaded. Loading default model...")
71
  available_models = find_available_models()
@@ -78,180 +349,197 @@ def run_single_frame(
78
  raise gr.Error("فشل تحميل النموذج. تحقق من السجلات (Logs).")
79
 
80
  try:
81
- # --- 1. التحقق من المدخلات المطلوبة ---
82
  if not (rgb_image_path and measurements_path):
83
  raise gr.Error("الرجاء توفير الصورة الأمامية وملف القياسات على الأقل.")
84
-
85
- # --- 2. قراءة ومعالجة المدخلات مع معالجة أخطاء مفصلة ---
86
  try:
87
  rgb_image_pil = Image.open(rgb_image_path).convert("RGB")
88
  except Exception as e:
89
  raise gr.Error(f"فشل تحميل صورة الكاميرا الأمامية. تأكد من أن الملف صحيح. الخطأ: {e}")
90
-
91
  def load_optional_image(path, default_image):
92
  if path:
93
- try:
94
- return Image.open(path).convert("RGB")
95
- except Exception as e:
96
- raise gr.Error(f"فشل تحميل الصورة الاختيارية '{os.path.basename(path)}'. الخطأ: {e}")
97
  return default_image
98
-
99
  rgb_left_pil = load_optional_image(rgb_left_image_path, rgb_image_pil)
100
  rgb_right_pil = load_optional_image(rgb_right_image_path, rgb_image_pil)
101
  rgb_center_pil = load_optional_image(rgb_center_image_path, rgb_image_pil)
102
-
103
  if lidar_image_path:
104
  try:
105
  lidar_array = np.load(lidar_image_path)
106
  if lidar_array.max() > 0: lidar_array = (lidar_array / lidar_array.max()) * 255.0
107
  lidar_pil = Image.fromarray(lidar_array.astype(np.uint8)).convert('RGB')
108
- except Exception as e:
109
- raise gr.Error(f"فشل تحميل ملف الليدار (.npy). تأكد من أن الملف صحيح. الخطأ: {e}")
110
  else:
111
  lidar_pil = Image.fromarray(np.zeros((112, 112, 3), dtype=np.uint8))
112
-
113
  try:
114
  with open(measurements_path, 'r') as f: m_dict = json.load(f)
115
- except Exception as e:
116
- raise gr.Error(f"فشل تحميل أو قراءة ملف القياسات (.json). تأكد من أنه بصيغة صحيحة. الخطأ: {e}")
117
-
118
- # --- 3. تحويل البيانات إلى تنسورات ---
119
  front_tensor = transform(rgb_image_pil).unsqueeze(0).to(device)
120
  left_tensor = transform(rgb_left_pil).unsqueeze(0).to(device)
121
  right_tensor = transform(rgb_right_pil).unsqueeze(0).to(device)
122
  center_tensor = transform(rgb_center_pil).unsqueeze(0).to(device)
123
  lidar_tensor = lidar_transform(lidar_pil).unsqueeze(0).to(device)
124
-
125
- measurements_tensor = torch.tensor([[
126
- m_dict.get('x',0.0), m_dict.get('y',0.0), m_dict.get('theta',0.0), m_dict.get('speed',5.0),
127
- m_dict.get('steer',0.0), m_dict.get('throttle',0.0), float(m_dict.get('brake',0.0)),
128
- m_dict.get('command',2.0), float(m_dict.get('is_junction',0.0)), float(m_dict.get('should_brake',0.0))
129
- ]], dtype=torch.float32).to(device)
130
-
131
  target_point_tensor = torch.tensor([target_point_list], dtype=torch.float32).to(device)
132
-
133
  inputs = {'rgb': front_tensor, 'rgb_left': left_tensor, 'rgb_right': right_tensor, 'rgb_center': center_tensor, 'lidar': lidar_tensor, 'measurements': measurements_tensor, 'target_point': target_point_tensor}
134
-
135
- # --- 4. تشغيل النموذج ---
136
  with torch.no_grad():
137
  outputs = model_to_use(inputs)
138
  traffic, waypoints, is_junction, traffic_light, stop_sign, _ = outputs
139
-
140
- # --- 5. المعالجة اللاحقة والتصوّر ---
141
  speed, pos, theta = m_dict.get('speed',5.0), [m_dict.get('x',0.0), m_dict.get('y',0.0)], m_dict.get('theta',0.0)
142
  traffic_np, waypoints_np = traffic[0].detach().cpu().numpy().reshape(20,20,-1), waypoints[0].detach().cpu().numpy() * WAYPOINT_SCALE_FACTOR
143
  tracker, controller = Tracker(), InterfuserController(ControllerConfig())
144
  updated_traffic = tracker.update_and_predict(traffic_np.copy(), pos, theta, 0)
145
  steer, throttle, brake, metadata = controller.run_step(speed, waypoints_np, is_junction.sigmoid()[0,1].item(), traffic_light.sigmoid()[0,0].item(), stop_sign.sigmoid()[0,1].item(), updated_traffic)
146
-
147
- # ... (كود الرسم)
148
  map_t0, counts_t0 = render(updated_traffic, t=0)
149
  map_t1, counts_t1 = render(updated_traffic, t=T1_FUTURE_TIME)
150
  map_t2, counts_t2 = render(updated_traffic, t=T2_FUTURE_TIME)
151
  wp_map = render_waypoints(waypoints_np)
152
  self_car_map = render_self_car(np.array([0,0]), [math.cos(0), math.sin(0)], [4.0, 2.0])
153
- map_t0 = cv2.add(cv2.add(map_t0, wp_map), self_car_map)
154
- map_t0 = cv2.resize(map_t0, (400, 400))
155
  map_t1 = cv2.add(ensure_rgb(map_t1), ensure_rgb(self_car_map)); map_t1 = cv2.resize(map_t1, (200, 200))
156
  map_t2 = cv2.add(ensure_rgb(map_t2), ensure_rgb(self_car_map)); map_t2 = cv2.resize(map_t2, (200, 200))
157
  display = DisplayInterface()
158
  light_state, stop_sign_state = "Red" if traffic_light.sigmoid()[0,0].item() > 0.5 else "Green", "Yes" if stop_sign.sigmoid()[0,1].item() > 0.5 else "No"
159
- interface_data = {'camera_view': np.array(rgb_image_pil),'map_t0': map_t0,'map_t1': map_t1,'map_t2': map_t2,
160
- 'text_info': {'Control': f"S:{steer:.2f} T:{throttle:.2f} B:{int(brake)}",'Light': f"L: {light_state}",'Stop': f"St: {stop_sign_state}"},
161
- 'object_counts': {'t0': counts_t0,'t1': counts_t1,'t2': counts_t2}}
162
  dashboard_image = display.run_interface(interface_data)
163
-
164
- # --- 6. تجهيز المخرجات ---
165
  control_commands_dict = {"steer": steer, "throttle": throttle, "brake": bool(brake)}
166
  return Image.fromarray(dashboard_image), control_commands_dict
167
-
168
- except gr.Error as e:
169
- raise e # أعد إظهار أخطاء Gradio كما هي
170
  except Exception as e:
171
  print(traceback.format_exc())
172
  raise gr.Error(f"حدث خطأ غير متوقع أثناء معالجة الإطار: {e}")
173
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
 
175
  # ==============================================================================
176
- # 5. تعريف واجهة Gradio (لا تغيير هنا)
177
  # ==============================================================================
178
- # ... (كود الواجهة بالكامل يبقى كما هو من النسخة السابقة) ...
179
  available_models = find_available_models()
180
-
181
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"), css=".gradio-container {max-width: 95% !important;}") as demo:
 
182
  model_state = gr.State(value=None)
183
-
184
  gr.Markdown("# 🚗 محاكاة القيادة الذاتية باستخدام Interfuser")
185
  gr.Markdown("مرحباً بك في واجهة اختبار نموذج Interfuser. اتبع الخطوات أدناه لتشغيل المحا��اة على إطار واحد.")
186
-
187
  with gr.Row():
188
- # -- العمود الأيسر: الإعدادات والمدخلات --
189
  with gr.Column(scale=1):
190
  with gr.Group():
191
  gr.Markdown("## ⚙️ الخطوة 1: اختر النموذج")
192
  with gr.Row():
193
- model_selector = gr.Dropdown(
194
- label="النماذج المتاحة",
195
- choices=available_models,
196
- value=available_models[0] if available_models else "لم يتم العثور على نماذج"
197
- )
198
  status_textbox = gr.Textbox(label="حالة النموذج", interactive=False)
199
-
200
  with gr.Group():
201
  gr.Markdown("## 🗂️ الخطوة 2: ارفع ملفات السيناريو")
202
-
203
  with gr.Group():
204
  gr.Markdown("**(مطلوب)**")
205
  api_rgb_image_path = gr.File(label="صورة الكاميرا الأمامية (RGB)", type="filepath")
206
  api_measurements_path = gr.File(label="ملف القياسات (JSON)", type="filepath")
207
-
208
  with gr.Accordion("📷 مدخلات اختيارية (كاميرات ومستشعرات إضافية)", open=False):
209
  api_rgb_left_image_path = gr.File(label="كاميرا اليسار (RGB)", type="filepath")
210
  api_rgb_right_image_path = gr.File(label="كاميرا اليمين (RGB)", type="filepath")
211
  api_rgb_center_image_path = gr.File(label="كاميرا الوسط (RGB)", type="filepath")
212
  api_lidar_image_path = gr.File(label="بيانات الليدار (NPY)", type="filepath")
213
-
214
  api_target_point_list = gr.JSON(label="📍 النقطة المستهدفة (x, y)", value=[0.0, 100.0])
215
-
216
  api_run_button = gr.Button("🚀 شغل المحاكاة", variant="primary", scale=2)
217
-
218
  with gr.Group():
219
  gr.Markdown("### ✨ أمثلة جاهزة")
220
  gr.Markdown("انقر على مثال لتعبئة الحقول تلقائياً (يتطلب وجود مجلد `examples`).")
221
- gr.Examples(
222
- examples=[
223
- [os.path.join(EXAMPLES_DIR, "sample1", "rgb.jpg"), os.path.join(EXAMPLES_DIR, "sample1", "measurements.json")],
224
- [os.path.join(EXAMPLES_DIR, "sample2", "rgb.jpg"), os.path.join(EXAMPLES_DIR, "sample2", "measurements.json")]
225
- ],
226
- inputs=[api_rgb_image_path, api_measurements_path],
227
- label="اختر سيناريو اختبار"
228
- )
229
-
230
- # -- العمود الأيمن: المخرجات --
231
  with gr.Column(scale=2):
232
  with gr.Group():
233
  gr.Markdown("## 📊 الخطوة 3: شاهد النتائج")
234
  api_output_image = gr.Image(label="لوحة التحكم المرئية (Dashboard)", type="pil", interactive=False)
235
  api_control_json = gr.JSON(label="أوامر التحكم (JSON)")
236
-
237
- # --- ربط منطق الواجهة ---
238
  if available_models:
239
  demo.load(fn=load_model, inputs=model_selector, outputs=[model_state, status_textbox])
240
-
241
  model_selector.change(fn=load_model, inputs=model_selector, outputs=[model_state, status_textbox])
242
-
243
- api_run_button.click(
244
- fn=run_single_frame,
245
- inputs=[model_state, api_rgb_image_path, api_rgb_left_image_path, api_rgb_right_image_path,
246
- api_rgb_center_image_path, api_lidar_image_path, api_measurements_path, api_target_point_list],
247
- outputs=[api_output_image, api_control_json],
248
- api_name="run_single_frame"
249
- )
250
 
251
- # ==============================================================================
252
- # 6. تشغيل التطبيق
253
- # ==============================================================================
254
- if __name__ == "__main__":
255
- if not available_models:
256
- print("تحذير: لم يتم العثور على أي ملفا�� نماذج (.pth) في مجلد 'model/weights'.")
257
- demo.queue().launch(debug=True, share=True, show_api=True)
 
 
 
 
 
 
 
 
1
+ # # app.py
2
+
3
+ # import os
4
+ # import json
5
+ # import traceback
6
+ # import torch
7
+ # import gradio as gr
8
+ # import numpy as np
9
+ # from PIL import Image
10
+ # import cv2
11
+ # import math
12
+
13
+ # # --- استيراد من الملفات المنظمة في مشروعك ---
14
+ # from model import build_interfuser_model
15
+ # from logic import (
16
+ # transform, lidar_transform, InterfuserController, ControllerConfig,
17
+ # Tracker, DisplayInterface, render, render_waypoints, render_self_car,
18
+ # ensure_rgb, WAYPOINT_SCALE_FACTOR, T1_FUTURE_TIME, T2_FUTURE_TIME
19
+ # )
20
+
21
+ # # ==============================================================================
22
+ # # 1. إعدادات ومسارات النماذج
23
+ # # ==============================================================================
24
+ # WEIGHTS_DIR = "model"
25
+ # EXAMPLES_DIR = "examples"
26
+ # device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
27
+
28
+ # MODELS_SPECIFIC_CONFIGS = {
29
+ # "interfuser_baseline": { "rgb_backbone_name": "r50", "embed_dim": 256, "direct_concat": True },
30
+ # "interfuser_lightweight": { "rgb_backbone_name": "r26", "embed_dim": 128, "enc_depth": 4, "dec_depth": 4, "direct_concat": True }
31
+ # }
32
+
33
+ # def find_available_models():
34
+ # if not os.path.isdir(WEIGHTS_DIR): return []
35
+ # return [f.replace(".pth", "") for f in os.listdir(WEIGHTS_DIR) if f.endswith(".pth")]
36
+
37
+ # # ==============================================================================
38
+ # # 2. الدوال الأساسية
39
+ # # ==============================================================================
40
+
41
+ # def load_model(model_name: str):
42
+ # if not model_name or "لم يتم" in model_name:
43
+ # return None, "الرجاء اختيار نموذج صالح."
44
+ # weights_path = os.path.join(WEIGHTS_DIR, f"{model_name}.pth")
45
+ # print(f"Building model: '{model_name}'")
46
+ # model_config = MODELS_SPECIFIC_CONFIGS.get(model_name, {})
47
+ # model = build_interfuser_model(model_config)
48
+ # if not os.path.exists(weights_path):
49
+ # gr.Warning(f"ملف الأوزان '{weights_path}' غير موجود.")
50
+ # else:
51
+ # try:
52
+ # state_dic = torch.load(weights_path, map_location=device, weights_only=True)
53
+ # model.load_state_dict(state_dic)
54
+ # print(f"تم تحميل أوزان النموذج '{model_name}' بنجاح.")
55
+ # except Exception as e:
56
+ # gr.Warning(f"فشل تحميل الأوزان للنموذج '{model_name}': {e}.")
57
+ # model.to(device)
58
+ # model.eval()
59
+ # return model, f"تم تحميل نموذج: {model_name}"
60
+
61
+
62
+ # def run_single_frame(
63
+ # model_from_state, rgb_image_path, rgb_left_image_path, rgb_right_image_path,
64
+ # rgb_center_image_path, lidar_image_path, measurements_path, target_point_list
65
+ # ):
66
+ # """
67
+ # (نسخة أكثر قوة مع معالجة أخطاء مفصلة)
68
+ # """
69
+ # if model_from_state is None:
70
+ # print("API session detected or model not loaded. Loading default model...")
71
+ # available_models = find_available_models()
72
+ # if not available_models: raise gr.Error("لا توجد نماذج متاحة للتحميل.")
73
+ # model_to_use, _ = load_model(available_models[0])
74
+ # else:
75
+ # model_to_use = model_from_state
76
+
77
+ # if model_to_use is None:
78
+ # raise gr.Error("فشل تحميل النموذج. تحقق من السجلات (Logs).")
79
+
80
+ # try:
81
+ # # --- 1. التحقق من المدخلات المطلوبة ---
82
+ # if not (rgb_image_path and measurements_path):
83
+ # raise gr.Error("الرجاء توفير الصورة الأمامية وملف القياسات على الأقل.")
84
+
85
+ # # --- 2. قراءة ومعالجة المدخلات مع معالجة أخطاء مفصلة ---
86
+ # try:
87
+ # rgb_image_pil = Image.open(rgb_image_path).convert("RGB")
88
+ # except Exception as e:
89
+ # raise gr.Error(f"فشل تحميل صورة الكاميرا الأمامية. تأكد من أن الملف صحيح. الخطأ: {e}")
90
+
91
+ # def load_optional_image(path, default_image):
92
+ # if path:
93
+ # try:
94
+ # return Image.open(path).convert("RGB")
95
+ # except Exception as e:
96
+ # raise gr.Error(f"فشل تحميل الصورة الاختيارية '{os.path.basename(path)}'. الخطأ: {e}")
97
+ # return default_image
98
+
99
+ # rgb_left_pil = load_optional_image(rgb_left_image_path, rgb_image_pil)
100
+ # rgb_right_pil = load_optional_image(rgb_right_image_path, rgb_image_pil)
101
+ # rgb_center_pil = load_optional_image(rgb_center_image_path, rgb_image_pil)
102
+
103
+ # if lidar_image_path:
104
+ # try:
105
+ # lidar_array = np.load(lidar_image_path)
106
+ # if lidar_array.max() > 0: lidar_array = (lidar_array / lidar_array.max()) * 255.0
107
+ # lidar_pil = Image.fromarray(lidar_array.astype(np.uint8)).convert('RGB')
108
+ # except Exception as e:
109
+ # raise gr.Error(f"فشل تحميل ملف الليدار (.npy). تأكد من أن الملف صحيح. الخطأ: {e}")
110
+ # else:
111
+ # lidar_pil = Image.fromarray(np.zeros((112, 112, 3), dtype=np.uint8))
112
+
113
+ # try:
114
+ # with open(measurements_path, 'r') as f: m_dict = json.load(f)
115
+ # except Exception as e:
116
+ # raise gr.Error(f"فشل تحميل أو قراءة ملف القياسات (.json). تأكد من أنه بصيغة صحيحة. الخطأ: {e}")
117
+
118
+ # # --- 3. تحويل البيانات إلى تنسورات ---
119
+ # front_tensor = transform(rgb_image_pil).unsqueeze(0).to(device)
120
+ # left_tensor = transform(rgb_left_pil).unsqueeze(0).to(device)
121
+ # right_tensor = transform(rgb_right_pil).unsqueeze(0).to(device)
122
+ # center_tensor = transform(rgb_center_pil).unsqueeze(0).to(device)
123
+ # lidar_tensor = lidar_transform(lidar_pil).unsqueeze(0).to(device)
124
+
125
+ # measurements_tensor = torch.tensor([[
126
+ # m_dict.get('x',0.0), m_dict.get('y',0.0), m_dict.get('theta',0.0), m_dict.get('speed',5.0),
127
+ # m_dict.get('steer',0.0), m_dict.get('throttle',0.0), float(m_dict.get('brake',0.0)),
128
+ # m_dict.get('command',2.0), float(m_dict.get('is_junction',0.0)), float(m_dict.get('should_brake',0.0))
129
+ # ]], dtype=torch.float32).to(device)
130
+
131
+ # target_point_tensor = torch.tensor([target_point_list], dtype=torch.float32).to(device)
132
+
133
+ # inputs = {'rgb': front_tensor, 'rgb_left': left_tensor, 'rgb_right': right_tensor, 'rgb_center': center_tensor, 'lidar': lidar_tensor, 'measurements': measurements_tensor, 'target_point': target_point_tensor}
134
+
135
+ # # --- 4. تشغيل النموذج ---
136
+ # with torch.no_grad():
137
+ # outputs = model_to_use(inputs)
138
+ # traffic, waypoints, is_junction, traffic_light, stop_sign, _ = outputs
139
+
140
+ # # --- 5. المعالجة اللاحقة والتصوّر ---
141
+ # speed, pos, theta = m_dict.get('speed',5.0), [m_dict.get('x',0.0), m_dict.get('y',0.0)], m_dict.get('theta',0.0)
142
+ # traffic_np, waypoints_np = traffic[0].detach().cpu().numpy().reshape(20,20,-1), waypoints[0].detach().cpu().numpy() * WAYPOINT_SCALE_FACTOR
143
+ # tracker, controller = Tracker(), InterfuserController(ControllerConfig())
144
+ # updated_traffic = tracker.update_and_predict(traffic_np.copy(), pos, theta, 0)
145
+ # steer, throttle, brake, metadata = controller.run_step(speed, waypoints_np, is_junction.sigmoid()[0,1].item(), traffic_light.sigmoid()[0,0].item(), stop_sign.sigmoid()[0,1].item(), updated_traffic)
146
+
147
+ # # ... (كود الرسم)
148
+ # map_t0, counts_t0 = render(updated_traffic, t=0)
149
+ # map_t1, counts_t1 = render(updated_traffic, t=T1_FUTURE_TIME)
150
+ # map_t2, counts_t2 = render(updated_traffic, t=T2_FUTURE_TIME)
151
+ # wp_map = render_waypoints(waypoints_np)
152
+ # self_car_map = render_self_car(np.array([0,0]), [math.cos(0), math.sin(0)], [4.0, 2.0])
153
+ # map_t0 = cv2.add(cv2.add(map_t0, wp_map), self_car_map)
154
+ # map_t0 = cv2.resize(map_t0, (400, 400))
155
+ # map_t1 = cv2.add(ensure_rgb(map_t1), ensure_rgb(self_car_map)); map_t1 = cv2.resize(map_t1, (200, 200))
156
+ # map_t2 = cv2.add(ensure_rgb(map_t2), ensure_rgb(self_car_map)); map_t2 = cv2.resize(map_t2, (200, 200))
157
+ # display = DisplayInterface()
158
+ # light_state, stop_sign_state = "Red" if traffic_light.sigmoid()[0,0].item() > 0.5 else "Green", "Yes" if stop_sign.sigmoid()[0,1].item() > 0.5 else "No"
159
+ # interface_data = {'camera_view': np.array(rgb_image_pil),'map_t0': map_t0,'map_t1': map_t1,'map_t2': map_t2,
160
+ # 'text_info': {'Control': f"S:{steer:.2f} T:{throttle:.2f} B:{int(brake)}",'Light': f"L: {light_state}",'Stop': f"St: {stop_sign_state}"},
161
+ # 'object_counts': {'t0': counts_t0,'t1': counts_t1,'t2': counts_t2}}
162
+ # dashboard_image = display.run_interface(interface_data)
163
+
164
+ # # --- 6. تجهيز المخرجات ---
165
+ # control_commands_dict = {"steer": steer, "throttle": throttle, "brake": bool(brake)}
166
+ # return Image.fromarray(dashboard_image), control_commands_dict
167
+
168
+ # except gr.Error as e:
169
+ # raise e # أعد إظهار أخطاء Gradio كما هي
170
+ # except Exception as e:
171
+ # print(traceback.format_exc())
172
+ # raise gr.Error(f"حدث خطأ غير متوقع أثناء معالجة الإطار: {e}")
173
+
174
+
175
+ # # ==============================================================================
176
+ # # 5. تعريف واجهة Gradio (لا تغيير هنا)
177
+ # # ==============================================================================
178
+ # # ... (كود الواجهة بالكامل يبقى كما هو من النسخة السابقة) ...
179
+ # available_models = find_available_models()
180
+
181
+ # with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"), css=".gradio-container {max-width: 95% !important;}") as demo:
182
+ # model_state = gr.State(value=None)
183
+
184
+ # gr.Markdown("# 🚗 محاكاة القيادة الذاتية باستخدام Interfuser")
185
+ # gr.Markdown("مرحباً بك في واجهة اختبار نموذج Interfuser. اتبع الخطوات أدناه لتشغيل المحاكاة على إطار واحد.")
186
+
187
+ # with gr.Row():
188
+ # # -- العمود الأيسر: الإعدادات والمدخلات --
189
+ # with gr.Column(scale=1):
190
+ # with gr.Group():
191
+ # gr.Markdown("## ⚙️ الخطوة 1: اختر النموذج")
192
+ # with gr.Row():
193
+ # model_selector = gr.Dropdown(
194
+ # label="النماذج المتاحة",
195
+ # choices=available_models,
196
+ # value=available_models[0] if available_models else "لم يتم العثور على نماذج"
197
+ # )
198
+ # status_textbox = gr.Textbox(label="حالة النموذج", interactive=False)
199
+
200
+ # with gr.Group():
201
+ # gr.Markdown("## 🗂️ الخطوة 2: ارفع ملفات السيناريو")
202
+
203
+ # with gr.Group():
204
+ # gr.Markdown("**(مطلوب)**")
205
+ # api_rgb_image_path = gr.File(label="صورة الكاميرا الأمامية (RGB)", type="filepath")
206
+ # api_measurements_path = gr.File(label="ملف القياسات (JSON)", type="filepath")
207
+
208
+ # with gr.Accordion("📷 مدخلات اختيارية (كاميرات ومستشعرات إضافية)", open=False):
209
+ # api_rgb_left_image_path = gr.File(label="كاميرا اليسار (RGB)", type="filepath")
210
+ # api_rgb_right_image_path = gr.File(label="كاميرا اليمين (RGB)", type="filepath")
211
+ # api_rgb_center_image_path = gr.File(label="كاميرا الوسط (RGB)", type="filepath")
212
+ # api_lidar_image_path = gr.File(label="بيانات الليدار (NPY)", type="filepath")
213
+
214
+ # api_target_point_list = gr.JSON(label="📍 النقطة المستهدفة (x, y)", value=[0.0, 100.0])
215
+
216
+ # api_run_button = gr.Button("🚀 شغل المحاكاة", variant="primary", scale=2)
217
+
218
+ # with gr.Group():
219
+ # gr.Markdown("### ✨ أمثلة جاهزة")
220
+ # gr.Markdown("انقر على مثال لتعبئة الحقول تلقائياً (يتطلب وجود مجلد `examples`).")
221
+ # gr.Examples(
222
+ # examples=[
223
+ # [os.path.join(EXAMPLES_DIR, "sample1", "rgb.jpg"), os.path.join(EXAMPLES_DIR, "sample1", "measurements.json")],
224
+ # [os.path.join(EXAMPLES_DIR, "sample2", "rgb.jpg"), os.path.join(EXAMPLES_DIR, "sample2", "measurements.json")]
225
+ # ],
226
+ # inputs=[api_rgb_image_path, api_measurements_path],
227
+ # label="اختر سيناريو اختبار"
228
+ # )
229
+
230
+ # # -- العمود الأيمن: المخرجات --
231
+ # with gr.Column(scale=2):
232
+ # with gr.Group():
233
+ # gr.Markdown("## 📊 الخطوة 3: شاهد النتائج")
234
+ # api_output_image = gr.Image(label="لوحة التحكم المرئية (Dashboard)", type="pil", interactive=False)
235
+ # api_control_json = gr.JSON(label="أوامر التحكم (JSON)")
236
+
237
+ # # --- ربط منطق الواجهة ---
238
+ # if available_models:
239
+ # demo.load(fn=load_model, inputs=model_selector, outputs=[model_state, status_textbox])
240
+
241
+ # model_selector.change(fn=load_model, inputs=model_selector, outputs=[model_state, status_textbox])
242
+
243
+ # api_run_button.click(
244
+ # fn=run_single_frame,
245
+ # inputs=[model_state, api_rgb_image_path, api_rgb_left_image_path, api_rgb_right_image_path,
246
+ # api_rgb_center_image_path, api_lidar_image_path, api_measurements_path, api_target_point_list],
247
+ # outputs=[api_output_image, api_control_json],
248
+ # api_name="run_single_frame"
249
+ # )
250
+
251
+ # # ==============================================================================
252
+ # # 6. تشغيل التطبيق
253
+ # # ==============================================================================
254
+ # if __name__ == "__main__":
255
+ # if not available_models:
256
+ # print("تحذير: لم يتم العثور على أي ملفات نماذج (.pth) في مجلد 'model/weights'.")
257
+ # demo.queue().launch(debug=True, share=True, show_api=True)
258
+
259
+
260
+
261
+
262
+ # الحديد
263
+ # app.py (النسخة المدمجة مع FastAPI)
264
 
265
  import os
266
  import json
 
271
  from PIL import Image
272
  import cv2
273
  import math
274
+ from fastapi import FastAPI, UploadFile, File, Form, HTTPException # ✅ استيراد FastAPI
275
+ from typing import List # ✅ استيراد للـ Type Hinting
276
 
277
  # --- استيراد من الملفات المنظمة في مشروعك ---
278
  from model import build_interfuser_model
 
282
  ensure_rgb, WAYPOINT_SCALE_FACTOR, T1_FUTURE_TIME, T2_FUTURE_TIME
283
  )
284
 
285
+ # ✅ ==============================================================================
286
+ # ✅ 0. إنشاء تطبيق FastAPI الرئيسي
287
+ # ✅ ==============================================================================
288
+ # هذا هو التطبيق الرئيسي الذي سيتم تشغيله.
289
+ # سيحتوي على كل من واجهة Gradio وواجهة API المخصصة.
290
+ app = FastAPI()
291
+
292
  # ==============================================================================
293
+ # 1. إعدادات ومسارات النماذج (لا تغيير)
294
  # ==============================================================================
295
  WEIGHTS_DIR = "model"
296
  EXAMPLES_DIR = "examples"
 
306
  return [f.replace(".pth", "") for f in os.listdir(WEIGHTS_DIR) if f.endswith(".pth")]
307
 
308
  # ==============================================================================
309
+ # 2. الدوال الأساسية (لا تغيير)
310
  # ==============================================================================
311
 
312
+ # ... (دالة load_model تبقى كما هي تمامًا) ...
313
  def load_model(model_name: str):
314
+ # ... نفس الكود ...
315
  if not model_name or "لم يتم" in model_name:
316
  return None, "الرجاء اختيار نموذج صالح."
317
  weights_path = os.path.join(WEIGHTS_DIR, f"{model_name}.pth")
 
331
  model.eval()
332
  return model, f"تم تحميل نموذج: {model_name}"
333
 
334
+ # ... (دالة run_single_frame تبقى كما هي تمامًا) ...
335
  def run_single_frame(
336
  model_from_state, rgb_image_path, rgb_left_image_path, rgb_right_image_path,
337
  rgb_center_image_path, lidar_image_path, measurements_path, target_point_list
338
  ):
339
+ # ... نفس الكود ...
 
 
340
  if model_from_state is None:
341
  print("API session detected or model not loaded. Loading default model...")
342
  available_models = find_available_models()
 
349
  raise gr.Error("فشل تحميل النموذج. تحقق من السجلات (Logs).")
350
 
351
  try:
352
+ # ... (بقية الكود داخل الدالة لا يتغير) ...
353
  if not (rgb_image_path and measurements_path):
354
  raise gr.Error("الرجاء توفير الصورة الأمامية وملف القياسات على الأقل.")
 
 
355
  try:
356
  rgb_image_pil = Image.open(rgb_image_path).convert("RGB")
357
  except Exception as e:
358
  raise gr.Error(f"فشل تحميل صورة الكاميرا الأمامية. تأكد من أن الملف صحيح. الخطأ: {e}")
 
359
  def load_optional_image(path, default_image):
360
  if path:
361
+ try: return Image.open(path).convert("RGB")
362
+ except Exception as e: raise gr.Error(f"فشل تحميل الصورة الاختيارية '{os.path.basename(path)}'. الخطأ: {e}")
 
 
363
  return default_image
 
364
  rgb_left_pil = load_optional_image(rgb_left_image_path, rgb_image_pil)
365
  rgb_right_pil = load_optional_image(rgb_right_image_path, rgb_image_pil)
366
  rgb_center_pil = load_optional_image(rgb_center_image_path, rgb_image_pil)
 
367
  if lidar_image_path:
368
  try:
369
  lidar_array = np.load(lidar_image_path)
370
  if lidar_array.max() > 0: lidar_array = (lidar_array / lidar_array.max()) * 255.0
371
  lidar_pil = Image.fromarray(lidar_array.astype(np.uint8)).convert('RGB')
372
+ except Exception as e: raise gr.Error(f"فشل تحميل ملف الليدار (.npy). تأكد من أن الملف صحيح. الخطأ: {e}")
 
373
  else:
374
  lidar_pil = Image.fromarray(np.zeros((112, 112, 3), dtype=np.uint8))
 
375
  try:
376
  with open(measurements_path, 'r') as f: m_dict = json.load(f)
377
+ except Exception as e: raise gr.Error(f"فشل تحميل أو قراءة ملف القياسات (.json). تأكد من أنه بصيغة صحيحة. الخطأ: {e}")
 
 
 
378
  front_tensor = transform(rgb_image_pil).unsqueeze(0).to(device)
379
  left_tensor = transform(rgb_left_pil).unsqueeze(0).to(device)
380
  right_tensor = transform(rgb_right_pil).unsqueeze(0).to(device)
381
  center_tensor = transform(rgb_center_pil).unsqueeze(0).to(device)
382
  lidar_tensor = lidar_transform(lidar_pil).unsqueeze(0).to(device)
383
+ measurements_tensor = torch.tensor([[m_dict.get('x',0.0), m_dict.get('y',0.0), m_dict.get('theta',0.0), m_dict.get('speed',5.0), m_dict.get('steer',0.0), m_dict.get('throttle',0.0), float(m_dict.get('brake',0.0)), m_dict.get('command',2.0), float(m_dict.get('is_junction',0.0)), float(m_dict.get('should_brake',0.0))]], dtype=torch.float32).to(device)
 
 
 
 
 
 
384
  target_point_tensor = torch.tensor([target_point_list], dtype=torch.float32).to(device)
 
385
  inputs = {'rgb': front_tensor, 'rgb_left': left_tensor, 'rgb_right': right_tensor, 'rgb_center': center_tensor, 'lidar': lidar_tensor, 'measurements': measurements_tensor, 'target_point': target_point_tensor}
 
 
386
  with torch.no_grad():
387
  outputs = model_to_use(inputs)
388
  traffic, waypoints, is_junction, traffic_light, stop_sign, _ = outputs
 
 
389
  speed, pos, theta = m_dict.get('speed',5.0), [m_dict.get('x',0.0), m_dict.get('y',0.0)], m_dict.get('theta',0.0)
390
  traffic_np, waypoints_np = traffic[0].detach().cpu().numpy().reshape(20,20,-1), waypoints[0].detach().cpu().numpy() * WAYPOINT_SCALE_FACTOR
391
  tracker, controller = Tracker(), InterfuserController(ControllerConfig())
392
  updated_traffic = tracker.update_and_predict(traffic_np.copy(), pos, theta, 0)
393
  steer, throttle, brake, metadata = controller.run_step(speed, waypoints_np, is_junction.sigmoid()[0,1].item(), traffic_light.sigmoid()[0,0].item(), stop_sign.sigmoid()[0,1].item(), updated_traffic)
 
 
394
  map_t0, counts_t0 = render(updated_traffic, t=0)
395
  map_t1, counts_t1 = render(updated_traffic, t=T1_FUTURE_TIME)
396
  map_t2, counts_t2 = render(updated_traffic, t=T2_FUTURE_TIME)
397
  wp_map = render_waypoints(waypoints_np)
398
  self_car_map = render_self_car(np.array([0,0]), [math.cos(0), math.sin(0)], [4.0, 2.0])
399
+ map_t0 = cv2.add(cv2.add(map_t0, wp_map), self_car_map); map_t0 = cv2.resize(map_t0, (400, 400))
 
400
  map_t1 = cv2.add(ensure_rgb(map_t1), ensure_rgb(self_car_map)); map_t1 = cv2.resize(map_t1, (200, 200))
401
  map_t2 = cv2.add(ensure_rgb(map_t2), ensure_rgb(self_car_map)); map_t2 = cv2.resize(map_t2, (200, 200))
402
  display = DisplayInterface()
403
  light_state, stop_sign_state = "Red" if traffic_light.sigmoid()[0,0].item() > 0.5 else "Green", "Yes" if stop_sign.sigmoid()[0,1].item() > 0.5 else "No"
404
+ interface_data = {'camera_view': np.array(rgb_image_pil),'map_t0': map_t0,'map_t1': map_t1,'map_t2': map_t2, 'text_info': {'Control': f"S:{steer:.2f} T:{throttle:.2f} B:{int(brake)}",'Light': f"L: {light_state}",'Stop': f"St: {stop_sign_state}"}, 'object_counts': {'t0': counts_t0,'t1': counts_t1,'t2': counts_t2}}
 
 
405
  dashboard_image = display.run_interface(interface_data)
 
 
406
  control_commands_dict = {"steer": steer, "throttle": throttle, "brake": bool(brake)}
407
  return Image.fromarray(dashboard_image), control_commands_dict
408
+ except gr.Error as e: raise e
 
 
409
  except Exception as e:
410
  print(traceback.format_exc())
411
  raise gr.Error(f"حدث خطأ غير متوقع أثناء معالجة الإطار: {e}")
412
 
413
+ # ✅ ==============================================================================
414
+ # ✅ 3. تعريف نقطة النهاية المخصصة (Custom API) باستخدام FastAPI
415
+ # ✅ ==============================================================================
416
+ @app.post("/api/predict_flutter", tags=["Flutter API"])
417
+ async def flutter_predict_endpoint(
418
+ rgb_image: UploadFile = File(..., description="صورة الكاميرا الأمامية المطلوبة"),
419
+ measurements_json: UploadFile = File(..., description="ملف القياسات المطلوب بصيغة JSON"),
420
+ target_point: str = Form(default='[0.0, 100.0]', description="النقطة المستهدفة كـ JSON string"),
421
+ # المدخلات الاختيارية
422
+ rgb_left_image: UploadFile = File(None),
423
+ rgb_right_image: UploadFile = File(None),
424
+ rgb_center_image: UploadFile = File(None),
425
+ lidar_data: UploadFile = File(None),
426
+ ):
427
+ """
428
+ نقطة نهاية بسيطة ومخصصة لتطبيق فلاتر.
429
+ تستقبل الملفات مباشرة وتستدعي دالة النموذج.
430
+ """
431
+ print("✅ Custom API endpoint /api/predict_flutter called!")
432
+
433
+ # دالة داخلية لحفظ الملفات المرفوعة مؤقتاً
434
+ async def save_upload_file(upload_file: UploadFile, destination: str):
435
+ if not upload_file: return None
436
+ try:
437
+ with open(destination, "wb") as f:
438
+ f.write(await upload_file.read())
439
+ return destination
440
+ except Exception as e:
441
+ raise HTTPException(status_code=500, detail=f"Could not save file: {e}")
442
+
443
+ # حفظ الملفات المطلوبة والاختيارية في مسارات مؤقتة
444
+ temp_rgb_path = await save_upload_file(rgb_image, "temp_rgb.png")
445
+ temp_measurements_path = await save_upload_file(measurements_json, "temp_measurements.json")
446
+ temp_left_path = await save_upload_file(rgb_left_image, "temp_left.png")
447
+ temp_right_path = await save_upload_file(rgb_right_image, "temp_right.png")
448
+ temp_center_path = await save_upload_file(rgb_center_image, "temp_center.png")
449
+ temp_lidar_path = await save_upload_file(lidar_data, "temp_lidar.npy")
450
+
451
+ try:
452
+ target_point_list = json.loads(target_point)
453
+ except json.JSONDecodeError:
454
+ raise HTTPException(status_code=400, detail="Invalid JSON format for target_point.")
455
+
456
+ try:
457
+ # استدعاء دالة النموذج مباشرة بالمسارات المؤقتة
458
+ # لا نحتاج لـ model_from_state لأننا سنقوم بتحميل النموذج مباشرة
459
+ dashboard_pil, commands_dict = run_single_frame(
460
+ model_from_state=None, # سيتم تحميل النموذج الافتراضي داخل الدالة
461
+ rgb_image_path=temp_rgb_path,
462
+ rgb_left_image_path=temp_left_path,
463
+ rgb_right_image_path=temp_right_path,
464
+ rgb_center_image_path=temp_center_path,
465
+ lidar_image_path=temp_lidar_path,
466
+ measurements_path=temp_measurements_path,
467
+ target_point_list=target_point_list
468
+ )
469
+
470
+ # FastAPI لا يمكنه إرجاع كائن PIL مباشرة، يجب تحويله
471
+ # يمكننا إعادته كـ Base64 أو حفظه وإرجاع مساره
472
+ # للتبسيط، سنرجع فقط أوامر التحكم
473
+ print("✅ Model execution successful. Returning control commands.")
474
+ return commands_dict
475
+
476
+ except gr.Error as e:
477
+ # تحويل أخطاء Gradio إلى أخطاء HTTP
478
+ raise HTTPException(status_code=400, detail=str(e))
479
+ except Exception as e:
480
+ print(traceback.format_exc())
481
+ raise HTTPException(status_code=500, detail=f"An internal server error occurred: {e}")
482
+ finally:
483
+ # ✅ تنظيف الملفات المؤقتة بعد الاستخدام
484
+ for path in [temp_rgb_path, temp_measurements_path, temp_left_path, temp_right_path, temp_center_path, temp_lidar_path]:
485
+ if path and os.path.exists(path):
486
+ os.remove(path)
487
+
488
 
489
  # ==============================================================================
490
+ # 4. تعريف واجهة Gradio (لا تغيير)
491
  # ==============================================================================
 
492
  available_models = find_available_models()
 
493
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"), css=".gradio-container {max-width: 95% !important;}") as demo:
494
+ # ... (كل كود واجهة Gradio يبقى كما هو تمامًا) ...
495
  model_state = gr.State(value=None)
 
496
  gr.Markdown("# 🚗 محاكاة القيادة الذاتية باستخدام Interfuser")
497
  gr.Markdown("مرحباً بك في واجهة اختبار نموذج Interfuser. اتبع الخطوات أدناه لتشغيل المحا��اة على إطار واحد.")
 
498
  with gr.Row():
 
499
  with gr.Column(scale=1):
500
  with gr.Group():
501
  gr.Markdown("## ⚙️ الخطوة 1: اختر النموذج")
502
  with gr.Row():
503
+ model_selector = gr.Dropdown(label="النماذج المتاحة", choices=available_models, value=available_models[0] if available_models else "لم يتم العثور على نماذج")
 
 
 
 
504
  status_textbox = gr.Textbox(label="حالة النموذج", interactive=False)
 
505
  with gr.Group():
506
  gr.Markdown("## 🗂️ الخطوة 2: ارفع ملفات السيناريو")
 
507
  with gr.Group():
508
  gr.Markdown("**(مطلوب)**")
509
  api_rgb_image_path = gr.File(label="صورة الكاميرا الأمامية (RGB)", type="filepath")
510
  api_measurements_path = gr.File(label="ملف القياسات (JSON)", type="filepath")
 
511
  with gr.Accordion("📷 مدخلات اختيارية (كاميرات ومستشعرات إضافية)", open=False):
512
  api_rgb_left_image_path = gr.File(label="كاميرا اليسار (RGB)", type="filepath")
513
  api_rgb_right_image_path = gr.File(label="كاميرا اليمين (RGB)", type="filepath")
514
  api_rgb_center_image_path = gr.File(label="كاميرا الوسط (RGB)", type="filepath")
515
  api_lidar_image_path = gr.File(label="بيانات الليدار (NPY)", type="filepath")
 
516
  api_target_point_list = gr.JSON(label="📍 النقطة المستهدفة (x, y)", value=[0.0, 100.0])
 
517
  api_run_button = gr.Button("🚀 شغل المحاكاة", variant="primary", scale=2)
 
518
  with gr.Group():
519
  gr.Markdown("### ✨ أمثلة جاهزة")
520
  gr.Markdown("انقر على مثال لتعبئة الحقول تلقائياً (يتطلب وجود مجلد `examples`).")
521
+ gr.Examples(examples=[[os.path.join(EXAMPLES_DIR, "sample1", "rgb.jpg"), os.path.join(EXAMPLES_DIR, "sample1", "measurements.json")], [os.path.join(EXAMPLES_DIR, "sample2", "rgb.jpg"), os.path.join(EXAMPLES_DIR, "sample2", "measurements.json")]], inputs=[api_rgb_image_path, api_measurements_path], label="اختر سيناريو اختبار")
 
 
 
 
 
 
 
 
 
522
  with gr.Column(scale=2):
523
  with gr.Group():
524
  gr.Markdown("## 📊 الخطوة 3: شاهد النتائج")
525
  api_output_image = gr.Image(label="لوحة التحكم المرئية (Dashboard)", type="pil", interactive=False)
526
  api_control_json = gr.JSON(label="أوامر التحكم (JSON)")
 
 
527
  if available_models:
528
  demo.load(fn=load_model, inputs=model_selector, outputs=[model_state, status_textbox])
 
529
  model_selector.change(fn=load_model, inputs=model_selector, outputs=[model_state, status_textbox])
530
+ api_run_button.click(fn=run_single_frame, inputs=[model_state, api_rgb_image_path, api_rgb_left_image_path, api_rgb_right_image_path, api_rgb_center_image_path, api_lidar_image_path, api_measurements_path, api_target_point_list], outputs=[api_output_image, api_control_json], api_name="run_single_frame")
 
 
 
 
 
 
 
531
 
532
+ # ==============================================================================
533
+ # ✅ 5. تركيب واجهة Gradio على تطبيق FastAPI
534
+ # ==============================================================================
535
+ # هذه هي الخطوة السحرية التي تدمج العالمين معًا.
536
+ app = gr.mount_ публіk(app, demo, path="/")
537
+
538
+ # ==============================================================================
539
+ # ✅ 6. تعديل requirements.txt
540
+ # ✅ ==============================================================================
541
+ # تأكد من أن ملف requirements.txt يحتوي على:
542
+ # fastapi
543
+ # uvicorn
544
+ # python-multipart
545
+ # (بالإضافة إلى مكتباتك الحالية مثل torch, gradio, etc.)