|
|
|
|
|
|
| const API_URL = "http://localhost:8000";
|
| let uploadedFile = null;
|
| let resultImageUrl = null;
|
| let resultMaskUrl = null;
|
|
|
|
|
|
|
|
|
| const uploadArea = document.getElementById("uploadArea");
|
| const imageInput = document.getElementById("imageInput");
|
| const imagePreviewContainer = document.getElementById("imagePreviewContainer");
|
| const imagePreview = document.getElementById("imagePreview");
|
| const promptSelect = document.getElementById("promptSelect");
|
| const predictBtn = document.getElementById("predictBtn");
|
| const clearBtn = document.getElementById("clearBtn");
|
| const loadingMsg = document.getElementById("loadingMsg");
|
| const successMsg = document.getElementById("successMsg");
|
| const errorMsg = document.getElementById("errorMsg");
|
| const infoMsg = document.getElementById("infoMsg");
|
| const resultsSection = document.getElementById("resultsSection");
|
| const placeholderMsg = document.getElementById("placeholderMsg");
|
| const statsBox = document.getElementById("statsBox");
|
| const comparisonSlider = document.getElementById("comparisonSlider");
|
| const sliderHandle = document.getElementById("sliderHandle");
|
| const sliderBefore = document.getElementById("sliderBefore");
|
| const sliderAfter = document.getElementById("sliderAfter");
|
|
|
|
|
|
|
|
|
|
|
|
|
| uploadArea.addEventListener("click", () => imageInput.click());
|
|
|
| uploadArea.addEventListener("dragover", (e) => {
|
| e.preventDefault();
|
| uploadArea.classList.add("dragging");
|
| });
|
|
|
| uploadArea.addEventListener("dragleave", () => {
|
| uploadArea.classList.remove("dragging");
|
| });
|
|
|
| uploadArea.addEventListener("drop", (e) => {
|
| e.preventDefault();
|
| uploadArea.classList.remove("dragging");
|
| handleFileUpload(e.dataTransfer.files[0]);
|
| });
|
|
|
| imageInput.addEventListener("change", (e) => {
|
| if (e.target.files.length > 0) {
|
| handleFileUpload(e.target.files[0]);
|
| }
|
| });
|
|
|
|
|
| let isSliding = false;
|
| sliderHandle.addEventListener("mousedown", () => {
|
| isSliding = true;
|
| });
|
| document.addEventListener("mouseup", () => {
|
| isSliding = false;
|
| });
|
| document.addEventListener("mousemove", (e) => {
|
| if (!isSliding || !comparisonSlider.classList.contains("hidden")) return;
|
|
|
| const rect = comparisonSlider.getBoundingClientRect();
|
| let x = e.clientX - rect.left;
|
|
|
| if (x < 0) x = 0;
|
| if (x > rect.width) x = rect.width;
|
|
|
| const percent = (x / rect.width) * 100;
|
| sliderAfter.style.width = percent + "%";
|
| sliderHandle.style.left = percent + "%";
|
| });
|
|
|
|
|
|
|
|
|
|
|
| function handleFileUpload(file) {
|
| if (!file.type.startsWith("image/")) {
|
| showError("Please upload a valid image file");
|
| return;
|
| }
|
|
|
| if (file.size > 10 * 1024 * 1024) {
|
| showError("File size must be less than 10MB");
|
| return;
|
| }
|
|
|
| uploadedFile = file;
|
|
|
| const reader = new FileReader();
|
| reader.onload = (e) => {
|
| imagePreview.src = e.target.result;
|
| imagePreviewContainer.classList.remove("hidden");
|
| uploadArea.style.opacity = "0.6";
|
| infoMsg.classList.remove("active");
|
|
|
|
|
| imagePreviewContainer.style.animation = "slideIn 0.5s ease-out";
|
| };
|
| reader.readAsDataURL(file);
|
| }
|
|
|
| async function predictSegmentation() {
|
| if (!uploadedFile) {
|
| showError("Please upload an image first");
|
| return;
|
| }
|
|
|
| const prompt = promptSelect.value;
|
| if (!prompt) {
|
| showError("Please select a detection type");
|
| return;
|
| }
|
|
|
| predictBtn.disabled = true;
|
| loadingMsg.classList.add("active");
|
| clearMessages();
|
|
|
| const startTime = performance.now();
|
|
|
| try {
|
| const formData = new FormData();
|
| formData.append("image", uploadedFile);
|
| formData.append("prompt", prompt);
|
|
|
| const response = await fetch(`${API_URL}/predict`, {
|
| method: "POST",
|
| body: formData
|
| });
|
|
|
| if (!response.ok) {
|
| const error = await response.json();
|
| showError(error.detail || "Inference failed");
|
| return;
|
| }
|
|
|
| const maskBlob = await response.blob();
|
| resultMaskUrl = URL.createObjectURL(maskBlob);
|
| resultImageUrl = imagePreview.src;
|
|
|
| const endTime = performance.now();
|
| const inferenceTime = (endTime - startTime).toFixed(1);
|
|
|
|
|
| document.getElementById("resultImage").src = resultImageUrl;
|
| document.getElementById("resultMask").src = resultMaskUrl;
|
|
|
|
|
| sliderBefore.src = resultImageUrl;
|
| sliderAfter.src = resultMaskUrl;
|
|
|
| resultsSection.classList.remove("hidden");
|
| comparisonSlider.classList.remove("hidden");
|
| placeholderMsg.classList.add("hidden");
|
|
|
|
|
| updateStats(inferenceTime);
|
| statsBox.classList.remove("hidden");
|
|
|
| showSuccess(`✨ Segmentation complete in ${inferenceTime}ms!`);
|
|
|
| } catch (error) {
|
| console.error("Error:", error);
|
| showError(`Network error: ${error.message}`);
|
| } finally {
|
| predictBtn.disabled = false;
|
| loadingMsg.classList.remove("active");
|
| }
|
| }
|
|
|
| function clearAll() {
|
| uploadedFile = null;
|
| resultImageUrl = null;
|
| resultMaskUrl = null;
|
| imageInput.value = "";
|
| promptSelect.value = "";
|
| imagePreviewContainer.classList.add("hidden");
|
| uploadArea.style.opacity = "1";
|
| resultsSection.classList.add("hidden");
|
| comparisonSlider.classList.add("hidden");
|
| placeholderMsg.classList.remove("hidden");
|
| statsBox.classList.add("hidden");
|
| clearMessages();
|
| infoMsg.classList.add("active");
|
| }
|
|
|
| function updateStats(inferenceTime) {
|
|
|
| document.getElementById("inferenceTime").textContent = inferenceTime + " ms";
|
|
|
|
|
| const sizeKB = (uploadedFile.size / 1024).toFixed(1);
|
| document.getElementById("imageSize").textContent = sizeKB + " KB";
|
|
|
|
|
| document.getElementById("maskPixels").textContent = "256×256";
|
| }
|
|
|
| function showSuccess(message) {
|
| successMsg.textContent = message;
|
| successMsg.classList.add("active");
|
| errorMsg.classList.remove("active");
|
|
|
|
|
| setTimeout(() => {
|
| successMsg.classList.remove("active");
|
| }, 5000);
|
| }
|
|
|
| function showError(message) {
|
| errorMsg.textContent = "❌ " + message;
|
| errorMsg.classList.add("active");
|
| successMsg.classList.remove("active");
|
| }
|
|
|
| function clearMessages() {
|
| successMsg.classList.remove("active");
|
| errorMsg.classList.remove("active");
|
| }
|
|
|
|
|
|
|
|
|
| console.log("🚀 AI Segmentation Studio - Ready!");
|
| console.log(`📡 API: ${API_URL}`);
|
| console.log("🎨 Powered by ResNet18 + UNet");
|
|
|