| | 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() |