AI-powered web application that detects and counts drones in uploaded images using a trained YOLOv8 model.
Swarmtally is a full-stack AI application that runs YOLOv8 drone detection inference on user-uploaded images. Upload a photo, and the system returns an annotated image with bounding boxes, a drone count, and per-detection confidence scores — all in under 200ms on a GPU.
Design theme: "Duty and Honor" — a military-inspired tactical interface using olive, saffron, and navy.
The default dashboard features a clean, military-tactical grid layout displaying the active AI model version, detection class, confidence threshold, and a drag-and-drop secure imagery transfer drop zone.
Once an image is selected, the visual image checker loads it instantly, displaying details like filename, file size, format, and status. This allows the operator to verify they have selected the correct image before submitting it to the YOLOv8 model for automated detection.
The results page displays the annotated image with saffron bounding boxes around detected targets, an absolute drone count, a threat level badge, and a tactical contact confidence log list displaying per-drone confidence progress bars and values.
- 🎯 Real-time drone detection using a custom-trained YOLOv8n model
- 📊 Per-drone confidence scores with visual progress bars
- 🖼️ Annotated output images with saffron bounding boxes (DRN-01, DRN-02...)
- ⚡ Fast non-blocking inference — runs in an async thread pool to handle concurrent requests efficiently
- 🧹 Session Auto-Cleanup — deletes uploaded files and annotated results from the server instantly when the user completes a session or runs another detection
- 🐳 Docker-ready — one command to run the full stack (with optimized python-based container healthchecks)
- 🚀 Deployment-ready — Vercel (frontend) + Render (backend) with explicit CORS configurations
- 📱 Responsive — works on desktop and mobile
| Layer | Technology | Version |
|---|---|---|
| Frontend | Next.js (App Router) + TypeScript | 14 |
| Styling | Tailwind CSS | 3.4 |
| Backend | FastAPI + Uvicorn | 0.111+ |
| AI Model | Ultralytics YOLOv8n | 8.2+ |
| Image Processing | OpenCV (headless) | 4.8+ |
| Containerization | Docker + Docker Compose | – |
| Frontend Deploy | Vercel | – |
| Backend Deploy | Render | – |
Drone_detection/
├── .github/
│ └── workflows/
│ └── ci.yml ← GitHub Actions CI pipeline
│
├── backend/
│ ├── main.py ← FastAPI app + YOLOv8 inference (core logic)
│ ├── config.py ← Centralized constants and settings
│ ├── logging_config.py ← Structured logging setup
│ ├── best.pt ← Trained YOLOv8 model weights
│ ├── requirements.txt ← Python dependencies
│ ├── Dockerfile ← Backend container definition
│ ├── render.yaml ← Render deployment config
│ ├── Procfile ← Process file for Render/Heroku
│ ├── runtime.txt ← Python version for Render
│ ├── .env.example ← Environment variable template
│ ├── uploads/ ← Auto-created: temp upload storage
│ └── outputs/ ← Auto-created: annotated result images
│
├── frontend/
│ ├── src/
│ │ ├── app/
│ │ │ ├── page.tsx ← Upload / Dashboard page
│ │ │ ├── results/page.tsx ← Detection results page
│ │ │ ├── layout.tsx ← Root layout + SEO metadata
│ │ │ ├── globals.css ← Global styles + design tokens
│ │ │ └── api/detect/route.ts ← Next.js API proxy to backend
│ │ ├── components/
│ │ │ ├── Navbar.tsx ← Desktop navigation
│ │ │ ├── MobileNav.tsx ← Mobile bottom navigation
│ │ │ ├── UploadZone.tsx ← Drag-and-drop file upload
│ │ │ └── SkeletonLoader.tsx ← Loading skeleton UI
│ │ ├── lib/
│ │ │ └── api.ts ← Type-safe API service layer
│ │ └── types/
│ │ └── detection.ts ← Shared TypeScript types
│ ├── Dockerfile ← Frontend container definition
│ ├── vercel.json ← Vercel deployment config
│ ├── next.config.js ← Next.js configuration
│ ├── tailwind.config.js ← Tailwind + design system
│ ├── tsconfig.json ← TypeScript configuration
│ ├── .eslintrc.json ← ESLint rules
│ ├── .prettierrc ← Prettier formatting config
│ ├── package.json ← Node.js dependencies + scripts
│ └── .env.example ← Frontend env variable template
│
├── dataset/ ← Training dataset (not used at runtime)
├── training_Results/ ← YOLOv8 training run artifacts
├── docker-compose.yml ← Full-stack Docker orchestration
├── .env.example ← Root env template (for Docker)
├── Makefile ← Developer shortcuts
├── .gitignore ← Git ignore rules
├── .dockerignore ← Docker build context exclusions
├── LICENSE ← MIT License
├── CONTRIBUTING.md ← Contribution guide
└── README.md ← This file
User Uploads Image
↓
Next.js Frontend (port 3000)
– File validation (type, size max 10MB)
– Drag-and-drop or click upload
↓ POST /api/detect (Next.js proxy)
FastAPI Backend (port 8000)
– File extension validation
– Size & dimension verification (max 10MB, max 8192px)
– OpenCV image decode
↓
YOLOv8 Inference (best.pt)
conf_threshold = 0.25 (run in non-blocking thread pool)
↓
count = len(results[0].boxes)
↓
Draw saffron bounding boxes (OpenCV)
Labels: "DRN-01: 91.2%", "DRN-02: 88.9%"...
↓
Save annotated image → /outputs/
↓
Return JSON { drone_count, confidences, processed_image_url, inference_ms }
↓
Frontend results page
– Annotated image with tactical HUD overlay
– Drone count (large number, padded to 2 digits)
– Threat level badge (Area Clear / Single / Multiple / Swarm)
– Per-drone confidence bars
– Session metadata (inference time, model version)
↓ Click Re-Detect (Session Ends)
DELETE /cleanup/{filename}
– Backend deletes the temporary annotated file and cleans up the session
| Tool | Version |
|---|---|
| Python | 3.10+ |
| Node.js | 18+ |
| npm | 9+ |
git clone https://github.com/YOUR_USERNAME/Drone_detection.git
cd Drone_detectioncd backend
# Create and activate virtual environment
python -m venv venv
# Windows
venv\Scripts\activate
# macOS / Linux
source venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Copy environment variables
cp .env.example .env
# Start the FastAPI server
uvicorn main:app --reload --host 0.0.0.0 --port 8000Note: Make sure
best.ptis in thebackend/folder before starting.
Backend will be available at: http://localhost:8000
- API documentation: http://localhost:8000/docs
- Health check: http://localhost:8000/health
cd frontend
# Install Node.js dependencies
npm install
# Copy environment variables
cp .env.example .env.local
# Edit .env.local if your backend runs on a different port
# Start the Next.js dev server
npm run devFrontend will be available at: http://localhost:3000
Run the complete stack with a single command:
# 1. Copy the root environment file
cp .env.example .env
# 2. Build and start both containers
docker-compose up --build| Service | URL |
|---|---|
| Frontend | http://localhost:3000 |
| Backend API | http://localhost:8000 |
| API Docs | http://localhost:8000/docs |
Stop the stack:
docker-compose downUsing Make shortcuts:
make docker-up # Build and start
make docker-down # Stop and remove
make docker-logs # Stream logsDocker note: Uploaded images and annotated results are persisted in named Docker volumes (
swarmtally-uploads,swarmtally-outputs). They survive container restarts.
Accepts a drone image and returns detection results.
Request: multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
file |
image/jpeg or image/png |
✅ | Drone image to analyze |
Success Response (200):
{
"drone_count": 5,
"confidences": [0.9124, 0.8891, 0.8732, 0.7651, 0.6943],
"processed_image_url": "/outputs/result_abc12345.jpg",
"inference_ms": 148.3
}Error Responses:
| Status | Description |
|---|---|
400 |
Invalid file type, empty file, or corrupt image |
500 |
Internal server error during inference |
Returns API health status.
{
"status": "ok",
"app": "Swarmtally",
"model": "best.pt",
"version": "1.0.0"
}Serves annotated result images as static files.
Deletes a temporary annotated result image from the server when a session completes or the user requests a new detection (idempotent, returns status 200). Only filenames matching result_[a-f0-9]{10}.jpg are accepted to ensure directory safety.
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
filename |
string |
✅ | The filename of the result image to delete |
Success Response (200):
{
"status": "ok",
"filename": "result_abc12345.jpg"
}| Variable | Default | Description |
|---|---|---|
FRONTEND_URL |
http://localhost:3000 |
Frontend URL for CORS allow-list |
PORT |
8000 |
Server port (set automatically by Render) |
LOG_LEVEL |
INFO |
Logging verbosity: DEBUG, INFO, WARNING, ERROR |
| Variable | Default | Description |
|---|---|---|
API_URL |
http://localhost:8000 |
Backend URL for the Next.js server-side proxy |
NEXT_PUBLIC_API_URL |
http://localhost:8000 |
Backend URL exposed to the browser (for image loading) |
# Start with hot reload
uvicorn main:app --reload --port 8000
# Run with custom log level
LOG_LEVEL=DEBUG uvicorn main:app --reload --port 8000npm run dev # Start development server
npm run build # Build production bundle
npm run start # Start production server
npm run lint # Run ESLint
npm run lint:fix # Auto-fix ESLint issues
npm run format # Auto-format with Prettier
npm run format:check # Check formatting without writing
npm run type-check # TypeScript type checkingmake install # Install all dependencies
make dev-backend # Start backend server
make dev-frontend # Start frontend server
make docker-up # Start with Docker Compose
make docker-down # Stop Docker containers
make lint # Lint frontend
make format # Format frontend
make type-check # TypeScript check
make clean # Remove build artifacts
make help # Show all commandsSwarmtally uses the "Duty and Honor" design palette — a military tactical aesthetic.
| Token | Hex | Usage |
|---|---|---|
| Olive (Primary) | #4b5320 |
Buttons, accents, navigation |
| Saffron (Secondary) | #fe9832 |
Bounding boxes, threat badges |
| Navy (Tertiary) | #222894 |
Secondary CTAs |
| Dark Olive | #343c0a |
Headlines, primary text |
| Surface | #f8f9fa |
Page background |
| Grid Line | #e1e3e4 |
Blueprint grid pattern |
| Font | Inter | All typography |
| Property | Value |
|---|---|
| Architecture | YOLOv8n (nano) |
| Framework | Ultralytics 8.2+ |
| Classes | 1 (drone) |
| Confidence threshold | 0.25 |
| Input | Any resolution JPEG/PNG |
| Output | Bounding boxes + confidence scores |
| File | backend/best.pt (~6MB) |
The model was trained on a custom drone dataset located in dataset/. The complete training results, including training curves, confusion matrices, and validation results, are provided directly in this repository under the training_Results/ folder.
To retrain:
# Install ultralytics
pip install ultralytics
# Train (customize paths and epochs)
yolo detect train model=yolov8n.pt data=dataset/data.yaml epochs=100 imgsz=640| Issue | Solution |
|---|---|
Model file not found |
Ensure best.pt is in backend/ folder |
Could not connect to detection service |
Start backend: uvicorn main:app --reload --port 8000 |
| Image loads but shows error | Check NEXT_PUBLIC_API_URL in .env.local matches backend port |
| CORS error | Check FRONTEND_URL env var on backend; ensure backend is running |
| Docker build fails (OpenCV) | The Dockerfile installs libgl1 and libglib2.0-0 — these are required |
| Render deployment timeout | Model loading takes ~30–60s cold start — health check allows 60s start period |
next.config.js standalone error |
If not using Docker, remove output: 'standalone' from next.config.js |
See CONTRIBUTING.md for the full contribution guide.
Quick summary:
- Fork the repository
- Create a branch:
git checkout -b feat/your-feature - Make changes and verify everything works
- Run
npm run lintandnpm run type-check - Open a pull request with a clear description
MIT – See LICENSE for details.
Built for educational and research purposes.



