AkashKumarave commited on
Commit
456bcc4
·
verified ·
1 Parent(s): 2ce637a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +130 -133
app.py CHANGED
@@ -1,162 +1,159 @@
1
- from fastapi import FastAPI, UploadFile, File, HTTPException
2
- from fastapi.responses import Response
3
- from fastapi.middleware.cors import CORSMiddleware
4
- import cv2
5
  import numpy as np
6
- import face_recognition
 
 
 
 
7
  import os
 
 
 
 
 
 
 
8
  import shutil
9
  import logging
 
10
 
11
  app = FastAPI()
12
 
13
- # Enable CORS to allow Framer frontend to connect
 
 
 
 
14
  app.add_middleware(
15
  CORSMiddleware,
16
- allow_origins=["*"],
17
  allow_credentials=True,
18
  allow_methods=["*"],
19
  allow_headers=["*"],
20
  )
21
 
22
- # Configure logging
23
- logging.basicConfig(level=logging.INFO)
24
-
25
- def get_face_data(image):
26
- """Detect face and landmarks in an image."""
27
- face_locations = face_recognition.face_locations(image)
28
- if len(face_locations) == 0:
29
- raise ValueError("No face detected in the image.")
30
- if len(face_locations) > 1:
31
- raise ValueError("Multiple faces detected; only one face per image is supported.")
32
-
33
- face_location = face_locations[0] # (top, right, bottom, left)
34
- landmarks = face_recognition.face_landmarks(image, face_locations=[face_location])
35
- if not landmarks:
36
- raise ValueError("Could not detect face landmarks.")
37
-
38
- return face_location, landmarks[0]
39
-
40
- def get_face_size(face_location):
41
- """Calculate the width and height of the face bounding box."""
42
- top, right, bottom, left = face_location
43
- width = right - left
44
- height = bottom - top
45
- return width, height
46
-
47
- def resize_face_image(source_img, target_face_size, source_face_location):
48
- """Resize the source image to match the target face size."""
49
- source_width, source_height = get_face_size(source_face_location)
50
- target_width, target_height = target_face_size
51
-
52
- # Calculate scaling factor to match the target face size
53
- scale_x = target_width / source_width
54
- scale_y = target_height / source_height
55
- scale = min(scale_x, scale_y) # Use the smaller scale to avoid distortion
56
-
57
- # Resize the source image
58
- new_width = int(source_img.shape[1] * scale)
59
- new_height = int(source_img.shape[0] * scale)
60
- resized_source = cv2.resize(source_img, (new_width, new_height), interpolation=cv2.INTER_AREA)
61
-
62
- return resized_source, scale
63
-
64
- def swap_faces(source_img, target_img):
65
- """Perform face swapping with size preservation and seamless blending."""
66
- # Convert images to RGB (face_recognition expects RGB)
67
- source_rgb = cv2.cvtColor(source_img, cv2.COLOR_BGR2RGB)
68
- target_rgb = cv2.cvtColor(target_img, cv2.COLOR_BGR2RGB)
69
-
70
- # Detect faces and landmarks
71
- source_face_location, source_landmarks = get_face_data(source_rgb)
72
- target_face_location, target_landmarks = get_face_data(target_rgb)
73
-
74
- # Calculate face sizes
75
- target_face_size = get_face_size(target_face_location)
76
-
77
- # Resize source image to match target face size
78
- resized_source, scale = resize_face_image(source_img, target_face_size, source_face_location)
79
-
80
- # Adjust source face location after resizing
81
- source_top, source_right, source_bottom, source_left = source_face_location
82
- adjusted_source_location = (
83
- int(source_top * scale),
84
- int(source_right * scale),
85
- int(source_bottom * scale),
86
- int(source_left * scale)
87
- )
88
-
89
- # Extract the source face region
90
- source_face = resized_source[
91
- adjusted_source_location[0]:adjusted_source_location[2],
92
- adjusted_source_location[3]:adjusted_source_location[1]
93
- ]
94
-
95
- # Calculate the center of the target face
96
- target_top, target_right, target_bottom, target_left = target_face_location
97
- target_center_x = (target_left + target_right) // 2
98
- target_center_y = (target_top + target_bottom) // 2
99
-
100
- # Create a mask for the source face
101
- mask = 255 * np.ones(source_face.shape, source_face.dtype)
102
-
103
- # Perform seamless cloning
104
- try:
105
- result = cv2.seamlessClone(
106
- source_face,
107
- target_img,
108
- mask,
109
- (target_center_x, target_center_y),
110
- cv2.NORMAL_CLONE
111
- )
112
- except Exception as e:
113
- logging.error(f"Seamless cloning failed: {str(e)}")
114
- raise ValueError("Failed to blend the faces seamlessly.")
115
-
116
- return result
117
 
118
  @app.post("/swap-face/")
119
- async def swap_face(
120
- source_file: UploadFile = File(...),
121
- target_file: UploadFile = File(...),
122
- doFaceEnhancer: bool = True
123
- ):
124
  try:
125
  # Save uploaded files temporarily
126
- source_path = f"temp_source_{source_file.filename}"
127
- target_path = f"temp_target_{target_file.filename}"
128
  output_path = "output.jpg"
129
 
 
 
130
  with open(source_path, "wb") as f:
131
- shutil.copyfileobj(source_file.file, f)
 
 
 
 
 
132
  with open(target_path, "wb") as f:
133
- shutil.copyfileobj(target_file.file, f)
 
 
 
134
 
135
- # Read images
136
  source_img = cv2.imread(source_path)
137
  target_img = cv2.imread(target_path)
138
-
139
  if source_img is None or target_img is None:
140
- raise ValueError("Failed to load one or both images.")
141
-
142
- # Perform custom face swap
143
- result_img = swap_faces(source_img, target_img)
144
 
145
- # Save the result
146
- cv2.imwrite(output_path, result_img)
147
-
148
- # Read the output image
149
- with open(output_path, "rb") as f:
150
- image_data = f.read()
151
-
152
- # Clean up temporary files
153
- for path in [source_path, target_path, output_path]:
154
- if os.path.exists(path):
155
- os.remove(path)
156
-
157
- # Return the swapped image
158
- return Response(content=image_data, media_type="image/jpeg")
159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  except Exception as e:
161
- logging.error(f"Error in face swap: {str(e)}")
162
- raise HTTPException(status_code=500, detail=f"Face swap failed: {str(e)}")
 
 
 
 
 
1
+ # -*- coding:UTF-8 -*-
2
+ #!/usr/bin/env python
 
 
3
  import numpy as np
4
+ import roop.globals
5
+ from roop.core import start, decode_execution_providers
6
+ from roop.processors.frame.core import get_frame_processors_modules
7
+ from roop.utilities import normalize_output_path
8
+ from roop.face_analyser import get_many_faces
9
  import os
10
+ import requests
11
+ import cv2
12
+ from pathlib import Path
13
+ from PIL import Image
14
+ from fastapi import FastAPI, File, UploadFile, HTTPException, BackgroundTasks
15
+ from fastapi.responses import FileResponse
16
+ from fastapi.middleware.cors import CORSMiddleware
17
  import shutil
18
  import logging
19
+ import time
20
 
21
  app = FastAPI()
22
 
23
+ # Configure logging
24
+ logging.basicConfig(level=logging.INFO)
25
+ logger = logging.getLogger(__name__)
26
+
27
+ # Add CORS middleware
28
  app.add_middleware(
29
  CORSMiddleware,
30
+ allow_origins=["*"], # Update with your Framer domain in production
31
  allow_credentials=True,
32
  allow_methods=["*"],
33
  allow_headers=["*"],
34
  )
35
 
36
+ article_text = """
37
+ <div style="text-align: center;">
38
+ <p>Enjoying the tool? Buy me a coffee and get exclusive prompt guides!</p>
39
+ <p><i>Instantly unlock helpful tips for creating better prompts!</i></p>
40
+ <div style="display: flex; justify-content: center;">
41
+ <a href=\"https://piczify.lemonsqueezy.com/buy/0f5206fa-68e8-42f6-9ca8-4f80c587c83e\">
42
+ <img src=\"https://www.buymeacoffee.com/assets/img/custom_images/yellow_img.png\"
43
+ alt=\"Buy Me a Coffee\"
44
+ style=\"height: 40px; width: auto; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); border-radius: 10px;\">
45
+ </a>
46
+ </div>
47
+ </div>
48
+ """
49
+
50
+ def download_model():
51
+ model_dir = Path("models")
52
+ model_path = model_dir / "inswapper_128.onnx"
53
+ model_url = "https://huggingface.co/ezioruan/inswapper_128.onnx/resolve/main/inswapper_128.onnx"
54
+
55
+ if not model_path.exists():
56
+ logger.info("Model not found. Downloading inswapper_128.onnx...")
57
+ model_dir.mkdir(exist_ok=True)
58
+ try:
59
+ response = requests.get(model_url, stream=True)
60
+ response.raise_for_status()
61
+ with open(model_path, 'wb') as f:
62
+ for chunk in response.iter_content(chunk_size=8192):
63
+ f.write(chunk)
64
+ logger.info("Model downloaded successfully.")
65
+ except Exception as e:
66
+ logger.error(f"Failed to download model: {e}")
67
+ raise RuntimeError("Could not download inswapper_128.onnx. Please download it manually from 'https://huggingface.co/ezioruan/inswapper_128.onnx' and place it in the 'models' folder.")
68
+ else:
69
+ logger.info("Model already exists at: %s", model_path)
70
+
71
+ download_model()
72
+
73
+ def cleanup_files(paths):
74
+ time.sleep(2) # Wait 2 seconds to ensure response is sent
75
+ for path in paths:
76
+ if os.path.exists(path):
77
+ os.remove(path)
78
+ logger.info("Cleaned up: %s", path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
  @app.post("/swap-face/")
81
+ async def swap_face(source_file: UploadFile = File(...), target_file: UploadFile = File(...), doFaceEnhancer: bool = True, background_tasks: BackgroundTasks = BackgroundTasks()):
 
 
 
 
82
  try:
83
  # Save uploaded files temporarily
84
+ source_path = "input.jpg"
85
+ target_path = "target.jpg"
86
  output_path = "output.jpg"
87
 
88
+ # Read and save source image
89
+ source_content = await source_file.read()
90
  with open(source_path, "wb") as f:
91
+ f.write(source_content)
92
+ source_image = Image.open(source_path)
93
+ source_image.save(source_path, quality=100, subsampling=0)
94
+
95
+ # Read and save target image, get its size
96
+ target_content = await target_file.read()
97
  with open(target_path, "wb") as f:
98
+ f.write(target_content)
99
+ target_image = Image.open(target_path)
100
+ target_size = target_image.size
101
+ target_image.save(target_path, quality=100, subsampling=0)
102
 
103
+ # Load images as NumPy arrays for face detection
104
  source_img = cv2.imread(source_path)
105
  target_img = cv2.imread(target_path)
 
106
  if source_img is None or target_img is None:
107
+ raise HTTPException(status_code=400, detail="Failed to load one or both images.")
 
 
 
108
 
109
+ # Debug: Check face detection
110
+ logger.info("Source faces detected: %s", get_many_faces(source_img))
111
+ logger.info("Target faces detected: %s", get_many_faces(target_img))
 
 
 
 
 
 
 
 
 
 
 
112
 
113
+ # Set up roop globals
114
+ roop.globals.source_path = source_path
115
+ roop.globals.target_path = target_path
116
+ roop.globals.output_path = normalize_output_path(
117
+ roop.globals.source_path, roop.globals.target_path, output_path
118
+ )
119
+ roop.globals.frame_processors = ["face_swapper", "face_enhancer"]
120
+ roop.globals.headless = True
121
+ roop.globals.keep_fps = True
122
+ roop.globals.keep_audio = True
123
+ roop.globals.keep_frames = False
124
+ roop.globals.many_faces = False
125
+ roop.globals.reference_face_position = 0
126
+ roop.globals.similar_face_distance = 0.6
127
+ roop.globals.video_encoder = "libx264"
128
+ roop.globals.video_quality = 18
129
+ roop.globals.max_memory = 16
130
+ roop.globals.execution_providers = decode_execution_providers(["cpu"])
131
+ roop.globals.execution_threads = 4
132
+
133
+ logger.info("Using frame processors: %s", roop.globals.frame_processors)
134
+
135
+ # Validate frame processors
136
+ for frame_processor in get_frame_processors_modules(
137
+ roop.globals.frame_processors
138
+ ):
139
+ if not frame_processor.pre_check():
140
+ raise HTTPException(status_code=500, detail=f"Pre-check failed for {frame_processor.__name__}")
141
+
142
+ # Perform face swap
143
+ start()
144
+ if os.path.exists(output_path):
145
+ output_image = Image.open(output_path)
146
+ output_image = output_image.resize(target_size, Image.Resampling.LANCZOS)
147
+ output_image.save(output_path, quality=100, subsampling=0)
148
+ logger.info("Returning FileResponse for %s", output_path)
149
+ background_tasks.add_task(cleanup_files, [source_path, target_path, output_path])
150
+ return FileResponse(output_path, media_type="image/jpeg", filename="output.jpg")
151
+ else:
152
+ raise HTTPException(status_code=500, detail="Face swap failed, output not generated")
153
  except Exception as e:
154
+ logger.error("Error in swap_face: %s", str(e))
155
+ raise HTTPException(status_code=500, detail=str(e))
156
+
157
+ if __name__ == "__main__":
158
+ import uvicorn
159
+ uvicorn.run(app, host="127.0.0.1", port=8000)