AntiGravity Bot commited on
Commit
3953079
·
1 Parent(s): c29bac1

Update: Medical Image Segmentation (2026-01-27 15:43)

Browse files
Files changed (1) hide show
  1. app.py +67 -80
app.py CHANGED
@@ -31,8 +31,8 @@ License: MIT
31
 
32
  @dataclass
33
  class Configs:
34
- NUM_CLASSES: int = 4 # including background.
35
- CLASSES: Tuple[str, ...] = ("Large bowel", "Small bowel", "Stomach")
36
  IMAGE_SIZE: Tuple[int, int] = (288, 288) # W, H
37
  MEAN: Tuple[float, ...] = (0.485, 0.456, 0.406)
38
  STD: Tuple[float, ...] = (0.229, 0.224, 0.225)
@@ -43,17 +43,6 @@ class Configs:
43
  def get_model(*, model_path, num_classes):
44
  """
45
  Load pre-trained SegFormer model.
46
-
47
- Args:
48
- model_path (str): Path to model directory containing config.json and pytorch_model.bin
49
- num_classes (int): Number of segmentation classes
50
-
51
- Returns:
52
- SegformerForSemanticSegmentation: Loaded model
53
-
54
- Raises:
55
- FileNotFoundError: If model files not found
56
- RuntimeError: If model loading fails
57
  """
58
  model = SegformerForSemanticSegmentation.from_pretrained(
59
  model_path,
@@ -67,27 +56,10 @@ def get_model(*, model_path, num_classes):
67
  def predict(input_image, model=None, preprocess_fn=None, device="cpu"):
68
  """
69
  Perform semantic segmentation on input medical image.
70
-
71
- Args:
72
- input_image (PIL.Image): Input medical image
73
- model (SegformerForSemanticSegmentation): Trained segmentation model
74
- preprocess_fn (callable): Image preprocessing function
75
- device (str or torch.device): Device to run inference on ('cpu' or 'cuda')
76
-
77
- Returns:
78
- Tuple[PIL.Image, str]:
79
- - Color-coded segmentation mask
80
- - Text with confidence scores for each organ
81
-
82
- Raises:
83
- ValueError: If input image is invalid
84
- RuntimeError: If model inference fails
85
-
86
- Example:
87
- >>> from PIL import Image
88
- >>> img = Image.open('medical_scan.png')
89
- >>> output, info = predict(img, model, preprocess_fn, device)
90
  """
 
 
 
91
  shape_H_W = input_image.size[::-1]
92
  input_tensor = preprocess_fn(input_image)
93
  input_tensor = input_tensor.unsqueeze(0).to(device)
@@ -102,10 +74,19 @@ def predict(input_image, model=None, preprocess_fn=None, device="cpu"):
102
  confidence_map = probs.max(dim=1)[0].cpu().squeeze().numpy()
103
 
104
  # Create segmentation info with confidence
105
- seg_info = [
106
- (preds_argmax == idx, f"{class_name} (confidence: {confidence_map[preds_argmax == idx].mean():.2%})")
107
- for idx, class_name in enumerate(Configs.CLASSES, 1)
108
- ]
 
 
 
 
 
 
 
 
 
109
 
110
  return (input_image, seg_info)
111
 
@@ -113,20 +94,13 @@ def predict(input_image, model=None, preprocess_fn=None, device="cpu"):
113
  if __name__ == "__main__":
114
  """
115
  Main application entry point.
116
-
117
- Initializes:
118
- - Device selection (GPU/CPU)
119
- - Model loading and setup
120
- - Image preprocessing pipeline
121
- - Gradio web interface
122
-
123
- The web interface allows users to:
124
- - Upload medical images
125
- - Generate segmentation predictions
126
- - View color-coded organ detection
127
- - See confidence scores
128
  """
129
- class2hexcolor = {"Dạ dày": "#007fff", "Ruột non": "#009A17", "Ruột già": "#FF0000"}
 
 
 
 
 
130
 
131
  DEVICE = torch.device("cuda:0") if torch.cuda.is_available() else torch.device("cpu")
132
 
@@ -140,11 +114,17 @@ if __name__ == "__main__":
140
  print(f"Error loading model: {e}")
141
  model_dir = "./segformer_trained_weights"
142
 
143
- # Sử dụng đường dẫn từ W&B artifact hoặc mô hình cục bộ
 
144
  model = get_model(model_path=model_dir, num_classes=Configs.NUM_CLASSES)
145
  model.to(DEVICE)
146
  model.eval()
147
- _ = model(torch.randn(1, 3, *Configs.IMAGE_SIZE[::-1], device=DEVICE))
 
 
 
 
 
148
 
149
  preprocess = TF.Compose(
150
  [
@@ -154,50 +134,57 @@ if __name__ == "__main__":
154
  ]
155
  )
156
 
157
- with gr.Blocks(title="Medical Image Segmentation") as demo:
158
  gr.Markdown("""
159
- <h1><center>🏥 Medical Image Segmentation with UW-Madison GI Tract Dataset</center></h1>
160
- <p><center>Phân đoạn tự động các cơ quan trong ảnh Y tế (Dạ dày, Ruột non, Ruột già)</center></p>
161
  """)
162
 
163
  with gr.Row():
164
  with gr.Column():
165
- gr.Markdown("### 📥 Input Image")
166
- img_input = gr.Image(type="pil", height=360, width=360, label="Input image")
167
 
168
  with gr.Column():
169
- gr.Markdown("### 📊 Predictions")
170
- img_output = gr.AnnotatedImage(label="Predictions", height=360, width=360, color_map=class2hexcolor)
171
-
172
- section_btn = gr.Button("🎯 Generate Predictions", size="lg")
 
 
 
 
 
 
173
  section_btn.click(partial(predict, model=model, preprocess_fn=preprocess, device=DEVICE), img_input, img_output)
174
 
175
  gr.Markdown("---")
176
- gr.Markdown("### 📸 Sample Images")
177
 
178
  images_dir = glob(os.path.join(os.getcwd(), "samples") + os.sep + "*.png")
179
- examples = [i for i in np.random.choice(images_dir, size=min(10, len(images_dir)), replace=False)]
180
-
181
- gr.Examples(
182
- examples=examples,
183
- inputs=img_input,
184
- outputs=img_output,
185
- fn=partial(predict, model=model, preprocess_fn=preprocess, device=DEVICE),
186
- cache_examples=False,
187
- label="Click to load example"
188
- )
 
189
 
190
  gr.Markdown("""
191
  ---
192
- ### 🎨 Color Legend
193
- - 🔵 **Blue (#007fff)**: Dạ dày (Stomach)
194
- - 🟢 **Green (#009A17)**: Ruột non (Small bowel)
195
- - 🔴 **Red (#FF0000)**: Ruột già (Large bowel)
196
 
197
- ### ℹ️ Information
198
- - Model: SegFormer (HuggingFace Transformers)
199
- - Input size: 288 × 288 pixels
200
- - Framework: PyTorch + Gradio
201
  """)
202
 
203
  demo.launch()
 
31
 
32
  @dataclass
33
  class Configs:
34
+ NUM_CLASSES: int = 4 # bao gồm background
35
+ CLASSES: Tuple[str, ...] = ("Ruột già", "Ruột non", "Dạ dày")
36
  IMAGE_SIZE: Tuple[int, int] = (288, 288) # W, H
37
  MEAN: Tuple[float, ...] = (0.485, 0.456, 0.406)
38
  STD: Tuple[float, ...] = (0.229, 0.224, 0.225)
 
43
  def get_model(*, model_path, num_classes):
44
  """
45
  Load pre-trained SegFormer model.
 
 
 
 
 
 
 
 
 
 
 
46
  """
47
  model = SegformerForSemanticSegmentation.from_pretrained(
48
  model_path,
 
56
  def predict(input_image, model=None, preprocess_fn=None, device="cpu"):
57
  """
58
  Perform semantic segmentation on input medical image.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  """
60
+ if input_image is None:
61
+ return None, []
62
+
63
  shape_H_W = input_image.size[::-1]
64
  input_tensor = preprocess_fn(input_image)
65
  input_tensor = input_tensor.unsqueeze(0).to(device)
 
74
  confidence_map = probs.max(dim=1)[0].cpu().squeeze().numpy()
75
 
76
  # Create segmentation info with confidence
77
+ seg_info = []
78
+
79
+ # Classes: 1=Ruột già, 2=Ruột non, 3=Dạ dày
80
+ for idx, class_name in enumerate(Configs.CLASSES, 1):
81
+ mask = preds_argmax == idx
82
+ if mask.sum() > 0:
83
+ # Chỉ tính confidence nếu có pixel được dự đoán
84
+ conf_score = confidence_map[mask].mean()
85
+ label = f"{class_name} ({conf_score:.1%})"
86
+ seg_info.append((mask, label))
87
+ else:
88
+ # Không hiển thị label nếu không phát hiện được
89
+ pass
90
 
91
  return (input_image, seg_info)
92
 
 
94
  if __name__ == "__main__":
95
  """
96
  Main application entry point.
 
 
 
 
 
 
 
 
 
 
 
 
97
  """
98
+ # Mapping màu sắc cho hiển thị
99
+ class2hexcolor = {
100
+ "Dạ dày": "#007fff", # Xanh dương
101
+ "Ruột non": "#009A17", # Xanh lá
102
+ "Ruột già": "#FF0000" # Đỏ
103
+ }
104
 
105
  DEVICE = torch.device("cuda:0") if torch.cuda.is_available() else torch.device("cpu")
106
 
 
114
  print(f"Error loading model: {e}")
115
  model_dir = "./segformer_trained_weights"
116
 
117
+ # Load model
118
+ print(f"Loading model from: {model_dir}")
119
  model = get_model(model_path=model_dir, num_classes=Configs.NUM_CLASSES)
120
  model.to(DEVICE)
121
  model.eval()
122
+
123
+ # Warmup
124
+ try:
125
+ _ = model(torch.randn(1, 3, *Configs.IMAGE_SIZE[::-1], device=DEVICE))
126
+ except Exception as e:
127
+ print(f"Warmup warning: {e}")
128
 
129
  preprocess = TF.Compose(
130
  [
 
134
  ]
135
  )
136
 
137
+ with gr.Blocks(title="Phân Đoạn Ảnh Y Tế") as demo:
138
  gr.Markdown("""
139
+ <h1><center>🏥 Phân Đoạn Ảnh Y Tế - Tập Dữ Liệu UW-Madison GI Tract</center></h1>
140
+ <p><center>Hệ thống tự động phát hiện và phân đoạn các cơ quan tiêu hóa: Dạ dày, Ruột non, Ruột già.</center></p>
141
  """)
142
 
143
  with gr.Row():
144
  with gr.Column():
145
+ gr.Markdown("### 📥 Ảnh Đầu Vào")
146
+ img_input = gr.Image(type="pil", height=360, width=360, label="Tải ảnh lên")
147
 
148
  with gr.Column():
149
+ gr.Markdown("### 📊 Kết Quả Dự Đoán")
150
+ # AnnotatedImage hiển thị ảnh gốc + các lớp mask
151
+ img_output = gr.AnnotatedImage(
152
+ label="Kết quả phân đoạn",
153
+ height=360,
154
+ width=360,
155
+ color_map=class2hexcolor
156
+ )
157
+
158
+ section_btn = gr.Button("🎯 Chạy Phân Đoạn", size="lg", variant="primary")
159
  section_btn.click(partial(predict, model=model, preprocess_fn=preprocess, device=DEVICE), img_input, img_output)
160
 
161
  gr.Markdown("---")
162
+ gr.Markdown("### 📸 Ảnh Mẫu (Click để thử)")
163
 
164
  images_dir = glob(os.path.join(os.getcwd(), "samples") + os.sep + "*.png")
165
+ if len(images_dir) > 0:
166
+ examples = [i for i in np.random.choice(images_dir, size=min(10, len(images_dir)), replace=False)]
167
+
168
+ gr.Examples(
169
+ examples=examples,
170
+ inputs=img_input,
171
+ outputs=img_output,
172
+ fn=partial(predict, model=model, preprocess_fn=preprocess, device=DEVICE),
173
+ cache_examples=False,
174
+ label="Thư viện ảnh mẫu"
175
+ )
176
 
177
  gr.Markdown("""
178
  ---
179
+ ### 🎨 Chú Thích Màu Sắc
180
+ - 🔵 **Xanh Dương**: Dạ dày
181
+ - 🟢 **Xanh **: Ruột non
182
+ - 🔴 **Đỏ**: Ruột già
183
 
184
+ ### ℹ️ Thông Tin Hệ Thống
185
+ - **Mô hình**: SegFormer (mit-b0)
186
+ - **Kích thước đầu vào**: 288 × 288 pixels
187
+ - **Framework**: PyTorch + Gradio
188
  """)
189
 
190
  demo.launch()