Abraham E. Tavarez commited on
Commit
bd55878
Β·
1 Parent(s): 56225c5

deepface scanning offloaded to modal cloud function

Browse files
Files changed (4) hide show
  1. app.py +43 -49
  2. detector/face.py +13 -0
  3. pyproject.toml +2 -0
  4. uv.lock +0 -0
app.py CHANGED
@@ -4,19 +4,30 @@ from detector.voice import verify_voices
4
  from detector.video import verify_faces_in_video
5
  from reports.pdf_report import generate_pdf_report
6
  from utils.youtube_utils import download_youtube_video
 
 
 
7
 
8
  # Holds latest results
9
  last_face_result = None
10
  last_voice_result = None
11
  last_video_results = None
12
 
13
-
14
- def start_scan(image, audio):
15
- return "Scanning in progress...", None
16
-
17
- def compare_faces(img1_path, img2_path):
 
 
18
  global last_face_result
19
- result = verify_faces(img1_path, img2_path)
 
 
 
 
 
 
20
  result_text = ""
21
 
22
  if "error" in result:
@@ -26,14 +37,19 @@ def compare_faces(img1_path, img2_path):
26
  result_text = f"βœ… Match! Distance: {result['distance']:.4f} (Threshold: {result['threshold']})"
27
  last_face_result = result_text
28
  return result_text
29
-
30
  else:
31
  result_text = f"❌ No Match. Distance: {result['distance']:.4f} (Threshold: {result['threshold']})"
32
  last_face_result = result_text
33
  return result_text
34
 
35
 
36
- def compare_voices(audio1, audio2):
 
 
 
 
 
37
  global last_voice_result
38
  result = verify_voices(audio1, audio2)
39
  result_text = ""
@@ -51,46 +67,35 @@ def compare_voices(audio1, audio2):
51
  return result_text
52
 
53
 
54
- def scan_video(video_file, ref_img, youtube_url=""):
 
 
 
 
 
 
55
  global last_video_results
56
-
57
  if youtube_url:
58
  try:
59
  video_file = download_youtube_video(youtube_url)
60
  except Exception as e:
61
  return f"❌ Error downloading YouTube video: {str(e)}"
62
-
63
  results = verify_faces_in_video(video_file, ref_img)
64
  report = ""
65
- last_video_results = results
66
  for r in results:
67
  if "error" in r:
68
  report += f"\n⚠️ Frame {r['frame']}: {r['error']}"
69
- # last_video_results.append(report)
70
  else:
71
  status = "βœ… Match" if r["verified"] else "❌ Mismatch"
72
  report += f"\nπŸ–Ό Frame {r['frame']}: {status} (Distance: {r['distance']})"
73
 
74
- # last_video_results.append(report)
75
  return report
76
 
77
 
78
- # def scan_video(video_path, ref_img):
79
- # global last_video_results
80
- # results = verify_faces_in_video(video_path, ref_img)
81
- # report = ""
82
- # last_video_results = results
83
- # for r in results:
84
- # if "error" in r:
85
- # report += f"\n⚠️ Frame {r['frame']}: {r['error']}"
86
- # # last_video_results.append(report)
87
- # else:
88
- # status = "βœ… Match" if r["verified"] else "❌ Mismatch"
89
- # report += f"\nπŸ–Ό Frame {r['frame']}: {status} (Distance: {r['distance']})"
90
-
91
- # # last_video_results.append(report)
92
- # return report
93
-
94
  def generate_report():
95
  return generate_pdf_report(last_face_result, last_voice_result, last_video_results)
96
 
@@ -111,9 +116,7 @@ with gr.Blocks(title="Deepfake Watchdog") as demo:
111
  output_text = gr.Textbox(label="Result")
112
  # output_gallery = gr.Gallery(label="Matched Results")
113
 
114
- run_button.click(
115
- compare_faces, inputs=[image1, image2], outputs=[output_text]
116
- )
117
 
118
  # Voice Verification
119
  with gr.Tab("🎀 Voice Verification"):
@@ -126,30 +129,21 @@ with gr.Blocks(title="Deepfake Watchdog") as demo:
126
  voice_output = gr.Textbox(label="Result")
127
 
128
  voice_btn.click(compare_voices, inputs=[audio1, audio2], outputs=voice_output)
129
-
130
- # Video DeepFake Scan
131
- # gr.Markdown("### πŸ“Ή Video Deepfake Scan")
132
- # with gr.Tab("πŸ“Ή Video Deepfake Scan"):
133
- # gr.Markdown("Upload a video and a reference image. We'll scan for deepfake face mismatches.")
134
-
135
- # ref_img = gr.Image(type="filepath", label="Reference Face")
136
- # video_input = gr.Video(label="Video File")
137
- # scan_btn = gr.Button("Scan Video")
138
- # scan_output = gr.Textbox(label="Scan Results", lines=10)
139
 
140
- # scan_btn.click(scan_video, inputs=[video_input, ref_img], outputs=scan_output)
141
-
142
  with gr.Tab("πŸ“Ή Video Deepfake Scan"):
143
- gr.Markdown("πŸ” Upload a video or paste a YouTube link and we'll analyze it for deepfake face swaps.")
144
-
 
 
145
  ref_img = gr.Image(type="filepath", label="Reference Face")
146
  video_input = gr.Video(label="Video File (optional)")
147
  youtube_url = gr.Textbox(label="YouTube URL (optional)")
148
  scan_btn = gr.Button("Scan Video")
149
  scan_output = gr.Textbox(label="Scan Results", lines=10)
150
 
151
- scan_btn.click(scan_video, inputs=[video_input, ref_img, youtube_url], outputs=scan_output)
152
-
 
153
 
154
  with gr.Tab("πŸ“„ Generate Report"):
155
  report_btn = gr.Button("Generate PDF Report")
 
4
  from detector.video import verify_faces_in_video
5
  from reports.pdf_report import generate_pdf_report
6
  from utils.youtube_utils import download_youtube_video
7
+ import modal
8
+ verify_faces_remote = modal.Function.lookup("deepface-agent", "verify_faces_remote")
9
+
10
 
11
  # Holds latest results
12
  last_face_result = None
13
  last_voice_result = None
14
  last_video_results = None
15
 
16
+ # @app.local_entrypoint()
17
+ def compare_faces(img1_path: str, img2_path: str) -> str:
18
+ """Use this tool to compare to faces for a match
19
+ Args:
20
+ img1_path: The path to the first image
21
+ img2_path: The path to the second image
22
+ """
23
  global last_face_result
24
+
25
+ # Read image files as bytes
26
+ with open(img1_path, "rb") as f1, open(img2_path, "rb") as f2:
27
+ img1_bytes = f1.read()
28
+ img2_bytes = f2.read()
29
+
30
+ result = verify_faces_remote.remote(img1_bytes, img2_bytes)
31
  result_text = ""
32
 
33
  if "error" in result:
 
37
  result_text = f"βœ… Match! Distance: {result['distance']:.4f} (Threshold: {result['threshold']})"
38
  last_face_result = result_text
39
  return result_text
40
+
41
  else:
42
  result_text = f"❌ No Match. Distance: {result['distance']:.4f} (Threshold: {result['threshold']})"
43
  last_face_result = result_text
44
  return result_text
45
 
46
 
47
+ def compare_voices(audio1: str, audio2: str) -> str:
48
+ """Use this tool to compare two voices for a match
49
+ Args:
50
+ audio1: The path to the first audio file
51
+ audio2: The path to the second audio file
52
+ """
53
  global last_voice_result
54
  result = verify_voices(audio1, audio2)
55
  result_text = ""
 
67
  return result_text
68
 
69
 
70
+ def scan_video(video_file: str, ref_img: str, youtube_url="") -> str:
71
+ """Use this tool to scan a video for deepfake face swaps
72
+ Args:
73
+ video_file: The path to the video file
74
+ ref_img: The path to the reference image
75
+ youtube_url: The YouTube URL (optional)
76
+ """
77
  global last_video_results
78
+
79
  if youtube_url:
80
  try:
81
  video_file = download_youtube_video(youtube_url)
82
  except Exception as e:
83
  return f"❌ Error downloading YouTube video: {str(e)}"
84
+
85
  results = verify_faces_in_video(video_file, ref_img)
86
  report = ""
87
+ last_video_results = results
88
  for r in results:
89
  if "error" in r:
90
  report += f"\n⚠️ Frame {r['frame']}: {r['error']}"
91
+
92
  else:
93
  status = "βœ… Match" if r["verified"] else "❌ Mismatch"
94
  report += f"\nπŸ–Ό Frame {r['frame']}: {status} (Distance: {r['distance']})"
95
 
 
96
  return report
97
 
98
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  def generate_report():
100
  return generate_pdf_report(last_face_result, last_voice_result, last_video_results)
101
 
 
116
  output_text = gr.Textbox(label="Result")
117
  # output_gallery = gr.Gallery(label="Matched Results")
118
 
119
+ run_button.click(compare_faces, inputs=[image1, image2], outputs=[output_text])
 
 
120
 
121
  # Voice Verification
122
  with gr.Tab("🎀 Voice Verification"):
 
129
  voice_output = gr.Textbox(label="Result")
130
 
131
  voice_btn.click(compare_voices, inputs=[audio1, audio2], outputs=voice_output)
 
 
 
 
 
 
 
 
 
 
132
 
 
 
133
  with gr.Tab("πŸ“Ή Video Deepfake Scan"):
134
+ gr.Markdown(
135
+ "πŸ” Upload a video or paste a YouTube link and we'll analyze it for deepfake face swaps."
136
+ )
137
+
138
  ref_img = gr.Image(type="filepath", label="Reference Face")
139
  video_input = gr.Video(label="Video File (optional)")
140
  youtube_url = gr.Textbox(label="YouTube URL (optional)")
141
  scan_btn = gr.Button("Scan Video")
142
  scan_output = gr.Textbox(label="Scan Results", lines=10)
143
 
144
+ scan_btn.click(
145
+ scan_video, inputs=[video_input, ref_img, youtube_url], outputs=scan_output
146
+ )
147
 
148
  with gr.Tab("πŸ“„ Generate Report"):
149
  report_btn = gr.Button("Generate PDF Report")
detector/face.py CHANGED
@@ -1,6 +1,19 @@
1
  from deepface import DeepFace
2
  import cv2
3
  import os
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
  def verify_faces(img1_path, img2_path, model_name="Facenet", detector_backend="opencv"):
6
  """
 
1
  from deepface import DeepFace
2
  import cv2
3
  import os
4
+ from modal_app.modal_app import verify_faces_remote
5
+ from fastapi import UploadFile
6
+
7
+ def face_verify_tool(img1: UploadFile, img2: UploadFile):
8
+ img1_bytes = img1.file.read()
9
+ img2_bytes = img2.file.read()
10
+
11
+ results = verify_faces_remote(img1_bytes, img2_bytes)
12
+
13
+ return results
14
+
15
+
16
+
17
 
18
  def verify_faces(img1_path, img2_path, model_name="Facenet", detector_backend="opencv"):
19
  """
pyproject.toml CHANGED
@@ -8,8 +8,10 @@ dependencies = [
8
  "deepface>=0.0.93",
9
  "fpdf>=1.7.2",
10
  "gradio[mcp]>=5.32.1",
 
11
  "opencv-python-headless>=4.11.0.86",
12
  "pydub>=0.25.1",
13
  "resemblyzer>=0.1.4",
14
  "tf-keras>=2.19.0",
 
15
  ]
 
8
  "deepface>=0.0.93",
9
  "fpdf>=1.7.2",
10
  "gradio[mcp]>=5.32.1",
11
+ "modal>=1.0.3",
12
  "opencv-python-headless>=4.11.0.86",
13
  "pydub>=0.25.1",
14
  "resemblyzer>=0.1.4",
15
  "tf-keras>=2.19.0",
16
+ "yt-dlp>=2025.5.22",
17
  ]
uv.lock CHANGED
The diff for this file is too large to render. See raw diff