|
import streamlit as st |
|
import openai |
|
import torch |
|
from transformers import pipeline |
|
from deepface import DeepFace |
|
import cv2 |
|
import numpy as np |
|
import random |
|
import os |
|
from moviepy.editor import * |
|
from faster_whisper import WhisperModel |
|
from insightface.app import FaceAnalysis |
|
import shutil |
|
import tempfile |
|
|
|
|
|
st.set_page_config(page_title="DeepFlick: AI Face Swap & Voice Clone", layout="wide") |
|
|
|
st.title("π DeepFlick: AI-Powered Face Swap & Voice Clone") |
|
|
|
|
|
face_app = FaceAnalysis(name="buffalo_l") |
|
face_app.prepare(ctx_id=0, det_size=(640, 640)) |
|
|
|
|
|
tts_pipeline = pipeline("text-to-speech", model="coqui-ai/TTS") |
|
|
|
|
|
whisper_model = WhisperModel("base", device="cuda" if torch.cuda.is_available() else "cpu") |
|
|
|
|
|
def swap_faces(source_img_path, target_img_path, output_path): |
|
source_img = cv2.imread(source_img_path) |
|
target_img = cv2.imread(target_img_path) |
|
|
|
|
|
source_faces = face_app.get(source_img) |
|
target_faces = face_app.get(target_img) |
|
|
|
if len(source_faces) == 0 or len(target_faces) == 0: |
|
return None |
|
|
|
|
|
swapped_img = DeepFace.swap(source_img, target_img, model_name="insightface") |
|
|
|
|
|
cv2.imwrite(output_path, swapped_img) |
|
return output_path |
|
|
|
|
|
def generate_voice(script_text): |
|
audio_path = f"output_{random.randint(1000,9999)}.wav" |
|
tts_audio = tts_pipeline(script_text) |
|
|
|
with open(audio_path, "wb") as f: |
|
f.write(tts_audio["audio"]) |
|
|
|
return audio_path |
|
|
|
|
|
def generate_captions(audio_path): |
|
segments, _ = whisper_model.transcribe(audio_path) |
|
captions = "\n".join([segment.text for segment in segments]) |
|
return captions |
|
|
|
|
|
st.sidebar.subheader("π Upload Images") |
|
source_img = st.sidebar.file_uploader("Upload Your Face (Source)", type=["jpg", "png"]) |
|
target_img = st.sidebar.file_uploader("Upload Target Face", type=["jpg", "png"]) |
|
|
|
st.sidebar.subheader("ποΈ AI Voice Options") |
|
script_text = st.sidebar.text_area("Enter Video Script:", "Type something interesting here...") |
|
use_ai_voice = st.sidebar.checkbox("Use AI Voice Clone", value=True) |
|
|
|
|
|
if st.sidebar.button("π¬ Generate AI Video"): |
|
if source_img and target_img and script_text: |
|
with st.spinner("Processing AI Face Swap & Voice Clone... π"): |
|
|
|
temp_source = tempfile.NamedTemporaryFile(delete=False, suffix=".jpg") |
|
temp_target = tempfile.NamedTemporaryFile(delete=False, suffix=".jpg") |
|
temp_source.write(source_img.read()) |
|
temp_target.write(target_img.read()) |
|
|
|
|
|
output_img_path = "swapped_face.jpg" |
|
swapped_face = swap_faces(temp_source.name, temp_target.name, output_img_path) |
|
|
|
if swapped_face: |
|
st.image(swapped_face, caption="AI-Swapped Face", use_column_width=True) |
|
|
|
|
|
if use_ai_voice: |
|
audio_path = generate_voice(script_text) |
|
st.audio(audio_path, format="audio/wav") |
|
|
|
|
|
captions = generate_captions(audio_path) |
|
st.write("π **Auto-Generated Captions:**", captions) |
|
|
|
|
|
video_clip = ImageSequenceClip([swapped_face] * 30, fps=10) |
|
video_audio = AudioFileClip(audio_path) if use_ai_voice else None |
|
final_video_path = f"deepflick_output_{random.randint(1000,9999)}.mp4" |
|
|
|
if video_audio: |
|
video_clip = video_clip.set_audio(video_audio) |
|
|
|
video_clip.write_videofile(final_video_path, codec="libx264") |
|
|
|
st.video(final_video_path) |
|
|
|
else: |
|
st.error("Face swap failed! Try using clearer images.") |
|
|
|
|
|
temp_source.close() |
|
temp_target.close() |
|
os.remove(temp_source.name) |
|
os.remove(temp_target.name) |
|
|
|
else: |
|
st.warning("β οΈ Please upload both images and enter a script.") |
|
|
|
|
|
st.markdown("---") |
|
st.subheader("π Search AI-Generated Videos") |
|
|
|
search_query = st.text_input("Search for a video by script content:") |
|
if st.button("π Search"): |
|
|
|
dummy_videos = { |
|
"Funny AI Skit": "deepflick_output_1234.mp4", |
|
"AI Meme Video": "deepflick_output_5678.mp4" |
|
} |
|
|
|
found_videos = {title: path for title, path in dummy_videos.items() if search_query.lower() in title.lower()} |
|
|
|
if found_videos: |
|
for title, path in found_videos.items(): |
|
st.markdown(f"### π₯ {title}") |
|
st.video(path) |
|
else: |
|
st.write("β No videos found matching your search.") |