ssoxye commited on
Commit
df3b13f
·
1 Parent(s): c94fce0

update sketch input

Browse files
Files changed (1) hide show
  1. app.py +95 -0
app.py CHANGED
@@ -125,6 +125,92 @@ def apply_parsing_white_mask_to_person_cv2(
125
  result_bgr = cv2.cvtColor(result_rgb, cv2.COLOR_RGB2BGR)
126
  return result_bgr
127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
  def compute_hw_from_person(person_path: str):
130
  img = _imread_or_raise(person_path)
@@ -402,6 +488,15 @@ def run_one(paths: Paths, prompt: str, steps: int = DEFAULT_STEPS):
402
  parsing_img = res["images"][0] if res.get("images") else None
403
  if parsing_img is None:
404
  raise RuntimeError("run_simple_extractor returned no parsing images.")
 
 
 
 
 
 
 
 
 
405
 
406
  # -------------------------------------------------
407
  # ✅ (2) UI sketch 업로드는 optional
 
125
  result_bgr = cv2.cvtColor(result_rgb, cv2.COLOR_RGB2BGR)
126
  return result_bgr
127
 
128
+ from typing import Optional, Tuple
129
+ import numpy as np
130
+ from PIL import Image
131
+
132
+ def clean_and_smooth_parsing_mask(
133
+ parsing_img: Image.Image,
134
+ *,
135
+ white_threshold: int = 128,
136
+ min_white_area: int = 300,
137
+ close_ksize: int = 7,
138
+ open_ksize: int = 3,
139
+ morph_iters: int = 1,
140
+ blur_ksize: int = 0,
141
+ ) -> Image.Image:
142
+ """
143
+ Clean small white blobs and smooth boundaries on a grayscale (0/255) PIL mask.
144
+
145
+ Args:
146
+ parsing_img: PIL.Image in 'L' mode recommended. White=foreground, Black=background.
147
+ white_threshold: threshold to binarize. >= threshold -> white(255), else black(0).
148
+ min_white_area: remove connected white components smaller than this pixel area.
149
+ close_ksize: kernel size for morphological closing (fill small holes, smooth edges).
150
+ open_ksize: kernel size for morphological opening (remove small spikes/noise).
151
+ morph_iters: number of iterations for close/open.
152
+ blur_ksize: optional gaussian blur kernel size (odd number, e.g. 7). 0 disables blur.
153
+
154
+ Returns:
155
+ A PIL.Image (mode 'L') cleaned + smoothed (values 0 or 255).
156
+ """
157
+ if not isinstance(parsing_img, Image.Image):
158
+ raise TypeError("parsing_img must be a PIL.Image.Image")
159
+
160
+ # Convert to grayscale
161
+ img_l = parsing_img.convert("L")
162
+ arr = np.array(img_l, dtype=np.uint8)
163
+
164
+ # Binarize -> 0/255
165
+ mask = np.where(arr >= white_threshold, 255, 0).astype(np.uint8)
166
+
167
+ # --- connected components: remove small white regions ---
168
+ try:
169
+ import cv2
170
+ except ImportError as e:
171
+ raise ImportError(
172
+ "This function requires opencv-python (cv2). Install with: pip install opencv-python"
173
+ ) from e
174
+
175
+ # Connected components on binary mask
176
+ # Note: cv2.connectedComponentsWithStats expects 0/255 (or 0/1), uint8.
177
+ num_labels, labels, stats, _ = cv2.connectedComponentsWithStats(mask, connectivity=8)
178
+
179
+ # Keep only components with area >= min_white_area (label 0 is background)
180
+ keep = np.zeros_like(mask)
181
+ for lab in range(1, num_labels):
182
+ area = int(stats[lab, cv2.CC_STAT_AREA])
183
+ if area >= min_white_area:
184
+ keep[labels == lab] = 255
185
+
186
+ mask = keep
187
+
188
+ # --- boundary smoothing via morphology ---
189
+ def _odd_or_one(k: int) -> int:
190
+ k = int(k)
191
+ if k <= 1:
192
+ return 1
193
+ return k if (k % 2 == 1) else (k + 1)
194
+
195
+ close_k = _odd_or_one(close_ksize)
196
+ open_k = _odd_or_one(open_ksize)
197
+
198
+ if close_k > 1:
199
+ k_close = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (close_k, close_k))
200
+ mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, k_close, iterations=int(morph_iters))
201
+
202
+ if open_k > 1:
203
+ k_open = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (open_k, open_k))
204
+ mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, k_open, iterations=int(morph_iters))
205
+
206
+ # --- optional blur for extra smooth boundary (then re-threshold) ---
207
+ if blur_ksize and int(blur_ksize) > 1:
208
+ b = _odd_or_one(int(blur_ksize))
209
+ mask_blur = cv2.GaussianBlur(mask, (b, b), 0)
210
+ mask = np.where(mask_blur >= 128, 255, 0).astype(np.uint8)
211
+
212
+ return Image.fromarray(mask, mode="L")
213
+
214
 
215
  def compute_hw_from_person(person_path: str):
216
  img = _imread_or_raise(person_path)
 
488
  parsing_img = res["images"][0] if res.get("images") else None
489
  if parsing_img is None:
490
  raise RuntimeError("run_simple_extractor returned no parsing images.")
491
+
492
+ parsing_img = clean_and_smooth_parsing_mask(
493
+ parsing_img,
494
+ min_white_area=300, # 작은 흰색 덩어리 제거 강도
495
+ close_ksize=9, # 경계 매끈 + 작은 구멍 메움
496
+ open_ksize=3, # 잔 노이즈 제거
497
+ morph_iters=1,
498
+ blur_ksize=7, # 더 부드럽게 (원치 않으면 0)
499
+ )
500
 
501
  # -------------------------------------------------
502
  # ✅ (2) UI sketch 업로드는 optional