You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Identifies the main subject or scene in an image using a pre-trained model,
* and translates its name into a specified language. The result is presented
* as the original image with the translated name overlaid.
*
* This function dynamically loads TensorFlow.js and the MobileNet model to perform
* image classification directly in the browser.
*
* @param {Image} originalImg The original javascript Image object to process.
* @param {string} targetLanguage The target language for translation (e.g., 'es', 'fr', 'de'). Defaults to 'es' (Spanish).
* @param {number} confidenceThreshold The minimum confidence score (0.0 to 1.0) required for a prediction to be considered valid. Defaults to 0.3.
* @returns {Promise<HTMLCanvasElement>} A Promise that resolves with a canvas element containing the original image with the identified and translated name overlaid.
*/
async function processImage(originalImg, targetLanguage = 'es', confidenceThreshold = 0.3) {
// Helper function to dynamically load a script and return a promise
const loadScript = (src) => {
return new Promise((resolve, reject) => {
// Resolve immediately if script already exists
if (document.querySelector(`script[src="${src}"]`)) {
return resolve();
}
const script = document.createElement('script');
script.src = src;
script.onload = () => resolve();
script.onerror = () => reject(new Error(`Script load error for ${src}`));
document.head.appendChild(script);
});
};
// Create the output canvas and draw the original image
const canvas = document.createElement('canvas');
canvas.width = originalImg.naturalWidth;
canvas.height = originalImg.naturalHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(originalImg, 0, 0);
// Display a loading message on the canvas while the model loads
const drawOverlayMessage = (message) => {
ctx.fillStyle = 'rgba(0, 0, 0, 0.6)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'white';
ctx.font = `bold ${Math.max(18, canvas.width / 20)}px Arial`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(message, canvas.width / 2, canvas.height / 2);
};
drawOverlayMessage('Loading AI Model...');
// Load TensorFlow.js and the MobileNet model
try {
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/mobilenet@2.1.0/dist/mobilenet.min.js');
} catch (error) {
console.error(error);
ctx.drawImage(originalImg, 0, 0); // Clear overlay
drawOverlayMessage('Error: Failed to load AI model.');
return canvas;
}
drawOverlayMessage('Analyzing Scene...');
let predictions;
try {
// Load the MobileNet model.
const model = await mobilenet.load();
// Classify the image.
predictions = await model.classify(originalImg);
} catch(e) {
console.error(e);
ctx.drawImage(originalImg, 0, 0); // Clear overlay
drawOverlayMessage('Error: Could not analyze image.');
return canvas;
}
// Filter predictions by confidence and get the top result
const validPredictions = predictions.filter(p => p.probability >= confidenceThreshold);
const topPrediction = validPredictions.length > 0 ? validPredictions[0] : null;
let originalName = 'Scene not identified';
if (topPrediction) {
// The result can be "Siamese cat, Siamese", so we take the first part.
originalName = topPrediction.className.split(',')[0];
}
// --- Translation Logic ---
const translations = {
'rocking chair': { es: 'mecedora', fr: 'fauteuil à bascule', de: 'Schaukelstuhl' },
'golden retriever': { es: 'golden retriever', fr: 'golden retriever', de: 'Golden Retriever' },
'sports car': { es: 'coche deportivo', fr: 'voiture de sport', de: 'Sportwagen' },
'street sign': { es: 'señal de tráfico', fr: 'panneau de signalisation', de: 'Verkehrszeichen' },
'desktop computer': { es: 'computadora de escritorio', fr: 'ordinateur de bureau', de: 'Desktop-Computer' },
'beach': { es: 'playa', fr: 'plage', de: 'Strand' },
'volcano': { es: 'volcán', fr: 'volcan', de: 'Vulkan' },
'lakeside': { es: 'orilla del lago', fr: 'bord du lac', de: 'Seeufer' },
'cat': { es: 'gato', fr: 'chat', de: 'Katze' },
'dog': { es: 'perro', fr: 'chien', de: 'Hund' },
'car': { es: 'coche', fr: 'voiture', de: 'Auto' },
'computer': { es: 'computadora', fr: 'ordinateur', de: 'Computer' },
'keyboard': { es: 'teclado', fr: 'clavier', de: 'Tastatur' },
'mountain': { es: 'montaña', fr: 'montagne', de: 'Berg' },
'ocean': { es: 'océano', fr: 'océan', de: 'Ozean' },
'house': { es: 'casa', fr: 'maison', de: 'Haus' },
'tree': { es: 'árbol', fr: 'arbre', de: 'Baum' },
'flower': { es: 'flor', fr: 'fleur', de: 'Blume' },
'sky': { es: 'cielo', fr: 'ciel', de: 'Himmel' },
'seashore': { es: 'costa', fr: 'rivage', de: 'Küste' },
};
let translatedName = originalName;
let translatableKey = null;
// Find the best matching key from our dictionary in the MobileNet result
const knownKeys = Object.keys(translations).sort((a,b) => b.length - a.length); // Prioritize longer keys
for (const key of knownKeys) {
if (originalName.toLowerCase().includes(key)) {
translatableKey = key;
break;
}
}
if (translatableKey && translations[translatableKey][targetLanguage]) {
translatedName = translations[translatableKey][targetLanguage];
}
// --- Render Final Output ---
ctx.drawImage(originalImg, 0, 0); // Redraw original image to clear the overlay
const capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
const textToDisplay = (translatedName.toLowerCase() !== originalName.toLowerCase())
? `${capitalize(translatedName)} (${capitalize(originalName)})`
: capitalize(originalName);
// Calculate font size based on image width for responsiveness
const fontSize = Math.max(16, Math.floor(canvas.width / 30));
ctx.font = `bold ${fontSize}px 'Helvetica Neue', Arial, sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
// Add a semi-transparent background for the text for better readability
const padding = fontSize * 0.5;
const textMetrics = ctx.measureText(textToDisplay);
const textHeight = fontSize;
const boxHeight = textHeight + padding;
ctx.fillStyle = 'rgba(0, 0, 0, 0.65)';
ctx.fillRect(0, canvas.height - boxHeight, canvas.width, boxHeight);
// Draw the final text
ctx.fillStyle = 'white';
ctx.fillText(textToDisplay, canvas.width / 2, canvas.height - (padding / 2));
return canvas;
}
Apply Changes