Spaces:
Running
on
Zero
Running
on
Zero
Fix OpenMP fork issues in background removal by using multiprocessing with spawn method. Maintains high-quality alpha matting while avoiding threading conflicts.
Browse files- hy3dshape/hy3dshape/rembg.py +93 -19
hy3dshape/hy3dshape/rembg.py
CHANGED
@@ -12,35 +12,109 @@
|
|
12 |
# fine-tuning enabling code and other elements of the foregoing made publicly available
|
13 |
# by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
|
14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
from PIL import Image
|
16 |
from rembg import remove, new_session
|
17 |
|
18 |
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
|
|
|
|
|
|
|
|
26 |
output = remove(
|
27 |
image,
|
28 |
-
session=
|
29 |
bgcolor=[255, 255, 255, 0],
|
30 |
alpha_matting=True,
|
31 |
-
alpha_matting_foreground_threshold=220,
|
32 |
-
alpha_matting_background_threshold=5,
|
33 |
-
alpha_matting_erode_structure_size=5,
|
34 |
-
alpha_matting_base_size=512,
|
35 |
)
|
36 |
-
|
37 |
-
|
38 |
-
# If alpha matting fails, fall back to simpler method
|
39 |
-
print("Alpha matting failed, falling back to standard background removal")
|
40 |
output = remove(
|
41 |
image,
|
42 |
-
session=
|
43 |
bgcolor=[255, 255, 255, 0],
|
44 |
-
alpha_matting=False,
|
45 |
)
|
46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
# fine-tuning enabling code and other elements of the foregoing made publicly available
|
13 |
# by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
|
14 |
|
15 |
+
import os
|
16 |
+
import io
|
17 |
+
import multiprocessing
|
18 |
+
import traceback
|
19 |
+
import numpy as np
|
20 |
+
|
21 |
+
# Set OpenMP environment variables to prevent fork() errors
|
22 |
+
os.environ["OMP_NUM_THREADS"] = "1" # Limit OpenMP to single thread
|
23 |
+
os.environ["PYTHONFAULTHANDLER"] = "1" # Better error reporting
|
24 |
+
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE" # Avoid Intel MKL errors
|
25 |
+
os.environ["OMP_THREAD_LIMIT"] = "1" # Limit total number of OpenMP threads
|
26 |
+
|
27 |
from PIL import Image
|
28 |
from rembg import remove, new_session
|
29 |
|
30 |
|
31 |
+
def _remove_bg_in_process(image_bytes, use_alpha_matting=True):
|
32 |
+
"""Function to run in a separate process to avoid OpenMP issues"""
|
33 |
+
try:
|
34 |
+
# Create a new session in this process
|
35 |
+
session = new_session()
|
36 |
+
|
37 |
+
# Convert bytes back to PIL Image
|
38 |
+
image = Image.open(io.BytesIO(image_bytes))
|
39 |
+
|
40 |
+
if use_alpha_matting:
|
41 |
+
# High quality mode with alpha matting
|
42 |
output = remove(
|
43 |
image,
|
44 |
+
session=session,
|
45 |
bgcolor=[255, 255, 255, 0],
|
46 |
alpha_matting=True,
|
47 |
+
alpha_matting_foreground_threshold=220,
|
48 |
+
alpha_matting_background_threshold=5,
|
49 |
+
alpha_matting_erode_structure_size=5,
|
50 |
+
alpha_matting_base_size=512,
|
51 |
)
|
52 |
+
else:
|
53 |
+
# Fallback mode without alpha matting
|
|
|
|
|
54 |
output = remove(
|
55 |
image,
|
56 |
+
session=session,
|
57 |
bgcolor=[255, 255, 255, 0],
|
58 |
+
alpha_matting=False,
|
59 |
)
|
60 |
+
|
61 |
+
# Convert back to bytes
|
62 |
+
output_bytes = io.BytesIO()
|
63 |
+
output.save(output_bytes, format='PNG')
|
64 |
+
return output_bytes.getvalue()
|
65 |
+
|
66 |
+
except Exception as e:
|
67 |
+
print(f"Background removal process error: {e}")
|
68 |
+
traceback.print_exc()
|
69 |
+
return None
|
70 |
+
|
71 |
+
|
72 |
+
class BackgroundRemover():
|
73 |
+
def __init__(self):
|
74 |
+
# Set multiprocessing start method to 'spawn' to avoid OpenMP fork issues
|
75 |
+
# Only set if not already set
|
76 |
+
if multiprocessing.get_start_method(allow_none=True) != 'spawn':
|
77 |
+
try:
|
78 |
+
multiprocessing.set_start_method('spawn', force=True)
|
79 |
+
except RuntimeError:
|
80 |
+
# If already set and not 'spawn', we'll have to work with what we have
|
81 |
+
print("Warning: Could not set multiprocessing start method to 'spawn'")
|
82 |
+
|
83 |
+
# We don't initialize the session here anymore as it will be created in the subprocess
|
84 |
+
self.session = None
|
85 |
+
|
86 |
+
def __call__(self, image: Image.Image):
|
87 |
+
try:
|
88 |
+
# Convert image to bytes for passing to subprocess
|
89 |
+
img_bytes = io.BytesIO()
|
90 |
+
image.save(img_bytes, format='PNG')
|
91 |
+
img_bytes = img_bytes.getvalue()
|
92 |
+
|
93 |
+
# Create a process context
|
94 |
+
ctx = multiprocessing.get_context('spawn')
|
95 |
+
|
96 |
+
# Try with alpha matting first (high quality)
|
97 |
+
with ctx.Pool(processes=1) as pool:
|
98 |
+
result = pool.apply(_remove_bg_in_process, args=(img_bytes, True))
|
99 |
+
|
100 |
+
if result is not None:
|
101 |
+
return Image.open(io.BytesIO(result))
|
102 |
+
|
103 |
+
# If alpha matting fails, try without it
|
104 |
+
print("Alpha matting failed, falling back to standard background removal")
|
105 |
+
with ctx.Pool(processes=1) as pool:
|
106 |
+
result = pool.apply(_remove_bg_in_process, args=(img_bytes, False))
|
107 |
+
|
108 |
+
if result is not None:
|
109 |
+
return Image.open(io.BytesIO(result))
|
110 |
+
|
111 |
+
# If all else fails, return the original image
|
112 |
+
print("Background removal completely failed, returning original image")
|
113 |
+
return image
|
114 |
+
|
115 |
+
except Exception as e:
|
116 |
+
print(f"Background removal error in main process: {e}")
|
117 |
+
traceback.print_exc()
|
118 |
+
# If all else fails, just return the original image
|
119 |
+
# This ensures the pipeline doesn't break completely
|
120 |
+
return image
|