Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -4,90 +4,92 @@ import numpy as np
|
|
4 |
from PIL import Image
|
5 |
import mediapipe as mp
|
6 |
|
7 |
-
# MediaPipe
|
8 |
mp_face_mesh = mp.solutions.face_mesh
|
9 |
-
face_mesh = mp_face_mesh.FaceMesh(static_image_mode=True)
|
10 |
|
11 |
-
# Get facial landmarks
|
12 |
def get_landmarks(image):
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
|
|
23 |
return None
|
24 |
|
25 |
-
# Morph images based on landmarks and alpha
|
26 |
def morph_images(img1, img2, alpha):
|
27 |
lm1 = get_landmarks(img1)
|
28 |
lm2 = get_landmarks(img2)
|
29 |
|
30 |
if lm1 is None or lm2 is None:
|
31 |
-
return Image.fromarray(img1) # fallback
|
32 |
|
33 |
lm_avg = ((1 - alpha) * lm1 + alpha * lm2).astype(np.int32)
|
34 |
|
35 |
-
# Triangulation
|
36 |
rect = (0, 0, img1.shape[1], img1.shape[0])
|
37 |
subdiv = cv2.Subdiv2D(rect)
|
38 |
for p in lm_avg:
|
39 |
-
subdiv.insert((p[0], p[1]))
|
40 |
triangles = subdiv.getTriangleList().astype(np.int32)
|
41 |
|
42 |
-
def get_indices(
|
43 |
idxs = []
|
44 |
-
for
|
45 |
-
for i, pt in enumerate(
|
46 |
-
if
|
47 |
idxs.append(i)
|
|
|
48 |
return idxs if len(idxs) == 3 else None
|
49 |
|
50 |
-
morphed = np.zeros_like(img1)
|
51 |
for tri in triangles:
|
52 |
pts = [(tri[0], tri[1]), (tri[2], tri[3]), (tri[4], tri[5])]
|
53 |
idxs = get_indices(pts, lm_avg.tolist())
|
54 |
-
if idxs is None:
|
|
|
55 |
|
56 |
t1 = np.float32([lm1[i] for i in idxs])
|
57 |
t2 = np.float32([lm2[i] for i in idxs])
|
58 |
t = np.float32([lm_avg[i] for i in idxs])
|
59 |
|
60 |
def warp_triangle(src, t_src, t_dst):
|
61 |
-
|
62 |
-
|
63 |
|
64 |
-
t_src_offset = np.array([[pt[0]-
|
65 |
-
t_dst_offset = np.array([[pt[0]-
|
66 |
|
67 |
-
mask = np.zeros((
|
68 |
cv2.fillConvexPoly(mask, np.int32(t_dst_offset), (1.0, 1.0, 1.0), 16, 0)
|
69 |
|
70 |
-
src_crop = src[
|
71 |
warp_mat = cv2.getAffineTransform(t_src_offset, t_dst_offset)
|
72 |
-
dst_crop = cv2.warpAffine(src_crop, warp_mat, (
|
73 |
-
|
74 |
-
morphed[
|
|
|
75 |
|
76 |
warp_triangle(img1, t1, t)
|
77 |
warp_triangle(img2, t2, t)
|
78 |
|
79 |
-
return Image.fromarray(np.uint8(morphed))
|
80 |
|
81 |
def process(mm_image, aa_image, ee_image, oo_image, ww_image, na_image, slider_aa, slider_oo, slider_ee, slider_ww, slider_na):
|
82 |
-
def
|
|
|
|
|
|
|
|
|
83 |
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
target = load(phoneme)
|
88 |
-
img = np.array(morph_images(img, target, s))
|
89 |
|
90 |
-
return Image.fromarray(cv2.cvtColor(
|
91 |
|
92 |
iface = gr.Interface(
|
93 |
fn=process,
|
|
|
4 |
from PIL import Image
|
5 |
import mediapipe as mp
|
6 |
|
7 |
+
# Setup MediaPipe
|
8 |
mp_face_mesh = mp.solutions.face_mesh
|
|
|
9 |
|
|
|
10 |
def get_landmarks(image):
|
11 |
+
with mp_face_mesh.FaceMesh(static_image_mode=True) as face_mesh:
|
12 |
+
rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
13 |
+
results = face_mesh.process(rgb)
|
14 |
+
h, w, _ = image.shape
|
15 |
+
|
16 |
+
if results.multi_face_landmarks:
|
17 |
+
landmarks = []
|
18 |
+
for pt in results.multi_face_landmarks[0].landmark:
|
19 |
+
x, y = int(pt.x * w), int(pt.y * h)
|
20 |
+
landmarks.append((x, y))
|
21 |
+
return np.array(landmarks, dtype=np.int32)
|
22 |
return None
|
23 |
|
|
|
24 |
def morph_images(img1, img2, alpha):
|
25 |
lm1 = get_landmarks(img1)
|
26 |
lm2 = get_landmarks(img2)
|
27 |
|
28 |
if lm1 is None or lm2 is None:
|
29 |
+
return Image.fromarray(cv2.cvtColor(img1, cv2.COLOR_BGR2RGB)) # fallback
|
30 |
|
31 |
lm_avg = ((1 - alpha) * lm1 + alpha * lm2).astype(np.int32)
|
32 |
|
|
|
33 |
rect = (0, 0, img1.shape[1], img1.shape[0])
|
34 |
subdiv = cv2.Subdiv2D(rect)
|
35 |
for p in lm_avg:
|
36 |
+
subdiv.insert((float(p[0]), float(p[1])))
|
37 |
triangles = subdiv.getTriangleList().astype(np.int32)
|
38 |
|
39 |
+
def get_indices(tri_pts, ref_pts):
|
40 |
idxs = []
|
41 |
+
for tp in tri_pts:
|
42 |
+
for i, pt in enumerate(ref_pts):
|
43 |
+
if abs(tp[0] - pt[0]) < 2 and abs(tp[1] - pt[1]) < 2:
|
44 |
idxs.append(i)
|
45 |
+
break
|
46 |
return idxs if len(idxs) == 3 else None
|
47 |
|
48 |
+
morphed = np.zeros_like(img1, dtype=np.float32)
|
49 |
for tri in triangles:
|
50 |
pts = [(tri[0], tri[1]), (tri[2], tri[3]), (tri[4], tri[5])]
|
51 |
idxs = get_indices(pts, lm_avg.tolist())
|
52 |
+
if idxs is None:
|
53 |
+
continue
|
54 |
|
55 |
t1 = np.float32([lm1[i] for i in idxs])
|
56 |
t2 = np.float32([lm2[i] for i in idxs])
|
57 |
t = np.float32([lm_avg[i] for i in idxs])
|
58 |
|
59 |
def warp_triangle(src, t_src, t_dst):
|
60 |
+
r_src = cv2.boundingRect(t_src)
|
61 |
+
r_dst = cv2.boundingRect(t_dst)
|
62 |
|
63 |
+
t_src_offset = np.array([[pt[0] - r_src[0], pt[1] - r_src[1]] for pt in t_src], np.float32)
|
64 |
+
t_dst_offset = np.array([[pt[0] - r_dst[0], pt[1] - r_dst[1]] for pt in t_dst], np.float32)
|
65 |
|
66 |
+
mask = np.zeros((r_dst[3], r_dst[2], 3), dtype=np.float32)
|
67 |
cv2.fillConvexPoly(mask, np.int32(t_dst_offset), (1.0, 1.0, 1.0), 16, 0)
|
68 |
|
69 |
+
src_crop = src[r_src[1]:r_src[1]+r_src[3], r_src[0]:r_src[0]+r_src[2]]
|
70 |
warp_mat = cv2.getAffineTransform(t_src_offset, t_dst_offset)
|
71 |
+
dst_crop = cv2.warpAffine(src_crop, warp_mat, (r_dst[2], r_dst[3]), flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101)
|
72 |
+
|
73 |
+
morphed[r_dst[1]:r_dst[1]+r_dst[3], r_dst[0]:r_dst[0]+r_dst[2]] *= (1 - mask)
|
74 |
+
morphed[r_dst[1]:r_dst[1]+r_dst[3], r_dst[0]:r_dst[0]+r_dst[2]] += dst_crop * mask
|
75 |
|
76 |
warp_triangle(img1, t1, t)
|
77 |
warp_triangle(img2, t2, t)
|
78 |
|
79 |
+
return Image.fromarray(cv2.cvtColor(np.uint8(morphed), cv2.COLOR_BGR2RGB))
|
80 |
|
81 |
def process(mm_image, aa_image, ee_image, oo_image, ww_image, na_image, slider_aa, slider_oo, slider_ee, slider_ww, slider_na):
|
82 |
+
def to_bgr(img): return cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
|
83 |
+
|
84 |
+
base = to_bgr(mm_image)
|
85 |
+
morph_order = [(slider_aa, aa_image), (slider_oo, oo_image), (slider_ee, ee_image),
|
86 |
+
(slider_ww, ww_image), (slider_na, na_image)]
|
87 |
|
88 |
+
for strength, image in morph_order:
|
89 |
+
if strength > 0:
|
90 |
+
base = cv2.cvtColor(np.array(morph_images(base, to_bgr(image), strength)), cv2.COLOR_RGB2BGR)
|
|
|
|
|
91 |
|
92 |
+
return Image.fromarray(cv2.cvtColor(base, cv2.COLOR_BGR2RGB))
|
93 |
|
94 |
iface = gr.Interface(
|
95 |
fn=process,
|