PPPPP / index.html
panchefukui's picture
Upload 2 files
1acc1a6 verified
Raw
History Blame Contribute Delete
5.81 kB
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>腦部腫瘤 MRI 分類系統</title>
<!-- 引入 ONNX Runtime Web,讓瀏覽器可以直接跑模型 -->
<script src="https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js"></script>
<style>
body { font-family: Arial, sans-serif; max-width: 600px; margin: 40px auto; padding: 20px; text-align: center; background-color: #f7f9fa; }
.container { background: white; padding: 30px; border-radius: 12px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
input[type="file"] { margin: 20px 0; }
#preview { max-width: 100%; max-height: 300px; margin-top: 15px; border-radius: 8px; display: none; }
#result { margin-top: 25px; font-weight: bold; text-align: left; background: #f0f4f8; padding: 15px; border-radius: 6px; display: none; }
.bar-container { background-color: #e0e0e0; border-radius: 4px; margin-top: 5px; height: 15px; width: 100%; }
.bar { background-color: #3b82f6; height: 100%; border-radius: 4px; width: 0%; transition: width 0.3s; }
#loading { color: #666; font-style: italic; display: none; }
</style>
</head>
<body>
<div class="container">
<h2>🧠 腦部腫瘤 MRI 分類系統 (純前端版)</h2>
<p style="color: #666;">模型完全在您的瀏覽器中執行,安全且完全免費</p>
<input type="file" id="imageLoader" accept="image/*">
<div id="loading">載入模型與運算中,請稍候...</div>
<img id="preview" alt="預覽圖">
<div id="result"></div>
</div>
<script>
// ⚠️ 請依據你第一步印出來的類別順序,修改這個陣列
const LABELS = ["Glioma", "Meningioma", "No Tumor", "Pituitary"];
let session = null;
// 初始化載入模型
async function initModel() {
try {
// 直接讀取跟 index.html 放在同一個目錄下的 model.onnx
session = await ort.InferenceSession.create('./model.onnx');
console.log("模型載入成功!");
} catch (e) {
alert("模型載入失敗,請確認 model.onnx 已上傳至正確路徑。");
console.error(e);
}
}
initModel();
document.getElementById('imageLoader').addEventListener('change', handleImage, false);
function handleImage(e) {
const reader = new FileReader();
reader.onload = function(event) {
const img = new Image();
img.onload = function() {
document.getElementById('preview').src = event.target.result;
document.getElementById('preview').style.display = 'inline-block';
runInference(img);
}
img.src = event.target.result;
}
reader.readAsDataURL(e.target.files[0]);
}
// 圖像預處理與推理 (等同於 Python 中的 transforms + forward)
async function runInference(img) {
if (!session) { alert("模型尚未準備就緒,請稍候"); return; }
document.getElementById('loading').style.display = 'block';
document.getElementById('result').style.display = 'none';
// 1. 將圖片繪製到 Canvas 並縮放到 224x224
const canvas = document.createElement('canvas');
canvas.width = 224;
canvas.height = 224;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, 224, 224);
const imgData = ctx.getImageData(0, 0, 224, 224).data;
// 2. 實作 ImageNet 標準化 (Mean & Std) 並轉換成 (1, 3, 224, 224) 格式
const mean = [0.485, 0.456, 0.406];
const std = [0.229, 0.224, 0.225];
const float32Data = new Float32Array(3 * 224 * 224);
for (let i = 0; i < 224 * 224; i++) {
const r = imgData[i * 4] / 255.0;
const g = imgData[i * 4 + 1] / 255.0;
const b = imgData[i * 4 + 2] / 255.0;
// 轉成 FastAI 預期之排序:R通道、G通道、B通道分開排列
float32Data[i] = (r - mean[0]) / std[0]; // R
float32Data[i + 224 * 224] = (g - mean[1]) / std[1]; // G
float32Data[i + 2 * 224 * 224] = (b - mean[2]) / std[2]; // B
}
// 3. 建立 ONNX Tensor
const inputTensor = new ort.Tensor('float32', float32Data, [1, 3, 224, 224]);
// 4. 執行推理
const feeds = {};
feeds[session.inputNames[0]] = inputTensor;
const outputMap = await session.run(feeds);
const rawOutput = outputMap[session.outputNames[0]].data;
// 5. 計算 Softmax 機率值
const maxLogit = Math.max(...rawOutput);
const exps = Array.from(rawOutput).map(x => Math.exp(x - maxLogit));
const sumExps = exps.reduce((a, b) => a + b, 0);
const probs = exps.map(x => x / sumExps);
// 6. 渲染結果到畫面上
let htmlResult = "<h3>分析結果:</h3>";
let resultsList = LABELS.map((label, idx) => ({ label, prob: probs[idx] }));
resultsList.sort((a, b) => b.prob - a.prob); // 排序
resultsList.forEach(item => {
const percentage = (item.prob * 100).toFixed(2);
htmlResult += `
<div style="margin-bottom: 10px;">
<strong>${item.label}</strong>: ${percentage}%
<div class="bar-container"><div class="bar" style="width: ${percentage}%"></div></div>
</div>
`;
});
document.getElementById('loading').style.display = 'none';
document.getElementById('result').innerHTML = htmlResult;
document.getElementById('result').style.display = 'block';
}
</script>
</body>
</html>