From f9180b23687dbbd5233e2b07056b1c3201adc10b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 17 Nov 2025 10:12:51 +0000
Subject: [PATCH 1/5] Initial plan
From cb30936d8cf73b097cd76a040dfb1e01d5c9dc60 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 17 Nov 2025 10:20:13 +0000
Subject: [PATCH 2/5] Add Flask and FastAPI YOLO workshop examples with
complete implementations
Co-authored-by: mmaleki92 <74877833+mmaleki92@users.noreply.github.com>
---
.gitignore | 6 +
workshops/fastapi_yolo_detection/README.md | 459 ++++++++++++++++++
workshops/fastapi_yolo_detection/main.py | 363 ++++++++++++++
.../fastapi_yolo_detection/requirements.txt | 20 +
.../fastapi_yolo_detection/static/index.html | 329 +++++++++++++
.../fastapi_yolo_detection/static/style.css | 456 +++++++++++++++++
workshops/flask_yolo_detection/README.md | 292 +++++++++++
workshops/flask_yolo_detection/app.py | 206 ++++++++
.../flask_yolo_detection/requirements.txt | 17 +
.../flask_yolo_detection/templates/index.html | 231 +++++++++
.../templates/results.html | 291 +++++++++++
11 files changed, 2670 insertions(+)
create mode 100644 workshops/fastapi_yolo_detection/README.md
create mode 100644 workshops/fastapi_yolo_detection/main.py
create mode 100644 workshops/fastapi_yolo_detection/requirements.txt
create mode 100644 workshops/fastapi_yolo_detection/static/index.html
create mode 100644 workshops/fastapi_yolo_detection/static/style.css
create mode 100644 workshops/flask_yolo_detection/README.md
create mode 100644 workshops/flask_yolo_detection/app.py
create mode 100644 workshops/flask_yolo_detection/requirements.txt
create mode 100644 workshops/flask_yolo_detection/templates/index.html
create mode 100644 workshops/flask_yolo_detection/templates/results.html
diff --git a/.gitignore b/.gitignore
index cd92235..2aab7c6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,9 @@ __pycache__
*~
.idea
appmap.yml
+
+# Workshop temporary files
+workshops/*/uploads/
+workshops/*/results/
+workshops/*/venv/
+*.pt
diff --git a/workshops/fastapi_yolo_detection/README.md b/workshops/fastapi_yolo_detection/README.md
new file mode 100644
index 0000000..8e09b80
--- /dev/null
+++ b/workshops/fastapi_yolo_detection/README.md
@@ -0,0 +1,459 @@
+# FastAPI + YOLO Object Detection Workshop
+
+This workshop demonstrates how to build a modern REST API using FastAPI that performs real-time object detection using YOLOv8 (You Only Look Once) deep learning model.
+
+## đ Project Description
+
+This application provides a RESTful API for object detection with:
+- Modern async/await patterns for better performance
+- Automatic interactive API documentation (Swagger UI)
+- Clean JSON responses with detection results
+- File upload and image processing capabilities
+- Frontend interface for easy testing
+- Multiple API endpoints for various operations
+
+## đ¯ Learning Objectives
+
+By completing this workshop, you will learn:
+- How to build a FastAPI application with async/await
+- RESTful API design principles
+- How to handle file uploads in FastAPI
+- How to integrate YOLO object detection models
+- Image processing with OpenCV
+- API documentation with Swagger/OpenAPI
+- CORS (Cross-Origin Resource Sharing) configuration
+- Error handling and HTTP exceptions
+- Frontend-backend communication with async JavaScript
+
+## đ ī¸ Technologies Used
+
+- **FastAPI**: Modern, fast web framework for building APIs
+- **Uvicorn**: ASGI server for running the application
+- **YOLOv8**: State-of-the-art object detection model
+- **OpenCV**: Computer vision library
+- **Python-multipart**: For handling file uploads
+- **HTML/CSS/JavaScript**: Frontend interface
+
+## đĻ Installation
+
+### Prerequisites
+
+- Python 3.8 or higher
+- pip (Python package manager)
+- Virtual environment (recommended)
+
+### Step-by-Step Installation
+
+1. **Navigate to the project directory**:
+ ```bash
+ cd workshops/fastapi_yolo_detection
+ ```
+
+2. **Create a virtual environment** (recommended):
+ ```bash
+ python -m venv venv
+ ```
+
+3. **Activate the virtual environment**:
+
+ On Windows:
+ ```bash
+ venv\Scripts\activate
+ ```
+
+ On macOS/Linux:
+ ```bash
+ source venv/bin/activate
+ ```
+
+4. **Install dependencies**:
+ ```bash
+ pip install -r requirements.txt
+ ```
+
+ This will install:
+ - fastapi (modern web framework)
+ - uvicorn (ASGI server)
+ - python-multipart (file upload support)
+ - ultralytics (YOLO implementation)
+ - opencv-python (image processing)
+ - Pillow (image handling)
+ - numpy (numerical operations)
+
+5. **First run - YOLO model download**:
+
+ On the first run, YOLOv8 will automatically download the pre-trained model (~6MB for nano version).
+
+## đ How to Run
+
+### Option 1: Using Python directly
+
+```bash
+python main.py
+```
+
+### Option 2: Using Uvicorn command
+
+```bash
+uvicorn main:app --reload --host 0.0.0.0 --port 8000
+```
+
+The `--reload` flag enables auto-reload during development.
+
+### Accessing the Application
+
+Once running, you can access:
+
+1. **Web Interface**: http://localhost:8000
+ - User-friendly interface for uploading and detecting objects
+
+2. **Interactive API Docs (Swagger UI)**: http://localhost:8000/docs
+ - Try out API endpoints directly from the browser
+ - See request/response schemas
+ - Test authentication and file uploads
+
+3. **Alternative API Docs (ReDoc)**: http://localhost:8000/redoc
+ - Clean, readable documentation
+ - Better for sharing with team members
+
+## đ API Endpoints Documentation
+
+### 1. Homepage
+- **Endpoint**: `GET /`
+- **Description**: Serves the web interface
+- **Response**: HTML page
+
+### 2. Detect Objects
+- **Endpoint**: `POST /api/detect`
+- **Description**: Upload an image and perform object detection
+- **Parameters**:
+ - `file` (form-data): Image file (JPG, JPEG, or PNG)
+- **Response**:
+ ```json
+ {
+ "success": true,
+ "message": "Detection completed successfully",
+ "total_objects": 5,
+ "detections": [
+ {
+ "class": "person",
+ "confidence": 0.9234,
+ "bbox": {
+ "x1": 100,
+ "y1": 150,
+ "x2": 300,
+ "y2": 450
+ }
+ }
+ ],
+ "image_url": "/images/results/detected_abc123.jpg",
+ "original_url": "/images/uploads/abc123.jpg",
+ "filename": "my_photo.jpg"
+ }
+ ```
+
+### 3. Health Check
+- **Endpoint**: `GET /api/health`
+- **Description**: Check if API is running
+- **Response**:
+ ```json
+ {
+ "status": "healthy",
+ "model": "YOLOv8n",
+ "version": "1.0.0"
+ }
+ ```
+
+### 4. Supported Objects
+- **Endpoint**: `GET /api/supported-objects`
+- **Description**: Get list of detectable object classes
+- **Response**:
+ ```json
+ {
+ "total_classes": 80,
+ "classes": ["person", "bicycle", "car", ...]
+ }
+ ```
+
+### 5. Get Image
+- **Endpoint**: `GET /images/uploads/{filename}`
+- **Description**: Retrieve uploaded image
+- **Response**: Image file
+
+### 6. Get Result Image
+- **Endpoint**: `GET /images/results/{filename}`
+- **Description**: Retrieve processed image with bounding boxes
+- **Response**: Image file
+
+### 7. Cleanup (Development Only)
+- **Endpoint**: `DELETE /api/cleanup`
+- **Description**: Remove all temporary files
+- **Response**:
+ ```json
+ {
+ "success": true,
+ "message": "All temporary files cleaned up"
+ }
+ ```
+
+## đģ Usage Examples
+
+### Using cURL
+
+**Upload and detect objects**:
+```bash
+curl -X POST "http://localhost:8000/api/detect" \
+ -H "accept: application/json" \
+ -H "Content-Type: multipart/form-data" \
+ -F "file=@/path/to/your/image.jpg"
+```
+
+**Health check**:
+```bash
+curl http://localhost:8000/api/health
+```
+
+**Get supported objects**:
+```bash
+curl http://localhost:8000/api/supported-objects
+```
+
+### Using Python Requests
+
+```python
+import requests
+
+# Upload and detect
+with open('image.jpg', 'rb') as f:
+ files = {'file': f}
+ response = requests.post('http://localhost:8000/api/detect', files=files)
+ result = response.json()
+ print(f"Detected {result['total_objects']} objects")
+ for detection in result['detections']:
+ print(f"- {detection['class']}: {detection['confidence']:.2%}")
+
+# Health check
+response = requests.get('http://localhost:8000/api/health')
+print(response.json())
+```
+
+### Using JavaScript (Frontend)
+
+```javascript
+// Upload and detect
+const formData = new FormData();
+formData.append('file', fileInput.files[0]);
+
+const response = await fetch('/api/detect', {
+ method: 'POST',
+ body: formData
+});
+
+const data = await response.json();
+console.log(`Detected ${data.total_objects} objects`);
+```
+
+## đ Project Structure
+
+```
+fastapi_yolo_detection/
+âââ main.py # Main FastAPI application
+âââ static/
+â âââ index.html # Frontend interface
+â âââ style.css # Styling
+âââ uploads/ # Uploaded images (created automatically)
+âââ results/ # Processed images (created automatically)
+âââ requirements.txt # Python dependencies
+âââ README.md # This file
+```
+
+## đ§ Configuration Options
+
+You can modify these settings in `main.py`:
+
+### YOLO Model Selection
+```python
+# Choose model based on your needs:
+model = YOLO('yolov8n.pt') # Nano - fastest, least accurate
+model = YOLO('yolov8s.pt') # Small
+model = YOLO('yolov8m.pt') # Medium - balanced
+model = YOLO('yolov8l.pt') # Large
+model = YOLO('yolov8x.pt') # Extra Large - slowest, most accurate
+```
+
+### Server Configuration
+```python
+# In main.py, at the bottom:
+uvicorn.run(
+ "main:app",
+ host="0.0.0.0", # Listen on all interfaces
+ port=8000, # Change port if needed
+ reload=True # Auto-reload on code changes (development only)
+)
+```
+
+### File Upload Limits
+```python
+MAX_FILE_SIZE = 16 * 1024 * 1024 # 16MB
+ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'}
+```
+
+## đ Troubleshooting
+
+### Common Issues
+
+1. **Module not found errors**:
+ ```bash
+ # Ensure virtual environment is activated
+ source venv/bin/activate # or venv\Scripts\activate on Windows
+ pip install -r requirements.txt
+ ```
+
+2. **Port already in use**:
+ ```bash
+ # Change port in main.py or use a different port:
+ uvicorn main:app --port 8001
+ ```
+
+3. **YOLO model download fails**:
+ - Check internet connection
+ - Model downloads automatically to `~/.cache/torch/hub/`
+ - Retry - it will resume interrupted downloads
+
+4. **CORS errors in browser**:
+ - Already configured for development (`allow_origins=["*"]`)
+ - For production, specify exact origins in `CORSMiddleware`
+
+5. **File upload fails**:
+ - Check file size (max 16MB)
+ - Verify file format (JPG, JPEG, PNG only)
+ - Check server logs for detailed error messages
+
+### Debug Mode
+
+Enable detailed logging:
+```bash
+uvicorn main:app --reload --log-level debug
+```
+
+## đ Learning Resources
+
+### FastAPI Concepts Demonstrated
+
+1. **Async/Await**: Efficient handling of I/O operations
+2. **Dependency Injection**: Clean, testable code structure
+3. **Request Validation**: Automatic with Pydantic
+4. **Exception Handling**: HTTP exceptions with proper status codes
+5. **File Uploads**: Handling multipart form data
+6. **Static Files**: Serving frontend assets
+7. **Auto Documentation**: OpenAPI/Swagger integration
+
+### API Design Best Practices
+
+- **RESTful Endpoints**: Clear, predictable URL structure
+- **HTTP Methods**: Proper use of GET, POST, DELETE
+- **Status Codes**: Appropriate responses (200, 400, 404, 500)
+- **JSON Responses**: Consistent response format
+- **Error Messages**: Helpful, actionable error descriptions
+
+### Next Steps
+
+Enhance this project by:
+1. **Add Authentication**: JWT tokens, OAuth2
+2. **Database Integration**: Store detection history with SQLAlchemy
+3. **Batch Processing**: Handle multiple images at once
+4. **Video Support**: Process video files frame by frame
+5. **WebSocket**: Real-time detection updates
+6. **Model Selection**: Let users choose YOLO model version
+7. **Custom Training**: Fine-tune YOLO for specific objects
+8. **Docker**: Containerize the application
+9. **Cloud Deployment**: Deploy to AWS, GCP, or Azure
+10. **Testing**: Add unit and integration tests with pytest
+
+## đ Performance Considerations
+
+### Model Performance
+- **YOLOv8n**: ~5-10ms per image (CPU), ~1-2ms (GPU)
+- **YOLOv8s**: ~10-15ms per image (CPU), ~2-3ms (GPU)
+- **YOLOv8m**: ~15-25ms per image (CPU), ~3-5ms (GPU)
+
+### Optimization Tips
+1. Use smaller model (nano/small) for faster inference
+2. Resize large images before processing
+3. Implement caching for repeated detections
+4. Use GPU if available (CUDA-enabled PyTorch)
+5. Batch process multiple images together
+
+## đ Security Considerations
+
+For production deployment:
+1. **File Validation**: Verify file content, not just extension
+2. **Rate Limiting**: Prevent API abuse
+3. **CORS**: Restrict to specific origins
+4. **File Size Limits**: Already implemented (16MB)
+5. **Sanitize Filenames**: Already using UUID for safety
+6. **HTTPS**: Use TLS/SSL in production
+7. **Authentication**: Add API keys or OAuth2
+8. **Input Validation**: Already implemented with FastAPI
+
+## đ Notes for Instructors
+
+This workshop is designed to:
+- Demonstrate modern API development with FastAPI
+- Show async programming patterns in Python
+- Integrate machine learning models into web applications
+- Teach REST API best practices
+- Provide hands-on experience with real-world tools
+
+**Recommended Teaching Order**:
+1. Explain FastAPI basics and advantages over Flask
+2. Demonstrate async/await patterns
+3. Show automatic documentation features
+4. Walk through file upload handling
+5. Introduce YOLO and object detection
+6. Test API with Swagger UI
+7. Demonstrate frontend-backend communication
+8. Discuss deployment considerations
+
+**Time Estimate**: 2-3 hours for complete workshop
+
+**Prerequisites**:
+- Basic Python knowledge
+- Understanding of HTTP/REST concepts
+- Familiarity with async programming (helpful but not required)
+
+## đ¤ Contributing
+
+This is a teaching project. Contributions welcome:
+- Bug fixes
+- Documentation improvements
+- New features
+- Performance optimizations
+- Additional examples
+
+## đ License
+
+This project is created for educational purposes as part of Python teaching materials.
+
+## đ Acknowledgments
+
+- **FastAPI** team for the excellent framework
+- **Ultralytics** for YOLOv8 implementation
+- **Starlette** for ASGI foundation
+- **Pydantic** for data validation
+- **OpenCV** community for computer vision tools
+
+## đ Support
+
+For questions or issues:
+1. Check the troubleshooting section
+2. Review API documentation at `/docs`
+3. Check FastAPI documentation
+4. Review code comments
+5. Ask your instructor
+
+---
+
+**Happy Coding! đ**
+
+FastAPI makes building APIs fast and fun. Experiment with the code, try new features, and build something amazing!
diff --git a/workshops/fastapi_yolo_detection/main.py b/workshops/fastapi_yolo_detection/main.py
new file mode 100644
index 0000000..9f55d69
--- /dev/null
+++ b/workshops/fastapi_yolo_detection/main.py
@@ -0,0 +1,363 @@
+"""
+FastAPI + YOLO Object Detection Workshop
+========================================
+This application demonstrates how to build a modern REST API using FastAPI
+that performs object detection using YOLOv8 model.
+
+Features:
+- RESTful API endpoints for object detection
+- Async/await patterns for better performance
+- Automatic API documentation with Swagger UI
+- File upload and image processing
+- JSON response with detection results
+"""
+
+import os
+import uuid
+from pathlib import Path
+from typing import List, Dict
+import shutil
+
+from fastapi import FastAPI, File, UploadFile, HTTPException
+from fastapi.responses import HTMLResponse, FileResponse, JSONResponse
+from fastapi.staticfiles import StaticFiles
+from fastapi.middleware.cors import CORSMiddleware
+from ultralytics import YOLO
+import cv2
+import numpy as np
+from PIL import Image
+
+# Initialize FastAPI application
+app = FastAPI(
+ title="YOLO Object Detection API",
+ description="REST API for object detection using YOLOv8",
+ version="1.0.0"
+)
+
+# Configure CORS (Cross-Origin Resource Sharing)
+# This allows the frontend to communicate with the backend
+app.add_middleware(
+ CORSMiddleware,
+ allow_origins=["*"], # In production, specify exact origins
+ allow_credentials=True,
+ allow_methods=["*"],
+ allow_headers=["*"],
+)
+
+# Configuration
+UPLOAD_FOLDER = "uploads"
+RESULTS_FOLDER = "results"
+STATIC_FOLDER = "static"
+ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'}
+MAX_FILE_SIZE = 16 * 1024 * 1024 # 16MB
+
+# Create necessary folders
+os.makedirs(UPLOAD_FOLDER, exist_ok=True)
+os.makedirs(RESULTS_FOLDER, exist_ok=True)
+os.makedirs(STATIC_FOLDER, exist_ok=True)
+
+# Mount static files directory
+app.mount("/static", StaticFiles(directory=STATIC_FOLDER), name="static")
+
+# Initialize YOLO model
+print("Loading YOLO model...")
+model = YOLO('yolov8n.pt') # Nano version for faster inference
+print("Model loaded successfully!")
+
+
+def allowed_file(filename: str) -> bool:
+ """
+ Check if the uploaded file has an allowed extension.
+
+ Args:
+ filename (str): Name of the uploaded file
+
+ Returns:
+ bool: True if file extension is allowed, False otherwise
+ """
+ return '.' in filename and \
+ filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
+
+
+async def perform_detection(image_path: str, output_path: str) -> List[Dict]:
+ """
+ Perform object detection on an image using YOLO model.
+
+ Args:
+ image_path (str): Path to the input image
+ output_path (str): Path to save the output image with bounding boxes
+
+ Returns:
+ List[Dict]: List of dictionaries containing detection results
+ Each dict has: 'class', 'confidence', 'bbox'
+ """
+ # Read the image
+ image = cv2.imread(image_path)
+
+ if image is None:
+ raise ValueError("Could not read image file")
+
+ # Perform detection
+ results = model(image)
+
+ # Extract detection information
+ detections = []
+ for result in results:
+ boxes = result.boxes
+ for box in boxes:
+ # Get bounding box coordinates
+ x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
+
+ # Get confidence score
+ confidence = float(box.conf[0].cpu().numpy())
+
+ # Get class name
+ class_id = int(box.cls[0].cpu().numpy())
+ class_name = model.names[class_id]
+
+ # Store detection info
+ detections.append({
+ 'class': class_name,
+ 'confidence': round(confidence, 4),
+ 'bbox': {
+ 'x1': int(x1),
+ 'y1': int(y1),
+ 'x2': int(x2),
+ 'y2': int(y2)
+ }
+ })
+
+ # Draw bounding box on image
+ cv2.rectangle(image, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2)
+
+ # Add label with class name and confidence
+ label = f"{class_name}: {confidence:.2f}"
+ cv2.putText(image, label, (int(x1), int(y1) - 10),
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
+
+ # Save the image with bounding boxes
+ cv2.imwrite(output_path, image)
+
+ return detections
+
+
+@app.get("/", response_class=HTMLResponse)
+async def serve_homepage():
+ """
+ Serve the main HTML page for the web interface.
+
+ Returns:
+ HTMLResponse: The HTML content of the homepage
+ """
+ html_path = Path(STATIC_FOLDER) / "index.html"
+
+ if not html_path.exists():
+ return HTMLResponse(
+ content="
Error: index.html not found in static folder
",
+ status_code=404
+ )
+
+ with open(html_path, "r", encoding="utf-8") as f:
+ return HTMLResponse(content=f.read())
+
+
+@app.post("/api/detect", response_model=None)
+async def detect_objects(file: UploadFile = File(...)):
+ """
+ Upload an image and perform object detection.
+
+ Args:
+ file (UploadFile): The uploaded image file
+
+ Returns:
+ JSONResponse: Detection results including:
+ - success: boolean indicating success/failure
+ - detections: list of detected objects
+ - total_objects: number of objects detected
+ - image_url: URL to view the processed image
+ - original_url: URL to view the original image
+
+ Raises:
+ HTTPException: If file validation fails or processing error occurs
+ """
+ # Validate file is provided
+ if not file:
+ raise HTTPException(status_code=400, detail="No file uploaded")
+
+ # Validate file extension
+ if not allowed_file(file.filename):
+ raise HTTPException(
+ status_code=400,
+ detail="Invalid file type. Please upload JPG, JPEG, or PNG images."
+ )
+
+ # Validate file size
+ contents = await file.read()
+ if len(contents) > MAX_FILE_SIZE:
+ raise HTTPException(
+ status_code=400,
+ detail=f"File too large. Maximum size is {MAX_FILE_SIZE / 1024 / 1024}MB"
+ )
+
+ try:
+ # Generate unique filename
+ file_ext = file.filename.rsplit('.', 1)[1].lower()
+ unique_filename = f"{uuid.uuid4().hex}.{file_ext}"
+ upload_path = os.path.join(UPLOAD_FOLDER, unique_filename)
+
+ # Save uploaded file
+ with open(upload_path, "wb") as buffer:
+ buffer.write(contents)
+
+ # Generate output filename
+ output_filename = f"detected_{unique_filename}"
+ output_path = os.path.join(RESULTS_FOLDER, output_filename)
+
+ # Perform object detection
+ detections = await perform_detection(upload_path, output_path)
+
+ # Prepare response
+ response_data = {
+ "success": True,
+ "message": "Detection completed successfully",
+ "total_objects": len(detections),
+ "detections": detections,
+ "image_url": f"/images/results/{output_filename}",
+ "original_url": f"/images/uploads/{unique_filename}",
+ "filename": file.filename
+ }
+
+ return JSONResponse(content=response_data)
+
+ except Exception as e:
+ # Clean up files if they exist
+ if os.path.exists(upload_path):
+ os.remove(upload_path)
+ if os.path.exists(output_path):
+ os.remove(output_path)
+
+ raise HTTPException(
+ status_code=500,
+ detail=f"Error processing image: {str(e)}"
+ )
+
+
+@app.get("/images/uploads/{filename}")
+async def get_uploaded_image(filename: str):
+ """
+ Retrieve an uploaded image.
+
+ Args:
+ filename (str): Name of the uploaded file
+
+ Returns:
+ FileResponse: The requested image file
+
+ Raises:
+ HTTPException: If file not found
+ """
+ file_path = os.path.join(UPLOAD_FOLDER, filename)
+
+ if not os.path.exists(file_path):
+ raise HTTPException(status_code=404, detail="Image not found")
+
+ return FileResponse(file_path)
+
+
+@app.get("/images/results/{filename}")
+async def get_result_image(filename: str):
+ """
+ Retrieve a processed result image.
+
+ Args:
+ filename (str): Name of the result file
+
+ Returns:
+ FileResponse: The requested image file
+
+ Raises:
+ HTTPException: If file not found
+ """
+ file_path = os.path.join(RESULTS_FOLDER, filename)
+
+ if not os.path.exists(file_path):
+ raise HTTPException(status_code=404, detail="Image not found")
+
+ return FileResponse(file_path)
+
+
+@app.get("/api/health")
+async def health_check():
+ """
+ Health check endpoint to verify API is running.
+
+ Returns:
+ Dict: Status information
+ """
+ return {
+ "status": "healthy",
+ "model": "YOLOv8n",
+ "version": "1.0.0"
+ }
+
+
+@app.get("/api/supported-objects")
+async def get_supported_objects():
+ """
+ Get list of object classes that can be detected.
+
+ Returns:
+ Dict: List of supported object classes
+ """
+ return {
+ "total_classes": len(model.names),
+ "classes": list(model.names.values())
+ }
+
+
+@app.delete("/api/cleanup")
+async def cleanup_files():
+ """
+ Clean up uploaded and result files (for development/testing).
+
+ Returns:
+ Dict: Cleanup status
+ """
+ try:
+ # Remove all files in upload folder
+ for file in os.listdir(UPLOAD_FOLDER):
+ file_path = os.path.join(UPLOAD_FOLDER, file)
+ if os.path.isfile(file_path):
+ os.remove(file_path)
+
+ # Remove all files in results folder
+ for file in os.listdir(RESULTS_FOLDER):
+ file_path = os.path.join(RESULTS_FOLDER, file)
+ if os.path.isfile(file_path):
+ os.remove(file_path)
+
+ return {
+ "success": True,
+ "message": "All temporary files cleaned up"
+ }
+ except Exception as e:
+ raise HTTPException(
+ status_code=500,
+ detail=f"Error cleaning up files: {str(e)}"
+ )
+
+
+# Entry point for running the application
+if __name__ == "__main__":
+ import uvicorn
+
+ # Run the FastAPI application
+ # Access the API at: http://localhost:8000
+ # Access API docs at: http://localhost:8000/docs
+ # Access ReDoc at: http://localhost:8000/redoc
+ uvicorn.run(
+ "main:app",
+ host="0.0.0.0",
+ port=8000,
+ reload=True # Enable auto-reload during development
+ )
diff --git a/workshops/fastapi_yolo_detection/requirements.txt b/workshops/fastapi_yolo_detection/requirements.txt
new file mode 100644
index 0000000..1798553
--- /dev/null
+++ b/workshops/fastapi_yolo_detection/requirements.txt
@@ -0,0 +1,20 @@
+# FastAPI Framework
+fastapi==0.104.1
+
+# ASGI Server
+uvicorn[standard]==0.24.0
+
+# File Upload Support
+python-multipart==0.0.6
+
+# YOLO Object Detection
+ultralytics==8.0.228
+
+# Computer Vision
+opencv-python==4.8.1.78
+
+# Image Processing
+Pillow==10.1.0
+
+# Numerical Operations
+numpy==1.26.2
diff --git a/workshops/fastapi_yolo_detection/static/index.html b/workshops/fastapi_yolo_detection/static/index.html
new file mode 100644
index 0000000..a9d3238
--- /dev/null
+++ b/workshops/fastapi_yolo_detection/static/index.html
@@ -0,0 +1,329 @@
+
+
+
+
+
+ FastAPI YOLO Object Detection
+
+
+
+
+
+
+
+
+
+
+
đ¸
+
Click to select an image or drag & drop
+
Supported formats: JPG, JPEG, PNG (Max 16MB)
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Processing image... Please wait
+
+
+
+
+ â
Detection Complete!
+
+
+
+
+
+
đ¤ Original Image
+
![Original]()
+
+
+
đ¯ Detected Objects
+
![Detected]()
+
+
+
+
+
đ Detected Objects
+
+
+
+
+
+
+
+
+
+
+
+ âšī¸ About this API
+
+
Model: YOLOv8 (You Only Look Once)
+
Framework: FastAPI with async/await
+
Features: Real-time object detection, 80 object classes
+
API Documentation: Swagger UI | ReDoc
+
+
+ đ API Endpoints
+
+
+ POST
+ /api/detect
+ Upload image and detect objects
+
+
+ GET
+ /api/health
+ Check API health status
+
+
+ GET
+ /api/supported-objects
+ Get list of detectable objects
+
+
+ GET
+ /images/results/{filename}
+ Retrieve processed image
+
+
+
+
+
+
+
+
diff --git a/workshops/fastapi_yolo_detection/static/style.css b/workshops/fastapi_yolo_detection/static/style.css
new file mode 100644
index 0000000..fecf34e
--- /dev/null
+++ b/workshops/fastapi_yolo_detection/static/style.css
@@ -0,0 +1,456 @@
+/* Global Styles */
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ min-height: 100vh;
+ padding: 20px;
+ color: #333;
+}
+
+/* Container */
+.container {
+ max-width: 1200px;
+ margin: 0 auto;
+}
+
+/* Header */
+header {
+ text-align: center;
+ color: white;
+ margin-bottom: 40px;
+}
+
+header h1 {
+ font-size: 2.5em;
+ margin-bottom: 10px;
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
+}
+
+.subtitle {
+ font-size: 1.2em;
+ opacity: 0.95;
+}
+
+/* Upload Section */
+.upload-section {
+ margin-bottom: 40px;
+}
+
+.upload-card {
+ background: white;
+ border-radius: 20px;
+ padding: 40px;
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
+}
+
+.upload-area {
+ border: 3px dashed #667eea;
+ border-radius: 15px;
+ padding: 60px 40px;
+ text-align: center;
+ background: #f8f9ff;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ margin-bottom: 20px;
+}
+
+.upload-area:hover,
+.upload-area.drag-over {
+ border-color: #764ba2;
+ background: #f0f1ff;
+ transform: scale(1.02);
+}
+
+.upload-icon {
+ font-size: 5em;
+ margin-bottom: 20px;
+}
+
+.upload-text {
+ font-size: 1.3em;
+ color: #555;
+ margin-bottom: 10px;
+}
+
+.upload-hint {
+ color: #888;
+ font-size: 0.95em;
+}
+
+.file-name {
+ margin-top: 20px;
+ color: #667eea;
+ font-weight: bold;
+ font-size: 1.1em;
+}
+
+/* Buttons */
+.btn {
+ padding: 15px 40px;
+ border: none;
+ border-radius: 10px;
+ font-size: 1.1em;
+ font-weight: bold;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ margin: 5px;
+}
+
+.btn-primary {
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ color: white;
+ width: 100%;
+}
+
+.btn-primary:hover:not(:disabled) {
+ transform: translateY(-2px);
+ box-shadow: 0 5px 20px rgba(0, 0, 0, 0.3);
+}
+
+.btn-primary:disabled {
+ background: #ccc;
+ cursor: not-allowed;
+ opacity: 0.6;
+}
+
+.btn-secondary {
+ background: white;
+ color: #667eea;
+ border: 2px solid #667eea;
+}
+
+.btn-secondary:hover {
+ background: #667eea;
+ color: white;
+}
+
+/* Error Message */
+.error-message {
+ background: #ffe6e6;
+ border: 2px solid #ff4444;
+ color: #cc0000;
+ padding: 15px;
+ border-radius: 10px;
+ margin-top: 20px;
+ display: none;
+ text-align: center;
+}
+
+/* Loading Section */
+.loading-section {
+ background: white;
+ border-radius: 20px;
+ padding: 60px 40px;
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
+ text-align: center;
+}
+
+.loading-spinner {
+ width: 60px;
+ height: 60px;
+ border: 5px solid #f3f3f3;
+ border-top: 5px solid #667eea;
+ border-radius: 50%;
+ margin: 0 auto 20px;
+ animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+.loading-section p {
+ font-size: 1.2em;
+ color: #666;
+}
+
+/* Results Section */
+.results-section {
+ background: white;
+ border-radius: 20px;
+ padding: 40px;
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
+}
+
+.results-section h2 {
+ text-align: center;
+ color: #667eea;
+ font-size: 2em;
+ margin-bottom: 30px;
+}
+
+/* Statistics */
+.stats-container {
+ display: flex;
+ justify-content: space-around;
+ gap: 20px;
+ margin-bottom: 40px;
+ flex-wrap: wrap;
+}
+
+.stat-card {
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ color: white;
+ padding: 30px;
+ border-radius: 15px;
+ text-align: center;
+ min-width: 150px;
+ flex: 1;
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
+}
+
+.stat-number {
+ font-size: 3em;
+ font-weight: bold;
+ margin-bottom: 5px;
+}
+
+.stat-label {
+ font-size: 1em;
+ opacity: 0.9;
+}
+
+/* Images Container */
+.images-container {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
+ gap: 30px;
+ margin-bottom: 40px;
+}
+
+.image-card {
+ background: #f8f9ff;
+ border-radius: 15px;
+ padding: 20px;
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
+}
+
+.image-card h3 {
+ color: #667eea;
+ margin-bottom: 15px;
+ font-size: 1.3em;
+}
+
+.image-card img {
+ width: 100%;
+ border-radius: 10px;
+ box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
+}
+
+/* Detections Container */
+.detections-container {
+ margin-bottom: 30px;
+}
+
+.detections-container h3 {
+ color: #667eea;
+ margin-bottom: 20px;
+ font-size: 1.5em;
+}
+
+.detections-list {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
+ gap: 15px;
+}
+
+.detection-item {
+ background: #f8f9ff;
+ border-left: 4px solid #667eea;
+ padding: 15px;
+ border-radius: 8px;
+ transition: transform 0.2s ease;
+}
+
+.detection-item:hover {
+ transform: translateX(5px);
+ box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
+}
+
+.detection-class {
+ font-weight: bold;
+ color: #333;
+ font-size: 1.1em;
+ margin-bottom: 5px;
+}
+
+.detection-confidence {
+ color: #667eea;
+ font-size: 0.95em;
+ margin-bottom: 8px;
+}
+
+.confidence-bar {
+ width: 100%;
+ height: 8px;
+ background: #e0e0e0;
+ border-radius: 4px;
+ overflow: hidden;
+}
+
+.confidence-fill {
+ height: 100%;
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
+ width: 0%;
+}
+
+.no-detections {
+ text-align: center;
+ padding: 40px;
+ color: #888;
+ font-size: 1.2em;
+ grid-column: 1 / -1;
+}
+
+/* Actions */
+.actions {
+ display: flex;
+ justify-content: center;
+ gap: 20px;
+ flex-wrap: wrap;
+}
+
+/* Info Section */
+.info-section {
+ background: white;
+ border-radius: 20px;
+ padding: 40px;
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
+ margin-top: 30px;
+}
+
+.info-section h3 {
+ color: #667eea;
+ margin-bottom: 20px;
+ font-size: 1.5em;
+}
+
+.info-card {
+ background: #f8f9ff;
+ padding: 20px;
+ border-radius: 10px;
+ margin-bottom: 30px;
+}
+
+.info-card p {
+ margin: 10px 0;
+ color: #555;
+}
+
+.info-card a {
+ color: #667eea;
+ text-decoration: none;
+ font-weight: bold;
+}
+
+.info-card a:hover {
+ text-decoration: underline;
+}
+
+/* Endpoints List */
+.endpoints-list {
+ background: #f8f9ff;
+ padding: 20px;
+ border-radius: 10px;
+}
+
+.endpoint {
+ display: flex;
+ align-items: center;
+ padding: 15px;
+ margin: 10px 0;
+ background: white;
+ border-radius: 8px;
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
+ flex-wrap: wrap;
+ gap: 10px;
+}
+
+.method {
+ padding: 5px 15px;
+ border-radius: 5px;
+ font-weight: bold;
+ font-size: 0.85em;
+ min-width: 60px;
+ text-align: center;
+}
+
+.method.get {
+ background: #4caf50;
+ color: white;
+}
+
+.method.post {
+ background: #2196f3;
+ color: white;
+}
+
+.path {
+ font-family: 'Courier New', monospace;
+ color: #667eea;
+ font-weight: bold;
+ flex: 1;
+ min-width: 200px;
+}
+
+.desc {
+ color: #666;
+ flex: 2;
+ min-width: 200px;
+}
+
+/* Responsive Design */
+@media (max-width: 768px) {
+ header h1 {
+ font-size: 2em;
+ }
+
+ .subtitle {
+ font-size: 1em;
+ }
+
+ .upload-card {
+ padding: 20px;
+ }
+
+ .upload-area {
+ padding: 40px 20px;
+ }
+
+ .images-container {
+ grid-template-columns: 1fr;
+ }
+
+ .stats-container {
+ flex-direction: column;
+ }
+
+ .stat-card {
+ width: 100%;
+ }
+
+ .actions {
+ flex-direction: column;
+ }
+
+ .btn {
+ width: 100%;
+ }
+
+ .endpoint {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .path,
+ .desc {
+ width: 100%;
+ }
+}
diff --git a/workshops/flask_yolo_detection/README.md b/workshops/flask_yolo_detection/README.md
new file mode 100644
index 0000000..5503821
--- /dev/null
+++ b/workshops/flask_yolo_detection/README.md
@@ -0,0 +1,292 @@
+# Flask + YOLO Object Detection Workshop
+
+This workshop demonstrates how to build a web application using Flask that performs real-time object detection using YOLOv8 (You Only Look Once) deep learning model.
+
+## đ Project Description
+
+This application allows users to:
+- Upload images through a user-friendly web interface
+- Perform object detection using the state-of-the-art YOLOv8 model
+- View results with bounding boxes drawn around detected objects
+- See a list of all detected objects with their confidence scores
+- Download the processed images
+
+## đ¯ Learning Objectives
+
+By completing this workshop, you will learn:
+- How to build a Flask web application
+- How to handle file uploads in Flask
+- How to integrate machine learning models (YOLO) into web applications
+- How to process images using OpenCV and YOLO
+- How to create responsive HTML templates with Jinja2
+- How to serve static files and dynamic content
+- Error handling and input validation in web applications
+
+## đ ī¸ Technologies Used
+
+- **Flask**: Web framework for Python
+- **YOLOv8**: State-of-the-art object detection model
+- **OpenCV**: Computer vision library for image processing
+- **Pillow**: Python Imaging Library
+- **HTML/CSS/JavaScript**: Frontend technologies
+
+## đĻ Installation
+
+### Prerequisites
+
+- Python 3.8 or higher
+- pip (Python package manager)
+- Virtual environment (recommended)
+
+### Step-by-Step Installation
+
+1. **Clone the repository** (if not already done):
+ ```bash
+ cd workshops/flask_yolo_detection
+ ```
+
+2. **Create a virtual environment** (recommended):
+ ```bash
+ python -m venv venv
+ ```
+
+3. **Activate the virtual environment**:
+
+ On Windows:
+ ```bash
+ venv\Scripts\activate
+ ```
+
+ On macOS/Linux:
+ ```bash
+ source venv/bin/activate
+ ```
+
+4. **Install dependencies**:
+ ```bash
+ pip install -r requirements.txt
+ ```
+
+ This will install:
+ - Flask (web framework)
+ - ultralytics (YOLO model)
+ - opencv-python (image processing)
+ - Pillow (image handling)
+ - numpy (numerical operations)
+
+5. **First run - YOLO model download**:
+
+ On the first run, YOLOv8 will automatically download the pre-trained model (~6MB for nano version). This happens automatically when you start the application.
+
+## đ How to Run
+
+1. **Activate your virtual environment** (if not already activated)
+
+2. **Run the Flask application**:
+ ```bash
+ python app.py
+ ```
+
+3. **Open your web browser** and navigate to:
+ ```
+ http://localhost:5000
+ ```
+
+4. **You should see**:
+ - A beautiful, modern web interface
+ - A file upload area
+ - Instructions on how to use the application
+
+## đ Usage Guide
+
+### Uploading an Image
+
+1. Click on the upload area or drag and drop an image
+2. Select an image file (JPG, JPEG, or PNG format)
+3. Click "Detect Objects" button
+4. Wait for processing (usually takes 1-3 seconds)
+
+### Viewing Results
+
+The results page displays:
+- **Original Image**: Your uploaded image
+- **Detected Objects Image**: Image with bounding boxes and labels
+- **Detection Statistics**:
+ - Number of objects detected
+ - Average confidence score
+ - Number of unique object classes
+- **Detailed List**: Each detected object with:
+ - Object class/name
+ - Confidence score (0-100%)
+ - Visual confidence bar
+
+### Supported Objects
+
+YOLOv8 can detect 80 different object classes, including:
+- People, animals (cat, dog, bird, horse, etc.)
+- Vehicles (car, truck, bus, motorcycle, bicycle, etc.)
+- Indoor objects (chair, table, sofa, bed, etc.)
+- Electronics (TV, laptop, keyboard, mouse, etc.)
+- Kitchen items (bottle, cup, fork, knife, etc.)
+- And many more!
+
+## đ¨ Example Usage
+
+### Example 1: Detecting Objects in a Street Photo
+
+1. Upload a photo of a street scene
+2. The model will detect: cars, people, traffic lights, bicycles, etc.
+3. Each object will be highlighted with a green bounding box
+4. Confidence scores typically range from 70-95% for clear objects
+
+### Example 2: Detecting Objects in a Room
+
+1. Upload a photo of a room
+2. The model will detect: furniture, electronics, decorations, etc.
+3. Multiple instances of the same object will all be detected
+4. Lower confidence scores (50-70%) might appear for partially visible objects
+
+## đ Project Structure
+
+```
+flask_yolo_detection/
+âââ app.py # Main Flask application
+âââ templates/
+â âââ index.html # Homepage with upload form
+â âââ results.html # Results display page
+âââ uploads/ # Uploaded images (created automatically)
+âââ results/ # Processed images (created automatically)
+âââ requirements.txt # Python dependencies
+âââ README.md # This file
+```
+
+## đ§ Configuration
+
+You can modify the following settings in `app.py`:
+
+- **YOLO Model**: Change `yolov8n.pt` to:
+ - `yolov8s.pt` - Small (faster, less accurate)
+ - `yolov8m.pt` - Medium (balanced)
+ - `yolov8l.pt` - Large (slower, more accurate)
+ - `yolov8x.pt` - Extra large (slowest, most accurate)
+
+- **Max File Size**: Modify `MAX_CONTENT_LENGTH` (default: 16MB)
+
+- **Allowed Extensions**: Modify `ALLOWED_EXTENSIONS` set
+
+- **Server Port**: Change `port=5000` in the last line
+
+## đ Troubleshooting
+
+### Common Issues
+
+1. **Module not found errors**:
+ - Make sure you activated the virtual environment
+ - Run `pip install -r requirements.txt` again
+
+2. **YOLO model not downloading**:
+ - Check your internet connection
+ - The model downloads automatically on first run
+ - Manual download: The model is stored in `~/.cache/torch/hub/`
+
+3. **Image upload fails**:
+ - Check file size (must be under 16MB)
+ - Ensure file format is JPG, JPEG, or PNG
+ - Check file permissions
+
+4. **Port already in use**:
+ - Change the port number in `app.py`
+ - Or stop other applications using port 5000
+
+### Error Messages
+
+- **"No file uploaded"**: Make sure to select a file before clicking submit
+- **"Invalid file type"**: Only JPG, JPEG, and PNG files are supported
+- **"Error processing image"**: The image might be corrupted or in an unsupported format
+
+## đ Learning Resources
+
+### Understanding the Code
+
+**Flask Routes**:
+- `/` - Homepage with upload form
+- `/upload` - Handles file upload and processing (POST)
+- `/results` - Displays detection results
+- `/uploads/` - Serves uploaded images
+- `/results/` - Serves processed images
+
+**Key Functions**:
+- `allowed_file()` - Validates file extensions
+- `perform_detection()` - Runs YOLO model and draws bounding boxes
+- `upload_file()` - Handles the upload process
+- `results()` - Displays detection results
+
+### YOLOv8 Basics
+
+YOLO (You Only Look Once) is a state-of-the-art object detection model that:
+- Processes images in real-time
+- Detects multiple objects simultaneously
+- Provides bounding box coordinates and confidence scores
+- Can identify 80 different object classes
+
+### Next Steps
+
+To enhance this project, you could:
+1. Add video processing capability
+2. Implement real-time webcam detection
+3. Add a database to store detection history
+4. Create user accounts and authentication
+5. Deploy to a cloud platform (Heroku, AWS, etc.)
+6. Add custom object detection for specific use cases
+7. Implement object tracking across video frames
+
+## đ Notes for Instructors
+
+This workshop is designed to be:
+- **Beginner-friendly**: Clear comments and explanations
+- **Self-contained**: All files included, no external dependencies
+- **Interactive**: Visual feedback and real-time results
+- **Educational**: Demonstrates best practices in web development
+
+**Recommended Teaching Order**:
+1. Explain Flask basics and routing
+2. Demonstrate file upload handling
+3. Introduce YOLO and object detection concepts
+4. Walk through the image processing pipeline
+5. Show template rendering with Jinja2
+6. Discuss error handling and validation
+
+**Time Estimate**: 2-3 hours for complete workshop
+
+## đ¤ Contributing
+
+This is a teaching project. Feel free to:
+- Add new features
+- Improve the UI/UX
+- Fix bugs
+- Enhance documentation
+- Add more examples
+
+## đ License
+
+This project is created for educational purposes as part of Python teaching materials.
+
+## đ Acknowledgments
+
+- **Ultralytics** for the YOLOv8 implementation
+- **Flask** team for the excellent web framework
+- **OpenCV** community for computer vision tools
+
+## đ Support
+
+For questions or issues:
+1. Check the troubleshooting section
+2. Review the code comments
+3. Consult Flask and YOLO documentation
+4. Ask your instructor or classmates
+
+---
+
+**Happy Learning! đ**
+
+Remember: The best way to learn is by doing. Try modifying the code, experimenting with different settings, and building upon this foundation!
diff --git a/workshops/flask_yolo_detection/app.py b/workshops/flask_yolo_detection/app.py
new file mode 100644
index 0000000..9e40182
--- /dev/null
+++ b/workshops/flask_yolo_detection/app.py
@@ -0,0 +1,206 @@
+"""
+Flask + YOLO Object Detection Workshop
+======================================
+This application demonstrates how to build a web application using Flask
+that performs object detection using YOLOv8 model.
+
+Features:
+- Upload images through a web interface
+- Perform object detection using YOLOv8
+- Display results with bounding boxes
+- Show detected objects with confidence scores
+"""
+
+import os
+from flask import Flask, render_template, request, redirect, url_for, send_from_directory
+from werkzeug.utils import secure_filename
+from ultralytics import YOLO
+import cv2
+from pathlib import Path
+
+# Initialize Flask application
+app = Flask(__name__)
+
+# Configuration
+app.config['UPLOAD_FOLDER'] = 'uploads'
+app.config['RESULTS_FOLDER'] = 'results'
+app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max file size
+app.config['ALLOWED_EXTENSIONS'] = {'png', 'jpg', 'jpeg'}
+
+# Create necessary folders if they don't exist
+os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
+os.makedirs(app.config['RESULTS_FOLDER'], exist_ok=True)
+
+# Initialize YOLO model (YOLOv8n - nano version for faster inference)
+# The model will be downloaded automatically on first use
+print("Loading YOLO model...")
+model = YOLO('yolov8n.pt') # You can use yolov8s.pt, yolov8m.pt, yolov8l.pt, yolov8x.pt for better accuracy
+print("Model loaded successfully!")
+
+
+def allowed_file(filename):
+ """
+ Check if the uploaded file has an allowed extension.
+
+ Args:
+ filename (str): Name of the uploaded file
+
+ Returns:
+ bool: True if file extension is allowed, False otherwise
+ """
+ return '.' in filename and \
+ filename.rsplit('.', 1)[1].lower() in app.config['ALLOWED_EXTENSIONS']
+
+
+def perform_detection(image_path, output_path):
+ """
+ Perform object detection on an image using YOLO model.
+
+ Args:
+ image_path (str): Path to the input image
+ output_path (str): Path to save the output image with bounding boxes
+
+ Returns:
+ list: List of dictionaries containing detection results
+ Each dict has: 'class', 'confidence', 'bbox'
+ """
+ # Read the image
+ image = cv2.imread(image_path)
+
+ # Perform detection
+ results = model(image)
+
+ # Extract detection information
+ detections = []
+ for result in results:
+ boxes = result.boxes
+ for box in boxes:
+ # Get bounding box coordinates
+ x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
+
+ # Get confidence score
+ confidence = float(box.conf[0].cpu().numpy())
+
+ # Get class name
+ class_id = int(box.cls[0].cpu().numpy())
+ class_name = model.names[class_id]
+
+ # Store detection info
+ detections.append({
+ 'class': class_name,
+ 'confidence': confidence,
+ 'bbox': [int(x1), int(y1), int(x2), int(y2)]
+ })
+
+ # Draw bounding box on image
+ cv2.rectangle(image, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2)
+
+ # Add label with class name and confidence
+ label = f"{class_name}: {confidence:.2f}"
+ cv2.putText(image, label, (int(x1), int(y1) - 10),
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
+
+ # Save the image with bounding boxes
+ cv2.imwrite(output_path, image)
+
+ return detections
+
+
+@app.route('/')
+def index():
+ """
+ Render the homepage with file upload form.
+ """
+ return render_template('index.html')
+
+
+@app.route('/upload', methods=['POST'])
+def upload_file():
+ """
+ Handle file upload and perform object detection.
+
+ Returns:
+ Redirect to results page or error page
+ """
+ # Check if file was uploaded
+ if 'file' not in request.files:
+ return render_template('index.html', error='No file uploaded')
+
+ file = request.files['file']
+
+ # Check if filename is empty
+ if file.filename == '':
+ return render_template('index.html', error='No file selected')
+
+ # Check if file type is allowed
+ if not allowed_file(file.filename):
+ return render_template('index.html',
+ error='Invalid file type. Please upload JPG, JPEG, or PNG images.')
+
+ try:
+ # Save the uploaded file
+ filename = secure_filename(file.filename)
+ upload_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
+ file.save(upload_path)
+
+ # Generate output filename
+ output_filename = f"detected_{filename}"
+ output_path = os.path.join(app.config['RESULTS_FOLDER'], output_filename)
+
+ # Perform object detection
+ detections = perform_detection(upload_path, output_path)
+
+ # Redirect to results page
+ return redirect(url_for('results',
+ original=filename,
+ detected=output_filename))
+
+ except Exception as e:
+ return render_template('index.html',
+ error=f'Error processing image: {str(e)}')
+
+
+@app.route('/results')
+def results():
+ """
+ Display detection results with images and detected objects.
+ """
+ original_filename = request.args.get('original')
+ detected_filename = request.args.get('detected')
+
+ if not original_filename or not detected_filename:
+ return redirect(url_for('index'))
+
+ # Load detection results
+ output_path = os.path.join(app.config['RESULTS_FOLDER'], detected_filename)
+
+ # Re-run detection to get detection info (in production, you'd store this)
+ upload_path = os.path.join(app.config['UPLOAD_FOLDER'], original_filename)
+ detections = perform_detection(upload_path, output_path)
+
+ return render_template('results.html',
+ original_image=original_filename,
+ detected_image=detected_filename,
+ detections=detections)
+
+
+@app.route('/uploads/')
+def uploaded_file(filename):
+ """
+ Serve uploaded files.
+ """
+ return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
+
+
+@app.route('/results/')
+def result_file(filename):
+ """
+ Serve result files with bounding boxes.
+ """
+ return send_from_directory(app.config['RESULTS_FOLDER'], filename)
+
+
+if __name__ == '__main__':
+ # Run the Flask application
+ # Debug mode is enabled for development - disable in production!
+ app.run(debug=True, host='0.0.0.0', port=5000)
diff --git a/workshops/flask_yolo_detection/requirements.txt b/workshops/flask_yolo_detection/requirements.txt
new file mode 100644
index 0000000..d8a4a0a
--- /dev/null
+++ b/workshops/flask_yolo_detection/requirements.txt
@@ -0,0 +1,17 @@
+# Flask Framework
+Flask==3.0.0
+
+# YOLO Object Detection
+ultralytics==8.0.228
+
+# Computer Vision
+opencv-python==4.8.1.78
+
+# Image Processing
+Pillow==10.1.0
+
+# Numerical Operations
+numpy==1.26.2
+
+# HTTP utilities (for secure filename handling)
+Werkzeug==3.0.1
diff --git a/workshops/flask_yolo_detection/templates/index.html b/workshops/flask_yolo_detection/templates/index.html
new file mode 100644
index 0000000..80ff939
--- /dev/null
+++ b/workshops/flask_yolo_detection/templates/index.html
@@ -0,0 +1,231 @@
+
+
+
+
+
+ YOLO Object Detection - Flask Workshop
+
+
+
+
+
đ YOLO Object Detection
+
Upload an image to detect objects using YOLOv8
+
+ {% if error %}
+
+ â ī¸ {{ error }}
+
+ {% endif %}
+
+
+
+
+
âšī¸ How it works:
+
+ - Select an image from your device
+ - Click "Detect Objects" to upload
+ - YOLOv8 will analyze the image
+ - View results with bounding boxes and labels
+
+
+
+
+
+
+
diff --git a/workshops/flask_yolo_detection/templates/results.html b/workshops/flask_yolo_detection/templates/results.html
new file mode 100644
index 0000000..a59ec6c
--- /dev/null
+++ b/workshops/flask_yolo_detection/templates/results.html
@@ -0,0 +1,291 @@
+
+
+
+
+
+ Detection Results - YOLO Flask Workshop
+
+
+
+
+
â
Detection Complete!
+
Here are the results of the object detection
+
+
+
+
đ¤ Original Image
+
 }})
+
+
+
+
đ¯ Detected Objects
+
 }})
+
+
+
+
+
đ Detection Statistics
+
+
+
+
{{ detections|length }}
+
Objects Detected
+
+
+ {% if detections %}
+
+
{{ (detections | map(attribute='confidence') | sum / detections|length * 100) | round(1) }}%
+
Avg. Confidence
+
+
+
+
{{ detections | map(attribute='class') | unique | list | length }}
+
Unique Classes
+
+ {% endif %}
+
+
+ {% if detections %}
+
Detected Objects:
+
+ {% for detection in detections %}
+
+
{{ detection.class|title }}
+
+ Confidence: {{ (detection.confidence * 100)|round(1) }}%
+
+
+
+ {% endfor %}
+
+ {% else %}
+
+ đ No objects detected in the image.
+
+ Try uploading a different image with more visible objects.
+
+ {% endif %}
+
+
+
+
+
+
+
+
From afa70c03b9c279f02a269de69bdf118740ef8f6e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 17 Nov 2025 10:21:43 +0000
Subject: [PATCH 3/5] Fix security vulnerabilities by updating dependencies
Co-authored-by: mmaleki92 <74877833+mmaleki92@users.noreply.github.com>
---
workshops/fastapi_yolo_detection/requirements.txt | 6 +++---
workshops/flask_yolo_detection/requirements.txt | 4 ++--
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/workshops/fastapi_yolo_detection/requirements.txt b/workshops/fastapi_yolo_detection/requirements.txt
index 1798553..5d47a60 100644
--- a/workshops/fastapi_yolo_detection/requirements.txt
+++ b/workshops/fastapi_yolo_detection/requirements.txt
@@ -1,11 +1,11 @@
# FastAPI Framework
-fastapi==0.104.1
+fastapi==0.109.1
# ASGI Server
uvicorn[standard]==0.24.0
# File Upload Support
-python-multipart==0.0.6
+python-multipart==0.0.18
# YOLO Object Detection
ultralytics==8.0.228
@@ -14,7 +14,7 @@ ultralytics==8.0.228
opencv-python==4.8.1.78
# Image Processing
-Pillow==10.1.0
+Pillow==10.2.0
# Numerical Operations
numpy==1.26.2
diff --git a/workshops/flask_yolo_detection/requirements.txt b/workshops/flask_yolo_detection/requirements.txt
index d8a4a0a..e492933 100644
--- a/workshops/flask_yolo_detection/requirements.txt
+++ b/workshops/flask_yolo_detection/requirements.txt
@@ -8,10 +8,10 @@ ultralytics==8.0.228
opencv-python==4.8.1.78
# Image Processing
-Pillow==10.1.0
+Pillow==10.2.0
# Numerical Operations
numpy==1.26.2
# HTTP utilities (for secure filename handling)
-Werkzeug==3.0.1
+Werkzeug==3.0.3
From 6b9de08418e420c794b6d386437cfce95826167b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 17 Nov 2025 10:23:32 +0000
Subject: [PATCH 4/5] Add workshop overview README
---
workshops/README.md | 58 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 58 insertions(+)
create mode 100644 workshops/README.md
diff --git a/workshops/README.md b/workshops/README.md
new file mode 100644
index 0000000..a605435
--- /dev/null
+++ b/workshops/README.md
@@ -0,0 +1,58 @@
+# Python Teaching Workshops
+
+This directory contains comprehensive workshop examples for teaching Python web development with machine learning integration.
+
+## Available Workshops
+
+### 1. Flask + YOLO Object Detection (`flask_yolo_detection/`)
+
+A complete Flask web application demonstrating:
+- Web application development with Flask
+- File upload handling
+- YOLOv8 object detection integration
+- Template rendering with Jinja2
+- Error handling and validation
+
+**Best for**: Beginners learning Flask and web development basics
+
+### 2. FastAPI + YOLO Object Detection (`fastapi_yolo_detection/`)
+
+A modern REST API built with FastAPI demonstrating:
+- Async/await patterns in Python
+- RESTful API design
+- Automatic API documentation (Swagger/ReDoc)
+- Modern frontend-backend communication
+- Advanced error handling
+
+**Best for**: Students learning modern API development and async programming
+
+## Getting Started
+
+Each workshop is self-contained with its own:
+- README.md with detailed instructions
+- requirements.txt with all dependencies
+- Complete, working code examples
+- Documentation and usage examples
+
+Navigate to the specific workshop directory and follow the README.md instructions to get started.
+
+## Requirements
+
+- Python 3.8 or higher
+- pip package manager
+- Virtual environment (recommended)
+
+## Workshop Features
+
+Both workshops demonstrate:
+- â
Real-world application structure
+- â
Security best practices
+- â
Clear, commented code
+- â
Comprehensive documentation
+- â
Error handling
+- â
File validation
+- â
Modern UI design
+
+## Support
+
+For questions or issues with specific workshops, refer to the individual workshop README files.
From df0f6c3b5d7924fd4c7a8ad57a02b9ce94257571 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 17 Nov 2025 10:26:35 +0000
Subject: [PATCH 5/5] Improve security documentation and add environment
variable for debug mode
Co-authored-by: mmaleki92 <74877833+mmaleki92@users.noreply.github.com>
---
workshops/flask_yolo_detection/README.md | 24 ++++++++++++++++++++++++
workshops/flask_yolo_detection/app.py | 10 ++++++++--
2 files changed, 32 insertions(+), 2 deletions(-)
diff --git a/workshops/flask_yolo_detection/README.md b/workshops/flask_yolo_detection/README.md
index 5503821..040fcc7 100644
--- a/workshops/flask_yolo_detection/README.md
+++ b/workshops/flask_yolo_detection/README.md
@@ -176,6 +176,17 @@ You can modify the following settings in `app.py`:
- **Server Port**: Change `port=5000` in the last line
+- **Debug Mode**: For production deployment, disable debug mode:
+ ```bash
+ export FLASK_DEBUG=False
+ python app.py
+ ```
+ Or use a production WSGI server like Gunicorn:
+ ```bash
+ pip install gunicorn
+ gunicorn -w 4 -b 0.0.0.0:5000 app:app
+ ```
+
## đ Troubleshooting
### Common Issues
@@ -240,6 +251,19 @@ To enhance this project, you could:
6. Add custom object detection for specific use cases
7. Implement object tracking across video frames
+## đ Security Considerations
+
+This workshop is designed for **learning and development purposes**. For production deployment:
+
+1. **Debug Mode**: Always set `FLASK_DEBUG=False` or use a production WSGI server (Gunicorn, uWSGI)
+2. **File Validation**: Current implementation validates file extensions - consider adding file content validation
+3. **Rate Limiting**: Implement rate limiting to prevent abuse (e.g., using Flask-Limiter)
+4. **File Size**: Already implemented (16MB limit) but adjust based on your needs
+5. **HTTPS**: Use TLS/SSL certificates in production
+6. **Authentication**: Consider adding user authentication for production use
+7. **Input Sanitization**: File names are sanitized using `secure_filename()`
+8. **Temporary Files**: Consider implementing cleanup of old uploaded files
+
## đ Notes for Instructors
This workshop is designed to be:
diff --git a/workshops/flask_yolo_detection/app.py b/workshops/flask_yolo_detection/app.py
index 9e40182..ddadc7c 100644
--- a/workshops/flask_yolo_detection/app.py
+++ b/workshops/flask_yolo_detection/app.py
@@ -202,5 +202,11 @@ def result_file(filename):
if __name__ == '__main__':
# Run the Flask application
- # Debug mode is enabled for development - disable in production!
- app.run(debug=True, host='0.0.0.0', port=5000)
+ # Debug mode is enabled for development/workshop purposes only
+ # WARNING: Never use debug=True in production environments!
+ # For production, use a production WSGI server like Gunicorn or uWSGI
+ # Example: gunicorn -w 4 -b 0.0.0.0:5000 app:app
+
+ # Use environment variable to control debug mode (defaults to True for workshop)
+ debug_mode = os.environ.get('FLASK_DEBUG', 'True').lower() == 'true'
+ app.run(debug=debug_mode, host='0.0.0.0', port=5000)