Spaces:
Sleeping
Sleeping
Local Median Color Boundary
Browse files
app.py
CHANGED
|
@@ -56,16 +56,53 @@ def _process_saree_core(base_image: Image.Image, pattern_image: Image.Image):
|
|
| 56 |
# img_pil = base_image.convert("RGB") # <-- COMMENTED: this injects black behind transparency
|
| 57 |
# img_np = np.array(img_pil)
|
| 58 |
|
| 59 |
-
# ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
base_rgba = base_image.convert("RGBA")
|
| 61 |
-
_arr = np.array(base_rgba).astype(np.float32)
|
| 62 |
-
_rgb = _arr[..., :3]
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
# --- end NEW ---
|
| 70 |
|
| 71 |
# Prepare tensor
|
|
|
|
| 56 |
# img_pil = base_image.convert("RGB") # <-- COMMENTED: this injects black behind transparency
|
| 57 |
# img_np = np.array(img_pil)
|
| 58 |
|
| 59 |
+
# --- ORIGINAL (white matte) kept for reference ---
|
| 60 |
+
# base_rgba = base_image.convert("RGBA")
|
| 61 |
+
# _arr = np.array(base_rgba).astype(np.float32)
|
| 62 |
+
# _rgb = _arr[..., :3]
|
| 63 |
+
# _a = (_arr[..., 3:4] / 255.0)
|
| 64 |
+
# _rgb_over_white = _rgb * _a + (1.0 - _a) * 255.0
|
| 65 |
+
# _rgb_over_white = _rgb_over_white.astype(np.uint8)
|
| 66 |
+
# img_pil = Image.fromarray(_rgb_over_white, mode="RGB")
|
| 67 |
+
# img_np = _rgb_over_white
|
| 68 |
+
# --- end ORIGINAL ---
|
| 69 |
+
|
| 70 |
+
# --- NEW: alpha-aware RGB using median color along interior boundary as matte ---
|
| 71 |
base_rgba = base_image.convert("RGBA")
|
| 72 |
+
_arr = np.array(base_rgba).astype(np.float32) # (H,W,4), RGB in 0..255, A in 0..255
|
| 73 |
+
_rgb = _arr[..., :3] # (H,W,3)
|
| 74 |
+
_alpha8 = _arr[..., 3].astype(np.uint8) # (H,W) uint8 alpha
|
| 75 |
+
_a = (_alpha8.astype(np.float32) / 255.0)[..., None] # (H,W,1) float alpha
|
| 76 |
+
|
| 77 |
+
# Build a foreground mask from alpha (slightly strict to avoid wispy edges)
|
| 78 |
+
_fg_mask = (_alpha8 > 128).astype(np.uint8) * 255 # (H,W) 0/255
|
| 79 |
+
|
| 80 |
+
# Morphological interior boundary: foreground minus a 1-iteration erosion
|
| 81 |
+
_k = np.ones((3, 3), np.uint8)
|
| 82 |
+
_eroded = cv2.erode(_fg_mask, _k, iterations=1)
|
| 83 |
+
_boundary = cv2.bitwise_and(_fg_mask, cv2.bitwise_not(_eroded)) # thin interior ring
|
| 84 |
+
|
| 85 |
+
# If boundary is too thin/few pixels, widen the ring via morphological gradient
|
| 86 |
+
if int((_boundary > 0).sum()) < 100:
|
| 87 |
+
_dil = cv2.dilate(_fg_mask, _k, iterations=2)
|
| 88 |
+
_ero = cv2.erode(_fg_mask, _k, iterations=2)
|
| 89 |
+
_boundary = cv2.subtract(_dil, _ero)
|
| 90 |
+
|
| 91 |
+
_idx = (_boundary > 0)
|
| 92 |
+
if not np.any(_idx):
|
| 93 |
+
# Fallback: use entire foreground if boundary not found
|
| 94 |
+
_idx = (_fg_mask > 0)
|
| 95 |
+
|
| 96 |
+
# Compute median color over the selected boundary pixels (in 0..255 space)
|
| 97 |
+
_median_color = np.median(_rgb[_idx], axis=0) if np.any(_idx) else np.array([255.0, 255.0, 255.0], dtype=np.float32)
|
| 98 |
+
_median_color = _median_color.reshape(1, 1, 3) # (1,1,3)
|
| 99 |
+
|
| 100 |
+
# Composite RGB over median matte (avoid introducing black/white bias)
|
| 101 |
+
_rgb_over_matte = _rgb * _a + (1.0 - _a) * _median_color
|
| 102 |
+
_rgb_over_matte = np.clip(_rgb_over_matte, 0.0, 255.0).astype(np.uint8)
|
| 103 |
+
|
| 104 |
+
img_pil = Image.fromarray(_rgb_over_matte, mode="RGB")
|
| 105 |
+
img_np = _rgb_over_matte
|
| 106 |
# --- end NEW ---
|
| 107 |
|
| 108 |
# Prepare tensor
|