You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Detects and counts food items in an image using a pre-trained AI model.
* This function dynamically loads TensorFlow.js and the COCO-SSD model to perform object detection.
* It then draws the original image on a canvas, highlights the detected food items with bounding boxes,
* and displays a summary of the counts.
*
* @param {Image} originalImg The original input image as a JavaScript Image object.
* @param {number} confidenceThreshold The minimum confidence score (0.0 to 1.0) for a detection to be counted. Default is 0.5.
* @param {string} foodClasses A comma-separated string of food item names to detect. Defaults to items available in the COCO-SSD model.
* @returns {Promise<HTMLCanvasElement>} A Promise that resolves with a canvas element containing the annotated image.
*/
async function processImage(originalImg, confidenceThreshold = 0.5, foodClasses = 'banana,apple,sandwich,orange,broccoli,carrot,hot dog,pizza,donut,cake') {
// Helper to dynamically load a script and return a promise
const loadScript = (url) => {
return new Promise((resolve, reject) => {
// Avoid re-loading the same script
if (document.querySelector(`script[src="${url}"]`)) {
// Wait for the global object to be available if script is already loading
const checkInterval = setInterval(() => {
const globalName = url.includes('tfjs') ? 'tf' : 'cocoSsd';
if (window[globalName]) {
clearInterval(checkInterval);
resolve();
}
}, 100);
return;
}
const script = document.createElement('script');
script.src = url;
script.onload = resolve;
script.onerror = () => reject(new Error(`Script load error for ${url}`));
document.head.appendChild(script);
});
};
// Helper to generate a consistent color from a string (for bounding boxes)
const stringToColor = (str) => {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
let color = '#';
for (let i = 0; i < 3; i++) {
const value = (hash >> (i * 8)) & 0xFF;
// Ensure the color is not too dark
const clampedValue = Math.max(100, value);
color += ('00' + clampedValue.toString(16)).substr(-2);
}
return color;
};
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = originalImg.naturalWidth;
canvas.height = originalImg.naturalHeight;
ctx.drawImage(originalImg, 0, 0);
// Display a loading message
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
ctx.fillRect(0, canvas.height/2 - 25, canvas.width, 50);
ctx.fillStyle = 'white';
ctx.font = '24px Arial';
ctx.textAlign = 'center';
ctx.fillText('Analyzing image with AI...', canvas.width / 2, canvas.height / 2 + 8);
try {
// Load the necessary AI model libraries from a CDN
await loadScript('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.11.0/dist/tf.min.js');
await loadScript('https://cdn.jsdelivr.net/npm/@tensorflow-models/coco-ssd@2.2.2/dist/coco-ssd.min.js');
// Load the COCO-SSD model.
const model = await cocoSsd.load();
// Run object detection on the image
const predictions = await model.detect(originalImg);
// Clear the loading message by redrawing the original image
ctx.drawImage(originalImg, 0, 0);
const targetClasses = foodClasses.split(',').map(item => item.trim().toLowerCase());
const foodCounts = {};
// Process each prediction
predictions.forEach(prediction => {
if (prediction.score > confidenceThreshold && targetClasses.includes(prediction.class)) {
// Increment count for the detected class
foodCounts[prediction.class] = (foodCounts[prediction.class] || 0) + 1;
const [x, y, width, height] = prediction.bbox;
const color = stringToColor(prediction.class);
const label = `${prediction.class} (${Math.round(prediction.score * 100)}%)`;
// Draw the bounding box
ctx.strokeStyle = color;
ctx.lineWidth = Math.max(2, canvas.width / 400);
ctx.strokeRect(x, y, width, height);
// Draw the label background
ctx.fillStyle = color;
const fontHeight = Math.max(16, canvas.width / 50);
ctx.font = `${fontHeight}px Arial`;
const textWidth = ctx.measureText(label).width;
ctx.fillRect(x, y, textWidth + 10, fontHeight + 5);
// Draw the label text
ctx.fillStyle = '#000000';
ctx.fillText(label, x + 5, y + fontHeight);
}
});
// Draw the summary of counts in a semi-transparent box
const summaryKeys = Object.keys(foodCounts);
const boxHeight = (summaryKeys.length + 1) * 25 + 20;
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
ctx.fillRect(5, 5, 220, boxHeight);
ctx.fillStyle = '#FFFFFF';
ctx.font = '20px Arial';
let summaryTextY = 30;
ctx.textAlign = 'left';
ctx.fillText('Detected Food:', 15, summaryTextY);
summaryTextY += 25;
ctx.font = '16px Arial';
if (summaryKeys.length === 0) {
ctx.fillText('None', 15, summaryTextY);
} else {
summaryKeys.forEach(foodItem => {
ctx.fillText(`• ${foodItem}: ${foodCounts[foodItem]}`, 15, summaryTextY);
summaryTextY += 25;
});
}
} catch (error) {
console.error("Error processing image with AI model:", error);
// Display an error message on the canvas
ctx.drawImage(originalImg, 0, 0); // Redraw to clear loading message
ctx.fillStyle = 'rgba(255, 0, 0, 0.7)';
ctx.fillRect(0, canvas.height - 50, canvas.width, 50);
ctx.fillStyle = 'white';
ctx.font = '20px Arial';
ctx.textAlign = 'center';
ctx.fillText('Could not process image. Failed to load AI model.', canvas.width / 2, canvas.height - 20);
}
return canvas;
}
Apply Changes