Spaces:
Sleeping
Sleeping
Update faceless_processing.py
Browse files- faceless_processing.py +39 -8
faceless_processing.py
CHANGED
|
@@ -723,12 +723,38 @@ def _skeletonize(img: np.ndarray) -> np.ndarray:
|
|
| 723 |
|
| 724 |
|
| 725 |
def _build_interior_edges_canny(rgb: np.ndarray, subject_mask: np.ndarray, cfg: LineartConfig) -> np.ndarray:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 726 |
gray = cv2.cvtColor(rgb, cv2.COLOR_RGB2GRAY)
|
| 727 |
-
|
| 728 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 729 |
|
| 730 |
if getattr(cfg, 'is_cartoon', False):
|
| 731 |
# 1. Binarização adaptativa inteligente para extrair traços cheios (evita contorno duplo!)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 732 |
corners = [blurred[0, 0], blurred[0, -1], blurred[-1, 0], blurred[-1, -1]]
|
| 733 |
bg = float(np.median(corners))
|
| 734 |
if bg > 127: # Fundo claro, traços escuros
|
|
@@ -736,17 +762,17 @@ def _build_interior_edges_canny(rgb: np.ndarray, subject_mask: np.ndarray, cfg:
|
|
| 736 |
blurred, 255,
|
| 737 |
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
|
| 738 |
cv2.THRESH_BINARY_INV,
|
| 739 |
-
|
| 740 |
)
|
| 741 |
else: # Fundo escuro, traços claros
|
| 742 |
thresh = cv2.adaptiveThreshold(
|
| 743 |
blurred, 255,
|
| 744 |
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
|
| 745 |
cv2.THRESH_BINARY,
|
| 746 |
-
|
| 747 |
)
|
| 748 |
|
| 749 |
-
# 2. Canny complementar para não perder pequenos detalhes de alto contraste
|
| 750 |
canny_edges = cv2.Canny(blurred, cfg.canny_low, cfg.canny_high)
|
| 751 |
# Dilata Canny levemente para fundir com os traços do limiar adaptativo
|
| 752 |
kernel_c = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
|
|
@@ -769,12 +795,17 @@ def _build_interior_edges_canny(rgb: np.ndarray, subject_mask: np.ndarray, cfg:
|
|
| 769 |
|
| 770 |
contours, _ = cv2.findContours(edges, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
|
| 771 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 772 |
smoothed_contours = []
|
| 773 |
for cnt in contours:
|
| 774 |
perimeter = cv2.arcLength(cnt, False)
|
| 775 |
-
if perimeter <
|
| 776 |
continue
|
| 777 |
-
eps = 0.0012 * perimeter
|
| 778 |
smooth = cv2.approxPolyDP(cnt, eps, False)
|
| 779 |
smoothed_contours.append(smooth)
|
| 780 |
|
|
@@ -785,7 +816,7 @@ def _build_interior_edges_canny(rgb: np.ndarray, subject_mask: np.ndarray, cfg:
|
|
| 785 |
contours=smoothed_contours,
|
| 786 |
min_dist_px=min_dist,
|
| 787 |
overlap_threshold=overlap_thresh,
|
| 788 |
-
min_perimeter=
|
| 789 |
thickness=cfg.interior_line_thickness,
|
| 790 |
shape=edges.shape
|
| 791 |
)
|
|
|
|
| 723 |
|
| 724 |
|
| 725 |
def _build_interior_edges_canny(rgb: np.ndarray, subject_mask: np.ndarray, cfg: LineartConfig) -> np.ndarray:
|
| 726 |
+
h, w = rgb.shape[:2]
|
| 727 |
+
max_dim = max(w, h)
|
| 728 |
+
scale = max_dim / 512.0
|
| 729 |
+
|
| 730 |
gray = cv2.cvtColor(rgb, cv2.COLOR_RGB2GRAY)
|
| 731 |
+
|
| 732 |
+
if getattr(cfg, 'is_cartoon', False):
|
| 733 |
+
# Para desenhos/cartoons, aplicamos um filtro bilateral muito mais forte para simplificar texturas (como pelos de animais ou texturas de pele)
|
| 734 |
+
d_bilateral = int(15 * scale)
|
| 735 |
+
if d_bilateral % 2 == 0:
|
| 736 |
+
d_bilateral += 1
|
| 737 |
+
d_bilateral = max(9, d_bilateral)
|
| 738 |
+
|
| 739 |
+
blurred = cv2.bilateralFilter(gray, d_bilateral, 150, 150)
|
| 740 |
+
|
| 741 |
+
k_gaussian = int(9 * scale)
|
| 742 |
+
if k_gaussian % 2 == 0:
|
| 743 |
+
k_gaussian += 1
|
| 744 |
+
k_gaussian = max(5, k_gaussian)
|
| 745 |
+
blurred = cv2.GaussianBlur(blurred, (k_gaussian, k_gaussian), 0)
|
| 746 |
+
else:
|
| 747 |
+
blurred = cv2.bilateralFilter(gray, cfg.bilateral_d, cfg.bilateral_sigma_color, cfg.bilateral_sigma_space)
|
| 748 |
+
blurred = cv2.GaussianBlur(blurred, (cfg.gaussian_ksize, cfg.gaussian_ksize), 0)
|
| 749 |
|
| 750 |
if getattr(cfg, 'is_cartoon', False):
|
| 751 |
# 1. Binarização adaptativa inteligente para extrair traços cheios (evita contorno duplo!)
|
| 752 |
+
# Usamos uma janela adaptativa maior para capturar linhas de estrutura e ignorar texturas locais
|
| 753 |
+
block_size = int(65 * scale)
|
| 754 |
+
if block_size % 2 == 0:
|
| 755 |
+
block_size += 1
|
| 756 |
+
block_size = max(25, block_size)
|
| 757 |
+
|
| 758 |
corners = [blurred[0, 0], blurred[0, -1], blurred[-1, 0], blurred[-1, -1]]
|
| 759 |
bg = float(np.median(corners))
|
| 760 |
if bg > 127: # Fundo claro, traços escuros
|
|
|
|
| 762 |
blurred, 255,
|
| 763 |
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
|
| 764 |
cv2.THRESH_BINARY_INV,
|
| 765 |
+
block_size, 8
|
| 766 |
)
|
| 767 |
else: # Fundo escuro, traços claros
|
| 768 |
thresh = cv2.adaptiveThreshold(
|
| 769 |
blurred, 255,
|
| 770 |
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
|
| 771 |
cv2.THRESH_BINARY,
|
| 772 |
+
block_size, 8
|
| 773 |
)
|
| 774 |
|
| 775 |
+
# 2. Canny complementar para não perder pequenos detalhes de alto contraste (ex: olhos)
|
| 776 |
canny_edges = cv2.Canny(blurred, cfg.canny_low, cfg.canny_high)
|
| 777 |
# Dilata Canny levemente para fundir com os traços do limiar adaptativo
|
| 778 |
kernel_c = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
|
|
|
|
| 795 |
|
| 796 |
contours, _ = cv2.findContours(edges, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
|
| 797 |
|
| 798 |
+
# Determinamos o perímetro mínimo de forma dinâmica para descartar pequenos riscos (ex: pelos)
|
| 799 |
+
min_perimeter = cfg.min_contour_perimeter
|
| 800 |
+
if getattr(cfg, 'is_cartoon', False):
|
| 801 |
+
min_perimeter = max(min_perimeter, 45.0 * scale)
|
| 802 |
+
|
| 803 |
smoothed_contours = []
|
| 804 |
for cnt in contours:
|
| 805 |
perimeter = cv2.arcLength(cnt, False)
|
| 806 |
+
if perimeter < min_perimeter:
|
| 807 |
continue
|
| 808 |
+
eps = 0.001 * perimeter if getattr(cfg, 'is_cartoon', False) else 0.0012 * perimeter
|
| 809 |
smooth = cv2.approxPolyDP(cnt, eps, False)
|
| 810 |
smoothed_contours.append(smooth)
|
| 811 |
|
|
|
|
| 816 |
contours=smoothed_contours,
|
| 817 |
min_dist_px=min_dist,
|
| 818 |
overlap_threshold=overlap_thresh,
|
| 819 |
+
min_perimeter=min_perimeter,
|
| 820 |
thickness=cfg.interior_line_thickness,
|
| 821 |
shape=edges.shape
|
| 822 |
)
|