You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, confidenceThreshold = 0.5) {
/**
* Dynamically loads a script and returns a promise that resolves when it's loaded.
* Caches promises to avoid reloading the same script.
* @param {string} url The URL of the script to load.
* @param {string} id A unique ID for the script tag.
* @returns {Promise<void>}
*/
const _loadScript = (url, id) => {
if (!window._scriptPromises) {
window._scriptPromises = {};
}
if (!window._scriptPromises[id]) {
window._scriptPromises[id] = new Promise((resolve, reject) => {
if (document.getElementById(id)) {
return resolve();
}
const script = document.createElement('script');
script.src = url;
script.id = id;
script.onload = () => resolve();
script.onerror = (err) => {
delete window._scriptPromises[id];
reject(err);
};
document.head.appendChild(script);
});
}
return window._scriptPromises[id];
};
/**
* A dictionary mapping pairs or groups of detected nouns to specific verbs.
* Keys are sorted object class names, joined by a comma.
*/
const interactionMap = {
'person,bicycle': ['riding'],
'person,car': ['driving'],
'person,motorcycle': ['riding'],
'person,skateboard': ['skateboarding'],
'person,surfboard': ['surfing'],
'person,skis': ['skiing'],
'person,snowboard': ['snowboarding'],
'person,horse': ['riding'],
'person,sports ball': ['playing', 'throwing', 'catching'],
'person,frisbee': ['throwing', 'catching'],
'person,kite': ['flying'],
'person,baseball bat': ['swinging', 'playing baseball'],
'person,baseball glove': ['catching', 'playing baseball'],
'person,tennis racket': ['playing tennis'],
'person,cell phone': ['talking', 'texting', 'using'],
'person,laptop': ['working', 'typing', 'using'],
'person,keyboard': ['typing'],
'person,book': ['reading'],
'person,chair': ['sitting'],
'person,couch': ['sitting', 'resting'],
'person,bed': ['sleeping', 'lying down'],
'person,dining table': ['eating', 'sitting'],
'person,fork': ['eating'],
'person,knife': ['eating', 'cutting'],
'person,spoon': ['eating'],
'person,bowl': ['eating'],
'person,cup': ['drinking'],
'person,wine glass': ['drinking'],
'person,bottle': ['drinking'],
'person,sandwich': ['eating'],
'person,pizza': ['eating'],
'person,donut': ['eating'],
'person,cake': ['eating'],
'person,toothbrush': ['brushing teeth'],
'person,umbrella': ['holding'],
};
/**
* A dictionary mapping single detected nouns to generic verbs.
* This is used as a fallback if no specific interaction is found.
*/
const nounToVerbMap = {
'person': ['standing', 'posing', 'looking'],
'bird': ['flying', 'perching'],
'cat': ['sitting', 'sleeping', 'playing'],
'dog': ['running', 'playing', 'sitting'],
'horse': ['running', 'grazing', 'standing'],
'boat': ['sailing', 'floating'],
'airplane': ['flying', 'landing'],
'train': ['traveling', 'moving'],
'car': ['parking'],
'motorcycle': ['parking'],
'bus': ['driving'],
'truck': ['driving'],
};
const canvas = document.createElement('canvas');
const FONT_SIZE = Math.max(16, Math.min(originalImg.width, originalImg.height) / 30);
const PADDING = FONT_SIZE / 2;
canvas.width = originalImg.width;
canvas.height = originalImg.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(originalImg, 0, 0);
/**
* Helper function to draw text on the canvas with a background.
* @param {string} text The text to display.
*/
const drawTextOnCanvas = (text) => {
ctx.font = `bold ${FONT_SIZE}px 'Arial', sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const textMetrics = ctx.measureText(text);
const textWidth = textMetrics.width;
const boxHeight = FONT_SIZE + PADDING * 2;
const boxY = canvas.height - boxHeight;
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
ctx.fillRect(0, boxY, canvas.width, boxHeight);
ctx.fillStyle = 'white';
ctx.fillText(text, canvas.width / 2, boxY + boxHeight / 2);
};
drawTextOnCanvas('Analyzing image, please wait...');
try {
await _loadScript('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@4.11.0/dist/tf.min.js', 'tfjs_script');
await _loadScript('https://cdn.jsdelivr.net/npm/@tensorflow-models/coco-ssd@2.2.2/dist/coco-ssd.min.js', 'cocossd_script');
// Ensure tf is ready, sometimes there can be a race condition.
await tf.ready();
const model = await cocoSsd.load();
const predictions = await model.detect(originalImg);
const filteredPredictions = predictions.filter(p => p.score >= confidenceThreshold);
const detectedNouns = new Set(filteredPredictions.map(p => p.class));
const identifiedVerbs = new Set();
const usedNouns = new Set();
// 1. Check for specific interactions first
for (const key in interactionMap) {
const nounsInKey = key.split(',');
const isInteractionPresent = nounsInKey.every(noun => detectedNouns.has(noun));
if (isInteractionPresent) {
interactionMap[key].forEach(verb => identifiedVerbs.add(verb));
nounsInKey.forEach(noun => usedNouns.add(noun));
}
}
// 2. Add generic verbs for nouns that were not part of an interaction
detectedNouns.forEach(noun => {
if (!usedNouns.has(noun) && nounToVerbMap[noun]) {
nounToVerbMap[noun].forEach(verb => identifiedVerbs.add(verb));
}
});
// Redraw image to clear the loading message
ctx.drawImage(originalImg, 0, 0);
if (identifiedVerbs.size > 0) {
const verbString = Array.from(identifiedVerbs).join(', ');
drawTextOnCanvas(`Potential Verbs: ${verbString}`);
} else {
drawTextOnCanvas('No specific actions identified.');
}
} catch (error) {
console.error("Image processing error:", error);
ctx.drawImage(originalImg, 0, 0); // Redraw to be safe
drawTextOnCanvas('Error: Could not analyze the image.');
}
return canvas;
}
Apply Changes