You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, targetLang = 'es', minConfidence = 0.5) {
/**
* Dynamically loads a script and returns a promise that resolves when the script is loaded.
* @param {string} url - The URL of the script to load.
* @param {string} globalName - The global variable name the script is expected to create on the window object.
* @returns {Promise<void>}
*/
const loadScript = async (url, globalName) => {
if (window[globalName]) {
return; // Already loaded
}
await new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = url;
script.crossOrigin = 'anonymous';
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
};
/**
* Translates text from English to a target language using a free, public API.
* @param {string} text - The English text to translate.
* @param {string} lang - The target language code (e.g., 'es', 'fr').
* @returns {Promise<string>} The translated text, or the original text if translation fails.
*/
const translateText = async (text, lang) => {
if (lang.toLowerCase() === 'en') return text;
// Use a simple cache to avoid re-translating the same word in a single run
if (!window.translationCache) window.translationCache = {};
const cacheKey = `${text}-${lang}`;
if (window.translationCache[cacheKey]) {
return window.translationCache[cacheKey];
}
try {
const url = `https://api.mymemory.translated.net/get?q=${encodeURIComponent(text)}&langpair=en|${lang}`;
const response = await fetch(url);
if (!response.ok) return text; // Fallback to original text
const data = await response.json();
const translated = data.responseData.translatedText || text;
window.translationCache[cacheKey] = translated;
return translated;
} catch (error) {
console.error('Translation API failed:', error);
return text; // Fallback to original text
}
};
// 1. Setup Canvas
const canvas = document.createElement('canvas');
canvas.width = originalImg.naturalWidth;
canvas.height = originalImg.naturalHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(originalImg, 0, 0);
// 2. Load dependencies (TensorFlow.js and COCO-SSD model)
try {
await loadScript('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@4.11.0/dist/tf.min.js', 'tf');
await loadScript('https://cdn.jsdelivr.net/npm/@tensorflow-models/coco-ssd@2.2.2/dist/coco-ssd.min.js', 'cocoSsd');
} catch (error) {
console.error("Failed to load ML model scripts:", error);
ctx.fillStyle = 'red';
ctx.font = '20px sans-serif';
ctx.fillText('Error: Could not load AI model scripts.', 10, 30);
return canvas;
}
// 3. Load the COCO-SSD model (cache it on the window object for performance)
if (!window.cocoSsdModel) {
ctx.fillStyle = "rgba(0, 0, 0, 0.7)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "white";
ctx.font = "30px sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("Loading AI Model (first use only)...", canvas.width / 2, canvas.height / 2);
window.cocoSsdModel = await window.cocoSsd.load();
}
const model = window.cocoSsdModel;
// Redraw the original image in case the loading message was shown
ctx.drawImage(originalImg, 0, 0);
// 4. Run object detection
const predictions = await model.detect(originalImg, undefined, parseFloat(minConfidence));
if (predictions.length === 0) {
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
ctx.font = '24px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = "middle";
ctx.fillText('No objects detected.', canvas.width / 2, canvas.height / 2);
return canvas;
}
// 5. Translate all detected class labels concurrently
const translationPromises = predictions.map(p => translateText(p.class, targetLang));
const translatedLabels = await Promise.all(translationPromises);
// 6. Draw the results on the canvas
const colors = ['#FF3838', '#FF9D97', '#FF701F', '#FFB21D', '#CFD231', '#48F281', '#3498DB', '#1ABC9C', '#9B59B6'];
predictions.forEach((prediction, i) => {
const [x, y, width, height] = prediction.bbox;
const color = colors[i % colors.length];
// Draw bounding box
ctx.strokeStyle = color;
ctx.lineWidth = 4;
ctx.strokeRect(x, y, width, height);
// Prepare label text
const score = Math.round(prediction.score * 100);
const originalLabel = prediction.class;
const translatedLabel = translatedLabels[i];
const labelText = `${translatedLabel} (${originalLabel}) ${score}%`;
// Draw label with a background
ctx.font = '16px sans-serif';
const textMetrics = ctx.measureText(labelText);
const textWidth = textMetrics.width;
const textHeight = 24;
// Position label above the box, but flip inside if it would be off-screen
let labelY = y;
if (labelY < textHeight) {
labelY = y + height - textHeight;
} else {
labelY = y - textHeight;
}
ctx.fillStyle = color;
ctx.fillRect(x, labelY, textWidth + 10, textHeight);
ctx.fillStyle = 'white';
ctx.textBaseline = 'middle';
ctx.fillText(labelText, x + 5, labelY + textHeight / 2);
});
return canvas;
}
Apply Changes