Spaces:
Running
on
T4
Running
on
T4
Commit
·
437b94f
1
Parent(s):
8a0a72a
feat(api): improve pink/magenta auto-detection with tolerance - automatically segment pink as white (remove), rest as black (keep)
Browse files- api/main.py +52 -54
api/main.py
CHANGED
|
@@ -264,73 +264,71 @@ def inpaint_multipart(
|
|
| 264 |
return resp
|
| 265 |
|
| 266 |
if mask_is_painted:
|
| 267 |
-
#
|
| 268 |
-
|
| 269 |
-
|
| 270 |
|
| 271 |
-
|
| 272 |
-
magenta_mask = np.all(m_rgb == [255, 0, 255], axis=2).astype(np.uint8) * 255
|
| 273 |
|
| 274 |
-
# Method
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
|
| 282 |
-
#
|
| 283 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 284 |
|
| 285 |
-
#
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
binmask = cv2.
|
| 291 |
|
| 292 |
nonzero = int((binmask > 0).sum())
|
| 293 |
-
log.info("
|
| 294 |
-
int((magenta_mask > 0).sum()), int((binmask_diff > 0).sum()), nonzero)
|
| 295 |
|
| 296 |
if nonzero < 50:
|
| 297 |
-
log.warning("Very few pixels detected! Trying
|
| 298 |
-
#
|
| 299 |
-
|
| 300 |
-
|
| 301 |
nonzero = int((binmask > 0).sum())
|
| 302 |
-
log.info("
|
| 303 |
-
|
| 304 |
-
# Build RGBA mask: convert to proper black/white mask
|
| 305 |
-
# White (255) = remove, Black (0) = keep (standard convention)
|
| 306 |
-
mask_rgba = np.zeros((binmask.shape[0], binmask.shape[1], 4), dtype=np.uint8)
|
| 307 |
-
|
| 308 |
-
# Set RGB channels: white where paint detected, black elsewhere
|
| 309 |
-
mask_rgba[:, :, 0] = binmask # R
|
| 310 |
-
mask_rgba[:, :, 1] = binmask # G
|
| 311 |
-
mask_rgba[:, :, 2] = binmask # B
|
| 312 |
-
# Set alpha to opaque so it's treated as a standard RGB mask
|
| 313 |
-
mask_rgba[:, :, 3] = 255
|
| 314 |
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
binmask_clean = cv2.morphologyEx(binmask_clean, cv2.MORPH_OPEN, kernel)
|
| 319 |
-
mask_rgba[:, :, 0] = binmask_clean
|
| 320 |
-
mask_rgba[:, :, 1] = binmask_clean
|
| 321 |
-
mask_rgba[:, :, 2] = binmask_clean
|
| 322 |
-
|
| 323 |
-
log.info("Auto-converted painted image to black/white mask: %d white pixels (to remove)",
|
| 324 |
-
int((binmask_clean > 0).sum()))
|
| 325 |
-
|
| 326 |
-
if int((binmask_clean > 0).sum()) < 50:
|
| 327 |
-
log.error("CRITICAL: Mask detection found very few pixels! Returning original image.")
|
| 328 |
-
# Return original image if mask is invalid
|
| 329 |
-
result = np.array(img.convert("RGB"))
|
| 330 |
result_name = f"output_{uuid.uuid4().hex}.png"
|
| 331 |
result_path = os.path.join(OUTPUT_DIR, result_name)
|
| 332 |
Image.fromarray(result).save(result_path)
|
| 333 |
-
return {"result": result_name, "error": "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 334 |
else:
|
| 335 |
mask_rgba = _load_rgba_mask_from_image(m)
|
| 336 |
|
|
|
|
| 264 |
return resp
|
| 265 |
|
| 266 |
if mask_is_painted:
|
| 267 |
+
# Auto-detect pink/magenta paint and convert to black/white mask
|
| 268 |
+
# White pixels = areas to remove, Black pixels = areas to keep
|
| 269 |
+
log.info("Auto-detecting pink/magenta paint from uploaded image...")
|
| 270 |
|
| 271 |
+
m_rgb = cv2.cvtColor(np.array(m), cv2.COLOR_RGBA2RGB)
|
|
|
|
| 272 |
|
| 273 |
+
# Method 1: Detect magenta/pink paint directly (RGB: 255, 0, 255)
|
| 274 |
+
# Allow some tolerance for slight variations (e.g., 250-255 for R/B, 0-10 for G)
|
| 275 |
+
magenta_detected = (
|
| 276 |
+
(m_rgb[:, :, 0] > 240) & # Red channel: high (240-255)
|
| 277 |
+
(m_rgb[:, :, 1] < 30) & # Green channel: low (0-30)
|
| 278 |
+
(m_rgb[:, :, 2] > 240) # Blue channel: high (240-255)
|
| 279 |
+
).astype(np.uint8) * 255
|
| 280 |
|
| 281 |
+
# Method 2: Also check if original image was provided to find differences
|
| 282 |
+
if img is not None:
|
| 283 |
+
img_rgb = cv2.cvtColor(np.array(img), cv2.COLOR_RGBA2RGB)
|
| 284 |
+
if img_rgb.shape == m_rgb.shape:
|
| 285 |
+
diff = cv2.absdiff(img_rgb, m_rgb)
|
| 286 |
+
gray_diff = cv2.cvtColor(diff, cv2.COLOR_RGB2GRAY)
|
| 287 |
+
# Any significant difference (>50) could be paint
|
| 288 |
+
diff_mask = (gray_diff > 50).astype(np.uint8) * 255
|
| 289 |
+
# Combine with magenta detection
|
| 290 |
+
binmask = cv2.bitwise_or(magenta_detected, diff_mask)
|
| 291 |
+
else:
|
| 292 |
+
binmask = magenta_detected
|
| 293 |
+
else:
|
| 294 |
+
# No original image provided, use magenta detection only
|
| 295 |
+
binmask = magenta_detected
|
| 296 |
|
| 297 |
+
# Clean up the mask: remove noise and fill small holes
|
| 298 |
+
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
|
| 299 |
+
# Close small gaps in the mask
|
| 300 |
+
binmask = cv2.morphologyEx(binmask, cv2.MORPH_CLOSE, kernel, iterations=2)
|
| 301 |
+
# Remove small noise
|
| 302 |
+
binmask = cv2.morphologyEx(binmask, cv2.MORPH_OPEN, kernel, iterations=1)
|
| 303 |
|
| 304 |
nonzero = int((binmask > 0).sum())
|
| 305 |
+
log.info("Pink/magenta paint detected: %d pixels marked for removal (white)", nonzero)
|
|
|
|
| 306 |
|
| 307 |
if nonzero < 50:
|
| 308 |
+
log.warning("Very few pixels detected! Trying stricter magenta detection...")
|
| 309 |
+
# Try more strict magenta detection (exact match)
|
| 310 |
+
magenta_strict = np.all(m_rgb == [255, 0, 255], axis=2).astype(np.uint8) * 255
|
| 311 |
+
binmask = cv2.morphologyEx(magenta_strict, cv2.MORPH_CLOSE, kernel, iterations=3)
|
| 312 |
nonzero = int((binmask > 0).sum())
|
| 313 |
+
log.info("Strict magenta detection: %d pixels", nonzero)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 314 |
|
| 315 |
+
if nonzero < 50:
|
| 316 |
+
log.error("CRITICAL: Could not detect pink/magenta paint! Returning original image.")
|
| 317 |
+
result = np.array(img.convert("RGB")) if img else np.array(m.convert("RGB"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 318 |
result_name = f"output_{uuid.uuid4().hex}.png"
|
| 319 |
result_path = os.path.join(OUTPUT_DIR, result_name)
|
| 320 |
Image.fromarray(result).save(result_path)
|
| 321 |
+
return {"result": result_name, "error": "pink/magenta paint detection failed - very few pixels detected"}
|
| 322 |
+
|
| 323 |
+
# Create black/white mask: white = remove (pink areas), black = keep (everything else)
|
| 324 |
+
mask_rgba = np.zeros((binmask.shape[0], binmask.shape[1], 4), dtype=np.uint8)
|
| 325 |
+
mask_rgba[:, :, 0] = binmask # R: white where pink detected
|
| 326 |
+
mask_rgba[:, :, 1] = binmask # G: white where pink detected
|
| 327 |
+
mask_rgba[:, :, 2] = binmask # B: white where pink detected
|
| 328 |
+
mask_rgba[:, :, 3] = 255 # Alpha: fully opaque
|
| 329 |
+
|
| 330 |
+
log.info("Successfully created black/white mask: %d white pixels (to remove), %d black pixels (to keep)",
|
| 331 |
+
nonzero, binmask.shape[0] * binmask.shape[1] - nonzero)
|
| 332 |
else:
|
| 333 |
mask_rgba = _load_rgba_mask_from_image(m)
|
| 334 |
|