rm background
Browse files
src/lib/components/MonsterGenerator/MonsterGenerator.svelte
CHANGED
|
@@ -329,7 +329,8 @@ Write your response within \`\`\`json\`\`\``;
|
|
| 329 |
// Process the image to make white background transparent
|
| 330 |
console.log('Processing image for transparency...');
|
| 331 |
try {
|
| 332 |
-
const
|
|
|
|
| 333 |
state.monsterImage = {
|
| 334 |
imageUrl: url,
|
| 335 |
imageData: transparentBase64,
|
|
|
|
| 329 |
// Process the image to make white background transparent
|
| 330 |
console.log('Processing image for transparency...');
|
| 331 |
try {
|
| 332 |
+
const largeWhiteSegmentThreshold = 0.1; // 10% of image area
|
| 333 |
+
const transparentBase64 = await makeWhiteTransparent(url, largeWhiteSegmentThreshold);
|
| 334 |
state.monsterImage = {
|
| 335 |
imageUrl: url,
|
| 336 |
imageData: transparentBase64,
|
src/lib/utils/imageProcessing.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
| 1 |
/**
|
| 2 |
* Converts an image URL to a base64 data URL with white background made transparent
|
| 3 |
* Uses flood-fill from edges to only remove background white, preserving internal white
|
|
|
|
| 4 |
*/
|
| 5 |
-
export async function makeWhiteTransparent(imageUrl: string): Promise<string> {
|
| 6 |
return new Promise((resolve, reject) => {
|
| 7 |
const img = new Image();
|
| 8 |
img.crossOrigin = 'anonymous';
|
|
@@ -114,13 +115,77 @@ export async function makeWhiteTransparent(imageUrl: string): Promise<string> {
|
|
| 114 |
}
|
| 115 |
}
|
| 116 |
|
| 117 |
-
// Apply transparency based on mask
|
| 118 |
for (let i = 0; i < mask.length; i++) {
|
| 119 |
if (mask[i]) {
|
| 120 |
data[i * 4 + 3] = 0; // Set alpha to 0
|
| 121 |
}
|
| 122 |
}
|
| 123 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
// Put the modified image data back
|
| 125 |
ctx.putImageData(imageData, 0, 0);
|
| 126 |
|
|
|
|
| 1 |
/**
|
| 2 |
* Converts an image URL to a base64 data URL with white background made transparent
|
| 3 |
* Uses flood-fill from edges to only remove background white, preserving internal white
|
| 4 |
+
* Also removes large white segments that exceed the threshold percentage
|
| 5 |
*/
|
| 6 |
+
export async function makeWhiteTransparent(imageUrl: string, largeSegmentThreshold: number = 0.1): Promise<string> {
|
| 7 |
return new Promise((resolve, reject) => {
|
| 8 |
const img = new Image();
|
| 9 |
img.crossOrigin = 'anonymous';
|
|
|
|
| 115 |
}
|
| 116 |
}
|
| 117 |
|
| 118 |
+
// Apply transparency based on edge flood fill mask
|
| 119 |
for (let i = 0; i < mask.length; i++) {
|
| 120 |
if (mask[i]) {
|
| 121 |
data[i * 4 + 3] = 0; // Set alpha to 0
|
| 122 |
}
|
| 123 |
}
|
| 124 |
|
| 125 |
+
// Now detect and remove large white segments
|
| 126 |
+
const totalPixels = width * height;
|
| 127 |
+
const segmentMask = new Uint8Array(width * height);
|
| 128 |
+
const visited = new Uint8Array(width * height);
|
| 129 |
+
|
| 130 |
+
// Find all white segments using flood fill
|
| 131 |
+
for (let y = 0; y < height; y++) {
|
| 132 |
+
for (let x = 0; x < width; x++) {
|
| 133 |
+
const idx = y * width + x;
|
| 134 |
+
|
| 135 |
+
// Skip if already transparent, visited, or not white
|
| 136 |
+
if (mask[idx] || visited[idx] || !isWhite(idx)) continue;
|
| 137 |
+
|
| 138 |
+
// Start flood fill for this white segment
|
| 139 |
+
const segmentPixels: number[] = [];
|
| 140 |
+
const segmentQueue: number[] = [idx];
|
| 141 |
+
visited[idx] = 1;
|
| 142 |
+
|
| 143 |
+
while (segmentQueue.length > 0) {
|
| 144 |
+
const currentIdx = segmentQueue.pop()!;
|
| 145 |
+
segmentPixels.push(currentIdx);
|
| 146 |
+
|
| 147 |
+
const cx = currentIdx % width;
|
| 148 |
+
const cy = Math.floor(currentIdx / width);
|
| 149 |
+
|
| 150 |
+
// Check 4 neighbors
|
| 151 |
+
const neighbors = [
|
| 152 |
+
{ dx: -1, dy: 0 }, { dx: 1, dy: 0 },
|
| 153 |
+
{ dx: 0, dy: -1 }, { dx: 0, dy: 1 }
|
| 154 |
+
];
|
| 155 |
+
|
| 156 |
+
for (const { dx, dy } of neighbors) {
|
| 157 |
+
const nx = cx + dx;
|
| 158 |
+
const ny = cy + dy;
|
| 159 |
+
|
| 160 |
+
if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
|
| 161 |
+
const nIdx = ny * width + nx;
|
| 162 |
+
|
| 163 |
+
if (!visited[nIdx] && !mask[nIdx] && isWhite(nIdx) && colorSimilar(currentIdx, nIdx)) {
|
| 164 |
+
visited[nIdx] = 1;
|
| 165 |
+
segmentQueue.push(nIdx);
|
| 166 |
+
}
|
| 167 |
+
}
|
| 168 |
+
}
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
// Check if this segment is larger than threshold
|
| 172 |
+
const segmentSize = segmentPixels.length / totalPixels;
|
| 173 |
+
if (segmentSize > largeSegmentThreshold) {
|
| 174 |
+
// Mark all pixels in this segment for removal
|
| 175 |
+
for (const pixelIdx of segmentPixels) {
|
| 176 |
+
segmentMask[pixelIdx] = 1;
|
| 177 |
+
}
|
| 178 |
+
}
|
| 179 |
+
}
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
// Apply transparency to large segments
|
| 183 |
+
for (let i = 0; i < segmentMask.length; i++) {
|
| 184 |
+
if (segmentMask[i]) {
|
| 185 |
+
data[i * 4 + 3] = 0; // Set alpha to 0
|
| 186 |
+
}
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
// Put the modified image data back
|
| 190 |
ctx.putImageData(imageData, 0, 0);
|
| 191 |
|