| import gradio as gr |
| import torch |
| import torch.nn as nn |
| from torchvision import transforms, models |
| from PIL import Image |
| import numpy as np |
|
|
| |
| from pytorch_grad_cam import GradCAM |
| from pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget |
| from pytorch_grad_cam.utils.image import show_cam_on_image |
|
|
| |
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") |
|
|
| |
| |
| model_pneumonia = models.resnet50(weights=None) |
| num_ftrs = model_pneumonia.fc.in_features |
| model_pneumonia.fc = nn.Linear(num_ftrs, 2) |
|
|
| model_path_pneumonia = "best_pneumonia_model.pth" |
|
|
| |
| try: |
| |
| model_pneumonia.load_state_dict(torch.load(model_path_pneumonia, map_location=device, weights_only=False)) |
| print(f"成功載入模型權重:{model_path_pneumonia}") |
| except Exception as e: |
| print(f"模型載入失敗,請確認檔案是否存在:{e}") |
|
|
| model_pneumonia.to(device) |
| model_pneumonia.eval() |
|
|
| |
| target_layers = [model_pneumonia.layer4[-1]] |
|
|
| |
| img_transform = transforms.Compose([ |
| transforms.Resize((224, 224)), |
| transforms.ToTensor(), |
| transforms.Normalize(mean=[0.485, 0.456, 0.406], |
| std=[0.229, 0.224, 0.225]), |
| ]) |
|
|
| |
| def predict_pneumonia(image): |
| if image is None: |
| return "請上傳影像", None, "無影像" |
| |
| |
| pil_img = Image.fromarray(image).convert("RGB") |
| input_tensor = img_transform(pil_img).unsqueeze(0).to(device) |
| |
| |
| with torch.no_grad(): |
| output = model_pneumonia(input_tensor) |
| probs = torch.softmax(output, dim=1).cpu().numpy()[0] |
| pred_class = np.argmax(probs) |
| confidence = probs[pred_class] |
| |
| label = "PNEUMONIA" if pred_class == 1 else "NORMAL" |
| result_text = f"肺炎檢測結果:{label} (信心度:{confidence:.1%})" |
| |
| |
| |
| cam = GradCAM(model=model_pneumonia, target_layers=target_layers) |
| targets = [ClassifierOutputTarget(pred_class)] |
| |
| |
| grayscale_cam = cam(input_tensor=input_tensor, targets=targets)[0] |
| |
| |
| input_float_img = np.array(pil_img.resize((224, 224))).astype(np.float32) / 255.0 |
| visualization = show_cam_on_image(input_float_img, grayscale_cam, use_rgb=True) |
| |
| |
| if label == "PNEUMONIA" and confidence > 0.7: |
| risk_assessment = "🚨 **高風險**:高度懷疑肺炎,建議立即就醫並進行進一步檢查。" |
| elif label == "PNEUMONIA": |
| risk_assessment = "⚠️ **中等風險**:疑似肺炎徵象,建議儘快諮詢醫療專業人員。" |
| else: |
| risk_assessment = "✅ **正常**:目前影像未顯示明顯肺炎特徵,若有呼吸道症狀仍請留意。" |
| |
| return result_text, visualization, risk_assessment |
|
|
| |
| with gr.Blocks(title="肺炎檢測 AI 輔助系統") as demo: |
| gr.Markdown("# 🏥 肺炎檢測 AI 輔助系統") |
| gr.Markdown("這是一個基於深度學習的胸部 X 光影像分析工具。請上傳一張 X 光片,模型將分析是否存在肺炎徵兆,並顯示模型關注的區域。") |
| |
| with gr.Row(): |
| with gr.Column(scale=1): |
| img_input = gr.Image(label="1. 上傳胸部 X 光影像", type="numpy") |
| btn = gr.Button("🔍 開始檢測", variant="primary") |
| |
| with gr.Column(scale=1): |
| pneumonia_out = gr.Textbox(label="檢測結果") |
| cam_out = gr.Image(label="模型關注區域(紅色代表重點關注部位)") |
| risk_out = gr.Markdown(label="專業建議與風險評估") |
| |
| |
| btn.click( |
| fn=predict_pneumonia, |
| inputs=[img_input], |
| outputs=[pneumonia_out, cam_out, risk_out] |
| ) |
| |
| gr.Markdown("---") |
| gr.Markdown("⚠️ **免責聲明**:本工具僅作為研究及技術展示用途,**不可**取代專業醫師的醫療診斷。如果您感到身體不適,請務必尋求正式醫療協助。") |
|
|
| |
| if __name__ == "__main__": |
| |
| demo.launch() |