Arrcttacsrks commited on
Commit
0cffa18
·
verified ·
1 Parent(s): 226a763

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +200 -85
app.py CHANGED
@@ -19,36 +19,35 @@ from datasets import load_dataset, Dataset
19
  import json
20
  import shutil
21
  from dotenv import load_dotenv
 
 
22
 
23
  # Load environment variables
24
  load_dotenv()
25
 
 
 
 
 
 
26
  class FaceIntegrDataset:
27
  def __init__(self, repo_id="Arrcttacsrks/face_integrData"):
28
- # Get token from environment variable
29
  self.token = os.getenv('hf_token')
30
  if not self.token:
31
  raise ValueError("HF_TOKEN environment variable is not set")
32
-
33
  self.repo_id = repo_id
34
  self.api = HfApi()
35
-
36
- # Login to Hugging Face
37
  login(self.token)
38
-
39
- # Create local temp directory for organizing files
40
  self.temp_dir = "temp_dataset"
41
  os.makedirs(self.temp_dir, exist_ok=True)
42
 
43
  def create_date_folder(self):
44
- """Create folder structure based on current date"""
45
  current_date = datetime.now().strftime("%Y-%m-%d")
46
  folder_path = os.path.join(self.temp_dir, current_date)
47
  os.makedirs(folder_path, exist_ok=True)
48
  return folder_path, current_date
49
 
50
  def save_metadata(self, source_path, target_path, output_path, timestamp):
51
- """Save metadata for the face swap operation"""
52
  metadata = {
53
  "timestamp": timestamp,
54
  "source_image": source_path,
@@ -59,9 +58,7 @@ class FaceIntegrDataset:
59
  return metadata
60
 
61
  def upload_to_hf(self, local_folder, date_folder):
62
- """Upload files to Hugging Face dataset"""
63
  try:
64
- # Upload the files
65
  self.api.upload_folder(
66
  folder_path=local_folder,
67
  repo_id=self.repo_id,
@@ -73,36 +70,26 @@ class FaceIntegrDataset:
73
  print(f"Error uploading to Hugging Face: {str(e)}")
74
  return False
75
 
 
76
  def swap_face(source_file, target_file, doFaceEnhancer):
77
  folder_path = None
78
  try:
79
- # Initialize dataset handler
80
  dataset_handler = FaceIntegrDataset()
81
-
82
- # Create date-based folder
83
  folder_path, date_folder = dataset_handler.create_date_folder()
84
-
85
- # Generate timestamp for unique identification
86
  timestamp = datetime.now().strftime("%S-%M-%H-%d-%m-%Y")
87
-
88
- # Save input images with timestamp in folder
89
  source_path = os.path.join(folder_path, f"source_{timestamp}.jpg")
90
  target_path = os.path.join(folder_path, f"target_{timestamp}.jpg")
91
  output_path = os.path.join(folder_path, f"OutputImage{timestamp}.jpg")
92
-
93
- # Save the input images
94
  if source_file is None or target_file is None:
95
  raise ValueError("Source and target images are required")
96
 
97
- source_image = Image.fromarray(source_file)
98
- source_image.save(source_path)
99
- target_image = Image.fromarray(target_file)
100
- target_image.save(target_path)
101
 
102
  print("source_path: ", source_path)
103
  print("target_path: ", target_path)
104
 
105
- # Set global paths
106
  roop.globals.source_path = source_path
107
  roop.globals.target_path = target_path
108
  roop.globals.output_path = normalize_output_path(
@@ -111,13 +98,11 @@ def swap_face(source_file, target_file, doFaceEnhancer):
111
  output_path
112
  )
113
 
114
- # Configure face processing options
115
  if doFaceEnhancer:
116
  roop.globals.frame_processors = ["face_swapper", "face_enhancer"]
117
  else:
118
  roop.globals.frame_processors = ["face_swapper"]
119
 
120
- # Set global parameters
121
  roop.globals.headless = True
122
  roop.globals.keep_fps = True
123
  roop.globals.keep_audio = True
@@ -136,15 +121,12 @@ def swap_face(source_file, target_file, doFaceEnhancer):
136
  roop.globals.output_path,
137
  )
138
 
139
- # Check frame processors
140
  for frame_processor in get_frame_processors_modules(roop.globals.frame_processors):
141
  if not frame_processor.pre_check():
142
  return None
143
 
144
- # Process the face swap
145
  start()
146
 
147
- # Save metadata
148
  metadata = dataset_handler.save_metadata(
149
  f"source_{timestamp}.jpg",
150
  f"target_{timestamp}.jpg",
@@ -152,12 +134,10 @@ def swap_face(source_file, target_file, doFaceEnhancer):
152
  timestamp
153
  )
154
 
155
- # Save metadata to JSON file in the same folder
156
  metadata_path = os.path.join(folder_path, f"metadata_{timestamp}.json")
157
  with open(metadata_path, 'w') as f:
158
  json.dump(metadata, f, indent=4)
159
 
160
- # Upload to Hugging Face
161
  upload_success = dataset_handler.upload_to_hf(folder_path, date_folder)
162
 
163
  if upload_success:
@@ -165,11 +145,9 @@ def swap_face(source_file, target_file, doFaceEnhancer):
165
  else:
166
  print("Failed to upload files to Hugging Face dataset")
167
 
168
- # Read the output image before cleaning up
169
  if os.path.exists(output_path):
170
  output_image = Image.open(output_path)
171
  output_array = np.array(output_image)
172
- # Clean up temp folder after reading the image
173
  shutil.rmtree(folder_path)
174
  return output_array
175
  else:
@@ -184,8 +162,113 @@ def swap_face(source_file, target_file, doFaceEnhancer):
184
  shutil.rmtree(folder_path)
185
  raise gr.Error(f"Face swap failed: {str(e)}")
186
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  def create_interface():
188
- # Create custom style
189
  custom_css = """
190
  .container {
191
  max-width: 1200px;
@@ -199,11 +282,9 @@ def create_interface():
199
  padding: 10px;
200
  }
201
  """
202
-
203
- # Gradio interface setup
204
  title = "Face - Integrator"
205
  description = r"""
206
- Please upload source and target images to begin the face swap process.
207
  """
208
  article = r"""
209
  <div style="text-align: center; max-width: 650px; margin: 40px auto;">
@@ -212,65 +293,99 @@ def create_interface():
212
  </p>
213
  </div>
214
  """
215
-
216
- # Create Gradio interface with improved layout
217
  with gr.Blocks(title=title, css=custom_css) as app:
218
  gr.Markdown(f"<h1 style='text-align: center;'>{title}</h1>")
219
  gr.Markdown(description)
220
-
221
- with gr.Row():
222
- with gr.Column(scale=1):
223
- source_image = gr.Image(
224
- label="Source Image",
225
- type="numpy",
226
- sources=["upload"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
  )
228
-
229
- with gr.Column(scale=1):
230
- target_image = gr.Image(
231
- label="Target Image",
232
- type="numpy",
233
- sources=["upload"]
 
 
 
 
 
 
 
 
 
 
234
  )
235
-
236
- with gr.Column(scale=1):
237
- output_image = gr.Image(
238
- label="Output Image",
239
- type="numpy",
240
- interactive=False,
241
- elem_classes="output-image"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  )
243
-
244
- with gr.Row():
245
- enhance_checkbox = gr.Checkbox(
246
- label="Apply the algorithm?",
247
- info="Image Quality Improvement",
248
- value=False
249
- )
250
-
251
- with gr.Row():
252
- process_btn = gr.Button(
253
- "Process Face Swap",
254
- variant="primary",
255
- size="lg"
256
- )
257
-
258
- # Set up the processing event
259
- process_btn.click(
260
- fn=swap_face,
261
- inputs=[source_image, target_image, enhance_checkbox],
262
- outputs=output_image,
263
- api_name="swap_face"
264
- )
265
-
266
  gr.Markdown(article)
267
-
268
  return app
269
 
270
  def main():
271
- # Create and launch the interface
272
  app = create_interface()
273
  app.launch(share=False)
274
 
275
  if __name__ == "__main__":
276
- main()
 
19
  import json
20
  import shutil
21
  from dotenv import load_dotenv
22
+ import cv2
23
+ from insightface.app import FaceAnalysis
24
 
25
  # Load environment variables
26
  load_dotenv()
27
 
28
+ # Hàm tính cosine similarity để mày so sánh "điểm tương đồng" của khuôn mặt
29
+ def cosine_similarity(a, b):
30
+ return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b) + 1e-6)
31
+
32
+ # Class FaceIntegrDataset nguyên bản (cho image swap, không cần "xịn" cho video)
33
  class FaceIntegrDataset:
34
  def __init__(self, repo_id="Arrcttacsrks/face_integrData"):
 
35
  self.token = os.getenv('hf_token')
36
  if not self.token:
37
  raise ValueError("HF_TOKEN environment variable is not set")
 
38
  self.repo_id = repo_id
39
  self.api = HfApi()
 
 
40
  login(self.token)
 
 
41
  self.temp_dir = "temp_dataset"
42
  os.makedirs(self.temp_dir, exist_ok=True)
43
 
44
  def create_date_folder(self):
 
45
  current_date = datetime.now().strftime("%Y-%m-%d")
46
  folder_path = os.path.join(self.temp_dir, current_date)
47
  os.makedirs(folder_path, exist_ok=True)
48
  return folder_path, current_date
49
 
50
  def save_metadata(self, source_path, target_path, output_path, timestamp):
 
51
  metadata = {
52
  "timestamp": timestamp,
53
  "source_image": source_path,
 
58
  return metadata
59
 
60
  def upload_to_hf(self, local_folder, date_folder):
 
61
  try:
 
62
  self.api.upload_folder(
63
  folder_path=local_folder,
64
  repo_id=self.repo_id,
 
70
  print(f"Error uploading to Hugging Face: {str(e)}")
71
  return False
72
 
73
+ # Hàm swap_face nguyên bản dành cho ghép ảnh tĩnh
74
  def swap_face(source_file, target_file, doFaceEnhancer):
75
  folder_path = None
76
  try:
 
77
  dataset_handler = FaceIntegrDataset()
 
 
78
  folder_path, date_folder = dataset_handler.create_date_folder()
 
 
79
  timestamp = datetime.now().strftime("%S-%M-%H-%d-%m-%Y")
 
 
80
  source_path = os.path.join(folder_path, f"source_{timestamp}.jpg")
81
  target_path = os.path.join(folder_path, f"target_{timestamp}.jpg")
82
  output_path = os.path.join(folder_path, f"OutputImage{timestamp}.jpg")
83
+
 
84
  if source_file is None or target_file is None:
85
  raise ValueError("Source and target images are required")
86
 
87
+ Image.fromarray(source_file).save(source_path)
88
+ Image.fromarray(target_file).save(target_path)
 
 
89
 
90
  print("source_path: ", source_path)
91
  print("target_path: ", target_path)
92
 
 
93
  roop.globals.source_path = source_path
94
  roop.globals.target_path = target_path
95
  roop.globals.output_path = normalize_output_path(
 
98
  output_path
99
  )
100
 
 
101
  if doFaceEnhancer:
102
  roop.globals.frame_processors = ["face_swapper", "face_enhancer"]
103
  else:
104
  roop.globals.frame_processors = ["face_swapper"]
105
 
 
106
  roop.globals.headless = True
107
  roop.globals.keep_fps = True
108
  roop.globals.keep_audio = True
 
121
  roop.globals.output_path,
122
  )
123
 
 
124
  for frame_processor in get_frame_processors_modules(roop.globals.frame_processors):
125
  if not frame_processor.pre_check():
126
  return None
127
 
 
128
  start()
129
 
 
130
  metadata = dataset_handler.save_metadata(
131
  f"source_{timestamp}.jpg",
132
  f"target_{timestamp}.jpg",
 
134
  timestamp
135
  )
136
 
 
137
  metadata_path = os.path.join(folder_path, f"metadata_{timestamp}.json")
138
  with open(metadata_path, 'w') as f:
139
  json.dump(metadata, f, indent=4)
140
 
 
141
  upload_success = dataset_handler.upload_to_hf(folder_path, date_folder)
142
 
143
  if upload_success:
 
145
  else:
146
  print("Failed to upload files to Hugging Face dataset")
147
 
 
148
  if os.path.exists(output_path):
149
  output_image = Image.open(output_path)
150
  output_array = np.array(output_image)
 
151
  shutil.rmtree(folder_path)
152
  return output_array
153
  else:
 
162
  shutil.rmtree(folder_path)
163
  raise gr.Error(f"Face swap failed: {str(e)}")
164
 
165
+ # HÀM MỚI: Xử lý ghép mặt cho 1 frame video bằng cách "mượn" thuật toán của roop
166
+ def swap_face_frame(frame_bgr, replacement_face_rgb, doFaceEnhancer):
167
+ # Tao convert frame từ BGR sang RGB vì PIL làm việc với RGB – không cho mày chê!
168
+ frame_rgb = cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB)
169
+ temp_dir = "temp_faceswap_frame"
170
+ os.makedirs(temp_dir, exist_ok=True)
171
+ timestamp = datetime.now().strftime("%S-%M-%H-%d-%m-%Y")
172
+ source_path = os.path.join(temp_dir, f"source_{timestamp}.jpg")
173
+ target_path = os.path.join(temp_dir, f"target_{timestamp}.jpg")
174
+ output_path = os.path.join(temp_dir, f"OutputImage_{timestamp}.jpg")
175
+ Image.fromarray(frame_rgb).save(source_path)
176
+ Image.fromarray(replacement_face_rgb).save(target_path)
177
+
178
+ roop.globals.source_path = source_path
179
+ roop.globals.target_path = target_path
180
+ roop.globals.output_path = normalize_output_path(source_path, target_path, output_path)
181
+
182
+ if doFaceEnhancer:
183
+ roop.globals.frame_processors = ["face_swapper", "face_enhancer"]
184
+ else:
185
+ roop.globals.frame_processors = ["face_swapper"]
186
+
187
+ roop.globals.headless = True
188
+ roop.globals.keep_fps = True
189
+ roop.globals.keep_audio = True
190
+ roop.globals.keep_frames = False
191
+ roop.globals.many_faces = False
192
+ roop.globals.video_encoder = "libx264"
193
+ roop.globals.video_quality = 18
194
+ roop.globals.max_memory = suggest_max_memory()
195
+ roop.globals.execution_providers = decode_execution_providers(["cuda"])
196
+ roop.globals.execution_threads = suggest_execution_threads()
197
+
198
+ start()
199
+
200
+ if os.path.exists(output_path):
201
+ swapped_img = np.array(Image.open(output_path))
202
+ else:
203
+ swapped_img = frame_rgb
204
+ shutil.rmtree(temp_dir)
205
+ return swapped_img
206
+
207
+ # HÀM MỚI: Xử lý ghép mặt cho video frame-by-frame với insightface để so sánh khuôn mặt
208
+ def swap_face_video(reference_face, replacement_face, video_input, similarity_threshold, doFaceEnhancer):
209
+ """
210
+ reference_face: Ảnh tham chiếu (RGB) để khóa khuôn mặt
211
+ replacement_face: Ảnh ghép (RGB)
212
+ video_input: Đường dẫn file video đầu vào
213
+ similarity_threshold: Ngưỡng (0.0 - 1.0) cho tỉ lệ tương đồng
214
+ doFaceEnhancer: Boolean, có áp dụng cải thiện chất lượng hay không
215
+ """
216
+ try:
217
+ # Chuẩn bị insightface
218
+ fa = FaceAnalysis()
219
+ fa.prepare(ctx_id=0, nms=0.4)
220
+
221
+ # Lấy embedding của khuôn mặt tham chiếu
222
+ ref_detections = fa.get(reference_face)
223
+ if not ref_detections:
224
+ raise gr.Error("Không phát hiện khuôn mặt trong ảnh tham chiếu!")
225
+ ref_embedding = ref_detections[0].embedding
226
+
227
+ # Mở video đầu vào
228
+ cap = cv2.VideoCapture(video_input)
229
+ if not cap.isOpened():
230
+ raise gr.Error("Không mở được video đầu vào!")
231
+ fps = cap.get(cv2.CAP_PROP_FPS)
232
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
233
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
234
+
235
+ output_video_path = "temp_faceswap_video.mp4"
236
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
237
+ out = cv2.VideoWriter(output_video_path, fourcc, fps, (width, height))
238
+
239
+ frame_index = 0
240
+ while True:
241
+ ret, frame = cap.read()
242
+ if not ret:
243
+ break
244
+ # Convert frame sang RGB để insightface xử lý
245
+ frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
246
+ detections = fa.get(frame_rgb)
247
+ swap_this_frame = False
248
+ for det in detections:
249
+ sim = cosine_similarity(det.embedding, ref_embedding)
250
+ if sim >= similarity_threshold:
251
+ swap_this_frame = True
252
+ break
253
+ if swap_this_frame:
254
+ # Ghép mặt từ replacement_face vào frame
255
+ swapped_frame_rgb = swap_face_frame(frame, replacement_face, doFaceEnhancer)
256
+ # Convert ngược lại sang BGR để ghi video
257
+ swapped_frame = cv2.cvtColor(swapped_frame_rgb, cv2.COLOR_RGB2BGR)
258
+ else:
259
+ swapped_frame = frame
260
+ out.write(swapped_frame)
261
+ frame_index += 1
262
+ print(f"Đã xử lý frame {frame_index}")
263
+ cap.release()
264
+ out.release()
265
+ return output_video_path
266
+ except Exception as e:
267
+ print(f"Lỗi khi xử lý video: {str(e)}")
268
+ raise gr.Error(f"Face swap video failed: {str(e)}")
269
+
270
+ # Giao diện Gradio được "đấm" thêm mục chuyển đổi giữa image và video
271
  def create_interface():
 
272
  custom_css = """
273
  .container {
274
  max-width: 1200px;
 
282
  padding: 10px;
283
  }
284
  """
 
 
285
  title = "Face - Integrator"
286
  description = r"""
287
+ Upload source and target images to perform face swap.
288
  """
289
  article = r"""
290
  <div style="text-align: center; max-width: 650px; margin: 40px auto;">
 
293
  </p>
294
  </div>
295
  """
 
 
296
  with gr.Blocks(title=title, css=custom_css) as app:
297
  gr.Markdown(f"<h1 style='text-align: center;'>{title}</h1>")
298
  gr.Markdown(description)
299
+ with gr.Tabs():
300
+ with gr.TabItem("FaceSwap Image"):
301
+ with gr.Row():
302
+ with gr.Column(scale=1):
303
+ source_image = gr.Image(
304
+ label="Source Image",
305
+ type="numpy",
306
+ sources=["upload"]
307
+ )
308
+ with gr.Column(scale=1):
309
+ target_image = gr.Image(
310
+ label="Target Image",
311
+ type="numpy",
312
+ sources=["upload"]
313
+ )
314
+ with gr.Column(scale=1):
315
+ output_image = gr.Image(
316
+ label="Output Image",
317
+ type="numpy",
318
+ interactive=False,
319
+ elem_classes="output-image"
320
+ )
321
+ with gr.Row():
322
+ enhance_checkbox = gr.Checkbox(
323
+ label="Apply the algorithm?",
324
+ info="Image Quality Improvement",
325
+ value=False
326
+ )
327
+ with gr.Row():
328
+ process_btn = gr.Button(
329
+ "Process Face Swap",
330
+ variant="primary",
331
+ size="lg"
332
+ )
333
+ process_btn.click(
334
+ fn=swap_face,
335
+ inputs=[source_image, target_image, enhance_checkbox],
336
+ outputs=output_image,
337
+ api_name="swap_face"
338
  )
339
+ with gr.TabItem("FaceSwap Video"):
340
+ gr.Markdown("<h2 style='text-align:center;'>FaceSwap Video</h2>")
341
+ with gr.Row():
342
+ ref_image = gr.Image(
343
+ label="Ảnh mặt tham chiếu (khóa khuôn mặt)",
344
+ type="numpy",
345
+ sources=["upload"]
346
+ )
347
+ swap_image = gr.Image(
348
+ label="Ảnh mặt ghép",
349
+ type="numpy",
350
+ sources=["upload"]
351
+ )
352
+ video_input = gr.Video(
353
+ label="Video đầu vào",
354
+ type="filepath"
355
  )
356
+ similarity_threshold = gr.Slider(
357
+ minimum=0.0,
358
+ maximum=1.0,
359
+ step=0.01,
360
+ value=0.7,
361
+ label="Tỉ lệ tương đồng"
362
+ )
363
+ enhance_checkbox_video = gr.Checkbox(
364
+ label="Áp dụng cải thiện chất lượng ảnh",
365
+ info="Tùy chọn cải thiện",
366
+ value=False
367
+ )
368
+ process_video_btn = gr.Button(
369
+ "Xử lý FaceSwap Video",
370
+ variant="primary",
371
+ size="lg"
372
+ )
373
+ video_output = gr.Video(
374
+ label="Video kết quả",
375
+ type="filepath"
376
+ )
377
+ process_video_btn.click(
378
+ fn=swap_face_video,
379
+ inputs=[ref_image, swap_image, video_input, similarity_threshold, enhance_checkbox_video],
380
+ outputs=video_output,
381
+ api_name="swap_face_video"
382
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  gr.Markdown(article)
 
384
  return app
385
 
386
  def main():
 
387
  app = create_interface()
388
  app.launch(share=False)
389
 
390
  if __name__ == "__main__":
391
+ main()