You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Isolates the main person from an image, applies clothing and body modifications,
* and returns the result on a new canvas.
*
* This function is an advanced demonstration and requires loading external libraries,
* so it may take a few moments to run the first time.
*
* @param {HTMLImageElement} originalImg The original image to process.
* @param {number} [fatness=1.2] A factor to scale the person's width. 1.0 is no change, 1.2 is 20% wider.
* @param {number} [dressShortness=0.6] A factor for dress length on the legs (0.0=mini, 1.0=maxi).
* @param {string} [dressColor='red'] The color to make the dress. Accepts any valid CSS color.
* @param {string} [tightsColor='black'] The color for the tights. Accepts any valid CSS color.
* @returns {Promise<HTMLCanvasElement>} A promise that resolves to a new canvas element with the transformed person on a transparent background.
*/
async function processImage(originalImg, fatness = 1.2, dressShortness = 0.6, dressColor = 'red', tightsColor = 'black') {
// Helper to dynamically load a script only once.
const loadScript = (url) => {
return new Promise((resolve, reject) => {
if (document.querySelector(`script[src="${url}"]`)) {
resolve();
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);
});
};
// 1. Load TensorFlow.js and BodyPix models
try {
await Promise.all([
loadScript('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-core@3.18.0/dist/tf-core.min.js'),
loadScript('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-converter@3.18.0/dist/tf-converter.min.js'),
loadScript('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-webgl@3.18.0/dist/tf-backend-webgl.min.js'),
loadScript('https://cdn.jsdelivr.net/npm/@tensorflow-models/body-pix@2.2.0/dist/body-pix.min.js')
]);
} catch (error) {
console.error("Failed to load required AI models.", error);
const errorCanvas = document.createElement('canvas');
errorCanvas.width = originalImg.width;
errorCanvas.height = originalImg.height;
const ctx = errorCanvas.getContext('2d');
ctx.drawImage(originalImg, 0, 0);
ctx.fillStyle = 'rgba(255, 0, 0, 0.7)';
ctx.font = '16px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('Error: Could not load AI model files.', errorCanvas.width / 2, errorCanvas.height / 2);
return errorCanvas;
}
// 2. Setup canvases
const w = originalImg.naturalWidth;
const h = originalImg.naturalHeight;
const finalCanvas = document.createElement('canvas');
finalCanvas.width = w;
finalCanvas.height = h;
const finalCtx = finalCanvas.getContext('2d');
const tempCanvas = document.createElement('canvas');
tempCanvas.width = w;
tempCanvas.height = h;
const tempCtx = tempCanvas.getContext('2d');
// Get original image pixel data
const originalCanvas = document.createElement('canvas');
originalCanvas.width = w;
originalCanvas.height = h;
const originalCtx = originalCanvas.getContext('2d');
originalCtx.drawImage(originalImg, 0, 0);
const originalImageData = originalCtx.getImageData(0, 0, w, h);
// 3. Load BodyPix and perform segmentation
const net = await bodyPix.load({
architecture: 'MobileNetV1',
outputStride: 16,
multiplier: 0.75,
quantBytes: 2
});
const segmentation = await net.segmentPersonParts(originalImg, {
flipHorizontal: false,
internalResolution: 'medium',
segmentationThreshold: 0.7
});
if (segmentation.allPoses.length === 0) {
finalCtx.drawImage(originalImg, 0, 0);
finalCtx.fillStyle = 'rgba(255, 0, 0, 0.7)';
finalCtx.font = '20px sans-serif';
finalCtx.textAlign = 'center';
finalCtx.fillText('Could not detect a person in the image.', w / 2, h / 2);
return finalCanvas;
}
// 4. Define body part groups and calculate clothing boundaries
const TORSO_PARTS = new Set([12, 13]);
const LEG_PARTS = new Set([14, 15, 16, 17, 18, 19, 20, 21]);
let clothingMinY = h;
let clothingMaxY = 0;
const clothingParts = new Set([...TORSO_PARTS, ...LEG_PARTS]);
for (let i = 0; i < segmentation.data.length; i++) {
if (clothingParts.has(segmentation.data[i])) {
const y = Math.floor(i / w);
if (y < clothingMinY) clothingMinY = y;
if (y > clothingMaxY) clothingMaxY = y;
}
}
const dressCutoffY = clothingMinY + (clothingMaxY - clothingMinY) * dressShortness;
// Helper to get RGB values from a CSS color string
const colorCache = {};
const getRgb = (colorStr) => {
if (colorCache[colorStr]) return colorCache[colorStr];
const colorCtx = document.createElement('canvas').getContext('2d');
colorCtx.fillStyle = colorStr;
colorCtx.fillRect(0, 0, 1, 1);
const [r, g, b] = colorCtx.getImageData(0, 0, 1, 1).data;
colorCache[colorStr] = { r, g, b };
return colorCache[colorStr];
};
const dressRgb = getRgb(dressColor);
const tightsRgb = getRgb(tightsColor);
// 5. Recolor the person on a temporary canvas
const tempImageData = tempCtx.createImageData(w, h);
for (let i = 0; i < segmentation.data.length; i++) {
const partId = segmentation.data[i];
if (partId === -1) continue; // Skip background
const offset = i * 4;
const y = Math.floor(i / w);
let newColor = null;
if (TORSO_PARTS.has(partId)) {
newColor = dressRgb;
} else if (LEG_PARTS.has(partId)) {
newColor = y < dressCutoffY ? dressRgb : tightsRgb;
}
if (newColor) {
tempImageData.data[offset] = newColor.r;
tempImageData.data[offset + 1] = newColor.g;
tempImageData.data[offset + 2] = newColor.b;
tempImageData.data[offset + 3] = originalImageData.data[offset + 3]; // Preserve alpha
} else {
// Keep original color for other parts (head, arms, feet)
tempImageData.data[offset] = originalImageData.data[offset];
tempImageData.data[offset + 1] = originalImageData.data[offset + 1];
tempImageData.data[offset + 2] = originalImageData.data[offset + 2];
tempImageData.data[offset + 3] = originalImageData.data[offset + 3];
}
}
tempCtx.putImageData(tempImageData, 0, 0);
// 6. Apply "fatness" transformation by stretching the result
let minX = w, minY = h, maxX = 0, maxY = 0;
for (let y = 0; y < h; y++) {
for (let x = 0; x < w; x++) {
if (tempImageData.data[(y * w + x) * 4 + 3] > 0) {
if (x < minX) minX = x;
if (x > maxX) maxX = x;
if (y < minY) minY = y;
if (y > maxY) maxY = y;
}
}
}
const personWidth = maxX - minX;
const personHeight = maxY - minY;
if (personWidth > 0 && personHeight > 0) {
const newWidth = personWidth * fatness;
const newX = minX - (newWidth - personWidth) / 2;
finalCtx.drawImage(
tempCanvas,
minX, minY, personWidth, personHeight,
newX, minY, newWidth, personHeight
);
}
// 7. Return the final canvas
return finalCanvas;
}
Apply Changes