Spaces:
Running
Running
File size: 6,042 Bytes
7815eae 69aa5c9 7815eae | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | <!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>貓狗分類分類系統</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>🧠 貓狗分類系統</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 = ['abyssinian', 'american shorthair', 'beagle', 'boxer', 'bulldog', 'chihuahua', 'corgi', 'dachshund', 'german shepherd', 'golden retriever', 'husky', 'labrador', 'maine coon', 'mumbai cat', 'persian cat', 'pomeranian', 'pug', 'ragdoll cat', 'rottwiler', 'shiba inu', 'siamese cat', 'sphynx', 'yorkshire terrier'];
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> |