vitor.fran commited on
Commit
f127edd
·
1 Parent(s): f9fd7e4

Improve faceless lineart detail for Space

Browse files
Files changed (2) hide show
  1. app.py +26 -3
  2. faceless_processing.py +20 -15
app.py CHANGED
@@ -15,6 +15,30 @@ def process_faceless(input_image, prompt="", negative_prompt="", is_cartoon=Fals
15
  # Configuração base
16
  cfg = LineartConfig()
17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  if is_cartoon:
19
  cfg.is_cartoon = True
20
  # Afrouxa as regras para não apagar detalhes do desenho, mas com limites seguros para bordar
@@ -59,8 +83,7 @@ def process_faceless(input_image, prompt="", negative_prompt="", is_cartoon=Fals
59
  cfg.face_min_size = (9999, 9999)
60
 
61
  # Mantém grossinho para bordar!
62
- cfg.silhouette_line_thickness = 5
63
- cfg.interior_line_thickness = 4
64
 
65
  # NÃO REDIMENSIONAMOS A IMAGEM – a função interna ( embroidery_processing.py) já lida com tamanhos variados.
66
  # Gera lineart e cutout usando a função otimizada
@@ -97,4 +120,4 @@ demo = gr.Interface(
97
  )
98
 
99
  if __name__ == "__main__":
100
- demo.launch()
 
15
  # Configuração base
16
  cfg = LineartConfig()
17
 
18
+ # The detail preset applies to portraits too. Previously it only affected
19
+ # cartoon mode, which made the public controls misleading.
20
+ if detail == "detailed":
21
+ cfg.lineart_threshold = 72
22
+ cfg.lineart_min_perimeter = 20.0
23
+ cfg.lineart_min_area = 5.0
24
+ cfg.min_line_distance = 4
25
+ cfg.silhouette_line_thickness = 5
26
+ cfg.interior_line_thickness = 4
27
+ elif detail == "soft":
28
+ cfg.lineart_threshold = 100
29
+ cfg.lineart_min_perimeter = 36.0
30
+ cfg.lineart_min_area = 10.0
31
+ cfg.min_line_distance = 7
32
+ cfg.silhouette_line_thickness = 4
33
+ cfg.interior_line_thickness = 3
34
+ else:
35
+ cfg.lineart_threshold = 88
36
+ cfg.lineart_min_perimeter = 28.0
37
+ cfg.lineart_min_area = 8.0
38
+ cfg.min_line_distance = 5
39
+ cfg.silhouette_line_thickness = 5
40
+ cfg.interior_line_thickness = 4
41
+
42
  if is_cartoon:
43
  cfg.is_cartoon = True
44
  # Afrouxa as regras para não apagar detalhes do desenho, mas com limites seguros para bordar
 
83
  cfg.face_min_size = (9999, 9999)
84
 
85
  # Mantém grossinho para bordar!
86
+ cfg.stroke_dilate_iterations = 0
 
87
 
88
  # NÃO REDIMENSIONAMOS A IMAGEM – a função interna ( embroidery_processing.py) já lida com tamanhos variados.
89
  # Gera lineart e cutout usando a função otimizada
 
120
  )
121
 
122
  if __name__ == "__main__":
123
+ demo.launch()
faceless_processing.py CHANGED
@@ -4,6 +4,7 @@ from PIL import Image, ImageFilter
4
  from dataclasses import dataclass
5
  from typing import Tuple, Optional
6
  import io
 
7
  import base64
8
  import os
9
  import urllib.request
@@ -144,16 +145,21 @@ class LineartConfig:
144
  lineart_coarse: bool = True
145
 
146
  # AUMENTADO: Força bruta para ignorar detalhes fracos
147
- lineart_threshold: int = 115
 
 
148
 
149
  # REDUZIDO: Permite traços de corpos menores (crianças em grupos)
150
- lineart_min_perimeter: float = 80.0
151
- lineart_min_area: float = 22.0
152
- lineart_morph_open: bool = True
153
  force_canny: bool = False
154
 
155
  interior_erode_size: int = 1
156
- interior_line_thickness: int = 3
 
 
 
157
 
158
  # ---- Canny fallback parametros ----
159
  canny_low: int = 50
@@ -688,8 +694,10 @@ def build_interior_edges(rgb: np.ndarray, subject_mask: np.ndarray, cfg: Lineart
688
  min_area=min_area
689
  )
690
 
691
- kernel_dilate = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
692
- filtered = cv2.dilate(filtered, kernel_dilate, iterations=1)
 
 
693
  filtered = cv2.GaussianBlur(filtered, (3, 3), 0)
694
 
695
  _, filtered = cv2.threshold(filtered, 127, 255, cv2.THRESH_BINARY)
@@ -821,8 +829,10 @@ def _build_interior_edges_canny(rgb: np.ndarray, subject_mask: np.ndarray, cfg:
821
  shape=edges.shape
822
  )
823
 
824
- kernel_dilate = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
825
- filtered = cv2.dilate(filtered, kernel_dilate, iterations=1)
 
 
826
  return filtered
827
 
828
 
@@ -1144,11 +1154,6 @@ def remove_background_subject(img: Image.Image) -> Image.Image:
1144
  def make_faceless_cutout(img: Image.Image) -> Image.Image:
1145
  return remove_background_subject(img)
1146
 
1147
-
1148
-
1149
-
1150
-
1151
-
1152
  def build_faceless_embroidery_assets(img: Image.Image, cfg: Optional[LineartConfig] = None, **kwargs) -> Tuple[Image.Image, Image.Image]:
1153
  if cfg is None:
1154
  cfg = LineartConfig()
@@ -1158,4 +1163,4 @@ def build_faceless_embroidery_assets(img: Image.Image, cfg: Optional[LineartConf
1158
  cfg.negative_prompt = kwargs["negative_prompt"]
1159
  lineart = make_faceless_lineart(img, cfg)
1160
  cutout = make_faceless_cutout(img)
1161
- return lineart, cutout
 
4
  from dataclasses import dataclass
5
  from typing import Tuple, Optional
6
  import io
7
+
8
  import base64
9
  import os
10
  import urllib.request
 
145
  lineart_coarse: bool = True
146
 
147
  # AUMENTADO: Força bruta para ignorar detalhes fracos
148
+ # Lower threshold keeps subtle clothing/hair contours that were previously
149
+ # discarded before they could be converted into embroidery lines.
150
+ lineart_threshold: int = 88
151
 
152
  # REDUZIDO: Permite traços de corpos menores (crianças em grupos)
153
+ lineart_min_perimeter: float = 28.0
154
+ lineart_min_area: float = 8.0
155
+ lineart_morph_open: bool = False
156
  force_canny: bool = False
157
 
158
  interior_erode_size: int = 1
159
+ interior_line_thickness: int = 4
160
+ # Extra dilation is deliberately opt-in. The old unconditional dilation
161
+ # made independent contours merge into thick blobs at embroidery scale.
162
+ stroke_dilate_iterations: int = 0
163
 
164
  # ---- Canny fallback parametros ----
165
  canny_low: int = 50
 
694
  min_area=min_area
695
  )
696
 
697
+ dilate_iterations = max(0, int(getattr(cfg, 'stroke_dilate_iterations', 0)))
698
+ if dilate_iterations:
699
+ kernel_dilate = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
700
+ filtered = cv2.dilate(filtered, kernel_dilate, iterations=dilate_iterations)
701
  filtered = cv2.GaussianBlur(filtered, (3, 3), 0)
702
 
703
  _, filtered = cv2.threshold(filtered, 127, 255, cv2.THRESH_BINARY)
 
829
  shape=edges.shape
830
  )
831
 
832
+ dilate_iterations = max(0, int(getattr(cfg, 'stroke_dilate_iterations', 0)))
833
+ if dilate_iterations:
834
+ kernel_dilate = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
835
+ filtered = cv2.dilate(filtered, kernel_dilate, iterations=dilate_iterations)
836
  return filtered
837
 
838
 
 
1154
  def make_faceless_cutout(img: Image.Image) -> Image.Image:
1155
  return remove_background_subject(img)
1156
 
 
 
 
 
 
1157
  def build_faceless_embroidery_assets(img: Image.Image, cfg: Optional[LineartConfig] = None, **kwargs) -> Tuple[Image.Image, Image.Image]:
1158
  if cfg is None:
1159
  cfg = LineartConfig()
 
1163
  cfg.negative_prompt = kwargs["negative_prompt"]
1164
  lineart = make_faceless_lineart(img, cfg)
1165
  cutout = make_faceless_cutout(img)
1166
+ return lineart, cutout