Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -24,7 +24,7 @@ except (ImportError, AttributeError):
|
|
24 |
|
25 |
|
26 |
|
27 |
-
def get_face_mask_box(img, feather):
|
28 |
h, w = img.shape[:2]
|
29 |
mask = np.zeros((h, w), dtype=np.uint8)
|
30 |
results = face_mesh.process(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
|
@@ -34,11 +34,17 @@ def get_face_mask_box(img, feather):
|
|
34 |
hull = cv2.convexHull(pts)
|
35 |
cv2.fillConvexPoly(mask, hull, 255)
|
36 |
x, y, bw, bh = cv2.boundingRect(hull)
|
37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
if feather > 0 and mask_roi.size > 0:
|
39 |
k = int(feather)
|
40 |
mask_roi = cv2.GaussianBlur(mask_roi, (k*2+1, k*2+1), 0)
|
41 |
-
return mask_roi, (
|
42 |
|
43 |
|
44 |
def cut_and_feather(img, feather):
|
@@ -160,14 +166,14 @@ def morph_faces(img1, img2, alpha, dim, step):
|
|
160 |
return (out*255).astype(np.uint8)
|
161 |
|
162 |
|
163 |
-
def process_video(video_path, ref_img, trans, res, step, feather):
|
164 |
cap = cv2.VideoCapture(video_path)
|
165 |
fps = cap.get(cv2.CAP_PROP_FPS) or 24
|
166 |
total = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
167 |
|
168 |
# Prepare masked reference
|
169 |
ref_bgr = cv2.cvtColor(ref_img, cv2.COLOR_RGB2BGR)
|
170 |
-
mask_ref, ref_box = get_face_mask_box(ref_bgr, feather)
|
171 |
if mask_ref is None:
|
172 |
return None, None, None, None
|
173 |
x_r, y_r, w_r, h_r = ref_box
|
@@ -190,7 +196,7 @@ def process_video(video_path, ref_img, trans, res, step, feather):
|
|
190 |
for i in range(total):
|
191 |
ret, frame = cap.read()
|
192 |
if not ret: break
|
193 |
-
mask_roi, box = get_face_mask_box(frame, feather)
|
194 |
if mask_roi is None:
|
195 |
out_vid.write(frame)
|
196 |
continue
|
@@ -233,31 +239,31 @@ def process_video(video_path, ref_img, trans, res, step, feather):
|
|
233 |
# --- Gradio App ---
|
234 |
css = """video, img { object-fit: contain !important; }"""
|
235 |
with gr.Blocks(css=css) as iface:
|
236 |
-
gr.Markdown("#
|
237 |
with gr.Row():
|
238 |
vid = gr.Video(label='Input Video')
|
239 |
-
ref = gr.Image(type='numpy', label='Reference
|
240 |
with gr.Row():
|
241 |
-
res = gr.Dropdown([256,
|
242 |
-
step = gr.Slider(1,
|
243 |
-
feather = gr.Slider(0,
|
244 |
-
|
|
|
245 |
btn = gr.Button('Generate Morph π')
|
246 |
out_vid = gr.Video(label='Morphed Video')
|
247 |
-
|
248 |
-
out_ref = gr.Image(label='
|
249 |
-
|
250 |
|
251 |
btn.click(
|
252 |
fn=process_video,
|
253 |
-
inputs=[vid,
|
254 |
-
outputs=[out_vid,
|
255 |
show_progress=True
|
256 |
)
|
257 |
-
gr.Markdown("---\n*
|
258 |
|
259 |
-
if __name__
|
260 |
iface.launch(debug=True)
|
261 |
|
262 |
|
263 |
-
|
|
|
24 |
|
25 |
|
26 |
|
27 |
+
def get_face_mask_box(img, feather, padding=0):
|
28 |
h, w = img.shape[:2]
|
29 |
mask = np.zeros((h, w), dtype=np.uint8)
|
30 |
results = face_mesh.process(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
|
|
|
34 |
hull = cv2.convexHull(pts)
|
35 |
cv2.fillConvexPoly(mask, hull, 255)
|
36 |
x, y, bw, bh = cv2.boundingRect(hull)
|
37 |
+
# apply padding
|
38 |
+
x_pad = max(x - padding, 0)
|
39 |
+
y_pad = max(y - padding, 0)
|
40 |
+
x2 = min(x + bw + padding, w)
|
41 |
+
y2 = min(y + bh + padding, h)
|
42 |
+
mask_roi = mask[y_pad:y2, x_pad:x2]
|
43 |
+
# inside feather
|
44 |
if feather > 0 and mask_roi.size > 0:
|
45 |
k = int(feather)
|
46 |
mask_roi = cv2.GaussianBlur(mask_roi, (k*2+1, k*2+1), 0)
|
47 |
+
return mask_roi, (x_pad, y_pad, x2 - x_pad, y2 - y_pad)
|
48 |
|
49 |
|
50 |
def cut_and_feather(img, feather):
|
|
|
166 |
return (out*255).astype(np.uint8)
|
167 |
|
168 |
|
169 |
+
def process_video(video_path, ref_img, trans, res, step, feather, padding):
|
170 |
cap = cv2.VideoCapture(video_path)
|
171 |
fps = cap.get(cv2.CAP_PROP_FPS) or 24
|
172 |
total = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
173 |
|
174 |
# Prepare masked reference
|
175 |
ref_bgr = cv2.cvtColor(ref_img, cv2.COLOR_RGB2BGR)
|
176 |
+
mask_ref, ref_box = get_face_mask_box(ref_bgr, feather, padding)
|
177 |
if mask_ref is None:
|
178 |
return None, None, None, None
|
179 |
x_r, y_r, w_r, h_r = ref_box
|
|
|
196 |
for i in range(total):
|
197 |
ret, frame = cap.read()
|
198 |
if not ret: break
|
199 |
+
mask_roi, box = get_face_mask_box(frame, feather, padding)
|
200 |
if mask_roi is None:
|
201 |
out_vid.write(frame)
|
202 |
continue
|
|
|
239 |
# --- Gradio App ---
|
240 |
css = """video, img { object-fit: contain !important; }"""
|
241 |
with gr.Blocks(css=css) as iface:
|
242 |
+
gr.Markdown("# Morph with Face-Shaped Composite and Padding")
|
243 |
with gr.Row():
|
244 |
vid = gr.Video(label='Input Video')
|
245 |
+
ref = gr.Image(type='numpy', label='Reference Image')
|
246 |
with gr.Row():
|
247 |
+
res = gr.Dropdown([256,384,512,768], value=512, label='Resolution')
|
248 |
+
step = gr.Slider(1,4,value=1,step=1,label='Landmark Sub-sampling')
|
249 |
+
feather = gr.Slider(0,50,value=10,step=1,label='Feather Radius')
|
250 |
+
padding = gr.Slider(0,100,value=10,step=1,label='Crop Padding (px)')
|
251 |
+
trans = gr.Slider(-1.0,1.0,value=0.0,step=0.05,label='Transition Level')
|
252 |
btn = gr.Button('Generate Morph π')
|
253 |
out_vid = gr.Video(label='Morphed Video')
|
254 |
+
out_crop = gr.Image(label='First Frame Crop')
|
255 |
+
out_ref = gr.Image(label='Masked Reference')
|
256 |
+
out_morph = gr.Image(label='Masked Morphed First Frame')
|
257 |
|
258 |
btn.click(
|
259 |
fn=process_video,
|
260 |
+
inputs=[vid,ref,trans,res,step,feather,padding],
|
261 |
+
outputs=[out_vid,out_crop,out_ref,out_morph],
|
262 |
show_progress=True
|
263 |
)
|
264 |
+
gr.Markdown("---\n*Added padding to the face crop for better framing.*")
|
265 |
|
266 |
+
if __name__=='__main__':
|
267 |
iface.launch(debug=True)
|
268 |
|
269 |
|
|