File size: 8,112 Bytes
3123ccc 8234608 3505500 8234608 3505500 3123ccc 8234608 3505500 8234608 3123ccc 8234608 3505500 8234608 3123ccc 8234608 3123ccc 8234608 3505500 8234608 3123ccc 8234608 3123ccc 8234608 3123ccc 3505500 8234608 3505500 3123ccc 8234608 3505500 8234608 3505500 8234608 3505500 8234608 3123ccc 8234608 3123ccc 3505500 3123ccc 3505500 8234608 3123ccc 8234608 3123ccc 8234608 3123ccc |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# app.py (Complete Version for Hugging Face Spaces)
import os
import gradio as gr
from refacer import Refacer
import imageio
from PIL import Image
import tempfile
import base64
import shutil
import time
# --- Configuration (Hardcoded for Hugging Face Spaces) ---
NUM_FACES = 8
FORCE_CPU = True
# --- Initialize Refacer ---
refacer = Refacer(force_cpu=FORCE_CPU)
# --- Core Functions ---
def run_image(*vars):
image_path = vars[0]
origins = vars[1:(NUM_FACES + 1)]
destinations = vars[(NUM_FACES + 1):(NUM_FACES * 2) + 1]
thresholds = vars[(NUM_FACES * 2) + 1:-2]
face_mode = vars[-2]
partial_reface_ratio = vars[-1]
disable_similarity = (face_mode in ["Single Face", "Multiple Faces"])
multiple_faces_mode = (face_mode == "Multiple Faces")
faces = [{'origin': origins[k] if not multiple_faces_mode else None, 'destination': destinations[k], 'threshold': thresholds[k] if not multiple_faces_mode else 0.0} for k in range(NUM_FACES) if destinations[k] is not None]
return refacer.reface_image(image_path, faces, disable_similarity=disable_similarity, multiple_faces_mode=multiple_faces_mode, partial_reface_ratio=partial_reface_ratio)
def run_video(*vars):
video_path = vars[0]
origins = vars[1:(NUM_FACES + 1)]
destinations = vars[(NUM_FACES + 1):(NUM_FACES * 2) + 1]
thresholds = vars[(NUM_FACES * 2) + 1:-3]
preview = vars[-3]
face_mode = vars[-2]
partial_reface_ratio = vars[-1]
disable_similarity = (face_mode in ["Single Face", "Multiple Faces"])
multiple_faces_mode = (face_mode == "Multiple Faces")
faces = [{'origin': origins[k] if not multiple_faces_mode else None, 'destination': destinations[k], 'threshold': thresholds[k] if not multiple_faces_mode else 0.0} for k in range(NUM_FACES) if destinations[k] is not None]
mp4_path, gif_path = refacer.reface(video_path, faces, preview=preview, disable_similarity=disable_similarity, multiple_faces_mode=multiple_faces_mode, partial_reface_ratio=partial_reface_ratio)
return mp4_path, gif_path
def load_first_frame(filepath):
if filepath is None: return None
with imageio.get_reader(filepath) as reader:
return reader.get_data(0)
def extract_faces_auto(filepath, max_faces=5, isvideo=False):
if filepath is None: return [None] * max_faces
if isvideo and os.path.getsize(filepath) > 10 * 1024 * 1024:
print("Video too large for auto-extract.")
return [None] * max_faces
try:
frame = load_first_frame(filepath)
faces = refacer.extract_faces_from_image(frame, max_faces=max_faces)
return faces + [None] * (max_faces - len(faces))
except Exception as e:
print(f"Could not extract faces: {e}")
return [None] * max_faces
def toggle_tabs_and_faces(mode):
if mode == "Single Face":
return [gr.update(visible=(i == 0)) for i in range(NUM_FACES)] + [gr.update(visible=False)] * NUM_FACES
elif mode == "Multiple Faces":
return [gr.update(visible=True)] * NUM_FACES + [gr.update(visible=False)] * NUM_FACES
else: # Faces By Match
return [gr.update(visible=True)] * NUM_FACES + [gr.update(visible=True)] * NUM_FACES
def handle_tif_preview(filepath):
if filepath is None: return None
with Image.open(filepath) as img, tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as temp_file:
img.convert('RGB').save(temp_file.name)
return temp_file.name
# --- UI ---
theme = gr.themes.Base(primary_hue="blue", secondary_hue="cyan")
with gr.Blocks(theme=theme, title="NeoRefacer - AI Refacer") as demo:
with open("icon.png", "rb") as f:
icon_data = base64.b64encode(f.read()).decode()
gr.Markdown(f'<div style="display: flex; align-items: center;"><img src="data:image/png;base64,{icon_data}" style="width:40px;height:40px;margin-right:10px;"><span style="font-size: 2em; font-weight: bold; color:#2563eb;">NeoRefacer</span></div>')
for mode in ["Image", "GIF", "TIFF", "Video"]:
with gr.Tab(f"{mode} Mode"):
is_video_or_gif = mode in ["Video", "GIF"]
with gr.Row():
if is_video_or_gif:
input_component = gr.Video(label=f"Original {mode}")
if mode == "GIF":
output_main = gr.Video(label="Refaced GIF (MP4)", interactive=False, format="mp4")
output_secondary = gr.Image(label="Refaced GIF (GIF)", type="filepath")
else: # Video
output_main = gr.Video(label="Refaced Video", interactive=False, format="mp4")
output_secondary = gr.File(visible=False) # Dummy component
elif mode == "TIFF":
input_component = gr.File(label="Original TIF", file_types=[".tif", ".tiff"])
output_main = gr.Image(label="Refaced TIF Preview", type="filepath")
output_secondary = gr.File(label="Refaced TIF (Download)", interactive=False)
else: # Image
input_component = gr.Image(label="Original image", type="filepath")
output_main = gr.Image(label="Refaced image", interactive=False, type="filepath")
with gr.Row():
face_mode_radio = gr.Radio(["Single Face", "Multiple Faces", "Faces By Match"], value="Single Face", label="Replacement Mode")
partial_reface_slider = gr.Slider(label="Reface Ratio (0=Full, 0.5=Half)", minimum=0.0, maximum=0.5, value=0.0, step=0.1)
reface_btn = gr.Button(f"Reface {mode}", variant="primary")
if is_video_or_gif:
preview_checkbox = gr.Checkbox(label="Preview (fast)", value=False)
origins, destinations, thresholds, face_tabs = [], [], [], []
for i in range(NUM_FACES):
with gr.Tab(f"Face #{i+1}", visible=(i==0)) as tab:
with gr.Row():
origin_img = gr.Image(label="Face to replace (Match)", visible=False)
dest_img = gr.Image(label="Destination face (Target)")
thresh_slider = gr.Slider(label="Threshold (for Match mode)", minimum=0.0, maximum=1.0, value=0.2)
origins.append(origin_img); destinations.append(dest_img); thresholds.append(thresh_slider); face_tabs.append(tab)
# Event Handlers
face_mode_radio.change(toggle_tabs_and_faces, inputs=face_mode_radio, outputs=face_tabs + origins)
if mode == "TIFF":
tif_preview = gr.Image(label="TIF Preview", type="filepath") # Specific for TIFF
input_component.change(handle_tif_preview, inputs=input_component, outputs=tif_preview)
input_component.change(lambda fp: extract_faces_auto(fp, max_faces=NUM_FACES), inputs=input_component, outputs=destinations)
reface_btn.click(lambda fp, *args: (handle_tif_preview(run_image(fp, *args)), run_image(fp, *args)), inputs=[input_component] + origins + destinations + thresholds + [face_mode_radio, partial_reface_slider], outputs=[output_main, output_secondary])
elif is_video_or_gif:
input_component.change(lambda fp: extract_faces_auto(fp, max_faces=NUM_FACES, isvideo=True), inputs=input_component, outputs=destinations)
reface_btn.click(run_video, inputs=[input_component] + origins + destinations + thresholds + [preview_checkbox, face_mode_radio, partial_reface_slider], outputs=[output_main, output_secondary])
else: # Image
input_component.change(lambda fp: extract_faces_auto(fp, max_faces=NUM_FACES), inputs=input_component, outputs=destinations)
reface_btn.click(run_image, inputs=[input_component] + origins + destinations + thresholds + [face_mode_radio, partial_reface_slider], outputs=[output_main])
# Load initial state for all tabs
demo.load(lambda: toggle_tabs_and_faces("Single Face"), outputs=face_tabs + origins)
# --- Launch app ---
demo.queue().launch()
|