Update index.html
Browse files- index.html +37 -27
index.html
CHANGED
|
@@ -340,9 +340,15 @@
|
|
| 340 |
</div>
|
| 341 |
|
| 342 |
<script>
|
| 343 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 344 |
let session = null;
|
| 345 |
let isReady = false;
|
|
|
|
| 346 |
|
| 347 |
const modelSelect = document.getElementById('modelSelect');
|
| 348 |
const fileInput = document.getElementById('fileInput');
|
|
@@ -354,28 +360,30 @@
|
|
| 354 |
const resultOutput = document.getElementById('resultOutput');
|
| 355 |
|
| 356 |
function initSelect() {
|
| 357 |
-
models.forEach(m => {
|
| 358 |
const opt = document.createElement('option');
|
| 359 |
-
opt.value =
|
| 360 |
-
opt.textContent = m;
|
| 361 |
modelSelect.appendChild(opt);
|
| 362 |
});
|
| 363 |
-
|
|
|
|
| 364 |
}
|
| 365 |
|
| 366 |
-
async function loadModel(
|
| 367 |
isReady = false;
|
| 368 |
submitBtn.disabled = true;
|
| 369 |
-
|
|
|
|
| 370 |
|
| 371 |
try {
|
| 372 |
if (session) {
|
| 373 |
await session.release();
|
| 374 |
session = null;
|
| 375 |
}
|
| 376 |
-
session = await ort.InferenceSession.create('./' +
|
| 377 |
isReady = true;
|
| 378 |
-
statusText.innerText = "✅ " +
|
| 379 |
|
| 380 |
if (previewImg.src && previewImg.style.display !== 'none') {
|
| 381 |
submitBtn.disabled = false;
|
|
@@ -408,62 +416,64 @@
|
|
| 408 |
const ctx = canvas.getContext('2d');
|
| 409 |
ctx.drawImage(imgElement, 0, 0, 224, 224);
|
| 410 |
const data = ctx.getImageData(0, 0, 224, 224).data;
|
| 411 |
-
|
| 412 |
const mean = [0.485, 0.456, 0.406];
|
| 413 |
const std = [0.229, 0.224, 0.225];
|
| 414 |
const floatData = new Float32Array(3 * 224 * 224);
|
| 415 |
-
|
| 416 |
for (let i = 0; i < 224 * 224; i++) {
|
| 417 |
const r = data[i * 4] / 255.0;
|
| 418 |
const g = data[i * 4 + 1] / 255.0;
|
| 419 |
const b = data[i * 4 + 2] / 255.0;
|
| 420 |
-
|
| 421 |
floatData[i] = (r - mean[0]) / std[0];
|
| 422 |
floatData[224 * 224 + i] = (g - mean[1]) / std[1];
|
| 423 |
floatData[2 * 224 * 224 + i] = (b - mean[2]) / std[2];
|
| 424 |
}
|
| 425 |
-
|
| 426 |
return new ort.Tensor('float32', floatData, [1, 3, 224, 224]);
|
| 427 |
}
|
| 428 |
|
| 429 |
submitBtn.addEventListener('click', async () => {
|
| 430 |
-
if (!isReady || !session || !previewImg.src) return;
|
| 431 |
|
| 432 |
submitBtn.disabled = true;
|
| 433 |
statusText.innerText = "⚡ 推論中...";
|
| 434 |
-
|
| 435 |
try {
|
| 436 |
const inputTensor = await preprocess(previewImg);
|
| 437 |
const feeds = {};
|
| 438 |
feeds[session.inputNames[0]] = inputTensor;
|
| 439 |
-
|
| 440 |
const start = performance.now();
|
| 441 |
const results = await session.run(feeds);
|
| 442 |
const duration = (performance.now() - start).toFixed(1);
|
| 443 |
-
|
| 444 |
const output = results[session.outputNames[0]].data;
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 450 |
|
| 451 |
// UI Render
|
| 452 |
emptyOutput.style.display = 'none';
|
| 453 |
resultOutput.style.display = 'flex';
|
| 454 |
-
|
| 455 |
const topClass = probNeed >= probTrash ? "need" : "trash";
|
| 456 |
const topConf = Math.max(probNeed, probTrash).toFixed(1);
|
| 457 |
-
|
| 458 |
document.getElementById('topClassName').innerText = topClass;
|
| 459 |
document.getElementById('topClassConf').innerText = "信頼度: " + topConf + "%";
|
| 460 |
-
|
| 461 |
document.getElementById('probNeedText').innerText = probNeed.toFixed(1) + "%";
|
| 462 |
document.getElementById('probNeedBar').style.width = probNeed + "%";
|
| 463 |
-
|
| 464 |
document.getElementById('probTrashText').innerText = probTrash.toFixed(1) + "%";
|
| 465 |
document.getElementById('probTrashBar').style.width = probTrash + "%";
|
| 466 |
-
|
| 467 |
statusText.innerText = "✅ 完了 (" + duration + "ms)";
|
| 468 |
} catch (e) {
|
| 469 |
statusText.innerText = "❌ エラー: " + e.message;
|
|
|
|
| 340 |
</div>
|
| 341 |
|
| 342 |
<script>
|
| 343 |
+
// --- 変更部分: モデルリストに仕様(type)を明記して定義 ---
|
| 344 |
+
const models = [
|
| 345 |
+
{ file: "tinymodelV3.onnx", type: "hsc" }, // 異常検知スコア (1出力)
|
| 346 |
+
{ file: "tinymodelV2.onnx", type: "softmax" } // 通常の分類 (2出力)
|
| 347 |
+
];
|
| 348 |
+
|
| 349 |
let session = null;
|
| 350 |
let isReady = false;
|
| 351 |
+
let currentModelConfig = null; // 現在選択されているモデルの設定を保持
|
| 352 |
|
| 353 |
const modelSelect = document.getElementById('modelSelect');
|
| 354 |
const fileInput = document.getElementById('fileInput');
|
|
|
|
| 360 |
const resultOutput = document.getElementById('resultOutput');
|
| 361 |
|
| 362 |
function initSelect() {
|
| 363 |
+
models.forEach((m, index) => {
|
| 364 |
const opt = document.createElement('option');
|
| 365 |
+
opt.value = index; // インデックスをvalueに設定
|
| 366 |
+
opt.textContent = m.file;
|
| 367 |
modelSelect.appendChild(opt);
|
| 368 |
});
|
| 369 |
+
// 選択時に該当するオブジェクトを渡す
|
| 370 |
+
modelSelect.addEventListener('change', (e) => loadModel(models[e.target.value]));
|
| 371 |
}
|
| 372 |
|
| 373 |
+
async function loadModel(modelConfig) {
|
| 374 |
isReady = false;
|
| 375 |
submitBtn.disabled = true;
|
| 376 |
+
currentModelConfig = modelConfig;
|
| 377 |
+
statusText.innerText = "⏳ " + modelConfig.file + " をロード中...";
|
| 378 |
|
| 379 |
try {
|
| 380 |
if (session) {
|
| 381 |
await session.release();
|
| 382 |
session = null;
|
| 383 |
}
|
| 384 |
+
session = await ort.InferenceSession.create('./' + modelConfig.file);
|
| 385 |
isReady = true;
|
| 386 |
+
statusText.innerText = "✅ " + modelConfig.file + " の準備完了";
|
| 387 |
|
| 388 |
if (previewImg.src && previewImg.style.display !== 'none') {
|
| 389 |
submitBtn.disabled = false;
|
|
|
|
| 416 |
const ctx = canvas.getContext('2d');
|
| 417 |
ctx.drawImage(imgElement, 0, 0, 224, 224);
|
| 418 |
const data = ctx.getImageData(0, 0, 224, 224).data;
|
|
|
|
| 419 |
const mean = [0.485, 0.456, 0.406];
|
| 420 |
const std = [0.229, 0.224, 0.225];
|
| 421 |
const floatData = new Float32Array(3 * 224 * 224);
|
|
|
|
| 422 |
for (let i = 0; i < 224 * 224; i++) {
|
| 423 |
const r = data[i * 4] / 255.0;
|
| 424 |
const g = data[i * 4 + 1] / 255.0;
|
| 425 |
const b = data[i * 4 + 2] / 255.0;
|
|
|
|
| 426 |
floatData[i] = (r - mean[0]) / std[0];
|
| 427 |
floatData[224 * 224 + i] = (g - mean[1]) / std[1];
|
| 428 |
floatData[2 * 224 * 224 + i] = (b - mean[2]) / std[2];
|
| 429 |
}
|
|
|
|
| 430 |
return new ort.Tensor('float32', floatData, [1, 3, 224, 224]);
|
| 431 |
}
|
| 432 |
|
| 433 |
submitBtn.addEventListener('click', async () => {
|
| 434 |
+
if (!isReady || !session || !previewImg.src || !currentModelConfig) return;
|
| 435 |
|
| 436 |
submitBtn.disabled = true;
|
| 437 |
statusText.innerText = "⚡ 推論中...";
|
|
|
|
| 438 |
try {
|
| 439 |
const inputTensor = await preprocess(previewImg);
|
| 440 |
const feeds = {};
|
| 441 |
feeds[session.inputNames[0]] = inputTensor;
|
| 442 |
+
|
| 443 |
const start = performance.now();
|
| 444 |
const results = await session.run(feeds);
|
| 445 |
const duration = (performance.now() - start).toFixed(1);
|
| 446 |
+
|
| 447 |
const output = results[session.outputNames[0]].data;
|
| 448 |
+
let probNeed, probTrash;
|
| 449 |
+
|
| 450 |
+
// --- 事前定義されたモデルの仕様(type)に基づいて計算を分岐 ---
|
| 451 |
+
if (currentModelConfig.type === "hsc") {
|
| 452 |
+
const trashScore = output[0];
|
| 453 |
+
probTrash = trashScore * 100;
|
| 454 |
+
probNeed = (1.0 - trashScore) * 100;
|
| 455 |
+
} else if (currentModelConfig.type === "softmax") {
|
| 456 |
+
const exp0 = Math.exp(output[0]);
|
| 457 |
+
const exp1 = Math.exp(output[1]);
|
| 458 |
+
const sum = exp0 + exp1;
|
| 459 |
+
probNeed = (exp0 / sum) * 100;
|
| 460 |
+
probTrash = (exp1 / sum) * 100;
|
| 461 |
+
}
|
| 462 |
|
| 463 |
// UI Render
|
| 464 |
emptyOutput.style.display = 'none';
|
| 465 |
resultOutput.style.display = 'flex';
|
|
|
|
| 466 |
const topClass = probNeed >= probTrash ? "need" : "trash";
|
| 467 |
const topConf = Math.max(probNeed, probTrash).toFixed(1);
|
| 468 |
+
|
| 469 |
document.getElementById('topClassName').innerText = topClass;
|
| 470 |
document.getElementById('topClassConf').innerText = "信頼度: " + topConf + "%";
|
| 471 |
+
|
| 472 |
document.getElementById('probNeedText').innerText = probNeed.toFixed(1) + "%";
|
| 473 |
document.getElementById('probNeedBar').style.width = probNeed + "%";
|
|
|
|
| 474 |
document.getElementById('probTrashText').innerText = probTrash.toFixed(1) + "%";
|
| 475 |
document.getElementById('probTrashBar').style.width = probTrash + "%";
|
| 476 |
+
|
| 477 |
statusText.innerText = "✅ 完了 (" + duration + "ms)";
|
| 478 |
} catch (e) {
|
| 479 |
statusText.innerText = "❌ エラー: " + e.message;
|