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 + + + +
+
+

🚀 FastAPI + YOLO Object Detection

+

Upload an image to detect objects using YOLOv8 with FastAPI

+
+ + +
+
+
+
📸
+
Click to select an image or drag & drop
+
Supported formats: JPG, JPEG, PNG (Max 16MB)
+ +
+
+ + + +
+
+
+ + + + + + + + +
+

â„šī¸ 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

+ Original Image +
+ +
+

đŸŽ¯ Detected Objects

+ Detected Image +
+
+ +
+

📊 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)