victorisgeek commited on
Commit
8f08d8b
·
verified ·
1 Parent(s): 634de14

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.py +237 -0
  2. inswapper_128.onnx +3 -0
  3. requirements.txt +5 -0
app.py ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import cv2
3
+ import os
4
+ import insightface
5
+ from insightface.app import FaceAnalysis
6
+ from insightface.data import get_image as ins_get_image
7
+
8
+ import gradio as gr
9
+
10
+ theme = gr.themes.Default(
11
+ font=['Helvetica', 'ui-sans-serif', 'system-ui', 'sans-serif'],
12
+ font_mono=['IBM Plex Mono', 'ui-monospace', 'Consolas', 'monospace'],
13
+ ).set(
14
+ border_color_primary='#c5c5d2',
15
+ button_large_padding='6px 12px',
16
+ body_text_color_subdued='#484848',
17
+ background_fill_secondary='#eaeaea'
18
+ )
19
+
20
+ def add_bbox_padding(bbox, margin=5):
21
+ return [
22
+ bbox[0] - margin,
23
+ bbox[1] - margin,
24
+ bbox[2] + margin,
25
+ bbox[3] + margin]
26
+
27
+
28
+ def select_handler(img, evt: gr.SelectData):
29
+ faces = app.get(img)
30
+ faces = sorted(faces, key = lambda x : x.bbox[0])
31
+ cropped_image = []
32
+ face_index = -1
33
+ sel_face_index = 0
34
+ print("Coords: ", evt.index[0],evt.index[1])
35
+ for face in faces:
36
+ box = face.bbox.astype(np.int32)
37
+ face_index = face_index + 1
38
+ if point_in_box((box[0], box[1]),(box[2],box[3]),(evt.index[0],evt.index[1])) == True:
39
+ # print("True ", face_index)
40
+ # print("Bbox org: ", box)
41
+ # Add ~25% margin to the box so the face is recognized correctly
42
+ margin = int((box[2]-box[0]) * 0.35)
43
+ box = add_bbox_padding(box,margin)
44
+ box = np.clip(box,0,None)
45
+ print("Bbox exp: ", box)
46
+ sel_face_index = face_index
47
+ cropped_image = img[box[1]:box[3],box[0]:box[2]]
48
+ return cropped_image, sel_face_index
49
+
50
+ def point_in_box(bl, tr, p) :
51
+ if (p[0] > bl[0] and p[0] < tr[0] and p[1] > bl[1] and p[1] < tr[1]) :
52
+ return True
53
+ else:
54
+ return False
55
+
56
+ def get_faces(img):
57
+ faces = app.get(img)
58
+ faces = sorted(faces, key = lambda x : x.bbox[0])
59
+ #boxed_faces = app.draw_on(img, faces)
60
+ #for i in range(len(faces)):
61
+ # face = faces[i]
62
+ # box = face.bbox.astype(np.int32)
63
+ # cv2.putText(boxed_faces,'Face#:%d'%(i), (box[0]-1, box[3]+14),cv2.FONT_HERSHEY_COMPLEX,0.7,(0,0,255),2)
64
+
65
+ return img, len(faces)
66
+
67
+ def swap_face_fct(img_source,face_index,img_swap_face):
68
+ faces = app.get(img_source)
69
+ faces = sorted(faces, key = lambda x : x.bbox[0])
70
+ src_face = app.get(img_swap_face)
71
+ src_face = sorted(src_face, key = lambda x : x.bbox[0])
72
+ #print("index:",faces)
73
+ res = swapper.get(img_source, faces[face_index], src_face[0], paste_back=True)
74
+ return res
75
+
76
+ def swap_video_fct(video_path, output_path, source_face, destination_face, tolerance, preview=-1, progress=gr.Progress()):
77
+
78
+ # Get the Destination Face parameters (the face which should be swapped)
79
+ dest_face = app.get(destination_face)
80
+ dest_face = sorted(dest_face, key = lambda x : x.bbox[0])
81
+
82
+ if(len(dest_face) == 0):
83
+ print("No dest face found")
84
+ return -1
85
+
86
+ dest_face_feats = []
87
+ dest_face_feats.append(dest_face[0].normed_embedding)
88
+ dest_face_feats = np.array(dest_face_feats, dtype=np.float32)
89
+
90
+ # Get the source face parameters (the face that replaces the original)
91
+ src_face = app.get(source_face)
92
+ src_face = sorted(src_face, key = lambda x : x.bbox[0])
93
+ if(len(src_face) == 0):
94
+ print("No source face found")
95
+ return -1
96
+
97
+ cap = cv2.VideoCapture(video_path)
98
+ ret, frame = cap.read()
99
+ fps = int(cap.get(cv2.CAP_PROP_FPS))
100
+ frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
101
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
102
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
103
+ fourcc = cv2.VideoWriter_fourcc(*'avc1')
104
+
105
+ # Use the same tmp dir from gradio if no output path is set
106
+ if(len(output_path) > 0):
107
+ out_path = output_path
108
+ else:
109
+ out_path = os.path.dirname(video_path) + "/out.mp4"
110
+
111
+ if preview == -1:
112
+ for_range = range(frame_count)
113
+ video_out = cv2.VideoWriter(out_path,fourcc,fps,(width,height))
114
+ else:
115
+ for_range = range(preview-1,preview)
116
+
117
+ for i in for_range:
118
+ progress(i/frame_count, desc="Processing")
119
+ cap.set(cv2.CAP_PROP_POS_FRAMES, i)
120
+ ret, frame = cap.read()
121
+ #frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
122
+
123
+ # Find all faces in the current frame
124
+ faces = app.get(frame)
125
+ faces = sorted(faces, key = lambda x : x.bbox[0])
126
+ # No face in Scene => copy input frame
127
+
128
+ if(len(faces) > 0):
129
+ feats = []
130
+ for face in faces:
131
+ feats.append(face.normed_embedding)
132
+ feats = np.array(feats, dtype=np.float32)
133
+ sims = np.dot(dest_face_feats, feats.T)
134
+ print(sims)
135
+ # find the index of the most similar face
136
+ max_index = np.argmax(sims)
137
+ print("Sim:", max_index)
138
+ if(sims[0][max_index]*100 >= (100-tolerance)):
139
+ frame = swapper.get(frame, faces[max_index], src_face[0], paste_back=True)
140
+ if preview == -1:
141
+ video_out.write(frame)
142
+ if preview == -1:
143
+ video_out.release()
144
+ return out_path
145
+ else:
146
+ return cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
147
+ ins_get_image
148
+
149
+ def analyze_video(video_path):
150
+ cap = cv2.VideoCapture(video_path)
151
+ fps = int(cap.get(cv2.CAP_PROP_FPS))
152
+ frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
153
+ length = frame_count/fps
154
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
155
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
156
+ return f"Resolution: {width}x{height}\nLength: {length}\nFps: {fps}\nFrames: {frame_count}"
157
+
158
+ def update_slider(video_path):
159
+ cap = cv2.VideoCapture(video_path)
160
+ fps = int(cap.get(cv2.CAP_PROP_FPS))
161
+ frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
162
+ length = frame_count/fps
163
+ return gr.update(minimum=0,maximum=frame_count,value=frame_count/2)
164
+
165
+ def show_preview(video_path, frame_number):
166
+ cap = cv2.VideoCapture(video_path)
167
+ cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
168
+ ret, frame = cap.read()
169
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
170
+ return frame
171
+
172
+ def create_interface():
173
+ title = 'Face Swap UI'
174
+ with gr.Blocks(analytics_enabled=False, title=title) as face_swap_ui:
175
+ with gr.Tab("Swap Face Image"):
176
+ with gr.Row():
177
+ with gr.Column():
178
+ image_input = gr.Image(label='Input Image (Click to select a face)').style(height=400)
179
+ with gr.Row():
180
+ analyze_button = gr.Button("Analyze")
181
+ with gr.Row():
182
+ with gr.Column():
183
+ face_num = gr.Number(label='Recognized Faces')
184
+ face_index_num = gr.Number(label='Face Index', precision=0)
185
+ selected_face = gr.Image(label='Face to swap', interactive=False)
186
+ swap_face = gr.Image(label='Swap Face')
187
+ swap_button = gr.Button("Swap")
188
+ with gr.Column():
189
+ image_output = gr.Image(label='Output Image',interactive=False)
190
+ #text_output = gr.Textbox(placeholder="What is your name?")
191
+ swap_button.click(fn=swap_face_fct, inputs=[image_input, face_index_num, swap_face], outputs=[image_output])
192
+ image_input.select(select_handler, image_input, [selected_face, face_index_num])
193
+ analyze_button.click(fn=get_faces, inputs=image_input, outputs=[image_input,face_num])
194
+ with gr.Tab("Swap Face Video"):
195
+ with gr.Row():
196
+ with gr.Column():
197
+ source_video = gr.Video()
198
+ video_info = gr.Textbox(label="Video Information")
199
+ gr.Markdown("Select a frame for preview with the slider. Then select the face which should be swapped by clicking on it with the cursor")
200
+ video_position = gr.Slider(label="Frame preview",interactive=True)
201
+ frame_preview = gr.Image(label="Frame preview")
202
+ face_index = gr.Textbox(label="Face-Index",interactive=False)
203
+ with gr.Row():
204
+ dest_face_vid = gr.Image(Label="Face tow swap",interactive=True)
205
+ source_face_vid = gr.Image(Label="New Face")
206
+ gr.Markdown("The higher the tolerance the more likely a wrong face will be swapped. 30-40 is a good starting point.")
207
+ face_tolerance = gr.Slider(label="Tolerance",value=40,interactive=True)
208
+ preview_video = gr.Button("Preview")
209
+ video_file_path = gr.Text(label="Output Video path incl. file.mp4 (when left empty it will be put in the gradio temp dir)")
210
+ process_video = gr.Button("Process")
211
+ with gr.Column():
212
+ with gr.Column(scale=1):
213
+ image_output = gr.Image()
214
+ output_video = gr.Video(interactive=False)
215
+ with gr.Column(scale=1):
216
+ pass
217
+ # Component Events
218
+ source_video.upload(fn=analyze_video,inputs=source_video,outputs=video_info)
219
+ video_info.change(fn=update_slider,inputs=source_video,outputs=video_position)
220
+ #preview_button.click(fn=show_preview,inputs=[source_video, video_position],outputs=frame_preview)
221
+ frame_preview.select(select_handler, frame_preview, [dest_face_vid, face_index ])
222
+ video_position.change(show_preview,inputs=[source_video, video_position],outputs=frame_preview)
223
+ process_video.click(fn=swap_video_fct,inputs=[source_video,video_file_path,source_face_vid,dest_face_vid, face_tolerance], outputs=output_video)
224
+ preview_video.click(fn=swap_video_fct,inputs=[source_video,video_file_path,source_face_vid,dest_face_vid, face_tolerance, video_position], outputs=image_output)
225
+
226
+ face_swap_ui.queue().launch()
227
+ #face_swap_ui.launch()
228
+
229
+
230
+
231
+ if __name__ == "__main__":
232
+
233
+ app = FaceAnalysis(name='buffalo_l')
234
+ app.prepare(ctx_id=0, det_size=(640, 640))
235
+ swapper = insightface.model_zoo.get_model('inswapper_128.onnx', download=True, download_zip=True)
236
+
237
+ create_interface()
inswapper_128.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e4a3f08c753cb72d04e10aa0f7dbe3deebbf39567d4ead6dce08e98aa49e16af
3
+ size 554253681
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ gradio
2
+ insightface
3
+ numpy
4
+ onnxruntime
5
+ opencv-python