NihalGazi commited on
Commit
a030e65
·
verified ·
1 Parent(s): a34d1ee

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +96 -129
app.py CHANGED
@@ -1,143 +1,110 @@
1
- import os
2
- os.system("pip install cmake")
3
- os.system("pip install dlib opencv-python numpy Pillow gradio")
4
-
5
  import gradio as gr
6
  import cv2
7
  import numpy as np
8
  from PIL import Image
9
- import dlib
10
-
11
- # --- Load models ---
12
- detector = dlib.get_frontal_face_detector()
13
- predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat") # Requires download
14
-
15
- # --- Helpers ---
16
- def load_image(file):
17
- img = cv2.imread(file.name)
18
- if img is None:
19
- raise ValueError(f"Failed to load image: {file.name}")
20
- return img
21
-
22
- def get_landmarks(img):
23
- gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
24
- faces = detector(gray)
25
- if len(faces) == 0:
26
- raise Exception("No face detected.")
27
- shape = predictor(gray, faces[0])
28
- return np.array([[p.x, p.y] for p in shape.parts()], np.int32)
29
-
30
- def apply_affine_transform(src, src_tri, dst_tri, size):
31
- warp_mat = cv2.getAffineTransform(np.float32(src_tri), np.float32(dst_tri))
32
- dst = cv2.warpAffine(src, warp_mat, (size[0], size[1]), None, flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101)
33
- return dst
34
-
35
- def morph_triangle(img1, img2, img, t1, t2, t, alpha):
36
- r1 = cv2.boundingRect(np.float32([t1]))
37
- r2 = cv2.boundingRect(np.float32([t2]))
38
- r = cv2.boundingRect(np.float32([t]))
39
-
40
- t1_rect = []
41
- t2_rect = []
42
- t_rect = []
43
-
44
- for i in range(3):
45
- t1_rect.append(((t1[i][0] - r1[0]),(t1[i][1] - r1[1])))
46
- t2_rect.append(((t2[i][0] - r2[0]),(t2[i][1] - r2[1])))
47
- t_rect.append(((t[i][0] - r[0]),(t[i][1] - r[1])))
48
-
49
- img1_rect = img1[r1[1]:r1[1]+r1[3], r1[0]:r1[0]+r1[2]]
50
- img2_rect = img2[r2[1]:r2[1]+r2[3], r2[0]:r2[0]+r2[2]]
51
-
52
- warp_img1 = apply_affine_transform(img1_rect, t1_rect, t_rect, (r[2], r[3]))
53
- warp_img2 = apply_affine_transform(img2_rect, t2_rect, t_rect, (r[2], r[3]))
54
-
55
- img_rect = (1.0 - alpha) * warp_img1 + alpha * warp_img2
56
-
57
- mask = np.zeros((r[3], r[2], 3), dtype=np.float32)
58
- cv2.fillConvexPoly(mask, np.int32(t_rect), (1.0, 1.0, 1.0), 16, 0)
59
- img[r[1]:r[1]+r[3], r[0]:r[0]+r[2]] = img[r[1]:r[1]+r[3], r[0]:r[0]+r[2]] * (1 - mask) + img_rect * mask
60
-
61
- def delaunay_triangulation(points, w, h):
62
- subdiv = cv2.Subdiv2D((0, 0, w, h))
63
- for p in points:
64
  subdiv.insert((p[0], p[1]))
65
- triangle_list = subdiv.getTriangleList()
66
- triangles = []
67
- for t in triangle_list:
68
- pts = [(int(t[0]), int(t[1])), (int(t[2]), int(t[3])), (int(t[4]), int(t[5]))]
69
- idx = []
70
- for pt in pts:
71
- for i, p in enumerate(points):
72
- if np.linalg.norm(np.array(pt) - p) < 1.0:
73
- idx.append(i)
74
- if len(idx) == 3:
75
- triangles.append(tuple(idx))
76
- return triangles
77
-
78
- def morph_faces(img1, img2, alpha=0.5):
79
- img1 = np.float32(img1)
80
- img2 = np.float32(img2)
81
- points1 = get_landmarks(img1)
82
- points2 = get_landmarks(img2)
83
- points = []
84
- for i in range(len(points1)):
85
- x = (1 - alpha) * points1[i][0] + alpha * points2[i][0]
86
- y = (1 - alpha) * points1[i][1] + alpha * points2[i][1]
87
- points.append((int(x), int(y)))
88
-
89
- morphed = np.zeros(img1.shape, dtype=img1.dtype)
90
- tri = delaunay_triangulation(points, img1.shape[1], img1.shape[0])
91
-
92
- for t in tri:
93
- t1 = [points1[t[0]], points1[t[1]], points1[t[2]]]
94
- t2 = [points2[t[0]], points2[t[1]], points2[t[2]]]
95
- t_ = [points[t[0]], points[t[1]], points[t[2]]]
96
- morph_triangle(img1, img2, morphed, t1, t2, t_, alpha)
 
 
 
 
 
 
97
 
98
  return Image.fromarray(np.uint8(morphed))
99
 
100
- # --- Gradio Function ---
101
- def lipsync_interface(mm, aa, ee, oo, ww, na, s_aa, s_oo, s_ee, s_ww, s_na):
102
- mm_img = load_image(mm)
103
- result = mm_img.copy()
104
-
105
- if s_aa > 0.0:
106
- aa_img = load_image(aa)
107
- result = np.array(morph_faces(result, aa_img, s_aa))
108
- if s_oo > 0.0:
109
- oo_img = load_image(oo)
110
- result = np.array(morph_faces(result, oo_img, s_oo))
111
- if s_ee > 0.0:
112
- ee_img = load_image(ee)
113
- result = np.array(morph_faces(result, ee_img, s_ee))
114
- if s_ww > 0.0:
115
- ww_img = load_image(ww)
116
- result = np.array(morph_faces(result, ww_img, s_ww))
117
- if s_na > 0.0:
118
- na_img = load_image(na)
119
- result = np.array(morph_faces(result, na_img, s_na))
120
-
121
- return Image.fromarray(result)
122
-
123
- # --- Gradio UI ---
124
  iface = gr.Interface(
125
- fn=lipsync_interface,
126
  inputs=[
127
- gr.Image(label="MM Image (Neutral)", type="file"),
128
- gr.Image(label="AA Image", type="file"),
129
- gr.Image(label="EE Image", type="file"),
130
- gr.Image(label="OO Image", type="file"),
131
- gr.Image(label="WW Image", type="file"),
132
- gr.Image(label="NA Image", type="file"),
133
-
134
- gr.Slider(0.0, 1.0, step=0.05, label="Morph Strength AA"),
135
- gr.Slider(0.0, 1.0, step=0.05, label="Morph Strength OO"),
136
- gr.Slider(0.0, 1.0, step=0.05, label="Morph Strength EE"),
137
- gr.Slider(0.0, 1.0, step=0.05, label="Morph Strength WW"),
138
- gr.Slider(0.0, 1.0, step=0.05, label="Morph Strength NA"),
139
  ],
140
- outputs=gr.Image(label="Interpolated Lip-sync Frame"),
141
  live=True
142
  )
143
 
 
 
 
 
 
1
  import gradio as gr
2
  import cv2
3
  import numpy as np
4
  from PIL import Image
5
+ import mediapipe as mp
6
+
7
+ # MediaPipe face mesh setup
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
+ rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
14
+ results = face_mesh.process(rgb)
15
+ h, w, _ = image.shape
16
+
17
+ if results.multi_face_landmarks:
18
+ landmarks = []
19
+ for pt in results.multi_face_landmarks[0].landmark:
20
+ x, y = int(pt.x * w), int(pt.y * h)
21
+ landmarks.append((x, y))
22
+ return np.array(landmarks, dtype=np.int32)
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(tri, points):
43
+ idxs = []
44
+ for p in tri:
45
+ for i, pt in enumerate(points):
46
+ if np.linalg.norm(np.array(p) - np.array(pt)) < 1.0:
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: continue
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
+ rect_src = cv2.boundingRect(t_src)
62
+ rect_dst = cv2.boundingRect(t_dst)
63
+
64
+ t_src_offset = np.array([[pt[0]-rect_src[0], pt[1]-rect_src[1]] for pt in t_src], np.float32)
65
+ t_dst_offset = np.array([[pt[0]-rect_dst[0], pt[1]-rect_dst[1]] for pt in t_dst], np.float32)
66
+
67
+ mask = np.zeros((rect_dst[3], rect_dst[2], 3), dtype=np.float32)
68
+ cv2.fillConvexPoly(mask, np.int32(t_dst_offset), (1.0, 1.0, 1.0), 16, 0)
69
+
70
+ src_crop = src[rect_src[1]:rect_src[1]+rect_src[3], rect_src[0]:rect_src[0]+rect_src[2]]
71
+ warp_mat = cv2.getAffineTransform(t_src_offset, t_dst_offset)
72
+ dst_crop = cv2.warpAffine(src_crop, warp_mat, (rect_dst[2], rect_dst[3]), flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101)
73
+ morphed[rect_dst[1]:rect_dst[1]+rect_dst[3], rect_dst[0]:rect_dst[0]+rect_dst[2]] *= (1 - mask)
74
+ morphed[rect_dst[1]:rect_dst[1]+rect_dst[3], rect_dst[0]:rect_dst[0]+rect_dst[2]] += dst_crop * mask
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 load(img): return cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
83
+
84
+ img = load(mm_image)
85
+ for s, phoneme in [(slider_aa, aa_image), (slider_oo, oo_image), (slider_ee, ee_image), (slider_ww, ww_image), (slider_na, na_image)]:
86
+ if s > 0.0:
87
+ target = load(phoneme)
88
+ img = np.array(morph_images(img, target, s))
89
+
90
+ return Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
91
+
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  iface = gr.Interface(
93
+ fn=process,
94
  inputs=[
95
+ gr.Image(label="MM Image (Neutral)"),
96
+ gr.Image(label="AA Image"),
97
+ gr.Image(label="EE Image"),
98
+ gr.Image(label="OO Image"),
99
+ gr.Image(label="WW Image"),
100
+ gr.Image(label="NA Image"),
101
+ gr.Slider(0, 1, 0.05, label="Strength AA"),
102
+ gr.Slider(0, 1, 0.05, label="Strength OO"),
103
+ gr.Slider(0, 1, 0.05, label="Strength EE"),
104
+ gr.Slider(0, 1, 0.05, label="Strength WW"),
105
+ gr.Slider(0, 1, 0.05, label="Strength NA"),
 
106
  ],
107
+ outputs=gr.Image(label="Lipsynced Output"),
108
  live=True
109
  )
110