Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

26 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Neural Style Transfer

CI Open In Colab


Live Demo Β  API Docs Β  Notebook


Transform any photograph into a masterpiece β€” powered by VGG-19, Gram matrix optimization, and a production-grade full-stack deployment across Vercel, Render, and Hugging Face Spaces.


✨ What Is This?

This is a production-grade Neural Style Transfer web application that applies the artistic style of famous paintings to your photographs using deep learning β€” in real time, from your browser.

This isn't a filter. This isn't a preset. This is genuine iterative pixel optimization β€” a frozen VGG-19 convolutional neural network repaints your image pixel-by-pixel, matching the texture statistics of a real painting using Gram matrix decomposition.

You upload a photo  +  You pick a painting  β†’  AI paints your photo in that style

πŸ–ΌοΈ Results Gallery

Original Photo Style Reference Stylized Output
Content Van Gogh β€” Starry Night Result
Content Hokusai β€” The Great Wave Result

πŸ“Έ Try it yourself β€” upload your own photo The app features a before/after drag slider with cinematic blur-reveal effect on every result.

βž• Add your own results: Drop before/after PNGs into docs/results/ and update this table.


🧠 The Algorithm β€” How It Actually Works

Based on the landmark paper:

A Neural Algorithm of Artistic Style β€” Gatys, Ecker & Bethge, 2015

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      NEURAL STYLE TRANSFER PIPELINE                         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                   β”‚          VGG-19  (weights FROZEN β€” never trained)        β”‚
β”‚  Content Image    β”‚                                                          β”‚
β”‚  (your photo)  ───┼──► conv1_1 ──────────────────────────► style loss β‘     β”‚
β”‚                   β”‚  β–Ί conv2_1 ──────────────────────────► style loss β‘‘    β”‚
β”‚  Style Image      β”‚  β–Ί conv3_1 ──────────────────────────► style loss β‘’    β”‚
β”‚  (the artwork) ───┼──► conv4_1 ──────────────────────────► style loss β‘£    β”‚
β”‚                   β”‚  β–Ί conv4_2 ──────────────────────────► content loss     β”‚
β”‚                   β”‚  β–Ί conv5_1 ──────────────────────────► style loss β‘€    β”‚
β”‚                   β”‚                                                          β”‚
β”‚                   β”‚  Style loss  = MSE( Gram(F_gen), Gram(F_style) )        β”‚
β”‚                   β”‚  Content loss = MSE( F_gen[4_2], F_content[4_2] )       β”‚
β”‚                   β”‚  Total loss  = Ξ± Β· content_loss + Ξ² Β· style_loss        β”‚
β”‚                   β”‚                                                          β”‚
β”‚  Canvas image ◄───┼── L-BFGS optimizer (minimizes Total loss on PIXELS)    β”‚
β”‚  (starts as       β”‚   ~300-400 iterations                                   β”‚
β”‚   content copy)   β”‚   Ξ²/Ξ± β‰ˆ 1,000,000  (style dominates)                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              ↓
                    Stylized Output ✨

πŸ”¬ The Gram Matrix β€” The Secret Sauce

The entire style capture mechanism is 3 lines of math:

def gram_matrix(features):              # features shape: [B, C, H, W]
    f = features.view(B * C, H * W)     # flatten spatial dimensions
    return torch.mm(f, f.t()) / (B*C*H*W)  # channel correlation matrix

# Result: CΓ—C matrix where cell[i,j] = "do channels i and j co-activate?"
# β†’ captures TEXTURE STATISTICS, completely ignoring spatial position
# β†’ same Gram matrix = same artistic texture, any composition

⚑ Why L-BFGS Over Adam?

Optimizer Iterations to convergence Use case
SGD ~10,000+ Large-scale training
Adam ~2,000+ Most deep learning
L-BFGS ~300–400 NST β€” small problem, needs curvature info

L-BFGS uses quasi-Newton second-order information β€” like reading a topographic map instead of walking blindly. For NST's small pixel-space optimization, it converges 5–10Γ— faster than Adam.


πŸ—οΈ System Architecture

Browser (React + Vite)
      β”‚
      β”‚  REST API  (JSON + multipart/form-data)
      β–Ό
FastAPI Backend (Render β€” Python 3.11)
      β”‚
      β”œβ”€β”€ POST /stylize ─────────────────────► Background Thread
      β”‚      └─ returns job_id instantly              β”‚
      β”‚                                               β”‚  run_style_transfer()
      β”œβ”€β”€ GET  /status/{id} ◄── poll / 3s ───────── β”‚  (3–8 min CPU)
      β”‚      └─ progress 0.0 β†’ 1.0                   β”‚
      β”‚                                               β”‚
      └── GET  /result/{id} ◄──── complete β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             └─ returns stylized PNG

NST Engine (nst_engine.py)
      β”œβ”€β”€ load_image()           PIL β†’ normalized tensor [1,3,H,W]
      β”œβ”€β”€ ContentLoss            MSE on conv4_2 feature maps
      β”œβ”€β”€ StyleLoss              MSE on Gram matrices (5 layers)
      β”œβ”€β”€ build_model_and_losses() VGG-19 with loss modules inserted inline
      └── run_style_transfer()   L-BFGS loop, progress callbacks

πŸ“ Project Structure

neural-style-transfer/
β”‚
β”œβ”€β”€ 🐍 backend/
β”‚   β”œβ”€β”€ main.py                # FastAPI β€” 6 endpoints, async job queue
β”‚   β”œβ”€β”€ nst_engine.py          # VGG-19 NST core β€” content/style loss, L-BFGS
β”‚   β”œβ”€β”€ download_styles.py     # Downloads 6 public domain artworks
β”‚   β”œβ”€β”€ requirements.txt
β”‚   β”œβ”€β”€ .python-version        # Pins Python 3.11 for Render
β”‚   β”œβ”€β”€ uploads/               # Temp storage for incoming images
β”‚   β”œβ”€β”€ outputs/               # Generated stylized results
β”‚   └── style_images/          # 6 preset masterworks (local only)
β”‚
β”œβ”€β”€ βš›οΈ  frontend/
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”œβ”€β”€ App.jsx            # State machine: idle β†’ processing β†’ done
β”‚   β”‚   β”œβ”€β”€ index.css          # Wildflowers palette + dark/light mode vars
β”‚   β”‚   β”œβ”€β”€ components/
β”‚   β”‚   β”‚   β”œβ”€β”€ UploadPanel.jsx    # Drag-and-drop with live preview
β”‚   β”‚   β”‚   β”œβ”€β”€ StyleGallery.jsx   # 6-card preset art style picker
β”‚   β”‚   β”‚   └── ResultViewer.jsx   # Before/after blur-reveal slider + download
β”‚   β”‚   β”œβ”€β”€ hooks/
β”‚   β”‚   β”‚   └── useJobPoller.js    # Polls /status every 3s (custom hook)
β”‚   β”‚   └── utils/
β”‚   β”‚       └── api.js             # All API calls in one place
β”‚   β”œβ”€β”€ public/style_images/   # Thumbnails served by Vercel CDN
β”‚   β”œβ”€β”€ vercel.json            # Vercel deployment config
β”‚   └── .env.production        # Points to Render backend URL
β”‚
β”œβ”€β”€ πŸ““ notebook/
β”‚   └── nst_colab.ipynb        # GPU notebook β€” visualizes Gram matrices + loss curves
β”‚
β”œβ”€β”€ πŸ€— spaces/
β”‚   └── app.py                 # Gradio UI for Hugging Face Spaces deployment
β”‚
β”œβ”€β”€ βš™οΈ  .github/
β”‚   β”œβ”€β”€ workflows/ci.yml       # Test β†’ Build β†’ Deploy on every push to main
β”‚   β”œβ”€β”€ CONTRIBUTING.md
β”‚   β”œβ”€β”€ ISSUE_TEMPLATE.md
β”‚   └── PULL_REQUEST_TEMPLATE.md
β”‚
β”œβ”€β”€ πŸ“š docs/
β”‚   β”œβ”€β”€ ARCHITECTURE.md        # Deep-dive system design
β”‚   └── results/               # Before/after example images
β”‚
β”œβ”€β”€ requirements.txt           # Root β€” used by Hugging Face Spaces
β”œβ”€β”€ .gitignore
β”œβ”€β”€ LICENSE                    # MIT
└── README.md                  ← you are here

πŸš€ Quick Start

☁️ Zero Setup β€” Use The Live App

Platform URL Notes
🌐 Vercel (React App) neural-style-transfer-pied.vercel.app Full UI, before/after slider
πŸ“‘ Render (API) neural-style-transfer-api.onrender.com/docs Interactive Swagger docs
πŸ€— HF Spaces (Gradio) huggingface.co/spaces/Tusharz/Neural-Style-Transfer ML community demo
πŸ““ Colab (Free GPU) Open Notebook T4 GPU, 30 sec/image

πŸ’» Run Locally

Prerequisites: Python 3.10+ Β· Node.js 18+ Β· Git

# 1. Clone
git clone https://github.com/TUSHARTAMRAKAR/Neural-Style-Transfer.git
cd neural-style-transfer

# 2. Backend β€” Terminal 1
cd backend
python -m venv venv
source venv/bin/activate        # Windows: venv\Scripts\activate
pip install -r requirements.txt
python download_styles.py       # Downloads 6 artworks automatically
uvicorn main:app --reload --port 8000

# 3. Frontend β€” Terminal 2
cd frontend
npm install --legacy-peer-deps
npm run dev
URL What
http://localhost:5173 React web app
http://localhost:8000/docs FastAPI interactive docs

πŸ“‘ API Reference

All endpoints are documented interactively at /docs (auto-generated by FastAPI).

GET  /              β†’ Health check
GET  /styles        β†’ List 6 preset artworks with metadata
POST /stylize       β†’ Submit job β†’ returns job_id immediately
GET  /status/{id}   β†’ Poll progress (0.0 β†’ 1.0) + loss metrics
GET  /result/{id}   β†’ Download stylized PNG
DEL  /job/{id}      β†’ Clean up uploaded files
GET  /jobs          β†’ List all jobs (debug)

Full Flow Example

# 1. Submit job
curl -X POST https://neural-style-transfer-api.onrender.com/stylize \
  -F "content_image=@photo.jpg" \
  -F "preset=starry_night" \
  -F "num_steps=300"

# Response:
# { "job_id": "f3a2b1c4-...", "status": "pending" }

# 2. Poll until complete
curl https://neural-style-transfer-api.onrender.com/status/f3a2b1c4-...
# { "status": "processing", "progress": 0.47, "step": 141, "total_steps": 300,
#   "content_loss": 1423.5, "style_loss": 98234.1 }

# 3. Download result
curl https://neural-style-transfer-api.onrender.com/result/f3a2b1c4-... \
  --output stylized.png

🎨 Style Presets & Tuned Settings

Key Artwork Artist Year Content Weight Style Weight
starry_night The Starry Night Van Gogh 1889 1Γ—10Β³ 1Γ—10⁹
the_scream The Scream Edvard Munch 1893 1Γ—10Β³ 8Γ—10⁸
kandinsky Composition VIII Kandinsky 1923 5Γ—10Β² 1.5Γ—10⁹
mosaic Ravenna Mosaic Byzantine 6th c. 1Γ—10Β³ 1.2Γ—10⁹
wave The Great Wave Hokusai 1831 1Γ—10Β³ 9Γ—10⁸
udnie Udnie Picabia 1913 5Γ—10Β² 1Γ—10⁹

Subject Mode Cheat Sheet

πŸ§‘ Portrait   β†’ steps: 300  Β· style: 80M   Β· content: 15K  (face preserved βœ…)
πŸŒ„ Landscape  β†’ steps: 400  Β· style: 400M  Β· content: 5K   (bold effect βœ…)
🎨 Max Style  β†’ steps: 400  Β· style: 900M  Β· content: 1K   (full artistic βœ…)

πŸ› οΈ Tech Stack

Layer Technology Version Why This Choice
AI Core PyTorch 2.x Industry standard, autograd, VGG-19 pretrained
Model VGG-19 (ImageNet) Pretrained Sequential layers ideal for NST feature extraction
Backend FastAPI 0.110 Async-native, auto-docs, Pydantic validation
Server Uvicorn 0.27 ASGI, production-grade, supports --reload
Frontend React 18 Component model, hooks, ecosystem
Build Vite 5.4 10Γ— faster than CRA, HMR, tree-shaking
Styling TailwindCSS 3.4 Utility-first, no CSS bloat
Fonts Playfair Display + Inter + JetBrains Mono β€” Elegant display + clean body + precise mono
Notebook Jupyter + Colab β€” Free T4 GPU, shareable, Gram matrix visualizations
Frontend Deploy Vercel β€” Global CDN, instant deploy, env vars, free
Backend Deploy Render β€” Docker-compatible, Python support, free tier
ML Demo Hugging Face Spaces β€” Gradio, ML community, free CPU
CI/CD GitHub Actions β€” Auto test + auto deploy on every push

πŸ”¬ Key Technical Decisions

Why VGG-19 over ResNet, EfficientNet, or Vision Transformers?

VGG-19's simple sequential architecture is essential for NST. We need to insert loss modules at specific intermediate layers and collect gradients — ResNet's skip connections and ViT's attention blocks would complicate the gradient flow dramatically. VGG's uniform conv→relu→pool structure gives us clean, hierarchical feature maps that perfectly separate low-level texture (early layers) from high-level structure (deep layers).

Why L-BFGS over Adam for optimization?

NST is an unusually small optimization problem β€” we're adjusting ~590K pixels (512Γ—512Γ—3) rather than millions of network parameters. L-BFGS's quasi-Newton line search with strong_wolfe conditions uses curvature information to take larger, smarter steps. It converges in ~300 iterations vs ~2000 for Adam. The max_iter=20 inner loop per step makes each outer iteration expensive but the total wall-clock time is dramatically lower.

Why does style_weight need to be 1,000,000Γ— content_weight?

Raw MSE on content features (conv4_2: 512 channels Γ— 32Γ—32 = 524,288 values) produces much larger gradients than style loss (Gram matrix: 64Γ—64 = 4,096 values per layer). Without the extreme ratio, content loss dominates and the result looks like "just contrast adjustment." The Ξ²/Ξ± β‰ˆ 1e6 ratio is what produces visible artistic transformation vs a subtle filter effect.

Why async job queue instead of synchronous response?

Style transfer takes 3–8 minutes on CPU. A synchronous HTTP response would timeout (default 30s), block the server thread, and give zero user feedback. The async job pattern β€” return job_id immediately, run NST in a background thread, poll /status/{id} every 3 seconds β€” is the correct architecture for any slow computation. It's what YouTube, Cloudinary, and every video/image processing service uses.

Why AvgPool instead of MaxPool in VGG-19?

We replace all MaxPool2d layers with AvgPool2d in the loss network. MaxPooling creates sharp edges in the gradient flow that produce visible artifacts in the stylized output. Average pooling produces smoother gradients β†’ cleaner, more painterly results. This detail is from the original Gatys et al. paper and makes a visible quality difference.


πŸ“Š Performance Benchmarks

Hardware Image Size Steps Time Quality
CPU (Intel i7-12th gen) 256Γ—256 300 ~3 min Good
CPU (Intel i7-12th gen) 384Γ—384 400 ~8 min Great
GPU (T4 β€” Colab free) 512Γ—512 400 ~35 sec Excellent
GPU (RTX 3080) 512Γ—512 400 ~12 sec Excellent
GPU (A100 β€” Colab Pro) 1024Γ—1024 500 ~45 sec Maximum

🌐 Deployment Architecture

GitHub (source of truth)
    β”‚
    β”œβ”€β”€β–Ί GitHub Actions CI
    β”‚         β”œβ”€β”€ Backend tests (Python)
    β”‚         β”œβ”€β”€ Frontend build (Vite)
    β”‚         └── Lint checks
    β”‚
    β”œβ”€β”€β–Ί Vercel (automatic on push to main)
    β”‚         └── React frontend β†’ global CDN
    β”‚               env: VITE_API_URL=https://neural-style-transfer-api.onrender.com
    β”‚
    β”œβ”€β”€β–Ί Render (automatic on push to main)
    β”‚         └── FastAPI backend β†’ Python 3.11
    β”‚               uvicorn main:app --host 0.0.0.0 --port $PORT
    β”‚
    └──► Hugging Face Spaces (git push hf main)
              └── Gradio app β†’ spaces/app.py
                    downloads style images at startup from GitHub raw URLs

🀝 Contributing

Contributions are very welcome! Here's how:

# Fork β†’ Clone β†’ Branch
git checkout -b feature/your-feature

# Make changes, then test
cd backend && python nst_engine.py          # smoke tests
cd frontend && npm run build                # build check

# Commit with conventional commits
git commit -m "feat: add your feature"
git commit -m "fix: fix the thing"
git commit -m "docs: update readme"

# Push and open PR
git push origin feature/your-feature

Ideas for contributions:

  • Fast NST (feed-forward network β€” 100Γ— faster inference)
  • WebSocket real-time progress instead of polling
  • User authentication + result history
  • Additional style presets
  • Mobile PWA wrapper

πŸ“š References

Resource Link
Original NST paper Gatys et al., 2015 β€” arxiv.org/abs/1508.06576
Fast NST paper Johnson et al., 2016 β€” arxiv.org/abs/1603.08155
VGG paper Simonyan & Zisserman, 2014 β€” arxiv.org/abs/1409.1556
PyTorch NST tutorial pytorch.org/tutorials/advanced/neural_style_tutorial
FastAPI docs fastapi.tiangolo.com
Vercel docs vercel.com/docs

πŸ“„ License

Distributed under the MIT License β€” see LICENSE for details.

Free to use, modify, and distribute. Attribution appreciated but not required.


Built with passion Β· Deployed with precision Β· Painted with algorithms




Tushar Tamrakar

Made with ❀️ by Tushar Tamrakar

Β 

If this project helped you, please consider giving it a ⭐ β€” it means a lot!


Star History Chart

About

🎨 Neural Style Transfer web app β€” apply Van Gogh, Hokusai & Kandinsky styles to your photos using VGG-19 + PyTorch. Full stack: FastAPI + React + Gradio. Live on Vercel.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages