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>