Song commited on
Commit
c1187c1
·
1 Parent(s): 129ada2
Files changed (2) hide show
  1. app.py +41 -24
  2. requirements.txt +2 -3
app.py CHANGED
@@ -5,23 +5,34 @@ from torchvision import transforms, models
5
  from PIL import Image
6
  import numpy as np
7
 
8
- # === 使用 pytorch-grad-cam ===
9
  from pytorch_grad_cam import GradCAM
10
  from pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget
11
  from pytorch_grad_cam.utils.image import show_cam_on_image
12
 
 
13
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
14
 
15
  # ==================== 1. 載入肺炎模型 (ResNet50) ====================
16
- model_pneumonia = models.resnet50(pretrained=False)
 
17
  num_ftrs = model_pneumonia.fc.in_features
18
- model_pneumonia.fc = nn.Linear(num_ftrs, 2) # NORMAL vs PNEUMONIA
19
 
20
  model_path_pneumonia = "best_pneumonia_model.pth"
21
- model_pneumonia.load_state_dict(torch.load(model_path_pneumonia, map_location=device))
 
 
 
 
 
 
 
 
22
  model_pneumonia.to(device)
23
  model_pneumonia.eval()
24
 
 
25
  target_layers = [model_pneumonia.layer4[-1]]
26
 
27
  # ==================== 2. 影像前處理 ====================
@@ -37,9 +48,11 @@ def predict_pneumonia(image):
37
  if image is None:
38
  return "請上傳影像", None, "無影像"
39
 
 
40
  pil_img = Image.fromarray(image).convert("RGB")
41
  input_tensor = img_transform(pil_img).unsqueeze(0).to(device)
42
 
 
43
  with torch.no_grad():
44
  output = model_pneumonia(input_tensor)
45
  probs = torch.softmax(output, dim=1).cpu().numpy()[0]
@@ -49,41 +62,44 @@ def predict_pneumonia(image):
49
  label = "PNEUMONIA" if pred_class == 1 else "NORMAL"
50
  result_text = f"肺炎檢測結果:{label} (信心度:{confidence:.1%})"
51
 
52
- # Grad-CAM
 
53
  cam = GradCAM(model=model_pneumonia, target_layers=target_layers)
54
  targets = [ClassifierOutputTarget(pred_class)]
 
 
55
  grayscale_cam = cam(input_tensor=input_tensor, targets=targets)[0]
56
 
57
- visualization = show_cam_on_image(np.array(pil_img.resize((224,224))) / 255.0,
58
- grayscale_cam,
59
- use_rgb=True)
60
- visualization = (visualization * 255).astype(np.uint8)
61
 
62
- # 風險評估
63
  if label == "PNEUMONIA" and confidence > 0.7:
64
- risk_assessment = "🚨 **高風險**:高度懷疑肺炎,建議立即就醫並進行抗生素治療。"
65
  elif label == "PNEUMONIA":
66
- risk_assessment = "⚠️ **中等風險**:疑似肺炎,建議評估。"
67
  else:
68
- risk_assessment = "✅ **正常**:目前未檢測到肺炎徵但仍需注意症狀變化。"
69
 
70
  return result_text, visualization, risk_assessment
71
 
72
  # ==================== 4. Gradio 介面 ====================
73
- with gr.Blocks(title="肺炎檢測系統") as demo:
74
- gr.Markdown("# 🏥 肺炎檢測系統")
75
- gr.Markdown("基於深度學習的胸部X光影像肺炎檢測系統")
76
 
77
  with gr.Row():
78
  with gr.Column(scale=1):
79
- img_input = gr.Image(label="上傳胸部X光影像", type="numpy")
80
- btn = gr.Button("開始檢測", variant="primary")
81
 
82
  with gr.Column(scale=1):
83
- pneumonia_out = gr.Textbox(label="肺炎檢測結果")
84
- cam_out = gr.Image(label="模型關注區域熱圖(紅色區域為模型���點關注)")
85
- risk_out = gr.Markdown(label="風險評估建議")
86
 
 
87
  btn.click(
88
  fn=predict_pneumonia,
89
  inputs=[img_input],
@@ -91,8 +107,9 @@ with gr.Blocks(title="肺炎檢測系統") as demo:
91
  )
92
 
93
  gr.Markdown("---")
94
- gr.Markdown("⚠️ 本系統研究與教育展示使用,非正式醫療診斷工具實際診斷師判斷為準。")
95
 
96
- # ==================== 5. 啟動 ====================
97
  if __name__ == "__main__":
98
- demo.launch(share=True)
 
 
5
  from PIL import Image
6
  import numpy as np
7
 
8
+ # === 使用 grad-cam (套件名稱已修正) ===
9
  from pytorch_grad_cam import GradCAM
10
  from pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget
11
  from pytorch_grad_cam.utils.image import show_cam_on_image
12
 
13
+ # 設定設備
14
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
15
 
16
  # ==================== 1. 載入肺炎模型 (ResNet50) ====================
17
+ # 使用 weights=None 取代已棄用的 pretrained=False
18
+ model_pneumonia = models.resnet50(weights=None)
19
  num_ftrs = model_pneumonia.fc.in_features
20
+ model_pneumonia.fc = nn.Linear(num_ftrs, 2) # 兩類:NORMAL vs PNEUMONIA
21
 
22
  model_path_pneumonia = "best_pneumonia_model.pth"
23
+
24
+ # 載入模型權重
25
+ try:
26
+ # 建議使用 weights_only=False 以確保舊版模型權重能正確讀取
27
+ model_pneumonia.load_state_dict(torch.load(model_path_pneumonia, map_location=device, weights_only=False))
28
+ print(f"成功載入模型權重:{model_path_pneumonia}")
29
+ except Exception as e:
30
+ print(f"模型載入失敗,請確認檔案是否存在:{e}")
31
+
32
  model_pneumonia.to(device)
33
  model_pneumonia.eval()
34
 
35
+ # 設定 Grad-CAM 目標層 (ResNet50 的最後一層捲積層)
36
  target_layers = [model_pneumonia.layer4[-1]]
37
 
38
  # ==================== 2. 影像前處理 ====================
 
48
  if image is None:
49
  return "請上傳影像", None, "無影像"
50
 
51
+ # 轉換圖片格式
52
  pil_img = Image.fromarray(image).convert("RGB")
53
  input_tensor = img_transform(pil_img).unsqueeze(0).to(device)
54
 
55
+ # 模型推論
56
  with torch.no_grad():
57
  output = model_pneumonia(input_tensor)
58
  probs = torch.softmax(output, dim=1).cpu().numpy()[0]
 
62
  label = "PNEUMONIA" if pred_class == 1 else "NORMAL"
63
  result_text = f"肺炎檢測結果:{label} (信心度:{confidence:.1%})"
64
 
65
+ # --- Grad-CAM 視覺化 ---
66
+ # 注意:GradCAM 在計算時需要梯度,所以不放在 no_grad 中
67
  cam = GradCAM(model=model_pneumonia, target_layers=target_layers)
68
  targets = [ClassifierOutputTarget(pred_class)]
69
+
70
+ # 產生熱圖
71
  grayscale_cam = cam(input_tensor=input_tensor, targets=targets)[0]
72
 
73
+ # 將原始圖片縮放到 224x224 以符合熱圖尺寸
74
+ input_float_img = np.array(pil_img.resize((224, 224))).astype(np.float32) / 255.0
75
+ visualization = show_cam_on_image(input_float_img, grayscale_cam, use_rgb=True)
 
76
 
77
+ # --- 風險評估建議 ---
78
  if label == "PNEUMONIA" and confidence > 0.7:
79
+ risk_assessment = "🚨 **高風險**:高度懷疑肺炎,建議立即就醫並進行進一步檢查。"
80
  elif label == "PNEUMONIA":
81
+ risk_assessment = "⚠️ **中等風險**:疑似肺炎徵象,建議諮詢療專業人員。"
82
  else:
83
+ risk_assessment = "✅ **正常**:目前影像顯示明顯肺炎徵,若有呼吸道症狀仍請留意。"
84
 
85
  return result_text, visualization, risk_assessment
86
 
87
  # ==================== 4. Gradio 介面 ====================
88
+ with gr.Blocks(title="肺炎檢測 AI 輔助系統") as demo:
89
+ gr.Markdown("# 🏥 肺炎檢測 AI 輔助系統")
90
+ gr.Markdown("這是一個基於深度學習的胸部 X 光影像分析工具。請上傳一張 X 光片,模型將分析是否存在肺炎徵兆,並顯示模型關注的區域。")
91
 
92
  with gr.Row():
93
  with gr.Column(scale=1):
94
+ img_input = gr.Image(label="1. 上傳胸部 X 光影像", type="numpy")
95
+ btn = gr.Button("🔍 開始檢測", variant="primary")
96
 
97
  with gr.Column(scale=1):
98
+ pneumonia_out = gr.Textbox(label="檢測結果")
99
+ cam_out = gr.Image(label="模型關注區域(紅色代表重點關注部位)")
100
+ risk_out = gr.Markdown(label="專業建議與風險評估")
101
 
102
+ # 設定按鈕邏輯
103
  btn.click(
104
  fn=predict_pneumonia,
105
  inputs=[img_input],
 
107
  )
108
 
109
  gr.Markdown("---")
110
+ gr.Markdown("⚠️ **免責聲明**:工具作為研究及技術展示用**不可**取代專業師的醫療診斷。如果您感到身體不適,務必尋求正式療協助。")
111
 
112
+ # ==================== 5. 啟動服務 ====================
113
  if __name__ == "__main__":
114
+ # 部署在 Hugging Face 時 share=True 是可選的,建議維持預設
115
+ demo.launch()
requirements.txt CHANGED
@@ -3,6 +3,5 @@ torch
3
  torchvision
4
  pillow
5
  numpy
6
- pytorch-grad-cam
7
- opencv-python
8
- matplotlib
 
3
  torchvision
4
  pillow
5
  numpy
6
+ grad-cam
7
+ opencv-python-headless