testing1 / app.py
AkashKumarave's picture
Update app.py
5ecba89 verified
raw
history blame
5.61 kB
# app.py
from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.responses import Response
from fastapi.middleware.cors import CORSMiddleware
import cv2
import numpy as np
import face_recognition
import os
import shutil
import logging
app = FastAPI()
# Enable CORS to allow Framer frontend to connect
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Configure logging
logging.basicConfig(level=logging.INFO)
def get_face_data(image):
"""Detect face and landmarks in an image."""
face_locations = face_recognition.face_locations(image)
if len(face_locations) == 0:
raise ValueError("No face detected in the image.")
if len(face_locations) > 1:
raise ValueError("Multiple faces detected; only one face per image is supported.")
face_location = face_locations[0] # (top, right, bottom, left)
landmarks = face_recognition.face_landmarks(image, face_locations=[face_location])
if not landmarks:
raise ValueError("Could not detect face landmarks.")
return face_location, landmarks[0]
def get_face_size(face_location):
"""Calculate the width and height of the face bounding box."""
top, right, bottom, left = face_location
width = right - left
height = bottom - top
return width, height
def resize_face_image(source_img, target_face_size, source_face_location):
"""Resize the source image to match the target face size."""
source_width, source_height = get_face_size(source_face_location)
target_width, target_height = target_face_size
# Calculate scaling factor to match the target face size
scale_x = target_width / source_width
scale_y = target_height / source_height
scale = min(scale_x, scale_y) # Use the smaller scale to avoid distortion
# Resize the source image
new_width = int(source_img.shape[1] * scale)
new_height = int(source_img.shape[0] * scale)
resized_source = cv2.resize(source_img, (new_width, new_height), interpolation=cv2.INTER_AREA)
return resized_source, scale
def swap_faces(source_img, target_img):
"""Perform face swapping with size preservation and seamless blending."""
# Convert images to RGB (face_recognition expects RGB)
source_rgb = cv2.cvtColor(source_img, cv2.COLOR_BGR2RGB)
target_rgb = cv2.cvtColor(target_img, cv2.COLOR_BGR2RGB)
# Detect faces and landmarks
source_face_location, source_landmarks = get_face_data(source_rgb)
target_face_location, target_landmarks = get_face_data(target_rgb)
# Calculate face sizes
target_face_size = get_face_size(target_face_location)
# Resize source image to match target face size
resized_source, scale = resize_face_image(source_img, target_face_size, source_face_location)
# Adjust source face location after resizing
source_top, source_right, source_bottom, source_left = source_face_location
adjusted_source_location = (
int(source_top * scale),
int(source_right * scale),
int(source_bottom * scale),
int(source_left * scale)
)
# Extract the source face region
source_face = resized_source[
adjusted_source_location[0]:adjusted_source_location[2],
adjusted_source_location[3]:adjusted_source_location[1]
]
# Calculate the center of the target face
target_top, target_right, target_bottom, target_left = target_face_location
target_center_x = (target_left + target_right) // 2
target_center_y = (target_top + target_bottom) // 2
# Create a mask for the source face
mask = 255 * np.ones(source_face.shape, source_face.dtype)
# Perform seamless cloning
try:
result = cv2.seamlessClone(
source_face,
target_img,
mask,
(target_center_x, target_center_y),
cv2.NORMAL_CLONE
)
except Exception as e:
logging.error(f"Seamless cloning failed: {str(e)}")
raise ValueError("Failed to blend the faces seamlessly.")
return result
@app.post("/swap-face/")
async def swap_face(
source_file: UploadFile = File(...),
target_file: UploadFile = File(...),
doFaceEnhancer: bool = True
):
try:
# Save uploaded files temporarily
source_path = f"temp_source_{source_file.filename}"
target_path = f"temp_target_{target_file.filename}"
output_path = "output.jpg"
with open(source_path, "wb") as f:
shutil.copyfileobj(source_file.file, f)
with open(target_path, "wb") as f:
shutil.copyfileobj(target_file.file, f)
# Read images
source_img = cv2.imread(source_path)
target_img = cv2.imread(target_path)
if source_img is None or target_img is None:
raise ValueError("Failed to load one or both images.")
# Perform custom face swap
result_img = swap_faces(source_img, target_img)
# Save the result
cv2.imwrite(output_path, result_img)
# Read the output image
with open(output_path, "rb") as f:
image_data = f.read()
# Clean up temporary files
for path in [source_path, target_path, output_path]:
if os.path.exists(path):
os.remove(path)
# Return the swapped image
return Response(content=image_data, media_type="image/jpeg")
except Exception as e:
logging.error(f"Error in face swap: {str(e)}")
raise HTTPException(status_code=500, detail=f"Face swap failed: {str(e)}")