EngReem85 commited on
Commit
0af8a7c
·
verified ·
1 Parent(s): 14f9601

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +472 -175
app.py CHANGED
@@ -1,189 +1,486 @@
1
- # =============================================
2
- # Diabetic Foot Ulcer Diagnosis App (Unified)
3
- # Ready for HuggingFace Spaces
4
- # =============================================
5
- import os
6
- import io
7
- import cv2
8
- import json
9
  import torch
 
10
  import numpy as np
11
  import gradio as gr
12
- import torch.nn as nn
13
- import torchvision.transforms as transforms
14
- from torchvision import models
15
  from PIL import Image
16
- import segmentation_models_pytorch as smp
 
 
 
17
  import albumentations as A
18
- from albumentations.pytorch import ToTensorV2
19
- import tensorflow as tf
20
- from tensorflow.keras.models import load_model
21
- from tensorflow.keras.applications import EfficientNetB3
22
- from tensorflow.keras.preprocessing.image import ImageDataGenerator
23
- from tensorflow.keras import layers, models as tf_models
24
- from sklearn.model_selection import train_test_split
25
- from torch.utils.data import DataLoader, Dataset
26
- from tqdm import tqdm
27
-
28
- # =============================================
29
- # Load pretrained models from Google Drive (unchanged section)
30
- # =============================================
31
-
32
- def load_models():
33
- device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
34
-
35
- # Load Segmentation Model (PyTorch)
36
- seg_model_path = '/content/drive/MyDrive/models/best_model.pth'
37
- seg_model = smp.Unet('efficientnet-b7', encoder_weights='imagenet', in_channels=3, classes=1)
38
- seg_model.load_state_dict(torch.load(seg_model_path, map_location=device))
39
- seg_model.eval()
40
-
41
- # Load Classification Model (TensorFlow)
42
- clf_model_path = '/content/drive/MyDrive/models/efficientnetb3_dfu_model.keras'
43
- clf_model = load_model(clf_model_path)
44
-
45
- return seg_model, clf_model, device
46
-
47
-
48
- # =============================================
49
- # Model Definitions (for optional retraining)
50
- # =============================================
51
-
52
- class FuSegNet(nn.Module):
53
- def __init__(self):
54
- super().__init__()
55
- self.model = smp.Unet('efficientnet-b7', encoder_weights='imagenet', in_channels=3, classes=1)
56
-
57
  def forward(self, x):
58
- return self.model(x)
59
-
60
-
61
- # =============================================
62
- # Smart Ulcer Detection (fallback)
63
- # =============================================
64
-
65
- def smart_ulcer_detection(image_path):
66
- image = cv2.imread(image_path)
67
- image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
68
- hsv = cv2.cvtColor(image_rgb, cv2.COLOR_RGB2HSV)
69
-
70
- lower_red1 = np.array([0, 30, 60])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  upper_red1 = np.array([10, 255, 255])
72
- lower_red2 = np.array([160, 30, 60])
73
- upper_red2 = np.array([179, 255, 255])
74
-
75
- mask1 = cv2.inRange(hsv, lower_red1, upper_red1)
76
- mask2 = cv2.inRange(hsv, lower_red2, upper_red2)
77
- mask = cv2.bitwise_or(mask1, mask2)
78
-
79
- mask = cv2.medianBlur(mask, 5)
80
- mask = cv2.dilate(mask, np.ones((3, 3), np.uint8), iterations=2)
81
-
82
- contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
83
- if not contours:
84
- return image_rgb, 0, 'No Ulcer Detected'
85
-
86
- total_area = image.shape[0] * image.shape[1]
87
- ulcer_area = sum(cv2.contourArea(c) for c in contours)
88
- area_ratio = ulcer_area / total_area
89
-
90
- if area_ratio < 0.01:
91
- severity = 'Low Risk'
92
- elif area_ratio < 0.05:
93
- severity = 'Medium Risk'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  else:
95
- severity = 'High Risk'
96
-
97
- mask_colored = cv2.applyColorMap(mask, cv2.COLORMAP_JET)
98
- overlay = cv2.addWeighted(image_rgb, 0.7, mask_colored, 0.3, 0)
99
- return overlay, area_ratio * 100, severity
100
-
101
-
102
- # =============================================
103
- # Gradio Interface Logic
104
- # =============================================
105
-
106
- def predict_ulcer(img1, img2):
107
- seg_model, clf_model, device = load_models()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  images = [img1, img2]
109
- results = []
 
110
 
111
- for img in images:
 
 
112
  if img is None:
 
 
 
 
 
 
 
 
113
  continue
114
- temp_path = '/tmp/temp_img.jpg'
115
- img.save(temp_path)
116
-
117
- # Classification (TensorFlow)
118
- img_tf = img.resize((300, 300))
119
- img_arr = np.array(img_tf) / 255.0
120
- pred = clf_model.predict(np.expand_dims(img_arr, axis=0))[0][0]
121
- classification = 'Abnormal (Ulcer)' if pred > 0.5 else 'Normal (Healthy)'
122
-
123
- # Segmentation (PyTorch)
124
- transform = A.Compose([
125
- A.Resize(512, 512),
126
- A.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)),
127
- ToTensorV2()
128
- ])
129
-
130
- image_np = np.array(img.convert('RGB'))
131
- image_tensor = transform(image=image_np)['image'].unsqueeze(0).to(device)
132
-
133
- with torch.no_grad():
134
- mask_pred = torch.sigmoid(seg_model(image_tensor)).cpu().numpy()[0, 0]
135
 
136
- mask_resized = cv2.resize(mask_pred, (image_np.shape[1], image_np.shape[0]))
137
- threshold = 0.3
138
- binary_mask = (mask_resized > threshold).astype(np.uint8)
139
- mask_color = cv2.applyColorMap((binary_mask * 255).astype(np.uint8), cv2.COLORMAP_JET)
140
- overlay = cv2.addWeighted(image_np, 0.7, mask_color, 0.3, 0)
141
-
142
- ulcer_ratio = (binary_mask.sum() / binary_mask.size) * 100
143
-
144
- if ulcer_ratio < 1:
145
- fallback_img, fallback_ratio, severity = smart_ulcer_detection(temp_path)
146
- overlay = fallback_img
147
- ulcer_ratio = fallback_ratio
148
- else:
149
- severity = (
150
- 'Low Risk' if ulcer_ratio < 5 else
151
- 'Medium Risk' if ulcer_ratio < 15 else
152
- 'High Risk'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  )
154
-
155
- results.append((Image.fromarray(overlay), {
156
- 'Classification': classification,
157
- 'Ulcer Area %': round(ulcer_ratio, 2),
158
- 'Severity': severity
159
- }))
160
-
161
- return results[0][0], json.dumps(results[0][1], indent=2), results[1][0], json.dumps(results[1][1], indent=2)
162
-
163
-
164
- # =============================================
165
- # Gradio App Setup
166
- # =============================================
167
-
168
- title = "🦶 Diabetic Foot Ulcer Diagnosis System (Unified)"
169
- description = "Upload two images to analyze ulcer severity using AI segmentation and classification."
170
-
171
- iface = gr.Interface(
172
- fn=predict_ulcer,
173
- inputs=[
174
- gr.Image(label="Upload Image 1"),
175
- gr.Image(label="Upload Image 2")
176
- ],
177
- outputs=[
178
- gr.Image(label="Result 1"),
179
- gr.JSON(label="Analysis 1"),
180
- gr.Image(label="Result 2"),
181
- gr.JSON(label="Analysis 2")
182
- ],
183
- title=title,
184
- description=description,
185
- allow_flagging='never'
186
- )
187
-
188
- if __name__ == '__main__':
189
- iface.launch(server_name='0.0.0.0', server_port=7860)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import tensorflow as tf
2
+ from tensorflow.keras.models import load_model
 
 
 
 
 
 
3
  import torch
4
+ import torch.nn as nn
5
  import numpy as np
6
  import gradio as gr
 
 
 
7
  from PIL import Image
8
+ import cv2
9
+ from torchvision import transforms
10
+ import gdown
11
+ import os
12
  import albumentations as A
13
+ import segmentation_models_pytorch as smp
14
+ import requests
15
+ from io import BytesIO
16
+
17
+ # تعريفات عالمية
18
+ classifier = None
19
+ segmenter = None
20
+ class_names = ["Abnormal(Ulcer)", "Normal(Healthy skin)"]
21
+ IMG_SIZE = 224
22
+ DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
23
+
24
+ class FUSegNet(nn.Module):
25
+ """FUSegNet نموذج مخصص لمطابقة هيكل"""
26
+ def __init__(self, encoder_name='efficientnet-b7', classes=1, activation='sigmoid'):
27
+ super(FUSegNet, self).__init__()
28
+
29
+ self.unet = smp.Unet(
30
+ encoder_name=encoder_name,
31
+ encoder_weights=None,
32
+ classes=classes,
33
+ activation=activation,
34
+ decoder_attention_type='pscse', # إضافة نوع الانتباه المخصص
35
+ )
36
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  def forward(self, x):
38
+ return self.unet(x)
39
+
40
+ def check_and_download(url, path, min_size_mb=50):
41
+ """التحقق من وجود الملفات وتحميلها إذا كانت مفقودة"""
42
+ if not os.path.exists(path) or os.path.getsize(path) < min_size_mb * 1024 * 1024:
43
+ print(f"📥 تحميل النموذج من Google Drive: {url}")
44
+ try:
45
+ gdown.download(url, path, quiet=False, fuzzy=True)
46
+ if os.path.exists(path):
47
+ size = os.path.getsize(path) / (1024 * 1024)
48
+ print(f"✅ تم التحميل بنجاح: {os.path.basename(path)} ({size:.2f} MB)")
49
+ else:
50
+ print(f"❌ فشل التحميل: {path}")
51
+ except Exception as e:
52
+ print(f"❌ خطأ في التحميل: {e}")
53
+ else:
54
+ size = os.path.getsize(path) / (1024 * 1024)
55
+ print(f"✅ الملف موجود: {os.path.basename(path)} ({size:.2f} MB)")
56
+
57
+ def initialize_models():
58
+ """تهيئة النماذج"""
59
+ global classifier, segmenter
60
+
61
+ # روابط ومسارات النماذج
62
+ EFF_MODEL_URL = "https://drive.google.com/uc?id=1vVmA_-D3pZPbKHrPFEbDJd2nexF1H9Ni"
63
+ SEG_MODEL_URL = "https://drive.google.com/uc?id=13jMlcH9yTSejL_IfMDXqdAiVy6Z_SpE1"
64
+
65
+ EFF_MODEL_PATH = "efficientnetb3_dfu_model.keras"
66
+ SEG_MODEL_PATH = "best_model.pth"
67
+
68
+ # تحميل النماذج
69
+ check_and_download(EFF_MODEL_URL, EFF_MODEL_PATH, min_size_mb=50)
70
+ check_and_download(SEG_MODEL_URL, SEG_MODEL_PATH, min_size_mb=100)
71
+
72
+ # ------ نموذج التصنيف (Keras)
73
+ try:
74
+ print("🔄 ��اري تحميل نموذج التصنيف...")
75
+ classifier = tf.keras.models.load_model(EFF_MODEL_PATH, compile=False)
76
+ print("✅ تم تحميل نموذج التصنيف بنجاح!")
77
+ except Exception as e:
78
+ print(f"❌ خطأ أثناء تحميل نموذج التصنيف: {e}")
79
+ classifier = None
80
+
81
+ # ------ نموذج التجزئة (FUSegNet)
82
+ try:
83
+ print("🔄 جاري تحميل نموذج FUSegNet...")
84
+
85
+ # بناء النموذج بنفس مواصفات التدريب
86
+ segmenter = FUSegNet(
87
+ encoder_name='efficientnet-b7',
88
+ classes=1,
89
+ activation='sigmoid'
90
+ )
91
+
92
+ # تحميل الأوزان
93
+ checkpoint = torch.load(SEG_MODEL_PATH, map_location=DEVICE)
94
+
95
+ # معالجة مرنة للأوزان
96
+ if 'state_dict' in checkpoint:
97
+ state_dict = checkpoint['state_dict']
98
+ else:
99
+ state_dict = checkpoint
100
+
101
+ # تنظيف مفاتيح state_dict
102
+ new_state_dict = {}
103
+ for k, v in state_dict.items():
104
+ # إزالة البادئات المختلفة
105
+ new_key = k.replace('module.', '').replace('model.', '').replace('unet.', '')
106
+ new_state_dict[new_key] = v
107
+
108
+ # تحميل الأوزان مع التعامل مع المفاتيح المفقودة
109
+ model_dict = segmenter.state_dict()
110
+
111
+ # 1. محاولة التحميل المباشر أولاً
112
+ try:
113
+ segmenter.load_state_dict(new_state_dict, strict=True)
114
+ print("✅ تم تحميل الأوزان بنجاح (وضع صارم)")
115
+ except:
116
+ # 2. إذا فشل، حاول التحميل المرن
117
+ matched_keys = []
118
+ missing_keys = []
119
+ unexpected_keys = []
120
+
121
+ for name, param in model_dict.items():
122
+ if name in new_state_dict:
123
+ if param.shape == new_state_dict[name].shape:
124
+ param.data.copy_(new_state_dict[name])
125
+ matched_keys.append(name)
126
+ else:
127
+ missing_keys.append(name)
128
+ else:
129
+ missing_keys.append(name)
130
+
131
+ for key in new_state_dict:
132
+ if key not in model_dict:
133
+ unexpected_keys.append(key)
134
+
135
+ print(f"📊 إحصائيات التحميل:")
136
+ print(f" - المفاتيح المتطابقة: {len(matched_keys)}")
137
+ print(f" - المفاتيح المفقودة: {len(missing_keys)}")
138
+ print(f" - المفاتيح غير المتوقعة: {len(unexpected_keys)}")
139
+
140
+ if len(matched_keys) > 0:
141
+ print("✅ تم تحميل بعض الأوزان بنجاح")
142
+ else:
143
+ print("❌ فشل تحميل الأوزان، استخدام النموذج بدون أوزان")
144
+
145
+ segmenter.to(DEVICE)
146
+ segmenter.eval()
147
+
148
+ # اختبار سريع للنموذج
149
+ with torch.no_grad():
150
+ test_input = torch.randn(1, 3, IMG_SIZE, IMG_SIZE).to(DEVICE)
151
+ test_output = segmenter(test_input)
152
+ print(f"🧪 اختبار النموذج: [{test_output.min().item():.6f}, {test_output.max().item():.6f}]")
153
+
154
+ except Exception as e:
155
+ print(f"❌ خطأ في تحميل نموذج التجزئة: {e}")
156
+ segmenter = None
157
+
158
+ def get_preprocessing_fn():
159
+ """دالة المعالجة المسبقة المتوافقة مع EfficientNet-B7"""
160
+ from segmentation_models_pytorch.encoders import get_preprocessing_fn
161
+ return get_preprocessing_fn('efficientnet-b7', pretrained='imagenet')
162
+
163
+ def smart_ulcer_detection(img: Image.Image):
164
+ """كشف القرحة باستخدام معالجة الصور المتقدمة"""
165
+ print("🔍 استخدام الخوارزمية الذكية للكشف عن القرحة...")
166
+
167
+ img_np = np.array(img)
168
+ height, width = img_np.shape[:2]
169
+
170
+ # 1. تحويل إلى مساحات لون مختلفة
171
+ hsv = cv2.cvtColor(img_np, cv2.COLOR_RGB2HSV)
172
+ lab = cv2.cvtColor(img_np, cv2.COLOR_RGB2LAB)
173
+
174
+ # 2. كشف الألوان المرتبطة بالقرحة
175
+ # الأحمر النشط
176
+ lower_red1 = np.array([0, 60, 60])
177
  upper_red1 = np.array([10, 255, 255])
178
+ lower_red2 = np.array([160, 60, 60])
179
+ upper_red2 = np.array([180, 255, 255])
180
+ red_mask = cv2.inRange(hsv, lower_red1, upper_red1) + cv2.inRange(hsv, lower_red2, upper_red2)
181
+
182
+ # البني/الأسود (أنسجة ميتة)
183
+ lower_brown = np.array([0, 40, 20])
184
+ upper_brown = np.array([20, 200, 150])
185
+ brown_mask = cv2.inRange(hsv, lower_brown, upper_brown)
186
+
187
+ # 3. كشف التغيرات في الإضاءة
188
+ l_channel = lab[:,:,0]
189
+ _, dark_areas = cv2.threshold(l_channel, 80, 255, cv2.THRESH_BINARY_INV)
190
+
191
+ # 4. دمج جميع الأقنعة
192
+ combined_mask = cv2.bitwise_or(red_mask, brown_mask)
193
+ combined_mask = cv2.bitwise_or(combined_mask, dark_areas)
194
+
195
+ # 5. تنظيف متقدم للقناع
196
+ kernel = np.ones((5,5), np.uint8)
197
+ combined_mask = cv2.morphologyEx(combined_mask, cv2.MORPH_OPEN, kernel)
198
+ combined_mask = cv2.morphologyEx(combined_mask, cv2.MORPH_CLOSE, kernel)
199
+
200
+ # 6. الاحتفاظ فقط بالمناطق الكبيرة
201
+ contours, _ = cv2.findContours(combined_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
202
+ final_mask = np.zeros_like(combined_mask)
203
+
204
+ min_area = height * width * 0.002 # 0.2% من مساحة الصورة
205
+
206
+ for contour in contours:
207
+ area = cv2.contourArea(contour)
208
+ if area > min_area:
209
+ perimeter = cv2.arcLength(contour, True)
210
+ if perimeter > 0:
211
+ circularity = 4 * np.pi * area / (perimeter * perimeter)
212
+ if circularity < 0.7: # استبعاد الأشكال الدائرية الكاملة
213
+ cv2.fillPoly(final_mask, [contour], 255)
214
+
215
+ ulcer_pixels = np.sum(final_mask > 0)
216
+ print(f"📊 الخوارزمية الذكية اكتشفت {ulcer_pixels} بيكسل قرحة")
217
+
218
+ return (final_mask > 0).astype(np.uint8)
219
+
220
+ def classify_image(img: Image.Image):
221
+ """تصنيف الصورة"""
222
+ if classifier is None:
223
+ # استخدام كشف بسيط للون الأحمر للتصنيف الافتراضي
224
+ img_np = np.array(img)
225
+ hsv = cv2.cvtColor(img_np, cv2.COLOR_RGB2HSV)
226
+ red_mask = cv2.inRange(hsv, np.array([0,50,50]), np.array([10,255,255])) + \
227
+ cv2.inRange(hsv, np.array([160,50,50]), np.array([180,255,255]))
228
+ red_ratio = np.sum(red_mask > 0) / red_mask.size
229
+ result = "Abnormal(Ulcer)" if red_ratio > 0.005 else "Normal(Healthy skin)"
230
+ print(f"🎯 التصنيف الافتراضي: {result} (نسبة الأحمر: {red_ratio:.4f})")
231
+ return result
232
+
233
+ try:
234
+ # معالجة الصورة للتصنيف
235
+ img_processed = img.resize((IMG_SIZE, IMG_SIZE))
236
+ img_array = tf.keras.preprocessing.image.img_to_array(img_processed)
237
+ img_array = np.expand_dims(img_array, axis=0)
238
+ img_array = tf.keras.applications.efficientnet.preprocess_input(img_array)
239
+
240
+ # التوقع
241
+ preds = classifier.predict(img_array, verbose=0)
242
+ pred_class = np.argmax(preds, axis=1)[0]
243
+ confidence = np.max(preds)
244
+
245
+ result = class_names[pred_class]
246
+ print(f"🎯 تصنيف النموذج: {result} (ثقة: {confidence:.3f})")
247
+ return result
248
+
249
+ except Exception as e:
250
+ print(f"❌ خطأ في التصنيف: {e}")
251
+ return "Abnormal(Ulcer)"
252
+
253
+ def segment_image(img: Image.Image):
254
+ """تجزئة الصورة باستخدام FUSegNet"""
255
+ if segmenter is not None:
256
+ try:
257
+ print("🔄 جاري تجزئة الصورة باستخدام FUSegNet...")
258
+
259
+ # معالجة الصورة بنفس طريقة التدريب
260
+ preprocessing_fn = get_preprocessing_fn()
261
+
262
+ img_np = np.array(img)
263
+ img_resized = cv2.resize(img_np, (IMG_SIZE, IMG_SIZE))
264
+
265
+ # تطبيق المعالجة المسبقة
266
+ processed_img = preprocessing_fn(img_resized)
267
+
268
+ # تحويل إلى tensor
269
+ img_tensor = torch.from_numpy(processed_img).permute(2, 0, 1).unsqueeze(0).float().to(DEVICE)
270
+
271
+ # التوقع
272
+ with torch.no_grad():
273
+ output = segmenter(img_tensor)
274
+ pred = output.squeeze().cpu().numpy()
275
+
276
+ print(f"📊 إخراج النموذج: [{pred.min():.6f}, {pred.max():.6f}]")
277
+
278
+ # تجربة عتبات مختلفة
279
+ best_threshold = 0.3
280
+ best_pixels = 0
281
+
282
+ for threshold in [0.2, 0.3, 0.4, 0.5]:
283
+ mask_bin = (pred >= threshold).astype(np.uint8)
284
+ mask_resized = cv2.resize(mask_bin, (img.width, img.height))
285
+ ulcer_pixels = np.sum(mask_resized)
286
+
287
+ if ulcer_pixels > best_pixels:
288
+ best_pixels = ulcer_pixels
289
+ best_threshold = threshold
290
+
291
+ if best_pixels > 0:
292
+ mask_bin = (pred >= best_threshold).astype(np.uint8)
293
+ mask_resized = cv2.resize(mask_bin, (img.width, img.height))
294
+ print(f"✅ FUSegNet اكتشف {best_pixels} بيكسل (threshold: {best_threshold})")
295
+ return mask_resized
296
+ else:
297
+ print("⚠️ FUSegNet لم يعط نتائج، استخدام الخوارزمية الذكية")
298
+
299
+ except Exception as e:
300
+ print(f"❌ خطأ في نموذج التجزئة: {e}")
301
+
302
+ # استخدام الخوارزمية الذكية كبديل
303
+ return smart_ulcer_detection(img)
304
+
305
+ def calculate_risk(mask):
306
+ """حساب مستوى الخطورة"""
307
+ ulcer_pixels = np.sum(mask == 1)
308
+ total_pixels = mask.size
309
+ percent = (ulcer_pixels / total_pixels) * 100
310
+
311
+ print(f"📏 مساحة القرحة: {ulcer_pixels}/{total_pixels} بيكسل ({percent:.4f}%)")
312
+
313
+ if ulcer_pixels == 0:
314
+ return 0.0, "No Risk", 0
315
+ elif percent <= 1:
316
+ return percent, "Low Risk", 1
317
+ elif percent <= 5:
318
+ return percent, "Medium Risk", 3
319
  else:
320
+ return percent, "High Risk", 5
321
+
322
+ def apply_mask_to_image(img: Image.Image, mask):
323
+ """تطبيق القناع على الصورة"""
324
+ img_np = np.array(img)
325
+
326
+ ulcer_pixels = np.sum(mask == 1)
327
+ if ulcer_pixels == 0:
328
+ print("✅ لا توجد قرحة مكتشفة")
329
+ return img
330
+
331
+ print(f"🎨 تطبيق القناع على {ulcer_pixels} بيكسل")
332
+
333
+ # إنشاء قناع ملون
334
+ colored_mask = np.zeros_like(img_np)
335
+ colored_mask[mask == 1] = [255, 0, 0] # أحمر
336
+
337
+ # دمج مع الشفافية
338
+ result = cv2.addWeighted(img_np, 0.6, colored_mask, 0.4, 0)
339
+
340
+ # إضافة حدود حمراء
341
+ contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
342
+ for contour in contours:
343
+ area = cv2.contourArea(contour)
344
+ if area > 20:
345
+ cv2.drawContours(result, [contour], -1, (255, 0, 0), 3)
346
+
347
+ return Image.fromarray(result)
348
+
349
+ def analyze_images(img1, img2):
350
+ """تحليل صورتين فقط لتحسين الأداء"""
351
  images = [img1, img2]
352
+ gallery_output = []
353
+ json_output = {}
354
 
355
+ for idx, img in enumerate(images):
356
+ image_key = f"image_{idx+1}"
357
+
358
  if img is None:
359
+ gallery_output.append(None)
360
+ json_output[image_key] = {
361
+ "status": "No image uploaded",
362
+ "classification": "None",
363
+ "ulcer_percentage": 0.0,
364
+ "risk_level": "None",
365
+ "risk_score": 0
366
+ }
367
  continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
368
 
369
+ try:
370
+ print(f"\n{'='*40}")
371
+ print(f"🖼️ معالجة الصورة {idx+1}...")
372
+ print(f"📐 حجم الصورة: {img.size}")
373
+
374
+ # التصنيف
375
+ classification = classify_image(img)
376
+ print(f"🎯 التصنيف: {classification}")
377
+
378
+ # التجزئة
379
+ mask = segment_image(img)
380
+ masked_img = apply_mask_to_image(img, mask)
381
+ ulcer_percent, risk_level, risk_score = calculate_risk(mask)
382
+
383
+ gallery_output.append(masked_img)
384
+ json_output[image_key] = {
385
+ "status": "Analyzed",
386
+ "classification": classification,
387
+ "ulcer_percentage": round(ulcer_percent, 4),
388
+ "risk_level": risk_level,
389
+ "risk_score": risk_score,
390
+ "ulcer_pixels": int(np.sum(mask == 1)),
391
+ "total_pixels": mask.size
392
+ }
393
+
394
+ except Exception as e:
395
+ print(f"❌ خطأ في الصورة {idx+1}: {e}")
396
+ gallery_output.append(img)
397
+ json_output[image_key] = {
398
+ "status": f"Error: {str(e)}",
399
+ "classification": "Error",
400
+ "ulcer_percentage": 0.0,
401
+ "risk_level": "Error",
402
+ "risk_score": 0
403
+ }
404
+
405
+ print(f"\n✅ تم معالجة {len([x for x in gallery_output if x is not None])} صور")
406
+ return gallery_output, json_output
407
+
408
+ # تهيئة النماذج
409
+ print("🚀 جاري تهيئة النماذج...")
410
+ initialize_models()
411
+
412
+ # واجهة Gradio
413
+ with gr.Blocks(title="Diabetic Foot Ulcer Analysis", theme=gr.themes.Soft()) as demo:
414
+ gr.Markdown("""
415
+ # 🦶 Diabetic Foot Ulcer Detection & Risk Analysis
416
+
417
+ **تحليل قرحة القدم السكري باستخدام الذكاء الاصطناعي المتقدم**
418
+
419
+ ⚡ **مميزات النظام:**
420
+ - تحليل صورتين فقط لتسريع الأداء
421
+ - كشف ذكي للقرحة باستخدام نموذج FUSegNet المدرب
422
+ - خوارزمية بديلة ذكية إذا فشلت النماذج
423
+ - تقييم دقيق لمستوى الخطورة
424
+ """)
425
+
426
+ with gr.Row():
427
+ with gr.Column(scale=1):
428
+ gr.Markdown("### 📤 رفع الصور")
429
+ with gr.Row():
430
+ img1 = gr.Image(type="pil", label="صورة القدم الأولى", height=250)
431
+ img2 = gr.Image(type="pil", label="صورة القدم الثانية", height=250)
432
+
433
+ analyze_btn = gr.Button(
434
+ "🔍 بدء التحليل",
435
+ variant="primary",
436
+ size="lg"
437
  )
438
+
439
+ gr.Markdown("""
440
+ **💡 نصائح للاستخدام:**
441
+ - اختر صور واضحة للقدم
442
+ - تجنب الصور المضغوطة كثيراً
443
+ - الصور الملونة تعطي نتائج أفضل
444
+ - تأكد من إضاءة جيدة للصورة
445
+ """)
446
+
447
+ with gr.Column(scale=1):
448
+ gr.Markdown("### 📊 النتائج")
449
+ gallery = gr.Gallery(
450
+ label="الصور المحللة",
451
+ columns=2,
452
+ height="auto",
453
+ object_fit="contain"
454
+ )
455
+
456
+ json_output = gr.JSON(
457
+ label="النتائج التفصيلية",
458
+ show_label=True
459
+ )
460
+
461
+ # تعليمات إضافية
462
+ gr.Markdown("""
463
+ ---
464
+ **📋 تفسير النتائج:**
465
+ - **No Risk**: لا توجد قرحة مكتشفة
466
+ - **Low Risk**: مساحة القرحة ≤ 1%
467
+ - **Medium Risk**: مساحة القرحة ≤ 5%
468
+ - **High Risk**: مساحة القرحة > 5%
469
+
470
+ **🔴 المناطق الحمراء**: تشير إلى ��واقع القرحة المكتشفة
471
+ """)
472
+
473
+ analyze_btn.click(
474
+ fn=analyze_images,
475
+ inputs=[img1, img2],
476
+ outputs=[gallery, json_output]
477
+ )
478
+
479
+ if __name__ == "__main__":
480
+ print("🌐 Starting Gradio server...")
481
+ print("✅ النظام جاهز لتحليل صورتين")
482
+ demo.launch(
483
+ server_name="0.0.0.0",
484
+ server_port=7860,
485
+ share=True
486
+ )