Harsh-1202 commited on
Commit
646b234
·
verified ·
1 Parent(s): b91b43d

Upload 3 files

Browse files
Files changed (3) hide show
  1. Dockerfile +22 -0
  2. app.py +128 -0
  3. face_swap.py +72 -0
Dockerfile ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ # System dependencies
4
+ RUN apt-get update && apt-get install -y \
5
+ ffmpeg \
6
+ libgl1-mesa-glx \
7
+ && rm -rf /var/lib/apt/lists/*
8
+
9
+ WORKDIR /app
10
+
11
+ # Copy all files
12
+ COPY . .
13
+
14
+ # Install Python dependencies
15
+ RUN pip install --upgrade pip
16
+ RUN pip install -r requirements.txt
17
+
18
+ # Expose port
19
+ EXPOSE 7860
20
+
21
+ # Run your Flask app
22
+ CMD ["python", "app.py"]
app.py ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
3
+ from flask import Flask, render_template, request, jsonify, Response, send_file
4
+ import cv2
5
+ import uuid
6
+ import numpy as np
7
+ from face_swap import face_swap
8
+ from moviepy.editor import VideoFileClip
9
+
10
+ app = Flask(__name__)
11
+ app.config['UPLOAD_FOLDER'] = 'uploads'
12
+ app.config['OUTPUT_FOLDER'] = 'outputs'
13
+
14
+ os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
15
+ os.makedirs(app.config['OUTPUT_FOLDER'], exist_ok=True)
16
+
17
+ source_face = None
18
+ anonymize = False
19
+ save_faces = False
20
+ webcam_active = False
21
+ cap = None
22
+
23
+
24
+ @app.route('/')
25
+ def landing():
26
+ return render_template('landing.html')
27
+
28
+
29
+ @app.route('/app')
30
+ def main_app():
31
+ return render_template('app.html')
32
+
33
+
34
+ @app.route('/upload-face', methods=['POST'])
35
+ def upload_face():
36
+ global source_face
37
+ file = request.files['file']
38
+ path = os.path.join(app.config['UPLOAD_FOLDER'], f"face_{uuid.uuid4().hex}.jpg")
39
+ file.save(path)
40
+ source_face = cv2.imread(path)
41
+ return jsonify({'status': 'success'})
42
+
43
+
44
+ @app.route('/upload-video', methods=['POST'])
45
+ def upload_video():
46
+ global source_face
47
+ file = request.files['file']
48
+ path = os.path.join(app.config['UPLOAD_FOLDER'], f"video_{uuid.uuid4().hex}.mp4")
49
+ file.save(path)
50
+
51
+ cap = cv2.VideoCapture(path)
52
+ width, height = int(cap.get(3)), int(cap.get(4))
53
+ fps = cap.get(cv2.CAP_PROP_FPS) or 25.0
54
+
55
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
56
+ output_path = os.path.join(app.config['OUTPUT_FOLDER'], f"output_{uuid.uuid4().hex}.mp4")
57
+ out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
58
+
59
+ while cap.isOpened():
60
+ ret, frame = cap.read()
61
+ if not ret:
62
+ break
63
+ result = face_swap(source_face, frame)
64
+ out.write(result)
65
+
66
+ cap.release()
67
+ out.release()
68
+
69
+ try:
70
+ original = VideoFileClip(path)
71
+ edited = VideoFileClip(output_path)
72
+ final = edited.set_audio(original.audio)
73
+ final_path = output_path.replace(".mp4", "_with_audio.mp4")
74
+ final.write_videofile(final_path, codec='libx264', audio_codec='aac')
75
+ return send_file(final_path, as_attachment=True)
76
+ except Exception as e:
77
+ print("Audio merge error:", e)
78
+ return send_file(output_path, as_attachment=True)
79
+
80
+
81
+ @app.route('/toggle-anonymize', methods=['POST'])
82
+ def toggle_anonymize():
83
+ global anonymize
84
+ anonymize = not anonymize
85
+ return jsonify({'status': 'success', 'anonymize': anonymize})
86
+
87
+
88
+ @app.route('/toggle-save-faces', methods=['POST'])
89
+ def toggle_save_faces():
90
+ global save_faces
91
+ save_faces = not save_faces
92
+ return jsonify({'status': 'success', 'save_faces': save_faces})
93
+
94
+
95
+ @app.route('/start-webcam')
96
+ def start_webcam():
97
+ global webcam_active, cap
98
+ if not webcam_active:
99
+ cap = cv2.VideoCapture(0)
100
+ webcam_active = True
101
+ return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
102
+
103
+
104
+ @app.route('/stop-webcam')
105
+ def stop_webcam():
106
+ global webcam_active, cap
107
+ webcam_active = False
108
+ if cap:
109
+ cap.release()
110
+ return jsonify({'status': 'stopped'})
111
+
112
+
113
+ def gen_frames():
114
+ global cap, webcam_active, source_face
115
+ while webcam_active and cap.isOpened():
116
+ ret, frame = cap.read()
117
+ if not ret:
118
+ break
119
+ frame = face_swap(source_face, frame)
120
+ _, buffer = cv2.imencode('.jpg', frame)
121
+ frame_bytes = buffer.tobytes()
122
+ yield (b'--frame\r\nContent-Type: image/jpeg\r\n\r\n' + frame_bytes + b'\r\n')
123
+ if cap:
124
+ cap.release()
125
+
126
+
127
+ if __name__ == '__main__':
128
+ app.run(debug=True)
face_swap.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import cv2
3
+ import numpy as np
4
+ import warnings
5
+ from datetime import datetime
6
+ from insightface.app import FaceAnalysis
7
+ from insightface.model_zoo import get_model
8
+
9
+ warnings.filterwarnings("ignore", category=FutureWarning)
10
+
11
+ # Try GPU, fallback to CPU
12
+ try:
13
+ face_analyzer = FaceAnalysis(name='buffalo_l', providers=['CUDAExecutionProvider'])
14
+ face_analyzer.prepare(ctx_id=0)
15
+ except Exception:
16
+ print("⚠️ CUDA not available for FaceAnalysis. Falling back to CPU.")
17
+ face_analyzer = FaceAnalysis(name='buffalo_l', providers=['CPUExecutionProvider'])
18
+ face_analyzer.prepare(ctx_id=0)
19
+
20
+ try:
21
+ swapper = get_model('model/insightface/inswapper_128.onnx', providers=['CUDAExecutionProvider'])
22
+ except Exception:
23
+ print("⚠️ CUDA not available for Swapper. Falling back to CPU.")
24
+ swapper = get_model('inswapper_128.onnx', providers=['CPUExecutionProvider'])
25
+
26
+ def face_swap(source_img, target_img, anonymize=False, save_faces=False):
27
+ if source_img is None:
28
+ return target_img
29
+
30
+ source_img_rgb = cv2.cvtColor(source_img, cv2.COLOR_BGR2RGB)
31
+ target_img_rgb = cv2.cvtColor(target_img, cv2.COLOR_BGR2RGB)
32
+
33
+ # Resize source to target if needed
34
+ if source_img_rgb.shape != target_img_rgb.shape:
35
+ source_img_rgb = cv2.resize(source_img_rgb, (target_img_rgb.shape[1], target_img_rgb.shape[0]))
36
+
37
+ src_faces = face_analyzer.get(source_img_rgb)
38
+ tgt_faces = face_analyzer.get(target_img_rgb)
39
+
40
+ if len(tgt_faces) == 0 or len(src_faces) == 0:
41
+ return cv2.cvtColor(target_img_rgb, cv2.COLOR_RGB2BGR)
42
+
43
+ src_face = src_faces[0]
44
+
45
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
46
+ output_dir = f"saved_faces/{timestamp}"
47
+ if save_faces:
48
+ os.makedirs(output_dir, exist_ok=True)
49
+
50
+ for i, tgt_face in enumerate(tgt_faces):
51
+ x1, y1, x2, y2 = tgt_face.bbox.astype(int)
52
+
53
+ if anonymize:
54
+ target_img_rgb[y1:y2, x1:x2] = cv2.GaussianBlur(target_img_rgb[y1:y2, x1:x2], (99, 99), 30)
55
+ else:
56
+ try:
57
+ target_img_rgb = swapper.get(target_img_rgb, tgt_face, src_face, paste_back=True)
58
+ except Exception as e:
59
+ print(f"Swapper error on face {i}: {e}")
60
+
61
+ if save_faces:
62
+ face_crop = target_img_rgb[y1:y2, x1:x2]
63
+ face_bgr = cv2.cvtColor(face_crop, cv2.COLOR_RGB2BGR)
64
+ cv2.imwrite(f"{output_dir}/swapped_face_{i}.jpg", face_bgr)
65
+
66
+ side_by_side = np.hstack([
67
+ cv2.resize(cv2.cvtColor(source_img_rgb, cv2.COLOR_RGB2BGR), (200, 200)),
68
+ cv2.resize(face_bgr, (200, 200))
69
+ ])
70
+ cv2.imwrite(f"{output_dir}/source_target_face_{i}.jpg", side_by_side)
71
+
72
+ return cv2.cvtColor(target_img_rgb, cv2.COLOR_RGB2BGR)