LogicGoInfotechSpaces commited on
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
Files changed (1) hide show
  1. 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
- # Derive mask from painted image - detect magenta paint (255, 0, 255) or any painted areas
268
- img_rgb = cv2.cvtColor(np.array(img), cv2.COLOR_RGBA2RGB)
269
- m_rgb = cv2.cvtColor(np.array(m), cv2.COLOR_RGBA2RGB)
270
 
271
- # Method 1: Detect magenta paint specifically (RGB: 255, 0, 255) - Streamlit canvas default
272
- magenta_mask = np.all(m_rgb == [255, 0, 255], axis=2).astype(np.uint8) * 255
273
 
274
- # Method 2: Detect any difference between original and painted
275
- diff = cv2.absdiff(img_rgb, m_rgb)
276
- gray_diff = cv2.cvtColor(diff, cv2.COLOR_RGB2GRAY)
277
- try:
278
- _, binmask_diff = cv2.threshold(gray_diff, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
279
- except Exception:
280
- _, binmask_diff = cv2.threshold(gray_diff, 30, 255, cv2.THRESH_BINARY)
281
 
282
- # Combine both methods (magenta OR significant difference)
283
- binmask = cv2.bitwise_or(magenta_mask, binmask_diff)
 
 
 
 
 
 
 
 
 
 
 
 
 
284
 
285
- # Optional: detect other bright colors that might be paint
286
- # Detect any bright non-original colors (R+G+B > threshold and differs from original)
287
- brightness = m_rgb.sum(axis=2)
288
- diff_magnitude = diff.sum(axis=2)
289
- bright_paint = ((brightness > 400) & (diff_magnitude > 50)).astype(np.uint8) * 255
290
- binmask = cv2.bitwise_or(binmask, bright_paint)
291
 
292
  nonzero = int((binmask > 0).sum())
293
- log.info("painted-mask detection: magenta=%d, diff=%d, combined=%d pixels",
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 alternative detection...")
298
- # Fallback: detect any significant color change
299
- color_change = np.sum(np.abs(img_rgb.astype(np.int16) - m_rgb.astype(np.int16)), axis=2)
300
- _, binmask = cv2.threshold(color_change.astype(np.uint8), 30, 255, cv2.THRESH_BINARY)
301
  nonzero = int((binmask > 0).sum())
302
- log.info("fallback detection: %d pixels", nonzero)
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
- # Also create a cleaner version: apply morphological operations to smooth edges
316
- kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
317
- binmask_clean = cv2.morphologyEx(binmask, cv2.MORPH_CLOSE, kernel)
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": "mask detection failed - very few pixels detected"}
 
 
 
 
 
 
 
 
 
 
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