Spaces:
Running
Running
Commit ·
f9ff1a1
1
Parent(s): e7a5a81
feat: CI/CD pipeline + Docker + HuggingFace Spaces deployment
Browse files- Add Dockerfile (multi-stage: Node build + Python runtime)
- Add startup.sh (auto-downloads inswapper model on cold start)
- Add requirements-deploy.txt (CPU-only for cloud)
- Add GitHub Actions CI workflow (backend tests + React build)
- Add GitHub Actions deploy workflow (push to HF Spaces with Firebase config injection)
- Add README.md with HuggingFace Spaces metadata
- Flask PORT now reads from environment variable (7860 for HF Spaces)
- Replace emoji feature icons with SVG icons
- .github/workflows/ci.yml +71 -0
- .github/workflows/deploy.yml +75 -0
- Dockerfile +49 -0
- README.md +73 -0
- frontend/src/pages/LandingPage.jsx +16 -7
- requirements-deploy.txt +32 -0
- startup.sh +47 -0
- web_app.py +4 -3
.github/workflows/ci.yml
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: CI
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
branches: [main, develop]
|
| 6 |
+
pull_request:
|
| 7 |
+
branches: [main]
|
| 8 |
+
|
| 9 |
+
jobs:
|
| 10 |
+
# ── Backend Tests ─────────────────────────────────────────────────────────
|
| 11 |
+
backend:
|
| 12 |
+
name: Backend Tests
|
| 13 |
+
runs-on: ubuntu-latest
|
| 14 |
+
|
| 15 |
+
steps:
|
| 16 |
+
- name: Checkout
|
| 17 |
+
uses: actions/checkout@v4
|
| 18 |
+
|
| 19 |
+
- name: Set up Python 3.10
|
| 20 |
+
uses: actions/setup-python@v5
|
| 21 |
+
with:
|
| 22 |
+
python-version: "3.10"
|
| 23 |
+
cache: pip
|
| 24 |
+
|
| 25 |
+
- name: Install CI dependencies (lightweight subset)
|
| 26 |
+
run: |
|
| 27 |
+
pip install --upgrade pip
|
| 28 |
+
pip install \
|
| 29 |
+
opencv-python-headless==4.9.0.80 \
|
| 30 |
+
numpy==1.26.4 \
|
| 31 |
+
Pillow==10.3.0 \
|
| 32 |
+
scipy==1.13.0 \
|
| 33 |
+
scikit-image==0.23.2 \
|
| 34 |
+
flask==3.0.3 \
|
| 35 |
+
flask-cors==4.0.1 \
|
| 36 |
+
python-dotenv==1.0.1 \
|
| 37 |
+
pytest==8.2.0
|
| 38 |
+
|
| 39 |
+
- name: Run tests
|
| 40 |
+
run: python -m pytest tests/ -v --tb=short
|
| 41 |
+
|
| 42 |
+
# ── Frontend Build ────────────────────────────────────────────────────────
|
| 43 |
+
frontend:
|
| 44 |
+
name: Frontend Build
|
| 45 |
+
runs-on: ubuntu-latest
|
| 46 |
+
|
| 47 |
+
steps:
|
| 48 |
+
- name: Checkout
|
| 49 |
+
uses: actions/checkout@v4
|
| 50 |
+
|
| 51 |
+
- name: Set up Node 20
|
| 52 |
+
uses: actions/setup-node@v4
|
| 53 |
+
with:
|
| 54 |
+
node-version: "20"
|
| 55 |
+
cache: npm
|
| 56 |
+
cache-dependency-path: frontend/package-lock.json
|
| 57 |
+
|
| 58 |
+
- name: Install dependencies
|
| 59 |
+
working-directory: frontend
|
| 60 |
+
run: npm ci --silent
|
| 61 |
+
|
| 62 |
+
- name: Build React app
|
| 63 |
+
working-directory: frontend
|
| 64 |
+
run: npm run build
|
| 65 |
+
|
| 66 |
+
- name: Upload React build artifact
|
| 67 |
+
uses: actions/upload-artifact@v4
|
| 68 |
+
with:
|
| 69 |
+
name: react-build
|
| 70 |
+
path: static/react/
|
| 71 |
+
retention-days: 7
|
.github/workflows/deploy.yml
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Deploy to Hugging Face Spaces
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
branches: [main]
|
| 6 |
+
|
| 7 |
+
jobs:
|
| 8 |
+
# ── Wait for CI to pass first ─────────────────────────────────────────────
|
| 9 |
+
deploy:
|
| 10 |
+
name: Push to Hugging Face Spaces
|
| 11 |
+
runs-on: ubuntu-latest
|
| 12 |
+
|
| 13 |
+
steps:
|
| 14 |
+
- name: Checkout (full history)
|
| 15 |
+
uses: actions/checkout@v4
|
| 16 |
+
with:
|
| 17 |
+
fetch-depth: 0
|
| 18 |
+
|
| 19 |
+
# ── Inject Firebase config from GitHub Secrets ──────────────────────
|
| 20 |
+
- name: Inject Firebase config into firebase.js
|
| 21 |
+
run: |
|
| 22 |
+
cat > frontend/src/firebase.js << 'EOF'
|
| 23 |
+
import { initializeApp } from 'firebase/app';
|
| 24 |
+
import { getAuth, GoogleAuthProvider, signInWithPopup, signOut, onAuthStateChanged } from 'firebase/auth';
|
| 25 |
+
|
| 26 |
+
const firebaseConfig = {
|
| 27 |
+
apiKey: "${{ secrets.FIREBASE_API_KEY }}",
|
| 28 |
+
authDomain: "${{ secrets.FIREBASE_AUTH_DOMAIN }}",
|
| 29 |
+
projectId: "${{ secrets.FIREBASE_PROJECT_ID }}",
|
| 30 |
+
storageBucket: "${{ secrets.FIREBASE_STORAGE_BUCKET }}",
|
| 31 |
+
messagingSenderId: "${{ secrets.FIREBASE_MESSAGING_SENDER_ID }}",
|
| 32 |
+
appId: "${{ secrets.FIREBASE_APP_ID }}",
|
| 33 |
+
};
|
| 34 |
+
|
| 35 |
+
const fbApp = initializeApp(firebaseConfig);
|
| 36 |
+
const auth = getAuth(fbApp);
|
| 37 |
+
const googleProvider = new GoogleAuthProvider();
|
| 38 |
+
|
| 39 |
+
export { auth, googleProvider, signInWithPopup, signOut, onAuthStateChanged };
|
| 40 |
+
EOF
|
| 41 |
+
|
| 42 |
+
# ── Build React with real Firebase config ────────────────────────────
|
| 43 |
+
- name: Set up Node 20
|
| 44 |
+
uses: actions/setup-node@v4
|
| 45 |
+
with:
|
| 46 |
+
node-version: "20"
|
| 47 |
+
cache: npm
|
| 48 |
+
cache-dependency-path: frontend/package-lock.json
|
| 49 |
+
|
| 50 |
+
- name: Install frontend dependencies
|
| 51 |
+
working-directory: frontend
|
| 52 |
+
run: npm ci --silent
|
| 53 |
+
|
| 54 |
+
- name: Build React app (with real Firebase config)
|
| 55 |
+
working-directory: frontend
|
| 56 |
+
run: npm run build
|
| 57 |
+
|
| 58 |
+
# ── Push to Hugging Face Spaces ──────────────────────────────────────
|
| 59 |
+
- name: Configure git for HF push
|
| 60 |
+
run: |
|
| 61 |
+
git config user.email "adivid198986@gmail.com"
|
| 62 |
+
git config user.name "ADiTyaRaj8969"
|
| 63 |
+
|
| 64 |
+
- name: Add built React to commit
|
| 65 |
+
run: |
|
| 66 |
+
git add static/react/ -f
|
| 67 |
+
git add frontend/src/firebase.js -f
|
| 68 |
+
git diff --cached --quiet || git commit -m "ci: build React with Firebase config [skip ci]"
|
| 69 |
+
|
| 70 |
+
- name: Push to Hugging Face Spaces
|
| 71 |
+
env:
|
| 72 |
+
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
| 73 |
+
run: |
|
| 74 |
+
git remote add hf https://ADiTyaRaj8969:${HF_TOKEN}@huggingface.co/spaces/ADiTyaRaj8969/FaceSwap || true
|
| 75 |
+
git push hf main --force
|
Dockerfile
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ── Stage 1: Build React frontend ────────────────────────────────────────────
|
| 2 |
+
FROM node:20-slim AS frontend-builder
|
| 3 |
+
|
| 4 |
+
WORKDIR /app
|
| 5 |
+
COPY frontend/package*.json frontend/
|
| 6 |
+
RUN cd frontend && npm ci --silent
|
| 7 |
+
COPY frontend/ frontend/
|
| 8 |
+
RUN mkdir -p static/react
|
| 9 |
+
RUN cd frontend && npm run build
|
| 10 |
+
# Output is at /app/static/react (vite outDir: ../static/react)
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
# ── Stage 2: Python runtime ───────────────────────────────────────────────────
|
| 14 |
+
FROM python:3.10-slim
|
| 15 |
+
|
| 16 |
+
# System libs required by OpenCV and MediaPipe
|
| 17 |
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 18 |
+
libgl1-mesa-glx \
|
| 19 |
+
libglib2.0-0 \
|
| 20 |
+
libsm6 \
|
| 21 |
+
libxrender1 \
|
| 22 |
+
libxext6 \
|
| 23 |
+
wget \
|
| 24 |
+
git \
|
| 25 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 26 |
+
|
| 27 |
+
WORKDIR /app
|
| 28 |
+
|
| 29 |
+
# Install Python dependencies (CPU-only for cloud deployment)
|
| 30 |
+
COPY requirements-deploy.txt .
|
| 31 |
+
RUN pip install --no-cache-dir -r requirements-deploy.txt
|
| 32 |
+
|
| 33 |
+
# Copy React build from stage 1
|
| 34 |
+
COPY --from=frontend-builder /app/static/react ./static/react
|
| 35 |
+
|
| 36 |
+
# Copy application code
|
| 37 |
+
COPY . .
|
| 38 |
+
|
| 39 |
+
# Create runtime directories
|
| 40 |
+
RUN mkdir -p models uploads/temp outputs/results
|
| 41 |
+
|
| 42 |
+
# HuggingFace Spaces listens on 7860
|
| 43 |
+
ENV PORT=7860
|
| 44 |
+
EXPOSE 7860
|
| 45 |
+
|
| 46 |
+
# Make startup script executable
|
| 47 |
+
RUN chmod +x startup.sh
|
| 48 |
+
|
| 49 |
+
CMD ["./startup.sh"]
|
README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: DeepFace Studio
|
| 3 |
+
colorFrom: green
|
| 4 |
+
colorTo: yellow
|
| 5 |
+
sdk: docker
|
| 6 |
+
app_port: 7860
|
| 7 |
+
pinned: false
|
| 8 |
+
license: mit
|
| 9 |
+
---
|
| 10 |
+
|
| 11 |
+
# DeepFace Studio
|
| 12 |
+
|
| 13 |
+
AI-powered face swap web application with seamless hair-to-neck blending, skin tone matching, and Firebase authentication.
|
| 14 |
+
|
| 15 |
+
## Features
|
| 16 |
+
- Live camera capture or image upload for source face
|
| 17 |
+
- Upload target face image
|
| 18 |
+
- Deep face swap using InsightFace inswapper model
|
| 19 |
+
- Hair-to-neck seamless blending (Laplacian + Poisson)
|
| 20 |
+
- Skin tone matching (CIE LAB colour transfer)
|
| 21 |
+
- Quality metrics after every swap
|
| 22 |
+
- Firebase Google Sign-in with 10-day session
|
| 23 |
+
- GPU + CPU support (auto-detected)
|
| 24 |
+
|
| 25 |
+
## Local Setup
|
| 26 |
+
|
| 27 |
+
### Requirements
|
| 28 |
+
- Python 3.10+
|
| 29 |
+
- Node.js 20+
|
| 30 |
+
|
| 31 |
+
### Installation
|
| 32 |
+
```bash
|
| 33 |
+
# Install Python dependencies
|
| 34 |
+
pip install -r requirements.txt
|
| 35 |
+
|
| 36 |
+
# Download the face swap model (~528 MB)
|
| 37 |
+
python scripts/download_models.py
|
| 38 |
+
|
| 39 |
+
# Build the React frontend
|
| 40 |
+
cd frontend
|
| 41 |
+
npm install
|
| 42 |
+
npm run build
|
| 43 |
+
cd ..
|
| 44 |
+
|
| 45 |
+
# Start the app
|
| 46 |
+
python web_app.py
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
Open http://localhost:5000
|
| 50 |
+
|
| 51 |
+
### Firebase Setup
|
| 52 |
+
1. Go to https://console.firebase.google.com
|
| 53 |
+
2. Create a new project
|
| 54 |
+
3. Add a Web App
|
| 55 |
+
4. Enable Google Sign-In under Authentication
|
| 56 |
+
5. Copy the config into `frontend/src/firebase.js`
|
| 57 |
+
6. Rebuild the frontend: `cd frontend && npm run build`
|
| 58 |
+
|
| 59 |
+
## Deployment (HuggingFace Spaces)
|
| 60 |
+
The app deploys automatically via GitHub Actions on every push to `main`.
|
| 61 |
+
The inswapper model is downloaded automatically at container startup.
|
| 62 |
+
|
| 63 |
+
## Tech Stack
|
| 64 |
+
- **Frontend**: React 18 + Vite + TailwindCSS + Framer Motion
|
| 65 |
+
- **Backend**: Python + Flask
|
| 66 |
+
- **AI Models**: InsightFace (inswapper_128), MediaPipe, OpenCV
|
| 67 |
+
- **Auth**: Firebase Google Sign-In
|
| 68 |
+
|
| 69 |
+
## Note
|
| 70 |
+
This tool is for educational purposes only. Use responsibly and only with images you own or have explicit consent to use.
|
| 71 |
+
|
| 72 |
+
---
|
| 73 |
+
© Aditya Raj 2026
|
frontend/src/pages/LandingPage.jsx
CHANGED
|
@@ -11,13 +11,22 @@ import AnimatedContent from '../components/ui/AnimatedContent';
|
|
| 11 |
import Magnet from '../components/ui/Magnet';
|
| 12 |
import ShinyText from '../components/ui/ShinyText';
|
| 13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
const FEATURES = [
|
| 15 |
-
{ icon:
|
| 16 |
-
{ icon:
|
| 17 |
-
{ icon:
|
| 18 |
-
{ icon:
|
| 19 |
-
{ icon:
|
| 20 |
-
{ icon:
|
| 21 |
];
|
| 22 |
|
| 23 |
const PIPELINE = ['Detect', 'Align', 'Swap', 'Tone Match', 'Blend', 'Enhance', 'Output'];
|
|
@@ -193,7 +202,7 @@ export default function LandingPage() {
|
|
| 193 |
{FEATURES.map((f, i) => (
|
| 194 |
<AnimatedContent key={f.title} delay={i * 0.07} direction="up">
|
| 195 |
<SpotlightCard className="bg-bg3 border border-forest rounded-2xl p-5 sm:p-7 h-full hover:border-olive hover:-translate-y-1 transition-all duration-200">
|
| 196 |
-
<div className="w-10 h-10 sm:w-11 sm:h-11 bg-olive/20 border border-olive/40 rounded-xl flex items-center justify-center text-
|
| 197 |
{f.icon}
|
| 198 |
</div>
|
| 199 |
<h3 className="text-lime font-bold mb-1.5 text-sm sm:text-base">{f.title}</h3>
|
|
|
|
| 11 |
import Magnet from '../components/ui/Magnet';
|
| 12 |
import ShinyText from '../components/ui/ShinyText';
|
| 13 |
|
| 14 |
+
const FEATURE_ICONS = {
|
| 15 |
+
camera: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5"><path d="M23 7l-7 5 7 5V7z"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>,
|
| 16 |
+
layers: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></svg>,
|
| 17 |
+
palette: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="10" r="3"/><path d="M12 13v2"/><circle cx="9" cy="17" r="1" fill="currentColor" stroke="none"/><circle cx="12" cy="18" r="1" fill="currentColor" stroke="none"/><circle cx="15" cy="17" r="1" fill="currentColor" stroke="none"/></svg>,
|
| 18 |
+
chart: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/></svg>,
|
| 19 |
+
zap: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>,
|
| 20 |
+
lock: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>,
|
| 21 |
+
};
|
| 22 |
+
|
| 23 |
const FEATURES = [
|
| 24 |
+
{ icon: FEATURE_ICONS.camera, title: 'Live Camera + Upload', desc: 'Capture from webcam or upload any image. Instant face detection with bounding box feedback.' },
|
| 25 |
+
{ icon: FEATURE_ICONS.layers, title: 'Hair to Neck Blending', desc: 'Seamless multi-layer mask blending from hairline through face to neck using Poisson cloning.' },
|
| 26 |
+
{ icon: FEATURE_ICONS.palette, title: 'Skin Tone Matching', desc: 'CIE LAB colour transfer adapts the swapped face to match target skin across all skin types.' },
|
| 27 |
+
{ icon: FEATURE_ICONS.chart, title: 'Quality Metrics', desc: 'Alignment score, blend quality, colour Delta E, and naturalness score after every swap.' },
|
| 28 |
+
{ icon: FEATURE_ICONS.zap, title: 'GPU + CPU Support', desc: 'CUDA optimised InsightFace on GPU, automatic CPU fallback. Same output quality on both.' },
|
| 29 |
+
{ icon: FEATURE_ICONS.lock, title: 'Fully Private', desc: 'All processing runs on your own machine. No images are uploaded to any external server.' },
|
| 30 |
];
|
| 31 |
|
| 32 |
const PIPELINE = ['Detect', 'Align', 'Swap', 'Tone Match', 'Blend', 'Enhance', 'Output'];
|
|
|
|
| 202 |
{FEATURES.map((f, i) => (
|
| 203 |
<AnimatedContent key={f.title} delay={i * 0.07} direction="up">
|
| 204 |
<SpotlightCard className="bg-bg3 border border-forest rounded-2xl p-5 sm:p-7 h-full hover:border-olive hover:-translate-y-1 transition-all duration-200">
|
| 205 |
+
<div className="w-10 h-10 sm:w-11 sm:h-11 bg-olive/20 border border-olive/40 rounded-xl flex items-center justify-center text-lime mb-3 sm:mb-4">
|
| 206 |
{f.icon}
|
| 207 |
</div>
|
| 208 |
<h3 className="text-lime font-bold mb-1.5 text-sm sm:text-base">{f.title}</h3>
|
requirements-deploy.txt
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# requirements-deploy.txt
|
| 2 |
+
# CPU-only deployment (HuggingFace Spaces / Render / Railway)
|
| 3 |
+
# GPU version: swap onnxruntime -> onnxruntime-gpu and torch CPU -> CUDA build
|
| 4 |
+
|
| 5 |
+
# Core web
|
| 6 |
+
flask==3.0.3
|
| 7 |
+
flask-cors==4.0.1
|
| 8 |
+
|
| 9 |
+
# Image processing
|
| 10 |
+
opencv-python-headless==4.9.0.80
|
| 11 |
+
numpy==1.26.4
|
| 12 |
+
Pillow==10.3.0
|
| 13 |
+
scipy==1.13.0
|
| 14 |
+
scikit-image==0.23.2
|
| 15 |
+
imageio==2.34.1
|
| 16 |
+
|
| 17 |
+
# Face detection and landmarks
|
| 18 |
+
insightface==0.7.3
|
| 19 |
+
onnxruntime==1.18.0
|
| 20 |
+
mediapipe==0.10.14
|
| 21 |
+
|
| 22 |
+
# PyTorch CPU-only (much smaller than CUDA build)
|
| 23 |
+
torch==2.3.0+cpu
|
| 24 |
+
torchvision==0.18.0+cpu
|
| 25 |
+
--extra-index-url https://download.pytorch.org/whl/cpu
|
| 26 |
+
|
| 27 |
+
# Color and utils
|
| 28 |
+
colormath==3.0.0
|
| 29 |
+
python-dotenv==1.0.1
|
| 30 |
+
requests==2.32.2
|
| 31 |
+
tqdm==4.66.4
|
| 32 |
+
pyyaml==6.0.1
|
startup.sh
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
set -e
|
| 3 |
+
|
| 4 |
+
echo ""
|
| 5 |
+
echo "======================================"
|
| 6 |
+
echo " DeepFace Studio — Starting Up"
|
| 7 |
+
echo "======================================"
|
| 8 |
+
echo ""
|
| 9 |
+
|
| 10 |
+
# Download inswapper_128.onnx if not already present
|
| 11 |
+
MODEL_PATH="models/inswapper_128.onnx"
|
| 12 |
+
MODEL_URL="https://huggingface.co/ezioruan/inswapper_128.onnx/resolve/main/inswapper_128.onnx"
|
| 13 |
+
MODEL_SIZE_MB=528
|
| 14 |
+
|
| 15 |
+
if [ ! -f "$MODEL_PATH" ]; then
|
| 16 |
+
echo "Downloading face swap model (~${MODEL_SIZE_MB} MB)..."
|
| 17 |
+
mkdir -p models
|
| 18 |
+
python3 - <<'PYEOF'
|
| 19 |
+
import os, requests, sys
|
| 20 |
+
|
| 21 |
+
url = "https://huggingface.co/ezioruan/inswapper_128.onnx/resolve/main/inswapper_128.onnx"
|
| 22 |
+
dest = "models/inswapper_128.onnx"
|
| 23 |
+
|
| 24 |
+
resp = requests.get(url, stream=True, timeout=300)
|
| 25 |
+
total = int(resp.headers.get("content-length", 0))
|
| 26 |
+
done = 0
|
| 27 |
+
|
| 28 |
+
with open(dest, "wb") as f:
|
| 29 |
+
for chunk in resp.iter_content(chunk_size=4 * 1024 * 1024):
|
| 30 |
+
f.write(chunk)
|
| 31 |
+
done += len(chunk)
|
| 32 |
+
if total:
|
| 33 |
+
pct = done * 100 // total
|
| 34 |
+
mb = done // 1024 // 1024
|
| 35 |
+
print(f"\r {mb} / {total // 1024 // 1024} MB ({pct}%)", end="", flush=True)
|
| 36 |
+
|
| 37 |
+
print("\n Model downloaded successfully.")
|
| 38 |
+
PYEOF
|
| 39 |
+
else
|
| 40 |
+
echo "Model already present — skipping download."
|
| 41 |
+
fi
|
| 42 |
+
|
| 43 |
+
echo ""
|
| 44 |
+
echo "Starting Flask server on port ${PORT:-7860}..."
|
| 45 |
+
echo ""
|
| 46 |
+
|
| 47 |
+
exec python web_app.py
|
web_app.py
CHANGED
|
@@ -230,6 +230,7 @@ def api_download(filename):
|
|
| 230 |
|
| 231 |
|
| 232 |
if __name__ == "__main__":
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
|
|
|
|
|
| 230 |
|
| 231 |
|
| 232 |
if __name__ == "__main__":
|
| 233 |
+
port = int(os.environ.get("PORT", 5000))
|
| 234 |
+
debug = os.environ.get("FLASK_DEBUG", "true").lower() == "true"
|
| 235 |
+
print(f"Starting Face Swap Web App on port {port}...")
|
| 236 |
+
app.run(debug=debug, host="0.0.0.0", port=port)
|