fire-detection / main.js
cforge42's picture
Add Innomium Ember static fire detection Space.
9282073
Raw
History Blame Contribute Delete
6.45 kB
import { FireDetector, drawBoxes, className } from "./detector.js";
const fileInput = document.getElementById("file-input");
const detectBtn = document.getElementById("detect-btn");
const clearBtn = document.getElementById("clear-btn");
const sampleBtn = document.getElementById("sample-btn");
const statusEl = document.getElementById("status");
const statusDot = document.getElementById("status-dot");
const inputCanvas = document.getElementById("input-canvas");
const outputCanvas = document.getElementById("output-canvas");
const inputFrame = inputCanvas.closest(".canvas-frame");
const outputFrame = outputCanvas.closest(".canvas-frame");
const jsonOutput = document.getElementById("json-output");
const dropzone = document.getElementById("dropzone");
const countValue = document.getElementById("count-value");
const latencyValue = document.getElementById("latency-value");
const outputBadge = document.getElementById("output-badge");
const detector = new FireDetector();
let currentImage = null;
function setStatus(message, state = "ready") {
statusEl.textContent = message;
statusDot.className = "status-dot";
if (state === "loading") statusDot.classList.add("status-dot--loading");
else if (state === "error") statusDot.classList.add("status-dot--error");
else statusDot.classList.add("status-dot--ready");
}
function setCanvasState(frame, hasImage) {
frame.classList.toggle("has-image", hasImage);
}
function drawImageToCanvas(canvas, image) {
canvas.width = image.width;
canvas.height = image.height;
const ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(image, 0, 0);
}
async function loadImageFromUrl(url) {
const response = await fetch(url);
if (!response.ok) throw new Error("Failed to load image");
const blob = await response.blob();
return loadImageFromFile(blob);
}
function loadImageFromFile(file) {
return new Promise((resolve, reject) => {
const url = URL.createObjectURL(file);
const image = new Image();
image.onload = () => {
URL.revokeObjectURL(url);
resolve(image);
};
image.onerror = () => {
URL.revokeObjectURL(url);
reject(new Error("Failed to load image"));
};
image.src = url;
});
}
function summarizeDetections(boxes) {
const counts = {};
for (const box of boxes) {
const name = className(box.cls_id);
counts[name] = (counts[name] ?? 0) + 1;
}
return Object.entries(counts)
.map(([name, count]) => `${count} ${name}${count === 1 ? "" : "s"}`)
.join(", ");
}
function updateMetrics(count, latencyMs, boxes = null) {
countValue.textContent = count ?? "—";
latencyValue.textContent = latencyMs != null ? `${latencyMs} ms` : "—";
if (count == null) {
outputBadge.classList.add("hidden");
return;
}
const summary = boxes?.length ? summarizeDetections(boxes) : `${count} detections`;
outputBadge.textContent = summary;
outputBadge.classList.remove("hidden");
}
function clearDemo() {
currentImage = null;
fileInput.value = "";
jsonOutput.textContent = "[]";
setCanvasState(inputFrame, false);
setCanvasState(outputFrame, false);
detectBtn.disabled = true;
clearBtn.disabled = true;
updateMetrics(null, null);
setStatus("Model ready. Upload a scene or load the example.", "ready");
}
async function setCurrentImage(image) {
currentImage = image;
drawImageToCanvas(inputCanvas, currentImage);
drawImageToCanvas(outputCanvas, currentImage);
setCanvasState(inputFrame, true);
setCanvasState(outputFrame, true);
jsonOutput.textContent = "[]";
detectBtn.disabled = false;
clearBtn.disabled = false;
updateMetrics(null, null);
}
async function loadImage(file) {
setStatus("Loading image...", "loading");
const image = await loadImageFromFile(file);
await setCurrentImage(image);
setStatus("Image loaded. Running Ember...", "loading");
await runDetection();
}
async function loadExample() {
try {
setStatus("Loading example scene...", "loading");
const image = await loadImageFromUrl("./example_input.png");
await setCurrentImage(image);
setStatus("Example loaded. Running Ember...", "loading");
await runDetection();
} catch (error) {
setStatus(error.message, "error");
}
}
async function runDetection() {
if (!currentImage) return;
detectBtn.disabled = true;
setStatus("Analyzing scene...", "loading");
const started = performance.now();
try {
const boxes = await detector.predictImage(currentImage);
const latencyMs = Math.round(performance.now() - started);
drawImageToCanvas(outputCanvas, currentImage);
drawBoxes(outputCanvas.getContext("2d"), boxes);
jsonOutput.textContent = JSON.stringify(boxes, null, 2);
updateMetrics(boxes.length, latencyMs, boxes);
const headline =
boxes.length === 0
? "No fire hazards detected in this frame."
: `Ember found ${boxes.length} detection${boxes.length === 1 ? "" : "s"} in ${latencyMs} ms.`;
setStatus(headline, "ready");
} catch (error) {
console.error(error);
setStatus(`Detection failed: ${error.message}`, "error");
} finally {
detectBtn.disabled = false;
}
}
fileInput.addEventListener("change", async (event) => {
const file = event.target.files?.[0];
if (!file) return;
try {
await loadImage(file);
} catch (error) {
setStatus(error.message, "error");
}
});
detectBtn.addEventListener("click", runDetection);
clearBtn.addEventListener("click", clearDemo);
sampleBtn.addEventListener("click", loadExample);
dropzone.addEventListener("dragover", (event) => {
event.preventDefault();
dropzone.classList.add("is-dragover");
});
dropzone.addEventListener("dragleave", () => dropzone.classList.remove("is-dragover"));
dropzone.addEventListener("drop", async (event) => {
event.preventDefault();
dropzone.classList.remove("is-dragover");
const file = event.dataTransfer?.files?.[0];
if (!file || !file.type.startsWith("image/")) {
setStatus("Please drop an image file.", "error");
return;
}
try {
await loadImage(file);
} catch (error) {
setStatus(error.message, "error");
}
});
try {
setStatus("Loading Ember model...", "loading");
await detector.load("./weights.onnx");
setStatus("Ember ready. Upload a scene or load the example.", "ready");
} catch (error) {
console.error(error);
setStatus("Failed to load model. Check that weights.onnx is available.", "error");
}