| import gradio as gr |
| import insightface |
| from insightface.app import FaceAnalysis |
| from PIL import Image |
| import numpy as np |
|
|
| |
| wellcomingMessage = """ |
| <h1>Face Swap</h1> |
| <p>by <a href="https://www.tonyassi.com/" target="_blank" style="color:#97d8be;">Tony Assi</a></p> |
| <h2>Try out <a href="https://huggingface.co/spaces/tonyassi/video-face-swap" target="_blank">Video Face Swap</a> ❤️</h2> |
| """ |
|
|
| |
| assert insightface.__version__ >= '0.7' |
|
|
| |
| app = FaceAnalysis(name='buffalo_l') |
| app.prepare(ctx_id=0, det_size=(640, 640)) |
|
|
| |
| swapper = insightface.model_zoo.get_model('inswapper_128.onnx', download=True, download_zip=True) |
|
|
| |
| def swap_faces(src_img, dest_img): |
| |
| gr.Warning( |
| 'Skip the limits — HD, priority queue & no watermark at ' |
| '<a href="https://www.face-swap.co/?utm_source=hfspace_faceswap&utm_medium=warning" ' |
| 'target="_blank" rel="noopener">face-swap.co</a>' |
| ) |
|
|
| if src_img is None or dest_img is None: |
| raise gr.Error("Please upload both a source and a target image.") |
|
|
| src_faces = app.get(src_img) |
| dest_faces = app.get(dest_img) |
|
|
| if len(src_faces) == 0 or len(dest_faces) == 0: |
| raise gr.Error("No faces detected in one of the images. Try clearer, front-facing photos.") |
|
|
| |
| source_face = src_faces[0] |
| dest_face = dest_faces[0] |
| result = swapper.get(dest_img, dest_face, source_face, paste_back=True) |
|
|
| out_img = Image.fromarray(np.uint8(result)).convert("RGB") |
|
|
| |
| gr.Info( |
| '✨ Like this preview?<br>' |
| '<strong>Get HD face swaps</strong> (higher resolution, priority queue & no watermark) — ' |
| '<a href="https://www.face-swap.co/?utm_source=hfspace_faceswap&utm_medium=info" ' |
| 'target="_blank" rel="noopener">Upgrade on face-swap.co</a>', |
| duration=8 |
| ) |
|
|
| return out_img |
|
|
| def open_side(): |
| |
| return gr.Sidebar(open=True) |
|
|
| |
| CUSTOM_CSS = """ |
| .sticky-cta { |
| position: sticky; top: 0; z-index: 1000; |
| background: #a5b4fc; |
| color: #0f172a; |
| padding: 10px 14px; |
| text-align: center; |
| border-bottom: 1px solid #333; |
| display: block; |
| text-decoration: none; |
| cursor: pointer; |
| } |
| .sticky-cta:hover { filter: brightness(0.97); } |
| .sticky-cta .pill { background:#4f46e5; color:#fff; padding:4px 10px; border-radius:999px; margin-left:10px; } |
| .sticky-cta .cta-link { font-weight:600; text-decoration: underline; } |
| |
| /* centered, single-label API CTA */ |
| .api-cta-wrap { text-align:center; margin-top:10px; } |
| .api-cta-hero { |
| display:inline-flex; align-items:center; gap:10px; |
| padding:10px 14px; border-radius:14px; |
| background: linear-gradient(90deg,#0ea5e9 0%, #a8a9de 100%); |
| color:#fff; font-weight:800; letter-spacing:0.1px; |
| box-shadow: 0 6px 22px rgba(99,102,241,0.35); |
| border: 1px solid rgba(255,255,255,0.22); |
| text-decoration:none; |
| } |
| .api-cta-hero:hover { filter:brightness(1.05); transform: translateY(-1px); transition: all .15s ease; } |
| |
| .api-cta-hero .new { |
| background:#fff; color:#0ea5e9; font-weight:900; |
| padding:2px 8px; border-radius:999px; font-size:12px; line-height:1; |
| } |
| .api-cta-hero .txt { font-weight:800; } |
| .api-cta-hero .chev { opacity:.95; } |
| @media (max-width: 520px){ |
| .api-cta-hero { padding:9px 12px; gap:8px; font-size:14px; } |
| .api-cta-hero .new { display:none; } |
| } |
| |
| /* floating bottom promo */ |
| .bottom-promo { |
| position: fixed; left: 50%; transform: translateX(-50%); |
| bottom: 16px; z-index: 1001; background:#0b0b0b; color:#fff; |
| border: 1px solid #2a2a2a; border-radius: 12px; padding: 10px 14px; |
| box-shadow: 0 8px 24px rgba(0,0,0,0.3); |
| } |
| .bottom-promo a { color:#4ea1ff; text-decoration:none; font-weight:600; } |
| |
| /* big CTA button */ |
| .upgrade-btn { width: 100%; font-size: 16px; padding: 10px 14px; } |
| |
| /* hero markdown centering + larger heading */ |
| #hero-md { |
| text-align: center; |
| } |
| #hero-md h3, |
| #hero-md .prose h3 { |
| font-size: 2.1rem; |
| line-height: 1.2; |
| font-weight: 800; |
| margin-bottom: 0.25rem; |
| } |
| #hero-md p, |
| #hero-md .prose p { |
| font-size: 1.05rem; |
| } |
| """ |
|
|
| |
| |
| with gr.Blocks(title="Image Face Swap") as demo: |
| |
| gr.HTML(f"<style>{CUSTOM_CSS}</style>") |
|
|
| |
| gr.HTML( |
| '<a class="sticky-cta" ' |
| 'href="https://www.face-swap.co/?utm_source=hfspace_faceswap&utm_medium=banner" ' |
| 'target="_blank" rel="noopener" aria-label="Upgrade to Pro on face-swap.co">' |
| '⚡ <strong>Upgrade to HD</strong> — priority queue & higher resolution swaps!' |
| '<span class="pill">GPU</span>' |
| '</a>' |
| ) |
|
|
| |
| gr.Markdown( |
| f""" |
| ### Image Face Swap (Preview) |
| [face-swap.co](https://www.face-swap.co/?utm_source=hfspace_faceswap&utm_medium=subtitle) |
| |
| **Free preview** runs on CPU and may be limited in resolution to keep it fast. |
| Want full-quality **HD AI face swap** with GPU speed? **[Go Pro ↗](https://www.face-swap.co/?utm_source=hfspace_faceswap&utm_medium=go_pro)** |
| |
| """, |
| elem_id="hero-md" |
| ) |
|
|
| |
| gr.HTML( |
| """ |
| <div class="api-cta-wrap"> |
| <a class="api-cta-hero" |
| href="https://www.face-swap.co/api?utm_source=hfspace_faceswap&utm_medium=hero_api_new" |
| target="_blank" rel="noopener" aria-label="Face Swap API"> |
| <span class="new">NEW</span> |
| <span class="txt">Face Swap API</span> |
| <span class="chev">↗</span> |
| </a> |
| </div> |
| """ |
| ) |
|
|
| with gr.Row(): |
| with gr.Column(scale=5): |
| src_img = gr.Image(type="numpy", label="Source Image (face to copy)") |
| dest_img = gr.Image(type="numpy", label="Target Image (face to replace)") |
|
|
| go = gr.Button("Swap Face", variant="primary") |
| pro = gr.Button("⚡ Upgrade to HD on face-swap.co", elem_classes=["upgrade-btn"]) |
|
|
| with gr.Column(scale=5): |
| out_img = gr.Image(label="Result") |
|
|
| |
| gr.Examples( |
| examples=[["./Images/kim.jpg", "./Images/marilyn.jpg"]], |
| inputs=[src_img, dest_img], |
| outputs=[out_img], |
| fn=swap_faces, |
| cache_examples=True, |
| run_on_click=True, |
| label="Try an example" |
| ) |
|
|
| |
| with gr.Sidebar(open=False) as side: |
| gr.Markdown( |
| "### Upgrade to HD 1920x1080\n" |
| "- Higher resolution face swaps\n" |
| "- Priority queue\n" |
| "- API access & automation\n" |
| "- No watermark" |
| ) |
| pro_pro = gr.Button("Open Pro Checkout", variant="primary") |
| pro_api = gr.Button("API Access", variant="primary") |
|
|
| |
| gr.HTML( |
| '<div class="bottom-promo">' |
| 'Want HD & faster processing? ' |
| '<a href="https://www.face-swap.co/?utm_source=hfspace_faceswap&utm_medium=upgrade" ' |
| 'target="_blank" rel="noopener">Upgrade</a>' |
| '</div>' |
| ) |
|
|
| |
| go.click(fn=open_side, inputs=None, outputs=side, queue=False) |
|
|
| |
| go.click(fn=swap_faces, inputs=[src_img, dest_img], outputs=out_img) |
|
|
| |
| pro.click( |
| fn=None, inputs=None, outputs=None, |
| js="()=>window.open('https://www.face-swap.co/?utm_source=hfspace_faceswap&utm_medium=upgrade_to_hd','_blank')" |
| ) |
| pro_pro.click( |
| fn=None, inputs=None, outputs=None, |
| js="()=>window.open('https://www.face-swap.co/?utm_source=hfspace_faceswap&utm_medium=sidebar_pro','_blank')" |
| ) |
| pro_api.click( |
| fn=None, inputs=None, outputs=None, |
| js="()=>window.open('https://www.face-swap.co/api?utm_source=hfspace_faceswap&utm_medium=sidebar_api','_blank')" |
| ) |
|
|
| demo.queue() |
|
|
| if __name__ == "__main__": |
| |
| |
| demo.launch(theme=gr.themes.Soft()) |
|
|