You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, confidenceThreshold = "0.5") {
// We return a container to display loading states to the user
// while the AI model downloads and runs asynchronously.
const container = document.createElement('div');
container.style.width = "100%";
container.style.display = "flex";
container.style.justifyContent = "center";
container.style.alignItems = "center";
container.style.backgroundColor = "#1e272e";
container.style.padding = "20px";
container.style.fontFamily = "sans-serif";
container.style.boxSizing = "border-box";
const canvas = document.createElement('canvas');
canvas.style.maxWidth = "100%";
canvas.style.maxHeight = "85vh";
canvas.style.boxShadow = "0 8px 16px rgba(0,0,0,0.5)";
canvas.style.borderRadius = "8px";
container.appendChild(canvas);
const ctx = canvas.getContext('2d');
// Ensure we have a valid initial size
const initW = originalImg.naturalWidth || originalImg.width || 600;
const initH = originalImg.naturalHeight || originalImg.height || 400;
canvas.width = initW;
canvas.height = initH;
let progress = 0.1;
// Helper for loading screens
const drawProcessingMessage = (msg) => {
ctx.fillStyle = "#2d3436";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#dfe6e9";
ctx.font = `bold ${Math.max(20, Math.floor(canvas.width / 25))}px sans-serif`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(msg, canvas.width/2, canvas.height/2, canvas.width - 40);
// Simple visual progress bar
progress += 0.2;
const currentProgress = Math.min(progress, 0.95);
const barWidth = canvas.width * 0.4;
const barHeight = 8;
ctx.fillStyle = "#636e72";
ctx.fillRect((canvas.width - barWidth) / 2, canvas.height / 2 + 30, barWidth, barHeight);
ctx.fillStyle = "#0984e3";
ctx.fillRect((canvas.width - barWidth) / 2, canvas.height / 2 + 30, barWidth * currentProgress, barHeight);
};
drawProcessingMessage("Initializing Collector Engine...");
const minConfidence = parseFloat(confidenceThreshold) || 0.5;
// Main Async Processor
(async () => {
try {
// Function to dynamically load external scripts needed
const loadScript = async (src, globalVar) => {
if (window[globalVar]) return; // Already exists
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
};
drawProcessingMessage("Loading TensorFlow.js...");
await loadScript('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs', 'tf');
drawProcessingMessage("Loading COCO-SSD Object Detector...");
await loadScript('https://cdn.jsdelivr.net/npm/@tensorflow-models/coco-ssd', 'cocoSsd');
drawProcessingMessage("Initializing AI Models...");
const model = await window.cocoSsd.load();
drawProcessingMessage("Scanning image for vehicles...");
// Draw original image into an off-screen canvas to feed to model
// This guarantees we only act on fully materialized pixel data
const imgCanvas = document.createElement('canvas');
imgCanvas.width = initW;
imgCanvas.height = initH;
const imgCtx = imgCanvas.getContext('2d');
imgCtx.drawImage(originalImg, 0, 0, initW, initH);
// Execute the model on our canvas
const predictions = await model.detect(imgCanvas);
// We want cars, trucks, and buses for a complete "car collector"
const carClasses = ['car', 'truck', 'bus'];
const carPredictions = predictions.filter(
p => carClasses.includes(p.class) && p.score >= minConfidence
);
if (carPredictions.length === 0) {
// Return case where no cars are detected
ctx.fillStyle = "#2d3436";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#ff7675";
ctx.font = `bold ${Math.max(20, Math.floor(canvas.width / 25))}px sans-serif`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("No cars found in this image.", canvas.width/2, canvas.height/2 - 20, canvas.width - 40);
ctx.fillStyle = "#dfe6e9";
ctx.font = `${Math.max(16, Math.floor(canvas.width / 35))}px sans-serif`;
ctx.fillText("Check out the original image below.", canvas.width/2, canvas.height/2 + 20);
// Show original after a small delay
setTimeout(() => {
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
}, 1500);
return;
}
// Calculations for arranging collected cars in a neat grid
const margin = 20;
const cellSizeX = 300;
const cellSizeY = 250;
const cols = Math.ceil(Math.sqrt(carPredictions.length));
const rows = Math.ceil(carPredictions.length / cols);
const headerHeight = 80;
// Adjust canvas size to fit our newly generated collage
canvas.width = cols * cellSizeX + (cols + 1) * margin;
canvas.height = headerHeight + rows * cellSizeY + (rows + 1) * margin;
// Paint collage background
ctx.fillStyle = "#2d3436";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Header text
ctx.fillStyle = "#74b9ff";
ctx.font = "bold 34px sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(`Image Cars Collector: Found ${carPredictions.length} Vehicle(s)`, canvas.width/2, headerHeight / 2);
// Render each vehicle piece individually
carPredictions.forEach((pred, index) => {
const [x, y, w, h] = pred.bbox;
const r = Math.floor(index / cols);
const c = index % cols;
const cellX = margin + c * (cellSizeX + margin);
const cellY = headerHeight + margin + r * (cellSizeY + margin);
// Card Shadow
ctx.fillStyle = "#dfe6e9";
ctx.shadowColor = "rgba(0,0,0,0.8)";
ctx.shadowBlur = 15;
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;
// Draw Card Background (Rounded Rectangle path for broad compatibility)
const radius = 15;
ctx.beginPath();
ctx.moveTo(cellX + radius, cellY);
ctx.lineTo(cellX + cellSizeX - radius, cellY);
ctx.quadraticCurveTo(cellX + cellSizeX, cellY, cellX + cellSizeX, cellY + radius);
ctx.lineTo(cellX + cellSizeX, cellY + cellSizeY - radius);
ctx.quadraticCurveTo(cellX + cellSizeX, cellY + cellSizeY, cellX + cellSizeX - radius, cellY + cellSizeY);
ctx.lineTo(cellX + radius, cellY + cellSizeY);
ctx.quadraticCurveTo(cellX, cellY + cellSizeY, cellX, cellY + cellSizeY - radius);
ctx.lineTo(cellX, cellY + radius);
ctx.quadraticCurveTo(cellX, cellY, cellX + radius, cellY);
ctx.closePath();
ctx.fill();
// Reset shadow before drawing image component
ctx.shadowColor = "transparent";
// Calculate dimensions for best fit centering
const imgPadding = 15;
const innerW = cellSizeX - imgPadding * 2;
const innerH = cellSizeY - 50 - imgPadding; // space for bottom text
const scale = Math.min(innerW / w, innerH / h);
const drawW = w * scale;
const drawH = h * scale;
const drawX = cellX + imgPadding + (innerW - drawW) / 2;
const drawY = cellY + imgPadding + (innerH - drawH) / 2;
// Crop directly to visually isolate car
ctx.drawImage(imgCanvas, x, y, w, h, drawX, drawY, drawW, drawH);
// Car Label (Type and Confidence)
ctx.fillStyle = "#2d3436";
ctx.font = "bold 20px sans-serif";
ctx.textAlign = "center";
ctx.fillText(
`${pred.class.toUpperCase()} - ${Math.round(pred.score * 100)}%`,
cellX + cellSizeX / 2,
cellY + cellSizeY - 25
);
});
} catch (err) {
// Error handling, ensuring exceptions are surfaced gracefully on-screen
ctx.fillStyle = "#d63031";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#ffffff";
ctx.font = `bold ${Math.max(16, Math.floor(canvas.width / 30))}px sans-serif`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("An Error Occurred:", canvas.width/2, canvas.height/2 - 20);
ctx.font = `${Math.max(14, Math.floor(canvas.width / 40))}px sans-serif`;
ctx.fillText(err.message, canvas.width/2, canvas.height/2 + 20, canvas.width - 40);
}
})();
return container;
}
Apply Changes