maomao88's picture
add example
c19dba1
const MAX_WIDTH = 256;
const MAX_HEIGHT = 256;
const gridSize = 14;
const canvasSize = 630;
const cellSize = canvasSize / gridSize;
const dropZone = document.getElementById('drop-zone');
const imageInput = document.getElementById('image-input');
const browseBtn = document.getElementById('browse-btn');
const preview = document.getElementById('preview');
const localImagesDiv = document.getElementById('local-images');
const attn = document.getElementById('attn');
let selectedFile = null;
const localSamples = [
{ name: "example1", src: "example/testImg.jpg" },
{ name: "example0", src: "example/testDogsImg.jpg" },
{ name: "example2", src: "example/testImg2.jpg" },
{ name: "example0", src: "example/testDogsImg2.jpg" },
];
// Render local images
localSamples.forEach((img, idx) => {
const thumb = document.createElement("img");
thumb.src = img.src;
thumb.alt = img.name;
thumb.title = img.name;
thumb.className = "img-example";
thumb.tabIndex = 0;
thumb.dataset.idx = idx;
thumb.addEventListener("click", async () => {
// Visual selection
document.querySelectorAll('.img-example').forEach(t => t.classList.remove('selected'));
thumb.classList.add('selected');
// Load image as File object using fetch (simulate user upload)
const file = await urlToFile(img.src, img.name + ".jpg", "image/jpeg");
selectedFile = file;
handleFile(file, true);
imageInput.value = ''; // clear manual input
});
localImagesDiv.appendChild(thumb);
});
async function urlToFile(url, filename, mimeType) {
// Fetch the image as blob then as File
const res = await fetch(url);
const blob = await res.blob();
return new File([blob], filename, { type: mimeType });
}
// Drag-and-drop handlers
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('dragover');
});
dropZone.addEventListener('dragleave', (e) => {
e.preventDefault();
dropZone.classList.remove('dragover');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('dragover');
if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
const file = e.dataTransfer.files[0];
selectedFile = file; // Store for form submit
handleFile(file, true); // Show preview in drop field
imageInput.value = '';
}
});
// Click to open file picker
browseBtn.addEventListener('click', (e) => {
e.preventDefault();
imageInput.click();
});
dropZone.addEventListener('click', (e) => {
if (e.target === dropZone || e.target === document.getElementById('drop-zone-text')) {
imageInput.click();
}
});
// File input change
imageInput.addEventListener('change', function (e) {
if (this.files && this.files.length > 0) {
selectedFile = this.files[0];
handleFile(this.files[0], true);
}
});
function drawImageAndGrid(ctx, img) {
ctx.clearRect(0, 0, canvasSize, canvasSize);
ctx.drawImage(img, 0, 0, canvasSize, canvasSize);
// Draw grid lines
ctx.strokeStyle = 'gray';
ctx.lineWidth = 3;
for (let i = 0; i <= gridSize; i++) {
ctx.beginPath();
ctx.moveTo(i * cellSize, 0);
ctx.lineTo(i * cellSize, canvasSize);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, i * cellSize);
ctx.lineTo(canvasSize, i * cellSize);
ctx.stroke();
}
}
function drawImageAndGridForAttn(ctx, img, index, attnData, threshold7, threshold8, threshold9) {
ctx.globalAlpha = 1.0; // Set transparency (0.0 - 1.0)
ctx.strokeStyle = '#ea580c';
ctx.clearRect(0, 0, canvasSize, canvasSize);
ctx.drawImage(img, 0, 0, canvasSize, canvasSize);
ctx.fillStyle = '#ffc800'
for (let row = 0; row <= gridSize; row++) {
for (let col = 0; col <= gridSize; col++) {
if (attnData[index][row*gridSize+col+1] > threshold9[index]) {
ctx.globalAlpha = 0.8;
ctx.fillRect(col * cellSize, row * cellSize, cellSize, cellSize);
}
else if (attnData[index][row*gridSize+col+1] > threshold8[index]) {
ctx.globalAlpha = 0.5;
ctx.fillRect(col * cellSize, row * cellSize, cellSize, cellSize);
}
else if (attnData[index][row*gridSize+col+1] > threshold7[index]) {
ctx.globalAlpha = 0.2;
ctx.fillRect(col * cellSize, row * cellSize, cellSize, cellSize);
}
}
}
}
// Handle image preview and resizing
function handleFile(file, showInDropZone = false) {
if (!file) return;
const img = new window.Image();
const url = URL.createObjectURL(file);
img.onload = function () {
let width = img.width;
let height = img.height;
if (width > MAX_WIDTH || height > MAX_HEIGHT) {
const ratio = Math.min(MAX_WIDTH / width, MAX_HEIGHT / height);
width = Math.round(width * ratio);
height = Math.round(height * ratio);
}
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, width, height);
const previewImg = document.createElement('img');
previewImg.src = canvas.toDataURL(file.type);
previewImg.alt = "Preview";
previewImg.style.maxWidth = MAX_WIDTH + "px";
previewImg.style.maxHeight = MAX_HEIGHT + "px";
previewImg.style.display = "block";
previewImg.style.margin = "12px auto 0 auto";
previewImg.style.borderRadius = "6px";
previewImg.style.boxShadow = "0 0 3px #ccc";
// Add close icon
const closeBtn = document.createElement('span');
closeBtn.textContent = "×";
closeBtn.className = "close-preview-btn";
closeBtn.title = "Remove image";
closeBtn.onclick = function(e) {
e.stopPropagation();
// Restore drop zone to original state
dropZone.innerHTML = `<span id="drop-zone-text">Drop image here or <button type="button" id="browse-btn">Browse</button></span>
<input type="file" id="image-input" accept="image/*" style="display: none;" required />`;
imageInput.value = '';
selectedFile = null;
// Re-attach listeners
document.getElementById('browse-btn').addEventListener('click', (e) => {
e.preventDefault();
imageInput.click();
});
// Re-attach input event
document.getElementById('image-input').addEventListener('change', function (e) {
if (this.files && this.files.length > 0) {
selectedFile = this.files[0];
handleFile(this.files[0], true);
}
});
attn.innerHTML = '';
attn.style.backgroundColor = "#b3b3b3"
};
if (showInDropZone) {
// Remove previous images
dropZone.innerHTML = '';
// Create a wrapper for image and close button
const wrapper = document.createElement('div');
wrapper.className = "preview-wrapper";
wrapper.style.position = "relative";
wrapper.appendChild(previewImg);
wrapper.appendChild(closeBtn);
dropZone.appendChild(wrapper);
} else {
preview.innerHTML = '';
const wrapper = document.createElement('div');
wrapper.className = "preview-wrapper";
wrapper.style.position = "relative";
wrapper.appendChild(previewImg);
wrapper.appendChild(closeBtn);
preview.appendChild(wrapper);
}
URL.revokeObjectURL(url);
};
img.src = url;
}
// Submit handler
document.getElementById('upload-form').addEventListener('submit', async function (e) {
e.preventDefault();
const resultDiv = document.getElementById('result');
resultDiv.textContent = 'Classifying...';
if (!selectedFile) {
resultDiv.textContent = 'Please select an image.';
return;
}
const formData = new FormData();
formData.append('file', selectedFile);
await sendToBackend(formData, resultDiv);
});
async function sendToBackend(formData, resultDiv) {
try {
const response = await fetch('/classify_img', {
method: 'POST',
body: formData
});
if (!response.ok) throw new Error('Server error');
const data = await response.json();
attn.innerHTML = '';
const attnCanvas = document.createElement('canvas');
attnCanvas.width = canvasSize;
attnCanvas.height = canvasSize;
const ctx = attnCanvas.getContext('2d');
const img = new Image();
console.log("selectedFile: ", selectedFile)
const url = URL.createObjectURL(selectedFile);
img.src = url
img.onload = function () {
drawImageAndGridForAttn(ctx, img, 0, data.attn, data.threshold7, data.threshold8, data.threshold9)
attn.appendChild(attnCanvas);
}
attnCanvas.addEventListener('click', (event) => {
// Get mouse position relative to canvas
const rect = attnCanvas.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
// Determine which grid cell was clicked
const col = Math.floor(x / cellSize);
const row = Math.floor(y / cellSize);
const index = row*gridSize+col+1
drawImageAndGridForAttn(ctx, img, index, data.attn, data.threshold7, data.threshold8, data.threshold9)
// Highlight the clicked cell
ctx.globalAlpha = 1.0;
ctx.strokeStyle = '#ea580c';
ctx.lineWidth = 5;
ctx.strokeRect(col * cellSize, row * cellSize, cellSize, cellSize);
});
resultDiv.textContent = `Prediction: ${data?.output?.replace(/^"|"$/g, '') || JSON.stringify(data?.output)}`;
} catch (err) {
resultDiv.textContent = 'Error: ' + err.message;
}
}