HariLogicgo commited on
Commit
4b9cc62
Β·
1 Parent(s): 4b4508d
Files changed (2) hide show
  1. app.py +40 -46
  2. requirements.txt +2 -2
app.py CHANGED
@@ -8,7 +8,6 @@ import insightface
8
  from insightface.app import FaceAnalysis
9
  from huggingface_hub import hf_hub_download
10
  import subprocess
11
- import tempfile
12
  import numpy as np
13
  import threading
14
  from fastapi import FastAPI, UploadFile, File, HTTPException, Response
@@ -17,11 +16,12 @@ from pydantic import BaseModel
17
  from motor.motor_asyncio import AsyncIOMotorClient
18
  from bson.objectid import ObjectId
19
  from gradio import mount_gradio_app
 
20
 
21
  # -------------------------------------------------
22
  # Paths
23
  # -------------------------------------------------
24
- REPO_ID = "HariLogicgo/face_swap_models" # <- your HF repo for models
25
  BASE_DIR = "./workspace"
26
  UPLOAD_DIR = os.path.join(BASE_DIR, "uploads")
27
  RESULT_DIR = os.path.join(BASE_DIR, "results")
@@ -29,38 +29,43 @@ MODELS_DIR = "./models"
29
 
30
  os.makedirs(UPLOAD_DIR, exist_ok=True)
31
  os.makedirs(RESULT_DIR, exist_ok=True)
 
32
 
33
  # -------------------------------------------------
34
  # Download models once
35
  # -------------------------------------------------
36
- inswapper_path = hf_hub_download(
37
- repo_id=REPO_ID,
38
- filename="models/inswapper_128.onnx",
39
- repo_type="model",
40
- local_dir=MODELS_DIR
41
- )
42
-
43
- buffalo_files = [
44
- "1k3d68.onnx",
45
- "2d106det.onnx",
46
- "genderage.onnx",
47
- "det_10g.onnx",
48
- "w600k_r50.onnx"
49
- ]
50
- for f in buffalo_files:
51
- hf_hub_download(
52
  repo_id=REPO_ID,
53
- filename=f"models/buffalo_l/{f}",
54
  repo_type="model",
55
  local_dir=MODELS_DIR
56
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
  # -------------------------------------------------
59
  # Initialize face analysis and swapper
60
  # -------------------------------------------------
61
- app = FaceAnalysis(name="buffalo_l", root=MODELS_DIR, providers=['CPUExecutionProvider'])
 
62
  app.prepare(ctx_id=0, det_size=(640, 640))
63
- swapper = insightface.model_zoo.get_model(inswapper_path, providers=['CPUExecutionProvider'])
64
 
65
  # -------------------------------------------------
66
  # CodeFormer setup
@@ -69,11 +74,11 @@ CODEFORMER_PATH = "CodeFormer/inference_codeformer.py"
69
 
70
  def ensure_codeformer():
71
  if not os.path.exists("CodeFormer"):
72
- subprocess.run("git clone https://github.com/sczhou/CodeFormer.git", shell=True)
73
- subprocess.run("pip install -r CodeFormer/requirements.txt", shell=True)
74
- subprocess.run("python CodeFormer/basicsr/setup.py develop", shell=True)
75
- subprocess.run("python CodeFormer/scripts/download_pretrained_models.py facelib", shell=True)
76
- subprocess.run("python CodeFormer/scripts/download_pretrained_models.py CodeFormer", shell=True)
77
 
78
  ensure_codeformer()
79
 
@@ -91,12 +96,12 @@ source_images_collection = database.get_collection("Source_Images")
91
  results_collection = database.get_collection("Results")
92
 
93
  # -------------------------------------------------
94
- # Lock for face swap to prevent concurrent file operations
95
  # -------------------------------------------------
96
  swap_lock = threading.Lock()
97
 
98
  # -------------------------------------------------
99
- # Pipeline Function (with lock for safety)
100
  # -------------------------------------------------
101
  def face_swap_and_enhance(src_img, tgt_img):
102
  try:
@@ -114,19 +119,16 @@ def face_swap_and_enhance(src_img, tgt_img):
114
  if not src_faces or not tgt_faces:
115
  return None, None, "❌ Face not detected in one of the images."
116
 
117
- # ---------------- Save swapped face ----------------
118
  unique_name = f"swapped_{uuid.uuid4().hex[:8]}.jpg"
119
  swapped_path = os.path.join(UPLOAD_DIR, unique_name)
120
  swapped_bgr = swapper.get(tgt_bgr, tgt_faces[0], src_faces[0])
121
  cv2.imwrite(swapped_path, swapped_bgr)
122
 
123
- # ---------------- Run CodeFormer ----------------
124
  cmd = f"python {CODEFORMER_PATH} -w 0.7 --input_path {swapped_path} --output_path {RESULT_DIR} --bg_upsampler realesrgan --face_upsample"
125
  result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
126
  if result.returncode != 0:
127
  return None, None, f"❌ CodeFormer failed:\n{result.stderr}"
128
 
129
- # ---------------- Locate final result ----------------
130
  final_results_dir = os.path.join(RESULT_DIR, "final_results")
131
  if not os.path.exists(final_results_dir):
132
  return None, None, "❌ CodeFormer did not produce final results."
@@ -144,7 +146,7 @@ def face_swap_and_enhance(src_img, tgt_img):
144
  return None, None, f"❌ Error: {str(e)}"
145
 
146
  # -------------------------------------------------
147
- # Gradio Interface (simplified)
148
  # -------------------------------------------------
149
  with gr.Blocks() as demo:
150
  gr.Markdown("Face Swap")
@@ -170,17 +172,14 @@ with gr.Blocks() as demo:
170
  # -------------------------------------------------
171
  app = FastAPI()
172
 
173
- # Redirect root to Gradio for backward compatibility
174
  @app.get("/")
175
  def root():
176
  return RedirectResponse("/gradio")
177
 
178
- # Health check
179
  @app.get("/health")
180
- def health():
181
  return {"status": "healthy"}
182
 
183
- # Upload source image
184
  @app.post("/source")
185
  async def upload_source(image: UploadFile = File(...)):
186
  contents = await image.read()
@@ -192,7 +191,6 @@ async def upload_source(image: UploadFile = File(...)):
192
  result = await source_images_collection.insert_one(doc)
193
  return {"source_id": str(result.inserted_id)}
194
 
195
- # Upload target image
196
  @app.post("/target")
197
  async def upload_target(image: UploadFile = File(...)):
198
  contents = await image.read()
@@ -204,12 +202,10 @@ async def upload_target(image: UploadFile = File(...)):
204
  result = await target_images_collection.insert_one(doc)
205
  return {"target_id": str(result.inserted_id)}
206
 
207
- # Face swap request model
208
  class FaceSwapRequest(BaseModel):
209
  source_id: str
210
  target_id: str
211
 
212
- # Perform face swap
213
  @app.post("/faceswap")
214
  async def perform_faceswap(request: FaceSwapRequest):
215
  source_doc = await source_images_collection.find_one({"_id": ObjectId(request.source_id)})
@@ -220,26 +216,21 @@ async def perform_faceswap(request: FaceSwapRequest):
220
  if not target_doc:
221
  raise HTTPException(status_code=404, detail="Target image not found")
222
 
223
- # Decode source
224
  source_array = np.frombuffer(source_doc["data"], np.uint8)
225
  source_bgr = cv2.imdecode(source_array, cv2.IMREAD_COLOR)
226
  source_rgb = cv2.cvtColor(source_bgr, cv2.COLOR_BGR2RGB)
227
 
228
- # Decode target
229
  target_array = np.frombuffer(target_doc["data"], np.uint8)
230
  target_bgr = cv2.imdecode(target_array, cv2.IMREAD_COLOR)
231
  target_rgb = cv2.cvtColor(target_bgr, cv2.COLOR_BGR2RGB)
232
 
233
- # Perform swap
234
  final_img, final_path, err = face_swap_and_enhance(source_rgb, target_rgb)
235
  if err:
236
  raise HTTPException(status_code=500, detail=err)
237
 
238
- # Read final bytes
239
  with open(final_path, "rb") as f:
240
  final_bytes = f.read()
241
 
242
- # Store result
243
  result_doc = {
244
  "source_id": request.source_id,
245
  "target_id": request.target_id,
@@ -250,7 +241,6 @@ async def perform_faceswap(request: FaceSwapRequest):
250
  result = await results_collection.insert_one(result_doc)
251
  return {"result_id": str(result.inserted_id)}
252
 
253
- # Download result
254
  @app.get("/download/{result_id}")
255
  async def download_result(result_id: str):
256
  doc = await results_collection.find_one({"_id": ObjectId(result_id)})
@@ -263,4 +253,8 @@ async def download_result(result_id: str):
263
  )
264
 
265
  # Mount Gradio at /gradio
266
- app = mount_gradio_app(app, demo, path="/gradio")
 
 
 
 
 
8
  from insightface.app import FaceAnalysis
9
  from huggingface_hub import hf_hub_download
10
  import subprocess
 
11
  import numpy as np
12
  import threading
13
  from fastapi import FastAPI, UploadFile, File, HTTPException, Response
 
16
  from motor.motor_asyncio import AsyncIOMotorClient
17
  from bson.objectid import ObjectId
18
  from gradio import mount_gradio_app
19
+ import uvicorn
20
 
21
  # -------------------------------------------------
22
  # Paths
23
  # -------------------------------------------------
24
+ REPO_ID = "HariLogicgo/face_swap_models"
25
  BASE_DIR = "./workspace"
26
  UPLOAD_DIR = os.path.join(BASE_DIR, "uploads")
27
  RESULT_DIR = os.path.join(BASE_DIR, "results")
 
29
 
30
  os.makedirs(UPLOAD_DIR, exist_ok=True)
31
  os.makedirs(RESULT_DIR, exist_ok=True)
32
+ os.makedirs(MODELS_DIR, exist_ok=True)
33
 
34
  # -------------------------------------------------
35
  # Download models once
36
  # -------------------------------------------------
37
+ def download_models():
38
+ inswapper_path = hf_hub_download(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  repo_id=REPO_ID,
40
+ filename="models/inswapper_128.onnx",
41
  repo_type="model",
42
  local_dir=MODELS_DIR
43
  )
44
+ buffalo_files = [
45
+ "1k3d68.onnx",
46
+ "2d106det.onnx",
47
+ "genderage.onnx",
48
+ "det_10g.onnx",
49
+ "w600k_r50.onnx"
50
+ ]
51
+ for f in buffalo_files:
52
+ hf_hub_download(
53
+ repo_id=REPO_ID,
54
+ filename=f"models/buffalo_l/{f}",
55
+ repo_type="model",
56
+ local_dir=MODELS_DIR
57
+ )
58
+ return inswapper_path
59
+
60
+ inswapper_path = download_models()
61
 
62
  # -------------------------------------------------
63
  # Initialize face analysis and swapper
64
  # -------------------------------------------------
65
+ providers = ['CUDAExecutionProvider', 'CPUExecutionProvider'] # Prefer CUDA
66
+ app = FaceAnalysis(name="buffalo_l", root=MODELS_DIR, providers=providers)
67
  app.prepare(ctx_id=0, det_size=(640, 640))
68
+ swapper = insightface.model_zoo.get_model(inswapper_path, providers=providers)
69
 
70
  # -------------------------------------------------
71
  # CodeFormer setup
 
74
 
75
  def ensure_codeformer():
76
  if not os.path.exists("CodeFormer"):
77
+ subprocess.run("git clone https://github.com/sczhou/CodeFormer.git", shell=True, check=True)
78
+ subprocess.run("pip install -r CodeFormer/requirements.txt", shell=True, check=True)
79
+ subprocess.run("python CodeFormer/basicsr/setup.py develop", shell=True, check=True)
80
+ subprocess.run("python CodeFormer/scripts/download_pretrained_models.py facelib", shell=True, check=True)
81
+ subprocess.run("python CodeFormer/scripts/download_pretrained_models.py CodeFormer", shell=True, check=True)
82
 
83
  ensure_codeformer()
84
 
 
96
  results_collection = database.get_collection("Results")
97
 
98
  # -------------------------------------------------
99
+ # Lock for face swap
100
  # -------------------------------------------------
101
  swap_lock = threading.Lock()
102
 
103
  # -------------------------------------------------
104
+ # Pipeline Function
105
  # -------------------------------------------------
106
  def face_swap_and_enhance(src_img, tgt_img):
107
  try:
 
119
  if not src_faces or not tgt_faces:
120
  return None, None, "❌ Face not detected in one of the images."
121
 
 
122
  unique_name = f"swapped_{uuid.uuid4().hex[:8]}.jpg"
123
  swapped_path = os.path.join(UPLOAD_DIR, unique_name)
124
  swapped_bgr = swapper.get(tgt_bgr, tgt_faces[0], src_faces[0])
125
  cv2.imwrite(swapped_path, swapped_bgr)
126
 
 
127
  cmd = f"python {CODEFORMER_PATH} -w 0.7 --input_path {swapped_path} --output_path {RESULT_DIR} --bg_upsampler realesrgan --face_upsample"
128
  result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
129
  if result.returncode != 0:
130
  return None, None, f"❌ CodeFormer failed:\n{result.stderr}"
131
 
 
132
  final_results_dir = os.path.join(RESULT_DIR, "final_results")
133
  if not os.path.exists(final_results_dir):
134
  return None, None, "❌ CodeFormer did not produce final results."
 
146
  return None, None, f"❌ Error: {str(e)}"
147
 
148
  # -------------------------------------------------
149
+ # Gradio Interface
150
  # -------------------------------------------------
151
  with gr.Blocks() as demo:
152
  gr.Markdown("Face Swap")
 
172
  # -------------------------------------------------
173
  app = FastAPI()
174
 
 
175
  @app.get("/")
176
  def root():
177
  return RedirectResponse("/gradio")
178
 
 
179
  @app.get("/health")
180
+ async def health():
181
  return {"status": "healthy"}
182
 
 
183
  @app.post("/source")
184
  async def upload_source(image: UploadFile = File(...)):
185
  contents = await image.read()
 
191
  result = await source_images_collection.insert_one(doc)
192
  return {"source_id": str(result.inserted_id)}
193
 
 
194
  @app.post("/target")
195
  async def upload_target(image: UploadFile = File(...)):
196
  contents = await image.read()
 
202
  result = await target_images_collection.insert_one(doc)
203
  return {"target_id": str(result.inserted_id)}
204
 
 
205
  class FaceSwapRequest(BaseModel):
206
  source_id: str
207
  target_id: str
208
 
 
209
  @app.post("/faceswap")
210
  async def perform_faceswap(request: FaceSwapRequest):
211
  source_doc = await source_images_collection.find_one({"_id": ObjectId(request.source_id)})
 
216
  if not target_doc:
217
  raise HTTPException(status_code=404, detail="Target image not found")
218
 
 
219
  source_array = np.frombuffer(source_doc["data"], np.uint8)
220
  source_bgr = cv2.imdecode(source_array, cv2.IMREAD_COLOR)
221
  source_rgb = cv2.cvtColor(source_bgr, cv2.COLOR_BGR2RGB)
222
 
 
223
  target_array = np.frombuffer(target_doc["data"], np.uint8)
224
  target_bgr = cv2.imdecode(target_array, cv2.IMREAD_COLOR)
225
  target_rgb = cv2.cvtColor(target_bgr, cv2.COLOR_BGR2RGB)
226
 
 
227
  final_img, final_path, err = face_swap_and_enhance(source_rgb, target_rgb)
228
  if err:
229
  raise HTTPException(status_code=500, detail=err)
230
 
 
231
  with open(final_path, "rb") as f:
232
  final_bytes = f.read()
233
 
 
234
  result_doc = {
235
  "source_id": request.source_id,
236
  "target_id": request.target_id,
 
241
  result = await results_collection.insert_one(result_doc)
242
  return {"result_id": str(result.inserted_id)}
243
 
 
244
  @app.get("/download/{result_id}")
245
  async def download_result(result_id: str):
246
  doc = await results_collection.find_one({"_id": ObjectId(result_id)})
 
253
  )
254
 
255
  # Mount Gradio at /gradio
256
+ app = mount_gradio_app(app, demo, path="/gradio")
257
+
258
+ # Run the app with Uvicorn
259
+ if __name__ == "__main__":
260
+ uvicorn.run(app, host="0.0.0.0", port=7860)
requirements.txt CHANGED
@@ -26,14 +26,14 @@ facexlib==0.2.5
26
  # Face swap / insightface
27
  insightface
28
  onnx
29
- onnxruntime-gpu # Changed from onnxruntime to support GPU
 
30
 
31
  # App deps
32
  gradio
33
  huggingface_hub
34
  fastapi
35
  uvicorn
36
- motor # Added for MongoDB async support
37
 
38
  # Optional but included in CodeFormer repo
39
  tb-nightly
 
26
  # Face swap / insightface
27
  insightface
28
  onnx
29
+ onnxruntime-gpu # GPU support
30
+ motor # MongoDB async support
31
 
32
  # App deps
33
  gradio
34
  huggingface_hub
35
  fastapi
36
  uvicorn
 
37
 
38
  # Optional but included in CodeFormer repo
39
  tb-nightly