EngReem85 commited on
Commit
e1069b0
·
verified ·
1 Parent(s): 8b15264

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +60 -50
app.py CHANGED
@@ -7,7 +7,7 @@ import os
7
  import math
8
 
9
  mp_pose = mp.solutions.pose
10
- pose = mp_pose.Pose(static_image_mode=False, min_detection_confidence=0.7, model_complexity=1)
11
 
12
  # ===========================
13
  # دوال مساعدة
@@ -46,7 +46,6 @@ def analyze_gait(video_file):
46
  if video_file is None:
47
  return "<div>❌ يرجى رفع فيديو أولًا.</div>", "<div></div>"
48
 
49
- # حفظ الفيديو مؤقتًا
50
  if hasattr(video_file, "name"):
51
  video_path = video_file.name
52
  else:
@@ -65,11 +64,10 @@ def analyze_gait(video_file):
65
  H = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT) or 480)
66
  px2m = 1.7 / (H * 0.8)
67
 
68
- # قياسات الحركة
69
  L_clear, R_clear = [], []
70
  L_angle, R_angle = [], []
71
  base_px_seq = []
72
- torso_tilt_seq = []
73
 
74
  ground_y = H * 0.92
75
  frames_processed = 0
@@ -86,9 +84,9 @@ def analyze_gait(video_file):
86
 
87
  person_detected = True
88
  lm = res.pose_landmarks.landmark
89
-
90
  def xy(idx): return [lm[idx].x * W, lm[idx].y * H]
91
 
 
92
  L_ank = xy(mp_pose.PoseLandmark.LEFT_ANKLE.value)
93
  R_ank = xy(mp_pose.PoseLandmark.RIGHT_ANKLE.value)
94
  L_knee = xy(mp_pose.PoseLandmark.LEFT_KNEE.value)
@@ -100,7 +98,7 @@ def analyze_gait(video_file):
100
  L_sh = xy(mp_pose.PoseLandmark.LEFT_SHOULDER.value)
101
  R_sh = xy(mp_pose.PoseLandmark.RIGHT_SHOULDER.value)
102
 
103
- # ارتفاع القدم
104
  Lc = max(0, (ground_y - min(L_ank[1], L_foot[1])) * px2m * 100)
105
  Rc = max(0, (ground_y - min(R_ank[1], R_foot[1])) * px2m * 100)
106
  L_clear.append(Lc)
@@ -112,16 +110,20 @@ def analyze_gait(video_file):
112
  L_angle.append(La)
113
  R_angle.append(Ra)
114
 
115
- # قاعدة القدمين
116
- base_px_seq.append(abs(L_ank[0] - R_ank[0]))
117
 
118
- # ميل الجذع
119
  mid_sh = [(L_sh[0]+R_sh[0])/2, (L_sh[1]+R_sh[1])/2]
120
  mid_hip= [(L_hip[0]+R_hip[0])/2, (L_hip[1]+R_hip[1])/2]
121
  vec = np.array([mid_sh[0]-mid_hip[0], mid_sh[1]-mid_hip[1]])
122
  tilt = abs(90 - abs(math.degrees(math.atan2(abs(vec[1]), abs(vec[0])+1e-6))))
123
  torso_tilt_seq.append(tilt)
124
 
 
 
 
 
125
  cap.release()
126
  try: os.unlink(video_path)
127
  except: pass
@@ -130,59 +132,70 @@ def analyze_gait(video_file):
130
  return "<div>❌ لم يتم اكتشاف شخص في الفيديو.</div>", "<div></div>"
131
 
132
  # ===========================
133
- # الإحصاءات والتحليل
134
  # ===========================
135
  avg_Lc, avg_Rc = _safe_mean(L_clear), _safe_mean(R_clear)
136
  std_Lc, std_Rc = _safe_std(L_clear), _safe_std(R_clear)
137
  avg_La, avg_Ra = _safe_mean(L_angle), _safe_mean(R_angle)
138
  avg_base_px = _safe_mean(base_px_seq)
139
  avg_tilt = _safe_mean(torso_tilt_seq)
 
140
  var_clear = max(std_Lc, std_Rc)
141
  diff_clear = abs(avg_Lc - avg_Rc)
142
  diff_angle = abs(avg_La - avg_Ra)
 
 
 
 
143
 
144
- # تحديد زاوية التصوير (أغلبها أمامية)
145
- view_ratio = avg_base_px / max(1, W)
146
- view = "frontal" if view_ratio > 0.16 else "side"
147
 
148
  # ===========================
149
- # منطق التصنيف الجديد (أعلى دقة)
150
  # ===========================
151
  score = 0
152
  strong_flags = 0
153
 
154
- # Foot Drop
155
- if min(avg_Lc, avg_Rc) < 3.5 or (var_clear > 9 and diff_angle < 20):
156
- score += 3
157
- strong_flags += 1
158
 
159
  # Neuropathy
160
- if (var_clear > 8 and diff_angle > 15) or diff_clear > 6:
161
- score += 2.5
162
- strong_flags += 1
163
 
164
  # Charcot
165
- if avg_base_px * px2m > 0.28 and avg_tilt > 9:
166
- score += 3
167
- strong_flags += 1
168
 
169
- # ميل الجذع الواضح
170
- if avg_tilt > 12:
171
  score += 1.5
172
 
173
- # زيادة الوزن للحالات الأمامية لأنها أكثر حساسية للانحراف
174
- if view == "frontal":
175
- score *= 1.2
176
 
177
- # تقييد الدرجة ضمن 0-10
178
  score = min(score, 10)
179
  norm_score = score / 10
180
 
181
  # ===========================
182
- # التقييم النهائي + زر الحجز
 
 
 
 
 
 
 
 
 
 
183
  # ===========================
184
  if norm_score >= 0.7 or strong_flags >= 2:
185
- level, color, desc = "🔴 عالية الخطورة", "#c62828", "تم رصد مؤشرات قوية تستدعي مراجعة طبيب متخصص."
186
  booking_html = """
187
  <div style="margin-top:10px">
188
  <a href="https://example.com/book" target="_blank"
@@ -193,7 +206,7 @@ def analyze_gait(video_file):
193
  </div>
194
  """
195
  elif norm_score >= 0.45:
196
- level, color, desc = "🟡 متوسطة الخطورة", "#fbc02d", "مؤشرات تستدعي متابعة طبية وقائية."
197
  booking_html = """
198
  <div style="margin-top:10px">
199
  <a href="https://example.com/book" target="_blank"
@@ -204,40 +217,37 @@ def analyze_gait(video_file):
204
  </div>
205
  """
206
  else:
207
- level, color, desc = "🟢 طبيعية", "#2e7d32", "المشية ضمن النطاق الطبيعي."
208
  booking_html = ""
209
 
210
- # ترجيح الحالة
211
- if score >= 7:
212
- if avg_base_px * px2m > 0.28:
213
- condition = "قدم شاركوت (Charcot Foot)"
214
- elif diff_angle > 15:
215
- condition = "اعتلال الأعصاب المحيطية / السكري"
216
- else:
217
- condition = "ضعف العضلة الظنبوبية (Foot Drop)"
218
- elif score >= 4:
219
- condition = "خلل بسيط غير محدد"
220
  else:
221
- condition = "لا توجد مؤشرات مرضية واضحة"
222
 
223
  html = f"""
224
  <div style='color:{color};font-weight:700;font-size:18px'>{level}</div>
225
  <div>👁️ زاوية التصوير: <b>{'أمامية' if view=='frontal' else 'جانبية'}</b></div>
 
226
  <div>🩺 الحالة المحتملة: <b>{condition}</b></div>
227
  <div>📊 درجة الخطورة: <b>{score:.1f}/10</b></div>
228
  <div>{desc}</div>
229
  {booking_html}
230
- <div style='font-size:13px;color:#555;margin-top:8px'>⚠️ هذا تحليل مبدئي يعتمد على الفيديو فقط ولا يُغني عن الفحص الطبي.</div>
231
  """
232
-
233
  return html, _gauge_html(norm_score)
234
 
235
  # ===========================
236
  # واجهة Gradio
237
  # ===========================
238
- with gr.Blocks(title="تحليل المشية العصبية - v7 (نهائي ومحسّن)") as demo:
239
- gr.Markdown("## 🩺 نظام تحليل المشية العصبية (الإصدار 7)")
240
- gr.Markdown("🔹 التحليل مخصص للمقاطع الأمامية أو الجانبية (15–30 ثانية).<br>🔹 يظهر زر للحجز عند الحاجة إلى متابعة طبية.")
241
 
242
  with gr.Row():
243
  with gr.Column(scale=1):
 
7
  import math
8
 
9
  mp_pose = mp.solutions.pose
10
+ pose = mp_pose.Pose(static_image_mode=False, min_detection_confidence=0.75, model_complexity=2)
11
 
12
  # ===========================
13
  # دوال مساعدة
 
46
  if video_file is None:
47
  return "<div>❌ يرجى رفع فيديو أولًا.</div>", "<div></div>"
48
 
 
49
  if hasattr(video_file, "name"):
50
  video_path = video_file.name
51
  else:
 
64
  H = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT) or 480)
65
  px2m = 1.7 / (H * 0.8)
66
 
 
67
  L_clear, R_clear = [], []
68
  L_angle, R_angle = [], []
69
  base_px_seq = []
70
+ torso_tilt_seq, torso_side_lean_seq = [], []
71
 
72
  ground_y = H * 0.92
73
  frames_processed = 0
 
84
 
85
  person_detected = True
86
  lm = res.pose_landmarks.landmark
 
87
  def xy(idx): return [lm[idx].x * W, lm[idx].y * H]
88
 
89
+ # النقاط
90
  L_ank = xy(mp_pose.PoseLandmark.LEFT_ANKLE.value)
91
  R_ank = xy(mp_pose.PoseLandmark.RIGHT_ANKLE.value)
92
  L_knee = xy(mp_pose.PoseLandmark.LEFT_KNEE.value)
 
98
  L_sh = xy(mp_pose.PoseLandmark.LEFT_SHOULDER.value)
99
  R_sh = xy(mp_pose.PoseLandmark.RIGHT_SHOULDER.value)
100
 
101
+ # ارتفاع القدم بالسنتيمتر
102
  Lc = max(0, (ground_y - min(L_ank[1], L_foot[1])) * px2m * 100)
103
  Rc = max(0, (ground_y - min(R_ank[1], R_foot[1])) * px2m * 100)
104
  L_clear.append(Lc)
 
110
  L_angle.append(La)
111
  R_angle.append(Ra)
112
 
113
+ # المسافة الأفقية بين الكاحلين
114
+ base_px_seq.append(abs(L_ank[0]-R_ank[0]))
115
 
116
+ # ميل الجذع للأمام والخلف
117
  mid_sh = [(L_sh[0]+R_sh[0])/2, (L_sh[1]+R_sh[1])/2]
118
  mid_hip= [(L_hip[0]+R_hip[0])/2, (L_hip[1]+R_hip[1])/2]
119
  vec = np.array([mid_sh[0]-mid_hip[0], mid_sh[1]-mid_hip[1]])
120
  tilt = abs(90 - abs(math.degrees(math.atan2(abs(vec[1]), abs(vec[0])+1e-6))))
121
  torso_tilt_seq.append(tilt)
122
 
123
+ # ميل جانبي للجذع (لتحديد انحراف نحو اليمين أو اليسار)
124
+ torso_side = (L_sh[0]+R_sh[0])/2 - (L_hip[0]+R_hip[0])/2
125
+ torso_side_lean_seq.append(torso_side)
126
+
127
  cap.release()
128
  try: os.unlink(video_path)
129
  except: pass
 
132
  return "<div>❌ لم يتم اكتشاف شخص في الفيديو.</div>", "<div></div>"
133
 
134
  # ===========================
135
+ # الإحصاءات المحسَّنة
136
  # ===========================
137
  avg_Lc, avg_Rc = _safe_mean(L_clear), _safe_mean(R_clear)
138
  std_Lc, std_Rc = _safe_std(L_clear), _safe_std(R_clear)
139
  avg_La, avg_Ra = _safe_mean(L_angle), _safe_mean(R_angle)
140
  avg_base_px = _safe_mean(base_px_seq)
141
  avg_tilt = _safe_mean(torso_tilt_seq)
142
+ avg_side_lean = _safe_mean(torso_side_lean_seq)
143
  var_clear = max(std_Lc, std_Rc)
144
  diff_clear = abs(avg_Lc - avg_Rc)
145
  diff_angle = abs(avg_La - avg_Ra)
146
+ px_base_ratio = avg_base_px / W
147
+
148
+ # زاوية التصوير
149
+ view = "frontal" if px_base_ratio > 0.14 else "side"
150
 
151
+ # نسبة الإطارات اللي فيها انخفاض شديد
152
+ low_ratio_L = sum(np.array(L_clear)<3.5)/len(L_clear)
153
+ low_ratio_R = sum(np.array(R_clear)<3.5)/len(R_clear)
154
 
155
  # ===========================
156
+ # منطق الصرامة العالية
157
  # ===========================
158
  score = 0
159
  strong_flags = 0
160
 
161
+ # ضعف القدم (Foot Drop)
162
+ if (min(avg_Lc, avg_Rc)<3.5) or (low_ratio_L>0.4 or low_ratio_R>0.4):
163
+ score += 3.5; strong_flags += 1
 
164
 
165
  # Neuropathy
166
+ if (var_clear>9 and diff_angle>15) or (diff_clear>6 and var_clear>8):
167
+ score += 3; strong_flags += 1
 
168
 
169
  # Charcot
170
+ if (px_base_ratio>0.25 and avg_tilt>10):
171
+ score += 3.5; strong_flags += 1
 
172
 
173
+ # ميل جانبي واضح للجذع
174
+ if abs(avg_side_lean) > W*0.03:
175
  score += 1.5
176
 
177
+ # اختلاف كبير بين القدمين
178
+ if diff_clear > 7:
179
+ score += 1.5
180
 
 
181
  score = min(score, 10)
182
  norm_score = score / 10
183
 
184
  # ===========================
185
+ # تحديد الجانب المتأثر
186
+ # ===========================
187
+ if avg_Lc < avg_Rc - 2.5 and avg_La < avg_Ra:
188
+ side = "اليسار"
189
+ elif avg_Rc < avg_Lc - 2.5 and avg_Ra < avg_La:
190
+ side = "اليمين"
191
+ else:
192
+ side = "كلا الجانبين / غير محدد"
193
+
194
+ # ===========================
195
+ # التقييم النهائي
196
  # ===========================
197
  if norm_score >= 0.7 or strong_flags >= 2:
198
+ level, color, desc = "🔴 عالية الخطورة", "#c62828", "تم رصد مؤشرات قوية لخلل في المشية."
199
  booking_html = """
200
  <div style="margin-top:10px">
201
  <a href="https://example.com/book" target="_blank"
 
206
  </div>
207
  """
208
  elif norm_score >= 0.45:
209
+ level, color, desc = "🟡 متوسطة الخطورة", "#fbc02d", "تم رصد مؤشرات تحتاج متابعة طبية."
210
  booking_html = """
211
  <div style="margin-top:10px">
212
  <a href="https://example.com/book" target="_blank"
 
217
  </div>
218
  """
219
  else:
220
+ level, color, desc = "🟢 طبيعية", "#2e7d32", "المشية ضمن الحدود الطبيعية."
221
  booking_html = ""
222
 
223
+ # الحالة
224
+ if strong_flags >= 2 and (px_base_ratio>0.25):
225
+ condition = "قدم شاركوت (Charcot Foot)"
226
+ elif low_ratio_L>0.4 or low_ratio_R>0.4:
227
+ condition = "ضعف العضلة الظنبوبية (Foot Drop)"
228
+ elif (var_clear>8 and diff_angle>15):
229
+ condition = "اعتلال الأعصاب المحيطية / السكري"
 
 
 
230
  else:
231
+ condition = "خلل بسيط غير محدد"
232
 
233
  html = f"""
234
  <div style='color:{color};font-weight:700;font-size:18px'>{level}</div>
235
  <div>👁️ زاوية التصوير: <b>{'أمامية' if view=='frontal' else 'جانبية'}</b></div>
236
+ <div>📍 الجانب المتأثر: <b>{side}</b></div>
237
  <div>🩺 الحالة المحتملة: <b>{condition}</b></div>
238
  <div>📊 درجة الخطورة: <b>{score:.1f}/10</b></div>
239
  <div>{desc}</div>
240
  {booking_html}
241
+ <div style='font-size:13px;color:#555;margin-top:8px'>⚠️ هذا التحليل يعتمد على أنماط زمنية وزوايا متعددة، ولا يُغني عن الفحص الطبي.</div>
242
  """
 
243
  return html, _gauge_html(norm_score)
244
 
245
  # ===========================
246
  # واجهة Gradio
247
  # ===========================
248
+ with gr.Blocks(title="تحليل المشية العصبية - v8 (دقيق وصارم)") as demo:
249
+ gr.Markdown("## 🩺 نظام تحليل المشية العصبية (الإصدار 8)")
250
+ gr.Markdown("🔹 النظام يستخدم خوارزميات متقدمة لتحليل المشية بدقة عالية.<br>🔹 يحدد الجانب المتأثر ويقترح حجز موعد عند الحاجة.")
251
 
252
  with gr.Row():
253
  with gr.Column(scale=1):