export interface TransparencyOptions { lowerBound?: { r: number; g: number; b: number }; upperBound?: { r: number; g: number; b: number }; autoDetectCorner?: boolean; tolerance?: number; } const DEFAULT_LOWER_BOUND = { r: 200, g: 200, b: 200 }; const DEFAULT_UPPER_BOUND = { r: 255, g: 255, b: 255 }; // #FFFFFF export async function removeWhiteBackground( imageFile: File | string, options: TransparencyOptions = {} ): Promise { return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => { try { const result = processImageTransparency(img, options); resolve(result); } catch (error) { reject(error); } }; img.onerror = () => { reject(new Error('Failed to load image')); }; if (typeof imageFile === 'string') { img.src = imageFile; } else { const reader = new FileReader(); reader.onload = (e) => { img.src = e.target?.result as string; }; reader.onerror = () => { reject(new Error('Failed to read image file')); }; reader.readAsDataURL(imageFile); } }); } function processImageTransparency( img: HTMLImageElement, options: TransparencyOptions ): string { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); if (!ctx) { throw new Error('Failed to get canvas context'); } canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; let lowerBound = options.lowerBound || DEFAULT_LOWER_BOUND; let upperBound = options.upperBound || DEFAULT_UPPER_BOUND; if (options.autoDetectCorner) { const cornerColor = detectCornerColor(imageData); const tolerance = options.tolerance || 10; lowerBound = { r: Math.max(0, cornerColor.r - tolerance), g: Math.max(0, cornerColor.g - tolerance), b: Math.max(0, cornerColor.b - tolerance) }; upperBound = { r: Math.min(255, cornerColor.r + tolerance), g: Math.min(255, cornerColor.g + tolerance), b: Math.min(255, cornerColor.b + tolerance) }; } for (let i = 0; i < data.length; i += 4) { const r = data[i]; const g = data[i + 1]; const b = data[i + 2]; if ( r >= lowerBound.r && r <= upperBound.r && g >= lowerBound.g && g <= upperBound.g && b >= lowerBound.b && b <= upperBound.b ) { data[i + 3] = 0; } } ctx.putImageData(imageData, 0, 0); return canvas.toDataURL('image/png'); } function detectCornerColor(imageData: ImageData): { r: number; g: number; b: number } { const { width, height, data } = imageData; const sampleSize = 5; const corners = [ { x: 0, y: 0 }, // top-left { x: width - sampleSize, y: 0 }, // top-right { x: 0, y: height - sampleSize }, // bottom-left { x: width - sampleSize, y: height - sampleSize } // bottom-right ]; let totalR = 0, totalG = 0, totalB = 0; let samples = 0; corners.forEach(corner => { for (let dy = 0; dy < sampleSize; dy++) { for (let dx = 0; dx < sampleSize; dx++) { const x = Math.min(width - 1, Math.max(0, corner.x + dx)); const y = Math.min(height - 1, Math.max(0, corner.y + dy)); const i = (y * width + x) * 4; totalR += data[i]; totalG += data[i + 1]; totalB += data[i + 2]; samples++; } } }); return { r: Math.round(totalR / samples), g: Math.round(totalG / samples), b: Math.round(totalB / samples) }; }