EngReem85 commited on
Commit
a9324fd
·
verified ·
1 Parent(s): 0ab681f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +49 -68
app.py CHANGED
@@ -6,15 +6,9 @@ import tempfile
6
  import os
7
  import math
8
 
9
- # ===========================
10
- # إعداد Mediapipe Pose
11
- # ===========================
12
  mp_pose = mp.solutions.pose
13
  pose = mp_pose.Pose(static_image_mode=False, min_detection_confidence=0.7, model_complexity=1)
14
 
15
- # ===========================
16
- # دوال مساعدة
17
- # ===========================
18
  def _dist(p1, p2): return math.hypot(p1[0]-p2[0], p1[1]-p2[1])
19
  def _angle(a, b, c):
20
  try:
@@ -29,9 +23,6 @@ def _angle(a, b, c):
29
  def _safe_mean(x): return float(np.mean(x)) if x else 0.0
30
  def _safe_std(x): return float(np.std(x)) if x else 0.0
31
 
32
- # ===========================
33
- # التحليل الرئيسي للفيديو
34
- # ===========================
35
  def analyze_gait(video_file):
36
  if video_file is None:
37
  return "<div>❌ يرجى رفع فيديو أولًا.</div>", None
@@ -55,11 +46,7 @@ def analyze_gait(video_file):
55
  H = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT) or 480)
56
  px2m = 1.7 / (H * 0.8)
57
 
58
- # القوائم للقياسات
59
- L_clear, R_clear = [], []
60
- L_angle, R_angle = [], []
61
- L_step, R_step = [], []
62
- base_widths = []
63
  prev_L_ank, prev_R_ank = None, None
64
  frames_processed, person_detected = 0, False
65
  ground_y = H * 0.92
@@ -83,24 +70,17 @@ def analyze_gait(video_file):
83
  L_foot = xy(mp_pose.PoseLandmark.LEFT_FOOT_INDEX.value)
84
  R_foot = xy(mp_pose.PoseLandmark.RIGHT_FOOT_INDEX.value)
85
 
86
- # الارتفاع بالسنتيمتر
87
  Lc = max(0, (ground_y - min(L_ank[1], L_foot[1])) * px2m * 100)
88
  Rc = max(0, (ground_y - min(R_ank[1], R_foot[1])) * px2m * 100)
89
- L_clear.append(Lc); R_clear.append(Rc)
 
90
 
91
- # زاوية الكاحل
92
  La = _angle(L_knee, L_ank, L_foot)
93
  Ra = _angle(R_knee, R_ank, R_foot)
94
- L_angle.append(La); R_angle.append(Ra)
95
-
96
  base_widths.append(abs(L_ank[0]-R_ank[0]) * px2m)
97
 
98
- if prev_L_ank is not None:
99
- L_step.append(_dist(L_ank, prev_L_ank) * px2m)
100
- if prev_R_ank is not None:
101
- R_step.append(_dist(R_ank, prev_R_ank) * px2m)
102
- prev_L_ank, prev_R_ank = L_ank, R_ank
103
-
104
  cap.release()
105
  try: os.unlink(video_path)
106
  except: pass
@@ -108,66 +88,69 @@ def analyze_gait(video_file):
108
  if not person_detected:
109
  return "<div>❌ لم يتم اكتشاف شخص في الفيديو.</div>", None
110
 
111
- # ===========================
112
- # إحصاءات أساسية
113
- # ===========================
114
  avg_Lc, avg_Rc = _safe_mean(L_clear), _safe_mean(R_clear)
115
  std_Lc, std_Rc = _safe_std(L_clear), _safe_std(R_clear)
116
  avg_La, avg_Ra = _safe_mean(L_angle), _safe_mean(R_angle)
117
- avg_Lstep, avg_Rstep = _safe_mean(L_step), _safe_mean(R_step)
118
  avg_base = _safe_mean(base_widths)
119
- duration_s = (frames_processed / fps) if fps > 0 else frames_processed / 30
120
- cadence = ((len(L_step) + len(R_step)) / 2) / duration_s * 60 if duration_s > 0 else 0
121
-
122
- # ===========================
123
- # ملامح الأنماط العصبية
124
- # ===========================
125
  diff_angle = abs(avg_La - avg_Ra)
126
  diff_clear = abs(avg_Lc - avg_Rc)
127
  var_clear = max(std_Lc, std_Rc)
128
  min_clear = min(avg_Lc, avg_Rc)
129
- avg_step = (avg_Lstep + avg_Rstep) / 2
130
 
131
- # ==== تحديد الحالة المحتملة ====
132
- foot_drop_like = (min_clear < 4 and diff_angle < 30)
133
- neuropathy_like = (var_clear > 10 and diff_angle > 15 and cadence < 70)
134
- charcot_like = (avg_base > 0.28 and var_clear > 8 and diff_angle > 10)
135
 
136
- # ==== حساب درجة الخطورة الكلية ====
137
  score = 0
138
- score += 2.5 if foot_drop_like else 0
139
- score += 2.0 if neuropathy_like else 0
140
- score += 2.5 if charcot_like else 0
141
- score += 1.0 if avg_step < 0.18 else 0
142
- score += 0.5 if cadence < 60 else 0
143
- score += 1.0 if diff_clear > 5 else 0
144
- score += 0.5 if var_clear > 7 else 0
 
 
 
 
 
 
 
 
 
 
 
 
145
 
146
  # تجاهل المشي الطليعي المنتظم
147
  if avg_Lc > 10 and avg_Rc > 10 and var_clear < 5:
148
  return (
149
  "<div style='color:#2e7d32;font-weight:600'>✅ المشية طليعية طبيعية.</div>"
150
- "<div>تم التعرف على مشية طليعية مستقرة دون مؤشرات لخلل عصبي.</div>"
151
- "<div style='font-size:13px;color:#555;margin-top:8px'>⚠️ المتابعة فقط عند وجود ألم أو خلل توازن.</div>",
152
  0.1
153
  )
154
 
155
- # ==== تقييم الحالة ====
156
- norm_score = min(score / 8, 1.0)
 
157
 
158
- if norm_score < 0.35:
159
  level, color, desc = "🟢 طبيعي", "#2e7d32", "المشية ضمن النطاق الطبيعي."
160
- elif norm_score < 0.6:
161
- level, color, desc = "🟡 متوسطة الخطورة", "#fbc02d", "هناك بعض الاختلافات الطفيفة فقط."
162
  else:
163
  level, color, desc = "🔴 عالية الخطورة", "#c62828", "تم رصد مؤشرات قوية لخلل في المشية."
164
 
165
- # نوع الحالة المرجّحة
166
- if charcot_like:
167
  condition = "قدم شاركوت (Charcot Foot)"
168
- elif foot_drop_like:
169
  condition = "ضعف العضلة الظنبوبية (Foot Drop)"
170
- elif neuropathy_like:
171
  condition = "اعتلال الأعصاب المحيطية / السكري"
172
  else:
173
  condition = "غير محددة بدقة"
@@ -175,25 +158,23 @@ def analyze_gait(video_file):
175
  html = f"""
176
  <div style='color:{color};font-weight:700;font-size:18px'>{level}</div>
177
  <div>🩺 الحالة المحتملة: <b>{condition}</b></div>
178
- <div>📊 درجة الخطورة: <b>{score:.1f}/8</b></div>
179
  <div>{desc}</div>
180
- <div style='font-size:13px;color:#555;margin-top:8px'>⚠️ هذا تحليل مبدئي يعتمد على نمط المشي فقط ولا يُغني عن التشخيص الطبي.</div>
181
  """
182
  return html, norm_score
183
 
184
- # ===========================
185
- # واجهة Gradio
186
- # ===========================
187
- with gr.Blocks(title="تحليل المشية العصبية - الإصدار المتوازن") as demo:
188
- gr.Markdown("## 🩺 نظام تحليل المشية العصبية (الإصدار المتوازن)")
189
- gr.Markdown("ارفع فيديو جانبي للمشي (15–30 ثانية) وسيقوم النظام بتصنيف الحالة مع درجة الخطورة.")
190
 
191
  with gr.Row():
192
  with gr.Column(scale=1):
193
  video_in = gr.File(label="📂 اختر فيديو المشي", file_types=[".mp4", ".avi", ".mov"], type="binary")
194
  analyze_btn = gr.Button("🔍 بدء التحليل", variant="primary")
195
  with gr.Column(scale=1):
196
- gauge = gr.Number(label="⚙️ درجة الخطورة (0-1)", interactive=False)
197
  out_html = gr.HTML("<i>النتيجة ستظهر هنا...</i>")
198
 
199
  analyze_btn.click(fn=analyze_gait, inputs=[video_in], outputs=[out_html, gauge])
 
6
  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
  def _dist(p1, p2): return math.hypot(p1[0]-p2[0], p1[1]-p2[1])
13
  def _angle(a, b, c):
14
  try:
 
23
  def _safe_mean(x): return float(np.mean(x)) if x else 0.0
24
  def _safe_std(x): return float(np.std(x)) if x else 0.0
25
 
 
 
 
26
  def analyze_gait(video_file):
27
  if video_file is None:
28
  return "<div>❌ يرجى رفع فيديو أولًا.</div>", None
 
46
  H = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT) or 480)
47
  px2m = 1.7 / (H * 0.8)
48
 
49
+ L_clear, R_clear, L_angle, R_angle, base_widths = [], [], [], [], []
 
 
 
 
50
  prev_L_ank, prev_R_ank = None, None
51
  frames_processed, person_detected = 0, False
52
  ground_y = H * 0.92
 
70
  L_foot = xy(mp_pose.PoseLandmark.LEFT_FOOT_INDEX.value)
71
  R_foot = xy(mp_pose.PoseLandmark.RIGHT_FOOT_INDEX.value)
72
 
 
73
  Lc = max(0, (ground_y - min(L_ank[1], L_foot[1])) * px2m * 100)
74
  Rc = max(0, (ground_y - min(R_ank[1], R_foot[1])) * px2m * 100)
75
+ L_clear.append(Lc)
76
+ R_clear.append(Rc)
77
 
 
78
  La = _angle(L_knee, L_ank, L_foot)
79
  Ra = _angle(R_knee, R_ank, R_foot)
80
+ L_angle.append(La)
81
+ R_angle.append(Ra)
82
  base_widths.append(abs(L_ank[0]-R_ank[0]) * px2m)
83
 
 
 
 
 
 
 
84
  cap.release()
85
  try: os.unlink(video_path)
86
  except: pass
 
88
  if not person_detected:
89
  return "<div>❌ لم يتم اكتشاف شخص في الفيديو.</div>", None
90
 
91
+ # ======== التحليل الإحصائي والديناميكي ========
 
 
92
  avg_Lc, avg_Rc = _safe_mean(L_clear), _safe_mean(R_clear)
93
  std_Lc, std_Rc = _safe_std(L_clear), _safe_std(R_clear)
94
  avg_La, avg_Ra = _safe_mean(L_angle), _safe_mean(R_angle)
 
95
  avg_base = _safe_mean(base_widths)
 
 
 
 
 
 
96
  diff_angle = abs(avg_La - avg_Ra)
97
  diff_clear = abs(avg_Lc - avg_Rc)
98
  var_clear = max(std_Lc, std_Rc)
99
  min_clear = min(avg_Lc, avg_Rc)
 
100
 
101
+ # نسب زمنية لأنماط غير طبيعية
102
+ L_low_ratio = sum(np.array(L_clear) < 4) / len(L_clear) if L_clear else 0
103
+ R_low_ratio = sum(np.array(R_clear) < 4) / len(R_clear) if R_clear else 0
104
+ dynamic_asym = np.mean(np.abs(np.array(L_clear) - np.array(R_clear))) if L_clear and R_clear else 0
105
 
106
+ # ======== خوارزمية التصنيف متعددة المعايير ========
107
  score = 0
108
+
109
+ # حساسية أعلى للحالات العصبية
110
+ if (L_low_ratio > 0.4 or R_low_ratio > 0.4) and min_clear < 3.5:
111
+ score += 3.0 # Foot drop قوي
112
+
113
+ if diff_angle > 20 and var_clear > 9:
114
+ score += 2.5 # Neuropathy قوي
115
+
116
+ if avg_base > 0.27 and var_clear > 7 and diff_angle > 10:
117
+ score += 2.5 # Charcot قوي
118
+
119
+ if dynamic_asym > 5 and var_clear > 8:
120
+ score += 1.5
121
+
122
+ if diff_clear > 6:
123
+ score += 1.0
124
+
125
+ if std_Lc > 10 or std_Rc > 10:
126
+ score += 0.5
127
 
128
  # تجاهل المشي الطليعي المنتظم
129
  if avg_Lc > 10 and avg_Rc > 10 and var_clear < 5:
130
  return (
131
  "<div style='color:#2e7d32;font-weight:600'>✅ المشية طليعية طبيعية.</div>"
132
+ "<div>تم التعرف على مشية طليعية منتظمة دون مؤشرات لخلل عصبي.</div>"
133
+ "<div style='font-size:13px;color:#555;margin-top:8px'>⚠️ يُوصى بالمراجعة فقط في حال وجود ألم أو ضعف توازن.</div>",
134
  0.1
135
  )
136
 
137
+ # ======== تحليل الحالة النهائية ========
138
+ max_score = 9.0
139
+ norm_score = min(score / max_score, 1.0)
140
 
141
+ if norm_score < 0.3:
142
  level, color, desc = "🟢 طبيعي", "#2e7d32", "المشية ضمن النطاق الطبيعي."
143
+ elif norm_score < 0.55:
144
+ level, color, desc = "🟡 متوسطة الخطورة", "#fbc02d", "بعض الاختلافات الطفيفة."
145
  else:
146
  level, color, desc = "🔴 عالية الخطورة", "#c62828", "تم رصد مؤشرات قوية لخلل في المشية."
147
 
148
+ # تحديد الحالة المحتملة
149
+ if score >= 6 and avg_base > 0.27:
150
  condition = "قدم شاركوت (Charcot Foot)"
151
+ elif (L_low_ratio > 0.4 or R_low_ratio > 0.4):
152
  condition = "ضعف العضلة الظنبوبية (Foot Drop)"
153
+ elif diff_angle > 20 and var_clear > 9:
154
  condition = "اعتلال الأعصاب المحيطية / السكري"
155
  else:
156
  condition = "غير محددة بدقة"
 
158
  html = f"""
159
  <div style='color:{color};font-weight:700;font-size:18px'>{level}</div>
160
  <div>🩺 الحالة المحتملة: <b>{condition}</b></div>
161
+ <div>📊 درجة الخطورة: <b>{score:.1f}/9</b></div>
162
  <div>{desc}</div>
163
+ <div style='font-size:13px;color:#555;margin-top:8px'>⚠️ التحليل يعتمد على الأنماط الديناميكية في الفيديو ولا يُغني عن الفحص الطبي.</div>
164
  """
165
  return html, norm_score
166
 
167
+
168
+ with gr.Blocks(title="تحليل المشية العصبية - الإصدار الخامس") as demo:
169
+ gr.Markdown("## 🩺 نظام تحليل المشية العصبية (v5) – توازن بين الحساسية والدقة")
170
+ gr.Markdown("ارفع فيديو جانبي للمشي (15–30 ثانية). النظام سيقيّم النمط العصبي بدقة عالية.")
 
 
171
 
172
  with gr.Row():
173
  with gr.Column(scale=1):
174
  video_in = gr.File(label="📂 اختر فيديو المشي", file_types=[".mp4", ".avi", ".mov"], type="binary")
175
  analyze_btn = gr.Button("🔍 بدء التحليل", variant="primary")
176
  with gr.Column(scale=1):
177
+ gauge = gr.Number(label="⚙️ درجة الخطورة (01)", interactive=False)
178
  out_html = gr.HTML("<i>النتيجة ستظهر هنا...</i>")
179
 
180
  analyze_btn.click(fn=analyze_gait, inputs=[video_in], outputs=[out_html, gauge])