Spaces:
Running
Running
push
Browse files- .dockerignore +20 -0
- Dockerfile +37 -0
- README.md +115 -10
- app.py +126 -0
- models/face-gen-gan/generator_model_100.h5 +3 -0
- requirements.txt +7 -0
.dockerignore
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__pycache__
|
| 2 |
+
*.pyc
|
| 3 |
+
*.pyo
|
| 4 |
+
*.pyd
|
| 5 |
+
.Python
|
| 6 |
+
*.so
|
| 7 |
+
*.egg
|
| 8 |
+
*.egg-info
|
| 9 |
+
dist
|
| 10 |
+
build
|
| 11 |
+
.git
|
| 12 |
+
.gitignore
|
| 13 |
+
.vscode
|
| 14 |
+
.idea
|
| 15 |
+
*.swp
|
| 16 |
+
*.swo
|
| 17 |
+
*~
|
| 18 |
+
.DS_Store
|
| 19 |
+
README.md
|
| 20 |
+
.dockerignore
|
Dockerfile
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.10-slim
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
# Install system dependencies
|
| 6 |
+
RUN apt-get update && apt-get install -y \
|
| 7 |
+
gcc \
|
| 8 |
+
g++ \
|
| 9 |
+
libhdf5-dev \
|
| 10 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 11 |
+
|
| 12 |
+
# Upgrade pip first
|
| 13 |
+
RUN pip install --upgrade pip
|
| 14 |
+
|
| 15 |
+
# Copy requirements and install lightweight packages first
|
| 16 |
+
COPY requirements.txt .
|
| 17 |
+
|
| 18 |
+
# Install packages separately to handle large downloads better
|
| 19 |
+
RUN pip install --default-timeout=1000 --no-cache-dir fastapi[all] uvicorn[standard] pydantic
|
| 20 |
+
|
| 21 |
+
# Install TensorFlow CPU (smaller and faster to download than GPU version)
|
| 22 |
+
RUN pip install --default-timeout=1000 --no-cache-dir tensorflow-cpu==2.15.0
|
| 23 |
+
|
| 24 |
+
# Install remaining packages
|
| 25 |
+
RUN pip install --default-timeout=1000 --no-cache-dir keras==2.15.0 numpy==1.26.4 pillow
|
| 26 |
+
|
| 27 |
+
# Copy application code
|
| 28 |
+
COPY app.py .
|
| 29 |
+
|
| 30 |
+
# Copy model files
|
| 31 |
+
COPY models/ ./models/
|
| 32 |
+
|
| 33 |
+
# Expose port
|
| 34 |
+
EXPOSE 8002
|
| 35 |
+
|
| 36 |
+
# Run the application
|
| 37 |
+
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8002"]
|
README.md
CHANGED
|
@@ -1,10 +1,115 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Face Generator FastAPI
|
| 2 |
+
|
| 3 |
+
FastAPI service for the GAN-based face generation model.
|
| 4 |
+
|
| 5 |
+
## Setup
|
| 6 |
+
|
| 7 |
+
### Local Development
|
| 8 |
+
|
| 9 |
+
1. Install dependencies:
|
| 10 |
+
```bash
|
| 11 |
+
pip install -r requirements.txt
|
| 12 |
+
```
|
| 13 |
+
|
| 14 |
+
2. Run the app:
|
| 15 |
+
```bash
|
| 16 |
+
python app.py
|
| 17 |
+
# or
|
| 18 |
+
uvicorn app:app --host 0.0.0.0 --port 8002 --reload
|
| 19 |
+
```
|
| 20 |
+
|
| 21 |
+
3. Access the API:
|
| 22 |
+
- API: http://localhost:8002
|
| 23 |
+
- Interactive docs: http://localhost:8002/docs
|
| 24 |
+
- ReDoc: http://localhost:8002/redoc
|
| 25 |
+
|
| 26 |
+
### Docker
|
| 27 |
+
|
| 28 |
+
1. Build the image:
|
| 29 |
+
```bash
|
| 30 |
+
docker build -t facegen-api .
|
| 31 |
+
```
|
| 32 |
+
|
| 33 |
+
2. Run the container:
|
| 34 |
+
```bash
|
| 35 |
+
docker run -p 8002:8002 facegen-api
|
| 36 |
+
```
|
| 37 |
+
|
| 38 |
+
## API Endpoints
|
| 39 |
+
|
| 40 |
+
### GET `/`
|
| 41 |
+
Health check and info
|
| 42 |
+
```bash
|
| 43 |
+
curl http://localhost:8002/
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
### GET `/health`
|
| 47 |
+
Detailed health status
|
| 48 |
+
```bash
|
| 49 |
+
curl http://localhost:8002/health
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
+
### POST `/generate`
|
| 53 |
+
Generate face images (returns PNG image)
|
| 54 |
+
|
| 55 |
+
Request body:
|
| 56 |
+
```json
|
| 57 |
+
{
|
| 58 |
+
"n_samples": 4,
|
| 59 |
+
"seed": 42
|
| 60 |
+
}
|
| 61 |
+
```
|
| 62 |
+
|
| 63 |
+
Example:
|
| 64 |
+
```bash
|
| 65 |
+
# Generate single face
|
| 66 |
+
curl -X POST http://localhost:8002/generate \
|
| 67 |
+
-H "Content-Type: application/json" \
|
| 68 |
+
-d '{"n_samples": 1, "seed": 42}' \
|
| 69 |
+
--output face.png
|
| 70 |
+
|
| 71 |
+
# Generate 4 faces in a grid
|
| 72 |
+
curl -X POST http://localhost:8002/generate \
|
| 73 |
+
-H "Content-Type: application/json" \
|
| 74 |
+
-d '{"n_samples": 4, "seed": 123}' \
|
| 75 |
+
--output faces_grid.png
|
| 76 |
+
```
|
| 77 |
+
|
| 78 |
+
### GET `/generate-single`
|
| 79 |
+
Quick endpoint to generate a single face
|
| 80 |
+
|
| 81 |
+
Example:
|
| 82 |
+
```bash
|
| 83 |
+
# Random face
|
| 84 |
+
curl http://localhost:8002/generate-single --output face.png
|
| 85 |
+
|
| 86 |
+
# With seed
|
| 87 |
+
curl "http://localhost:8002/generate-single?seed=42" --output face.png
|
| 88 |
+
```
|
| 89 |
+
|
| 90 |
+
## Model
|
| 91 |
+
|
| 92 |
+
- **Model**: Face Generation GAN
|
| 93 |
+
- **Location**: `./models/face-gen-gan`
|
| 94 |
+
- **Type**: Generative Adversarial Network
|
| 95 |
+
- **Port**: 8002
|
| 96 |
+
|
| 97 |
+
## Parameters
|
| 98 |
+
|
| 99 |
+
- **n_samples**: Number of faces to generate (1-16, default: 1)
|
| 100 |
+
- **seed**: Random seed for reproducibility (optional)
|
| 101 |
+
|
| 102 |
+
## Response Format
|
| 103 |
+
|
| 104 |
+
- Returns PNG image
|
| 105 |
+
- Single face: Original resolution
|
| 106 |
+
- Multiple faces: Grid layout (auto-calculated)
|
| 107 |
+
|
| 108 |
+
## Features
|
| 109 |
+
|
| 110 |
+
- CORS enabled for all origins
|
| 111 |
+
- Returns images as PNG
|
| 112 |
+
- Grid layout for multiple faces
|
| 113 |
+
- Reproducible generation with seeds
|
| 114 |
+
- FastAPI automatic documentation
|
| 115 |
+
- Health check endpoints
|
app.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI
|
| 2 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
+
from fastapi.responses import StreamingResponse
|
| 4 |
+
from pydantic import BaseModel
|
| 5 |
+
import numpy as np
|
| 6 |
+
from keras.models import load_model
|
| 7 |
+
from PIL import Image
|
| 8 |
+
import io
|
| 9 |
+
from typing import Optional
|
| 10 |
+
|
| 11 |
+
app = FastAPI(title="Face Generator API", version="1.0.0")
|
| 12 |
+
|
| 13 |
+
# Add CORS middleware
|
| 14 |
+
app.add_middleware(
|
| 15 |
+
CORSMiddleware,
|
| 16 |
+
allow_origins=["*"],
|
| 17 |
+
allow_credentials=True,
|
| 18 |
+
allow_methods=["*"],
|
| 19 |
+
allow_headers=["*"],
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
# Global variables for model
|
| 23 |
+
MODEL_PATH = "./models/face-gen-gan"
|
| 24 |
+
model = None
|
| 25 |
+
latent_dim = None
|
| 26 |
+
|
| 27 |
+
# Request models
|
| 28 |
+
class FaceGenRequest(BaseModel):
|
| 29 |
+
n_samples: Optional[int] = 1
|
| 30 |
+
seed: Optional[int] = None
|
| 31 |
+
|
| 32 |
+
@app.on_event("startup")
|
| 33 |
+
async def load_gan_model():
|
| 34 |
+
"""Load the GAN model on startup"""
|
| 35 |
+
global model, latent_dim
|
| 36 |
+
print(f"Loading face generation GAN model from {MODEL_PATH}...")
|
| 37 |
+
|
| 38 |
+
model = load_model(MODEL_PATH)
|
| 39 |
+
latent_dim = model.input_shape[1]
|
| 40 |
+
|
| 41 |
+
print(f"Model loaded successfully! Latent dimension: {latent_dim}")
|
| 42 |
+
|
| 43 |
+
@app.get("/")
|
| 44 |
+
def root():
|
| 45 |
+
return {
|
| 46 |
+
"message": "Face Generator API",
|
| 47 |
+
"status": "running",
|
| 48 |
+
"model": "face-gen-gan",
|
| 49 |
+
"latent_dim": latent_dim
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
@app.get("/health")
|
| 53 |
+
def health():
|
| 54 |
+
return {
|
| 55 |
+
"status": "healthy",
|
| 56 |
+
"model_loaded": model is not None,
|
| 57 |
+
"latent_dim": latent_dim
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
@app.post("/generate")
|
| 61 |
+
async def generate_faces(request: FaceGenRequest):
|
| 62 |
+
"""
|
| 63 |
+
Generate face images using the GAN model
|
| 64 |
+
Returns a PNG image (single face or grid of faces)
|
| 65 |
+
"""
|
| 66 |
+
if model is None:
|
| 67 |
+
return {"error": "Model not loaded"}
|
| 68 |
+
|
| 69 |
+
try:
|
| 70 |
+
# Validate n_samples
|
| 71 |
+
n_samples = max(1, min(request.n_samples, 16)) # Limit to 1-16
|
| 72 |
+
|
| 73 |
+
# Set seed if provided
|
| 74 |
+
if request.seed is not None:
|
| 75 |
+
np.random.seed(request.seed)
|
| 76 |
+
|
| 77 |
+
# Generate random latent points
|
| 78 |
+
latent_points = np.random.randn(n_samples, latent_dim)
|
| 79 |
+
|
| 80 |
+
# Generate images
|
| 81 |
+
generated_images = model.predict(latent_points, verbose=0)
|
| 82 |
+
|
| 83 |
+
# Scale from [-1, 1] to [0, 255]
|
| 84 |
+
generated_images = ((generated_images + 1) / 2.0 * 255).astype(np.uint8)
|
| 85 |
+
|
| 86 |
+
if n_samples == 1:
|
| 87 |
+
# Single image
|
| 88 |
+
img = Image.fromarray(generated_images[0])
|
| 89 |
+
else:
|
| 90 |
+
# Create a grid
|
| 91 |
+
grid_size = int(np.ceil(np.sqrt(n_samples)))
|
| 92 |
+
img_height, img_width = generated_images.shape[1:3]
|
| 93 |
+
|
| 94 |
+
# Create blank canvas
|
| 95 |
+
grid_img = np.ones((grid_size * img_height, grid_size * img_width, 3), dtype=np.uint8) * 255
|
| 96 |
+
|
| 97 |
+
# Fill grid with generated images
|
| 98 |
+
for i in range(n_samples):
|
| 99 |
+
row = i // grid_size
|
| 100 |
+
col = i % grid_size
|
| 101 |
+
grid_img[row*img_height:(row+1)*img_height,
|
| 102 |
+
col*img_width:(col+1)*img_width] = generated_images[i]
|
| 103 |
+
|
| 104 |
+
img = Image.fromarray(grid_img)
|
| 105 |
+
|
| 106 |
+
# Convert to bytes
|
| 107 |
+
buf = io.BytesIO()
|
| 108 |
+
img.save(buf, format='PNG')
|
| 109 |
+
buf.seek(0)
|
| 110 |
+
|
| 111 |
+
return StreamingResponse(buf, media_type="image/png")
|
| 112 |
+
|
| 113 |
+
except Exception as e:
|
| 114 |
+
return {"error": str(e)}
|
| 115 |
+
|
| 116 |
+
@app.get("/generate-single")
|
| 117 |
+
async def generate_single_face(seed: Optional[int] = None):
|
| 118 |
+
"""
|
| 119 |
+
Quick endpoint to generate a single face
|
| 120 |
+
"""
|
| 121 |
+
request = FaceGenRequest(n_samples=1, seed=seed)
|
| 122 |
+
return await generate_faces(request)
|
| 123 |
+
|
| 124 |
+
if __name__ == "__main__":
|
| 125 |
+
import uvicorn
|
| 126 |
+
uvicorn.run(app, host="0.0.0.0", port=8002)
|
models/face-gen-gan/generator_model_100.h5
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:cd167921ef33931acace69054326314197b8bc340bd702503f167710f21f7235
|
| 3 |
+
size 5563784
|
requirements.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi[all]
|
| 2 |
+
uvicorn[standard]
|
| 3 |
+
tensorflow-cpu==2.15.0
|
| 4 |
+
keras==2.15.0
|
| 5 |
+
numpy==1.26.4
|
| 6 |
+
pillow
|
| 7 |
+
pydantic
|