Spaces:
Runtime error
Runtime error
Commit
Β·
d2f86ce
1
Parent(s):
a0e7842
WIP
Browse files
app.py
CHANGED
@@ -12,6 +12,9 @@ from huggingface_hub import login
|
|
12 |
from diffusers import FluxControlNetPipeline, FluxControlNetModel
|
13 |
from diffusers.models import FluxMultiControlNetModel
|
14 |
|
|
|
|
|
|
|
15 |
"""
|
16 |
FLUXβ1 ControlNet demo
|
17 |
----------------------
|
@@ -64,39 +67,191 @@ pipe.set_progress_bar_config(disable=True)
|
|
64 |
# --------------------------------------------------
|
65 |
MODE_MAPPING = {
|
66 |
"canny": 0,
|
67 |
-
"
|
68 |
-
"
|
69 |
-
"
|
70 |
-
"
|
71 |
-
"
|
72 |
"low quality": 6,
|
73 |
}
|
74 |
|
75 |
MAX_SEED = 100
|
76 |
|
77 |
-
#
|
78 |
-
#
|
79 |
-
#
|
80 |
|
81 |
|
82 |
def _preview_canny(
|
83 |
pil_img: Image.Image, canny_threshold_1: int, canny_threshold_2: int
|
84 |
) -> Image.Image:
|
|
|
|
|
85 |
arr = np.array(pil_img.convert("RGB"))
|
86 |
edges = cv2.Canny(arr, threshold1=canny_threshold_1, threshold2=canny_threshold_2)
|
87 |
edges_rgb = cv2.cvtColor(edges, cv2.COLOR_GRAY2RGB)
|
88 |
return Image.fromarray(edges_rgb)
|
89 |
|
90 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
def _make_preview(
|
92 |
control_image: Image.Image,
|
93 |
mode: str,
|
94 |
-
canny_threshold_1: int,
|
95 |
-
canny_threshold_2: int,
|
96 |
) -> Image.Image:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
97 |
if mode == "canny":
|
98 |
return _preview_canny(control_image, canny_threshold_1, canny_threshold_2)
|
99 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
100 |
return control_image
|
101 |
|
102 |
|
|
|
12 |
from diffusers import FluxControlNetPipeline, FluxControlNetModel
|
13 |
from diffusers.models import FluxMultiControlNetModel
|
14 |
|
15 |
+
import warnings
|
16 |
+
from typing import Tuple
|
17 |
+
|
18 |
"""
|
19 |
FLUXβ1 ControlNet demo
|
20 |
----------------------
|
|
|
67 |
# --------------------------------------------------
|
68 |
MODE_MAPPING = {
|
69 |
"canny": 0,
|
70 |
+
"tile": 1,
|
71 |
+
"depth": 2,
|
72 |
+
"blur": 3,
|
73 |
+
"pose": 4,
|
74 |
+
"gray": 5,
|
75 |
"low quality": 6,
|
76 |
}
|
77 |
|
78 |
MAX_SEED = 100
|
79 |
|
80 |
+
# -----------------------------------------------------------------------------
|
81 |
+
# Preview helpers βΒ one small, selfβcontained function per mode
|
82 |
+
# -----------------------------------------------------------------------------
|
83 |
|
84 |
|
85 |
def _preview_canny(
|
86 |
pil_img: Image.Image, canny_threshold_1: int, canny_threshold_2: int
|
87 |
) -> Image.Image:
|
88 |
+
"""Fast Cannyβedge preview (already implemented)."""
|
89 |
+
|
90 |
arr = np.array(pil_img.convert("RGB"))
|
91 |
edges = cv2.Canny(arr, threshold1=canny_threshold_1, threshold2=canny_threshold_2)
|
92 |
edges_rgb = cv2.cvtColor(edges, cv2.COLOR_GRAY2RGB)
|
93 |
return Image.fromarray(edges_rgb)
|
94 |
|
95 |
|
96 |
+
# βββ tile ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ #
|
97 |
+
|
98 |
+
|
99 |
+
def _preview_tile(pil_img: Image.Image, grid: Tuple[int, int] = (2, 2)) -> Image.Image:
|
100 |
+
"""Replicates *pil_img* into an *nΓm* tiled grid (default 2Γ2).
|
101 |
+
|
102 |
+
This offers a quick visual hint of what a *tiling* control mode will do
|
103 |
+
(repeatable textures, etc.)."""
|
104 |
+
|
105 |
+
cols, rows = grid
|
106 |
+
img_rgb = pil_img.convert("RGB")
|
107 |
+
w, h = img_rgb.size
|
108 |
+
tiled = Image.new("RGB", (w * cols, h * rows))
|
109 |
+
for c in range(cols):
|
110 |
+
for r in range(rows):
|
111 |
+
tiled.paste(img_rgb, (c * w, r * h))
|
112 |
+
return tiled
|
113 |
+
|
114 |
+
|
115 |
+
# βββ depth βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ #
|
116 |
+
|
117 |
+
|
118 |
+
def _preview_depth(pil_img: Image.Image) -> Image.Image:
|
119 |
+
"""Very rough *depth* proxy using the Laplacian and a colormap.
|
120 |
+
|
121 |
+
βΈ Convert to gray
|
122 |
+
βΈ Run Laplacian to highlight depthβlike gradients
|
123 |
+
βΈ Apply a TURBO colormap to mimic depth heatβmap appearance"""
|
124 |
+
|
125 |
+
gray = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2GRAY)
|
126 |
+
lap = cv2.Laplacian(gray, cv2.CV_16S, ksize=3)
|
127 |
+
depth = cv2.convertScaleAbs(lap)
|
128 |
+
depth_color = cv2.applyColorMap(depth, cv2.COLORMAP_TURBO)
|
129 |
+
return Image.fromarray(depth_color)
|
130 |
+
|
131 |
+
|
132 |
+
# βββ blur βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ #
|
133 |
+
|
134 |
+
|
135 |
+
def _preview_blur(pil_img: Image.Image, ksize: int = 15) -> Image.Image:
|
136 |
+
"""Gaussian blur preview.
|
137 |
+
A single, relatively large kernel is enough for UI illustration."""
|
138 |
+
|
139 |
+
if ksize % 2 == 0:
|
140 |
+
ksize += 1 # kernel must be odd
|
141 |
+
blurred = cv2.GaussianBlur(np.array(pil_img), (ksize, ksize), sigmaX=0)
|
142 |
+
return Image.fromarray(blurred)
|
143 |
+
|
144 |
+
|
145 |
+
# βββ pose ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ #
|
146 |
+
|
147 |
+
|
148 |
+
def _preview_pose(pil_img: Image.Image) -> Image.Image:
|
149 |
+
"""Attempt a lightweight 2βD pose overlay using *mediapipe* if available.
|
150 |
+
|
151 |
+
If *mediapipe* is not installed (or CPU inference fails), we gracefully
|
152 |
+
fallback to an edgeβmap preview so the UI never crashes."""
|
153 |
+
|
154 |
+
try:
|
155 |
+
import mediapipe as mp # type: ignore
|
156 |
+
|
157 |
+
mp_pose = mp.solutions.pose
|
158 |
+
mp_drawing = mp.solutions.drawing_utils
|
159 |
+
|
160 |
+
img_bgr = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
|
161 |
+
with mp_pose.Pose(static_image_mode=True) as pose_estimator:
|
162 |
+
results = pose_estimator.process(
|
163 |
+
img_bgr[..., ::-1]
|
164 |
+
) # Mediapipe expects RGB
|
165 |
+
|
166 |
+
annotated = img_bgr.copy()
|
167 |
+
if results.pose_landmarks:
|
168 |
+
mp_drawing.draw_landmarks(
|
169 |
+
annotated, results.pose_landmarks, mp_pose.POSE_CONNECTIONS
|
170 |
+
)
|
171 |
+
annotated_rgb = cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB)
|
172 |
+
return Image.fromarray(annotated_rgb)
|
173 |
+
|
174 |
+
except Exception as exc: # pragma: no cover βΒ any import / runtime error
|
175 |
+
warnings.warn(
|
176 |
+
f"Pose preview failed ({exc!s}); falling back to Canny.", RuntimeWarning
|
177 |
+
)
|
178 |
+
# Return an edge map as a sensible fallback rather than exploding the UI
|
179 |
+
return _preview_canny(pil_img, 100, 200)
|
180 |
+
|
181 |
+
|
182 |
+
# βββ gray βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ #
|
183 |
+
|
184 |
+
|
185 |
+
def _preview_gray(pil_img: Image.Image) -> Image.Image:
|
186 |
+
"""Simple grayscale conversion, but keep a 3βchannel RGB image so the UI
|
187 |
+
widget pipeline stays consistent."""
|
188 |
+
|
189 |
+
gray = cv2.cvtColor(np.array(pil_img.convert("RGB")), cv2.COLOR_RGB2GRAY)
|
190 |
+
gray_rgb = cv2.cvtColor(gray, cv2.COLOR_GRAY2RGB)
|
191 |
+
return Image.fromarray(gray_rgb)
|
192 |
+
|
193 |
+
|
194 |
+
# βββ low quality βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ #
|
195 |
+
|
196 |
+
|
197 |
+
def _preview_low_quality(pil_img: Image.Image, factor: int = 8) -> Image.Image:
|
198 |
+
"""Mimic a lowβquality thumbnail: aggressively downsample then upscale.
|
199 |
+
|
200 |
+
The default *factor* (8Γ) is chosen to make artefacts obvious."""
|
201 |
+
|
202 |
+
img_rgb = pil_img.convert("RGB")
|
203 |
+
w, h = img_rgb.size
|
204 |
+
small = img_rgb.resize((max(1, w // factor), max(1, h // factor)), Image.BILINEAR)
|
205 |
+
low_q = small.resize(
|
206 |
+
(w, h), Image.NEAREST
|
207 |
+
) # upsample w/ Nearest to exaggerate blocks
|
208 |
+
return low_q
|
209 |
+
|
210 |
+
|
211 |
+
# -----------------------------------------------------------------------------
|
212 |
+
# Master dispatch
|
213 |
+
# -----------------------------------------------------------------------------
|
214 |
+
|
215 |
+
|
216 |
def _make_preview(
|
217 |
control_image: Image.Image,
|
218 |
mode: str,
|
219 |
+
canny_threshold_1: int = 100,
|
220 |
+
canny_threshold_2: int = 200,
|
221 |
) -> Image.Image:
|
222 |
+
"""Return a *quickβnβdirty* preview image for the requested *mode*.
|
223 |
+
|
224 |
+
Parameters
|
225 |
+
----------
|
226 |
+
control_image : PIL.Image
|
227 |
+
The input image selected by the user.
|
228 |
+
mode : str
|
229 |
+
One of the keys of :data:`MODE_MAPPING`.
|
230 |
+
canny_threshold_1 / 2 : int, optional
|
231 |
+
Only used if *mode* is "canny" (passed straight to OpenCV Canny).
|
232 |
+
"""
|
233 |
+
|
234 |
+
mode = mode.lower()
|
235 |
+
if mode not in MODE_MAPPING:
|
236 |
+
warnings.warn(f"Unknown preview mode '{mode}'. Returning untouched image.")
|
237 |
+
return control_image
|
238 |
+
|
239 |
if mode == "canny":
|
240 |
return _preview_canny(control_image, canny_threshold_1, canny_threshold_2)
|
241 |
+
if mode == "tile":
|
242 |
+
return _preview_tile(control_image)
|
243 |
+
if mode == "depth":
|
244 |
+
return _preview_depth(control_image)
|
245 |
+
if mode == "blur":
|
246 |
+
return _preview_blur(control_image)
|
247 |
+
if mode == "pose":
|
248 |
+
return _preview_pose(control_image)
|
249 |
+
if mode == "gray":
|
250 |
+
return _preview_gray(control_image)
|
251 |
+
if mode == "low quality":
|
252 |
+
return _preview_low_quality(control_image)
|
253 |
+
|
254 |
+
# Fallback βΒ should never happen due to early mode check
|
255 |
return control_image
|
256 |
|
257 |
|