You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Processes an image to either detect a point of action (saliency) or apply an "action"
* zoom-blur effect, based on the provided mode. This function interprets the name
* "Image Action Detector" by providing two distinct modes: 'detect' and 'action'.
*
* @param {Image} originalImg The original javascript Image object.
* @param {string} [mode='action'] The operation mode. Can be 'detect' or 'action'.
* @param {number} [strength=0.2] For 'action' mode: the intensity of the zoom blur, from 0 (none) to 1 (max).
* @param {number|string} [centerX=0.5] For 'action' mode: the horizontal center of the blur (0 to 1), or 'auto' to detect it.
* @param {number|string} [centerY=0.5] For 'action' mode: the vertical center of the blur (0 to 1), or 'auto' to detect it.
* @param {string} [markerColor='rgba(255, 0, 0, 0.8)'] For 'detect' mode: the color of the detector marker.
* @param {number} [markerSize=25] For 'detect' mode: the radius of the detector marker circle.
* @returns {HTMLCanvasElement} A canvas element with the processed image.
*/
async function processImage(originalImg, mode = 'action', strength = 0.2, centerX = 0.5, centerY = 0.5, markerColor = 'rgba(255, 0, 0, 0.8)', markerSize = 25) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const width = originalImg.naturalWidth || originalImg.width;
const height = originalImg.naturalHeight || originalImg.height;
canvas.width = width;
canvas.height = height;
const lowerCaseMode = String(mode).toLowerCase();
// --- DETECTOR MODE ---
// Detects the most salient point in the image (based on color saturation) and marks it.
if (lowerCaseMode === 'detect') {
// For performance, analyze a scaled-down version of the image.
const thumbWidth = 100;
const thumbHeight = Math.round((height / width) * thumbWidth);
const thumbCanvas = document.createElement('canvas');
const thumbCtx = thumbCanvas.getContext('2d');
thumbCanvas.width = thumbWidth;
thumbCanvas.height = thumbHeight;
thumbCtx.drawImage(originalImg, 0, 0, thumbWidth, thumbHeight);
const imageData = thumbCtx.getImageData(0, 0, thumbWidth, thumbHeight);
const data = imageData.data;
let maxScore = -1;
let salientX = 0;
let salientY = 0;
// Find the pixel with the highest saturation (a simple proxy for saliency).
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
const score = Math.max(r, g, b) - Math.min(r, g, b);
if (score > maxScore) {
maxScore = score;
const pixelIndex = i / 4;
salientX = pixelIndex % thumbWidth;
salientY = Math.floor(pixelIndex / thumbWidth);
}
}
// Scale the found coordinates back to the original image size.
const originalX = (salientX / thumbWidth) * width;
const originalY = (salientY / thumbHeight) * height;
// Draw the original image on the main canvas.
ctx.drawImage(originalImg, 0, 0, width, height);
// Draw a crosshair marker on the detected point.
const size = Number(markerSize);
ctx.strokeStyle = markerColor;
ctx.lineWidth = Math.max(2, size * 0.1);
ctx.beginPath();
// Circle
ctx.arc(originalX, originalY, size, 0, 2 * Math.PI);
// Horizontal line
ctx.moveTo(originalX - size * 1.5, originalY);
ctx.lineTo(originalX + size * 1.5, originalY);
// Vertical line
ctx.moveTo(originalX, originalY - size * 1.5);
ctx.lineTo(originalX, originalY + size * 1.5);
ctx.stroke();
return canvas;
}
// --- ACTION MODE ---
// Applies a zoom-blur effect centered on a specific point.
if (lowerCaseMode === 'action') {
let finalCenterX = centerX;
let finalCenterY = centerY;
// If 'auto' is specified, run the detection logic to find the center.
if (String(centerX).toLowerCase() === 'auto' || String(centerY).toLowerCase() === 'auto') {
const thumbWidth = 100;
const thumbHeight = Math.round((height / width) * thumbWidth);
const thumbCanvas = document.createElement('canvas');
const thumbCtx = thumbCanvas.getContext('2d');
thumbCanvas.width = thumbWidth;
thumbCanvas.height = thumbHeight;
thumbCtx.drawImage(originalImg, 0, 0, thumbWidth, thumbHeight);
const imageData = thumbCtx.getImageData(0, 0, thumbWidth, thumbHeight);
const data = imageData.data;
let maxScore = -1;
let salientX = 0;
let salientY = 0;
for (let i = 0; i < data.length; i += 4) {
const r = data[i], g = data[i + 1], b = data[i + 2];
const score = Math.max(r, g, b) - Math.min(r, g, b);
if (score > maxScore) {
maxScore = score;
const pixelIndex = i / 4;
salientX = pixelIndex % thumbWidth;
salientY = Math.floor(pixelIndex / thumbWidth);
}
}
finalCenterX = salientX / thumbWidth;
finalCenterY = salientY / thumbHeight;
}
// Sanitize and clamp numeric parameters.
const numStrength = Math.max(0, Math.min(1, Number(strength)));
const numCenterX = Math.max(0, Math.min(1, Number(finalCenterX)));
const numCenterY = Math.max(0, Math.min(1, Number(finalCenterY)));
if (numStrength < 0.01) {
ctx.drawImage(originalImg, 0, 0, width, height);
return canvas;
}
const absCenterX = width * numCenterX;
const absCenterY = height * numCenterY;
const samples = 30; // More samples create a smoother blur.
// Create the blur effect by layering semi-transparent, scaled copies of the image.
ctx.globalAlpha = 0.7 / samples;
for (let i = 0; i < samples; i++) {
const scale = 1.0 + (i / samples) * numStrength;
const scaledWidth = width * scale;
const scaledHeight = height * scale;
const drawX = absCenterX - (scaledWidth * numCenterX);
const drawY = absCenterY - (scaledHeight * numCenterY);
ctx.drawImage(originalImg, drawX, drawY, scaledWidth, scaledHeight);
}
// Draw the original image on top to ensure the focal point remains sharp and vibrant.
ctx.globalAlpha = 1.0;
ctx.drawImage(originalImg, 0, 0, width, height);
return canvas;
}
// Fallback for unrecognized modes.
ctx.drawImage(originalImg, 0, 0, width, height);
ctx.font = "bold 24px sans-serif";
ctx.fillStyle = "rgba(255, 0, 0, 0.8)";
ctx.textAlign = "center";
ctx.strokeStyle = "white";
ctx.lineWidth = 4;
const text = `Unknown mode: '${mode}'`;
ctx.strokeText(text, width / 2, height / 2);
ctx.fillText(text, width / 2, height / 2);
return canvas;
}
Apply Changes