diff --git a/.gitignore b/.gitignore index bb8ad3c..f52cba2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,222 +1,65 @@ -# Byte-compiled / optimized / DLL files +# Python virtual environment +venv/ +env/ +.env/ +.venv/ + +# Python cache files __pycache__/ *.py[cod] *$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -pip-wheel-metadata/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ .pytest_cache/ -# Translations -*.mo -*.pot +# Distribution / packaging +dist/ +build/ +*.egg-info/ -# Django stuff: +# IDE specific files +.idea/ +.vscode/ +*.swp +*.swo + +# YOLO specific +runs/ +*.pt +weights/ + +# Project specific +torch_compile_debug/ +training/train/ +training/val/ +dataset.yaml + +# Logs and temporary files *.log -local_settings.py -db.sqlite3 -db.sqlite3-journal +.DS_Store +temp/ +tmp/ -# Flask stuff: -instance/ -.webassets-cache +# Debug directories +torchinductor_*/ -# Scrapy stuff: -.scrapy +# Cache directories +.cache/ +*.cache -# Sphinx documentation -docs/_build/ +# Test coverage +coverage_html_report/ +.coverage +htmlcov/ +test_results/ -# PyBuilder -target/ +# Compiled files +*.so +*.dll +*.dylib # Jupyter Notebook .ipynb_checkpoints +*.ipynb -# IPython -profile_default/ -ipython_config.py - -# pyenv -.python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments +# Environment variables .env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ -myenv/ -memory_detection_env/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# Machine Learning specific -# Model weights and checkpoints -*.pt -*.pth -*.ckpt -*.h5 -*.pkl -*.joblib -runs/ -wandb/ -mlruns/ -.neptune/ - -# YOLOv8 specific -yolov8*.pt -best.pt -last.pt -exp*/ - -# Dataset cache -*.cache -.fiftyone/ - -# Tensorboard logs -logs/ -tb_logs/ - -# Jupyter notebook checkpoints -.ipynb_checkpoints/ - -# IDE specific -.vscode/ -.idea/ -*.swp -*.swo -*~ - -# OS specific -.DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -ehthumbs.db -Thumbs.db - -# Project specific -uploads/ -temp/ -tmp/ -annotated_*.png -test_*_result.png - -# Large files that shouldn't be committed -*.zip -*.tar.gz -*.rar - -# API keys and secrets -.env -config.ini -secrets.json - -# Temporary files -*.tmp -*.temp -*.bak -*.backup - -# Log files -*.log -logs/ - -# Coverage reports -htmlcov/ -.coverage - -# Virtual environment (additional patterns) -pyvenv.cfg -pip-selfcheck.json - -# PyTorch specific -*.pth.tar - -# Conda -.conda/ - -# Local configuration -local_config.py -config_local.py +.env.local diff --git a/API_DOCS.md b/API_DOCS.md deleted file mode 100644 index 186516c..0000000 --- a/API_DOCS.md +++ /dev/null @@ -1,214 +0,0 @@ -# Memory Module Detection API Documentation - -## Base URL -``` -http://localhost:5002 -``` - -## Endpoints - -### 1. Health Check -**GET** `/health` - -Check if the API is running and model is loaded. - -**Response:** -```json -{ - "status": "healthy", - "model_loaded": true, - "timestamp": "2025-07-12T07:41:46.123456" -} -``` - -### 2. API Information -**GET** `/api` - -Get basic API information and available endpoints. - -**Response:** -```json -{ - "name": "Memory Module Detection API", - "version": "1.0", - "description": "AI-powered memory module detection using YOLOv8", - "model_loaded": true, - "endpoints": ["/health", "/api", "/detect", "/detect/hardcoded", "/detect/base64"] -} -``` - -### 3. Upload Image Detection -**POST** `/detect` - -Upload an image file for memory module detection. - -**Request:** -- Content-Type: `multipart/form-data` -- Body: Form data with `file` field containing image -- Optional: `confidence` parameter (default: 0.8) - -**Response:** -```json -{ - "success": true, - "detections": [ - { - "x1": 100.5, - "y1": 150.2, - "x2": 200.8, - "y2": 250.6, - "confidence": 0.95, - "class": "memory_module" - } - ], - "num_detections": 1, - "annotated_image": "base64_encoded_image_string", - "confidence_threshold": 0.8 -} -``` - -### 4. Hardcoded Test Image -**GET** `/detect/hardcoded` - -Process the hardcoded test image for detection. - -**Query Parameters:** -- `confidence` (optional): Confidence threshold (default: 0.8) - -**Example:** -``` -GET /detect/hardcoded?confidence=0.9 -``` - -**Response:** -```json -{ - "success": true, - "detections": [...], - "num_detections": 2, - "annotated_image": "base64_encoded_image_string", - "confidence_threshold": 0.9, - "test_image_path": "training/memory/out1.png" -} -``` - -### 5. Base64 Image Detection -**POST** `/detect/base64` - -Process a base64 encoded image for detection. - -**Request:** -```json -{ - "image_data": "base64_encoded_image_string", - "confidence": 0.8 -} -``` - -**Response:** -```json -{ - "success": true, - "detections": [...], - "num_detections": 1, - "annotated_image": "base64_encoded_image_string", - "confidence_threshold": 0.8 -} -``` - -## Error Responses - -All endpoints return error responses in this format: -```json -{ - "error": "Error message description", - "success": false -} -``` - -Common HTTP status codes: -- `200` - Success -- `400` - Bad Request (invalid file, missing parameters) -- `404` - Not Found (endpoint or file not found) -- `413` - File Too Large (max 16MB) -- `500` - Internal Server Error (model not loaded, processing error) - -## Supported Image Formats -- PNG -- JPG/JPEG -- GIF -- BMP - -## File Size Limits -- Maximum upload size: 16MB - -## Detection Response Format - -Each detection object contains: -- `x1, y1`: Top-left corner coordinates -- `x2, y2`: Bottom-right corner coordinates -- `confidence`: Detection confidence score (0.0-1.0) -- `class`: Detected object class ("memory_module") - -## Usage Examples - -### cURL Examples - -**Health Check:** -```bash -curl http://localhost:5002/health -``` - -**Upload Image:** -```bash -curl -X POST -F "file=@image.jpg" -F "confidence=0.8" http://localhost:5002/detect -``` - -**Hardcoded Test:** -```bash -curl "http://localhost:5002/detect/hardcoded?confidence=0.9" -``` - -### Python Examples - -**Health Check:** -```python -import requests - -response = requests.get('http://localhost:5002/health') -print(response.json()) -``` - -**Upload Image:** -```python -import requests - -with open('image.jpg', 'rb') as f: - files = {'file': f} - data = {'confidence': 0.8} - response = requests.post('http://localhost:5002/detect', files=files, data=data) - print(response.json()) -``` - -**Base64 Detection:** -```python -import requests -import base64 - -with open('image.jpg', 'rb') as f: - image_data = base64.b64encode(f.read()).decode() - -payload = { - 'image_data': image_data, - 'confidence': 0.8 -} -response = requests.post('http://localhost:5002/detect/base64', json=payload) -print(response.json()) -``` - -## Model Information -- **Architecture:** YOLOv8 Nano -- **Classes:** memory_module -- **Input Size:** 640x640 -- **Accuracy:** 99.5% mAP50 -- **Inference Time:** ~37ms on CPU diff --git a/README.md b/README.md index 5fcd9f9..0d0c8d6 100644 --- a/README.md +++ b/README.md @@ -1,545 +1,194 @@ -# DS Task Recycling Project - Memory Module Detection +# DS Task Recycling Project -This project is a complete implementation of a Flask API that processes motherboard images and detects memory modules using YOLOv8. The API returns annotated images with bounding boxes drawn around each detected memory module. +This project is a Flask API that processes images of motherboards to detect memory modules. It uses computer vision to identify and draw bounding boxes around memory modules present in the input images. -## ๐Ÿš€ Quick Start +## Project Overview -### 1. Install Dependencies -```bash -pip install -r requirements.txt -``` - -### 2. Train the Model -```bash -python3 train.py --epochs 100 --batch 16 -``` - -### 3. Start the API -```bash -python3 main.py -``` - -### 4. Test the API -```bash -# Option 1: Use the Web Interface (Recommended for QA) -# Open browser and go to: http://localhost:5000 - -# Option 2: Use command line -# Test with hardcoded image -curl http://localhost:5000/detect/hardcoded - -# Upload an image -curl -X POST -F "image=@your_image.png" http://localhost:5000/detect - -# Option 3: Run automated tests -python3 test_api.py -``` - -## ๐Ÿ“‹ Project Overview - -- **Algorithm Used:** YOLOv8 Nano (ultralytics) - **Input Types:** - - Image upload via Flask API - - Base64 encoded images - - Hardcoded test image -- **Dataset:** 40 images (20 with memory modules, 20 without) -- **Output:** Annotated images with bounding boxes and confidence scores + - Image upload via the Flask API + - A hardcoded test image (memory_out19.png) for testing purposes -## ๐Ÿ—๏ธ Project Structure +- **Dataset:** + - 20 pictures of motherboards with memory + - 20 pictures of motherboards without memory -``` -ds_task_recycling_project/ -โ”œโ”€โ”€ main.py # Flask API application (main interface) -โ”œโ”€โ”€ api_docs.py # Swagger UI API documentation (developer only) -โ”œโ”€โ”€ train.py # YOLOv8 training script -โ”œโ”€โ”€ inference_utils.py # Detection and visualization utilities -โ”œโ”€โ”€ prepare_dataset.py # Dataset preparation script -โ”œโ”€โ”€ test_api.py # API testing script -โ”œโ”€โ”€ setup.py # Automated setup script -โ”œโ”€โ”€ requirements.txt # Python dependencies -โ”œโ”€โ”€ dataset.yaml # YOLO dataset configuration -โ”œโ”€โ”€ .gitignore # Git ignore file for ML projects -โ”œโ”€โ”€ VALIDATION_CHECKLIST.md # Project validation checklist -โ”œโ”€โ”€ templates/ # Frontend templates -โ”‚ โ””โ”€โ”€ index.html # QA testing web interface -โ”œโ”€โ”€ static/ # Frontend assets -โ”‚ โ”œโ”€โ”€ style.css # Styling for web interface -โ”‚ โ””โ”€โ”€ script.js # JavaScript for web interface -โ”œโ”€โ”€ venv/ # Virtual environment (created by user) -โ”œโ”€โ”€ training/ # Dataset directory -โ”‚ โ”œโ”€โ”€ memory/ # Images with memory modules + YOLO labels -โ”‚ โ”‚ โ”œโ”€โ”€ out1.png # Sample motherboard image with memory -โ”‚ โ”‚ โ”œโ”€โ”€ out1.txt # YOLO format annotation file -โ”‚ โ”‚ โ””โ”€โ”€ ... # 19 more image/label pairs -โ”‚ โ”œโ”€โ”€ no_memory/ # Images without memory modules -โ”‚ โ”‚ โ”œโ”€โ”€ out21.png # Sample motherboard image without memory -โ”‚ โ”‚ โ””โ”€โ”€ ... # 19 more images (no labels needed) -โ”‚ โ”œโ”€โ”€ train/ # Training split (80% = 32 images) -โ”‚ โ”‚ โ”œโ”€โ”€ images/ # Training images -โ”‚ โ”‚ โ””โ”€โ”€ labels/ # Training labels -โ”‚ โ””โ”€โ”€ val/ # Validation split (20% = 8 images) -โ”‚ โ”œโ”€โ”€ images/ # Validation images -โ”‚ โ””โ”€โ”€ labels/ # Validation labels -โ”œโ”€โ”€ uploads/ # Temporary upload directory (created at runtime) -โ””โ”€โ”€ runs/ # Training outputs (created after training) - โ””โ”€โ”€ detect/ - โ””โ”€โ”€ memory_module_detection/ - โ”œโ”€โ”€ weights/ - โ”‚ โ”œโ”€โ”€ best.pt # Best model weights - โ”‚ โ””โ”€โ”€ last.pt # Last epoch weights - โ”œโ”€โ”€ train_batch*.jpg # Training visualization - โ”œโ”€โ”€ val_batch*.jpg # Validation visualization - โ”œโ”€โ”€ confusion_matrix.png # Model performance metrics - โ”œโ”€โ”€ results.png # Training curves - โ””โ”€โ”€ args.yaml # Training arguments -``` +- **Output:** + - An annotated image with bounding boxes around each detected memory module + - For example, if there are two memory modules, two boxes are drawn; if only one is detected, then one box is drawn -### **๐Ÿ“ Key Files Description** +- **Annotation Tool:** + - [makesense.ai](https://www.makesense.ai/) was used for manual annotation -| File/Directory | Purpose | Usage | -|----------------|---------|-------| -| `main.py` | Main Flask API application | `python3 main.py` | -| `api_docs.py` | Swagger UI documentation (developer only) | `python3 api_docs.py` | -| `train.py` | YOLOv8 model training | `python3 train.py` | -| `inference_utils.py` | Detection utilities and classes | Imported by other scripts | -| `test_api.py` | Comprehensive API testing | `python3 test_api.py` | -| `setup.py` | Automated project setup | `python3 setup.py` | -| `templates/index.html` | Web interface for QA testing | Served by Flask | -| `static/` | CSS, JavaScript, and assets | Served by Flask | -| `training/` | Complete dataset with annotations | Used by training script | -| `runs/` | Model training outputs | Created after training | -| `venv/` | Python virtual environment | Created by user | +## Implementation Details -## ๐Ÿค– Algorithm Choice & Technical Decisions +### Algorithm Choice & Rationale -### 1. **Algorithm Choice: YOLOv8 Nano** +1. **Which algorithm was chosen?** + - YOLOv8 (specifically YOLOv8n - the nano version) was selected for this task + +2. **Why this algorithm?** + - Fast inference speed suitable for real-time applications + - Good balance between accuracy and computational requirements + - Built-in support for transfer learning + - Excellent performance on object detection tasks + - Easy integration with Python/Flask applications + - Robust community support and documentation -**Which algorithm will you use for detecting the memory modules?** -- **Answer:** YOLOv8 Nano (You Only Look Once version 8, Nano variant) +### Hardware Considerations -**Why do you choose this particular algorithm?** +3. **CPU/GPU Impact:** + - The current implementation runs on CPU for broader accessibility + - Model parameters were optimized for CPU performance: + - Reduced batch size (8) + - Lightweight augmentation + - Early stopping with patience=15 + - GPU support is available through YOLO if needed for scaling + - Current performance is suitable for the demo nature of the project -**Primary Reasons:** -- **State-of-the-art performance:** Latest evolution of YOLO family with superior accuracy -- **Real-time inference:** 37ms processing time, single-stage detector -- **Small object detection:** Excellent at detecting memory modules on motherboards -- **Pre-trained weights:** Leverages COCO dataset for transfer learning -- **Easy integration:** Ultralytics library with excellent Python API -- **Model efficiency:** Nano variant balances 99.5% mAP50 accuracy with speed -- **Production ready:** Proven architecture used in industrial applications +### Video Processing Approach -**Technical Advantages:** -- **Anchor-free design:** Eliminates anchor box tuning complexity -- **Advanced augmentation:** Built-in data augmentation strategies -- **Multi-scale detection:** Handles objects of different sizes effectively -- **Export flexibility:** ONNX, TensorRT support for deployment optimization -- **Active community:** Regular updates and extensive documentation +4. **Handling Video Input:** + - While not currently implemented, video processing would involve: + - Frame extraction + - Batch processing of frames + - Real-time detection using YOLO's video processing capabilities + - Optional frame skipping for performance optimization + - The current architecture can be extended for video by: + - Adding a video upload endpoint + - Implementing frame-by-frame processing + - Returning annotated video or real-time stream -### 2. **Hardware Considerations** - -**Does CPU or GPU have an impact on your decision? Please explain.** - -**Yes, hardware significantly impacts the implementation strategy:** - -**Training Phase:** -- **GPU Impact:** Critical for training efficiency - - **GPU Training:** 5-10 minutes for 50 epochs (recommended) - - **CPU Training:** 30-60 minutes for same epochs - - **Memory Requirements:** 4GB+ GPU memory recommended - - **Batch Size:** GPU allows larger batches (16-32) vs CPU (4-8) - -**Inference Phase:** -- **CPU Performance:** 37ms per image on modern CPU (Intel i5/i7, M1/M2) -- **GPU Performance:** 10-15ms per image, better for batch processing -- **Memory Usage:** CPU: 2-4GB RAM, GPU: 1-2GB VRAM -- **Edge Deployment:** Model runs efficiently on CPU-only devices - -**Decision Impact:** -- **Algorithm Choice:** YOLOv8 Nano chosen specifically for CPU compatibility -- **Deployment Flexibility:** No expensive GPU required for production -- **Cost Efficiency:** Reduces infrastructure costs -- **Scalability:** GPU enables high-throughput batch processing - -**Implementation:** -```python -# Auto-detection with fallback in train.py -device = 'cuda' if torch.cuda.is_available() else 'cpu' -print(f"Using device: {device}") -``` - -### 3. **Video Input Approach** - -**What if a video is provided instead of single images?** -**Does your approach change when processing videos? Please describe your approach.** - -**Yes, the approach would change significantly for video processing:** - -**Video Processing Strategy:** - -**1. Frame Extraction & Sampling** -```python -def process_video(video_path, fps_sample=5): - cap = cv2.VideoCapture(video_path) - frame_rate = cap.get(cv2.CAP_PROP_FPS) - frame_interval = int(frame_rate / fps_sample) # Sample every N frames - - frames = [] - frame_count = 0 - while cap.isOpened(): - ret, frame = cap.read() - if not ret: - break - if frame_count % frame_interval == 0: - frames.append(frame) - frame_count += 1 - return frames -``` - -**2. Batch Processing for Efficiency** -```python -def batch_detect_video(frames, batch_size=8): - results = [] - for i in range(0, len(frames), batch_size): - batch = frames[i:i+batch_size] - batch_results = model(batch) # Process multiple frames at once - results.extend(batch_results) - return results -``` - -**3. Temporal Consistency & Tracking** -```python -def apply_temporal_tracking(detections, frames): - tracker = DeepSORT() # Or ByteTrack for better performance - tracked_results = [] - - for frame_detections, frame in zip(detections, frames): - tracked_objects = tracker.update(frame_detections) - tracked_results.append(tracked_objects) - - return tracked_results -``` - -**4. Optimization Strategies** -- **Motion Detection:** Skip frames with no significant changes -- **Optical Flow:** Track objects between frames to reduce processing -- **Keyframe Selection:** Process only important frames -- **Parallel Processing:** Use multiple CPU cores/GPU streams -- **Memory Management:** Process in chunks to avoid overflow - -**5. Video-Specific Considerations** -- **Temporal Smoothing:** Apply filters to reduce detection jitter -- **Performance Scaling:** GPU becomes more critical for video processing -- **Storage Requirements:** Annotated videos require significant storage -- **Real-time Processing:** Streaming vs batch processing trade-offs - -**Potential API Endpoint:** -```python -@app.route('/detect/video', methods=['POST']) -def detect_video(): - # Upload video file - # Extract frames at specified FPS - # Batch process frames with YOLOv8 - # Apply temporal tracking for consistency - # Return annotated video or frame-by-frame results -``` - -## **Technical Questions Summary** - -The project successfully addresses all required technical questions: - -1. **โœ… Algorithm Choice:** YOLOv8 Nano selected for optimal balance of accuracy (99.5% mAP50), speed (37ms), and deployment flexibility -2. **โœ… Hardware Considerations:** Comprehensive CPU/GPU analysis with auto-detection and fallback strategies for maximum compatibility -3. **โœ… Video Processing:** Complete video processing strategy with frame extraction, batch processing, temporal tracking, and optimization techniques - -All technical decisions are implemented and validated in the working system. - -## Installation & Setup - -### Prerequisites -- Python 3.8+ -- pip or conda - -### Step-by-Step Installation - -1. **Clone/Download the project** -```bash -cd ds_task_recycling_project -``` - -2. **Install dependencies** -```bash -pip install -r requirements.txt -``` - -3. **Prepare dataset (if not already done)** -```bash -python3 prepare_dataset.py -``` - -4. **Train the model** -```bash -# Basic training (recommended) -python3 train.py - -# Custom training parameters -python3 train.py --epochs 150 --batch 8 --device cuda -``` - -5. **Start the Flask API** -```bash -python3 main.py -``` - -The API will be available at `http://localhost:5000` - -## ๐ŸŒ Web Interface for QA Testing - -We've included a comprehensive web interface for easy QA testing: - -### Features: -- **Drag & Drop Image Upload** - Easy image selection -- **Real-time API Status** - Shows if API and model are loaded -- **Multiple Test Options:** - - Test hardcoded image - - Upload custom images - - Run comprehensive API tests -- **Interactive Results** - View annotated images with detection details -- **Confidence Threshold Control** - Adjust detection sensitivity -- **Responsive Design** - Works on desktop and mobile - -### Access: -1. Start the API: `python3 main.py` -2. Open browser: `http://localhost:5000` -3. Use the interface to test detection functionality - -### QA Testing Workflow: -1. **Check API Status** - Verify green "API Online" indicator -2. **Test Hardcoded Image** - Click "Test Hardcoded Image" button -3. **Upload Custom Images** - Drag/drop or select motherboard images -4. **Adjust Confidence** - Use slider to test different thresholds -5. **Run All Tests** - Comprehensive API endpoint testing -6. **Review Results** - Check detection accuracy and annotations - -## ๐Ÿ“ก API Documentation - -### Base URL -``` -http://localhost:5000 -``` +## API Implementation ### Endpoints -#### 1. **GET /** - API Information +1. **Image Upload (`/detect`):** + ```http + POST /detect + Content-Type: multipart/form-data + ``` + - Accepts image uploads + - Returns annotated image with detection boxes + +2. **Test Detection (`/detect/test`):** + ```http + GET /detect/test + ``` + - Uses a hardcoded test image (memory_out19.png) + - Returns annotated image with detection boxes + +### Processing Workflow + +1. Image Reception: + - Via file upload or hardcoded test image +2. Detection: + - YOLOv8 processes the image + - Confidence threshold: 0.25 + - IoU threshold: 0.45 +3. Annotation: + - Bounding boxes drawn around detected modules +4. Response: + - Annotated image returned in PNG format + +## Model Training + +The model was trained with the following parameters: +- 50 epochs +- Image size: 640x640 +- Batch size: 8 +- Early stopping patience: 15 +- Augmentations: + - Rotation (ยฑ5ยฐ) + - Scale (0.5) + - Translation (0.1) + - Horizontal flip (0.5) + - Mosaic (1.0) + +## Dataset Preparation + ```bash -curl http://localhost:5000/ +training/ +โ”œโ”€โ”€ memory/ +โ”‚ โ””โ”€โ”€ (images with memory modules) #You have this +โ”œโ”€โ”€ no_memory/ +โ”‚ โ””โ”€โ”€ (images without memory modules) #You have this as well +โ”œโ”€โ”€ train/ +โ”‚ โ”œโ”€โ”€ images/ +โ”‚ โ”‚ โ”œโ”€โ”€ memory_*.png +โ”‚ โ”‚ โ””โ”€โ”€ no_memory_*.png +โ”‚ โ””โ”€โ”€ labels/ +โ”‚ โ”œโ”€โ”€ memory_*.txt +โ”‚ โ””โ”€โ”€ no_memory_*.txt +โ””โ”€โ”€ val/ + โ”œโ”€โ”€ images/ + โ”‚ โ”œโ”€โ”€ memory_*.png + โ”‚ โ””โ”€โ”€ no_memory_*.png + โ””โ”€โ”€ labels/ + โ”œโ”€โ”€ memory_*.txt + โ””โ”€โ”€ no_memory_*.txt + +dataset.yaml ``` -**Response:** -```json -{ - "message": "Memory Module Detection API", - "version": "1.0.0", - "endpoints": {...}, - "model_loaded": true, - "supported_formats": ["png", "jpg", "jpeg", "gif", "bmp"] -} +The dataset is organized as follows: +- `training/memory/`: Source directory for images with memory modules +- `training/no_memory/`: Source directory for images without memory modules +- `training/train/`: Training dataset + - `images/`: Contains both memory and no-memory images with appropriate prefixes + - `labels/`: Contains YOLO format annotation files +- `training/val/`: Validation dataset + - `images/`: Contains both memory and no-memory images with appropriate prefixes + - `labels/`: Contains YOLO format annotation files + +The `dataset.yaml` file contains: +```yaml +path: training # dataset root dir +train: train/images # train images +val: val/images # validation images +nc: 1 # number of classes +names: ['memory_module'] # class names ``` -#### 2. **GET /health** - Health Check +## Getting Started + +1. Clone the repository: + ```bash + git clone http://23.29.118.76:3000/michael/ds_task_recycling_project.git + ``` +2. Install dependencies: + ```bash + pip install -r requirements.txt + ``` +3. Prepare the dataset: + ```bash + python prepare_dataset.py + ``` +4. Train the model (if not already trained): + ```bash + python train.py + ``` +5. Run the Flask application: + ```bash + python run.py + ``` +6. Access the web interface at `http://localhost:5000` + +## Testing + +The project includes comprehensive tests for the detector: +- Batch detection testing +- Threshold optimization +- Various confidence/IoU threshold combinations + +Run tests with: ```bash -curl http://localhost:5000/health +pytest tests/ ``` -#### 3. **POST /detect** - Upload Image Detection -```bash -curl -X POST -F "image=@motherboard.png" -F "confidence=0.5" http://localhost:5000/detect -``` +## Future Improvements -**Response:** -```json -{ - "success": true, - "detections": [ - { - "bbox": [100, 150, 200, 250], - "confidence": 0.85, - "class": 0, - "class_name": "memory_module" - } - ], - "num_detections": 1, - "annotated_image": "base64_encoded_image...", - "confidence_threshold": 0.5 -} -``` - -#### 4. **GET /detect/hardcoded** - Test with Hardcoded Image -```bash -curl "http://localhost:5000/detect/hardcoded?confidence=0.5" -``` - -#### 5. **POST /detect/base64** - Base64 Image Detection -```bash -curl -X POST -H "Content-Type: application/json" \ - -d '{"image": "base64_string", "confidence": 0.5}' \ - http://localhost:5000/detect/base64 -``` - -## ๐Ÿงช Testing & Usage Examples - -### 1. **Test with Python requests** -```python -import requests -import base64 - -# Test hardcoded image -response = requests.get('http://localhost:5000/detect/hardcoded') -result = response.json() -print(f"Found {result['num_detections']} memory modules") - -# Upload image -with open('test_image.png', 'rb') as f: - files = {'image': f} - response = requests.post('http://localhost:5000/detect', files=files) - result = response.json() -``` - -### 2. **Test with curl** -```bash -# Basic detection -curl -X POST -F "image=@training/memory/out1.png" http://localhost:5000/detect - -# With custom confidence -curl -X POST -F "image=@training/memory/out1.png" -F "confidence=0.3" http://localhost:5000/detect -``` - -### 3. **Command Line Inference** -```bash -# Test single image -python3 inference_utils.py --image training/memory/out1.png --conf 0.5 - -# Validate trained model -python3 train.py --validate --model runs/detect/memory_module_detection/weights/best.pt -``` - -## ๐Ÿ“Š Training Details - -### Dataset Statistics -- **Total Images:** 40 (20 with memory, 20 without) -- **Training Split:** 32 images (80%) -- **Validation Split:** 8 images (20%) -- **Classes:** 1 (memory_module) -- **Annotation Format:** YOLO (normalized coordinates) - -### Training Configuration -```python -# Default training parameters -epochs = 100 -batch_size = 16 -image_size = 640 -confidence_threshold = 0.5 -iou_threshold = 0.45 -``` - -### Expected Training Time -- **GPU (RTX 3060+):** 5-10 minutes -- **CPU (Modern):** 30-60 minutes -- **Memory Usage:** 2-4GB RAM - -### Model Performance -After training, you should see: -- **mAP50:** >0.8 (80%+ accuracy at 50% IoU) -- **Precision:** >0.85 -- **Recall:** >0.80 - -## ๐Ÿ› Troubleshooting - -### Common Issues - -#### 1. **Model Not Found Error** -``` -Error: Model not found at runs/detect/memory_module_detection/weights/best.pt -``` -**Solution:** Train the model first -```bash -python3 train.py -``` - -#### 2. **CUDA Out of Memory** -``` -RuntimeError: CUDA out of memory -``` -**Solutions:** -- Reduce batch size: `python3 train.py --batch 8` -- Use CPU: `python3 train.py --device cpu` -- Close other GPU applications - -#### 3. **Import Error: ultralytics** -``` -ModuleNotFoundError: No module named 'ultralytics' -``` -**Solution:** -```bash -pip install ultralytics -``` - -#### 4. **Flask Port Already in Use** -``` -OSError: [Errno 48] Address already in use -``` -**Solution:** -```bash -# Kill process using port 5000 -lsof -ti:5000 | xargs kill -9 - -# Or use different port -python3 main.py # Edit main.py to change port -``` - -#### 5. **Low Detection Accuracy** -**Solutions:** -- Increase training epochs: `python3 train.py --epochs 200` -- Lower confidence threshold: `confidence=0.3` -- Check image quality and lighting -- Verify annotations are correct - -### Performance Optimization - -#### For Better Accuracy: -1. **More Training Data:** Add more annotated images -2. **Data Augmentation:** Already included in YOLOv8 -3. **Hyperparameter Tuning:** Adjust learning rate, batch size -4. **Model Size:** Use YOLOv8s or YOLOv8m for better accuracy - -#### For Faster Inference: -1. **Model Quantization:** Convert to TensorRT or ONNX -2. **Batch Processing:** Process multiple images together -3. **Image Resizing:** Use smaller input size (320x320) - -## ๐Ÿ“ File Descriptions - -- **`main.py`** - Flask API with all endpoints -- **`train.py`** - YOLOv8 training script with validation -- **`inference_utils.py`** - Detection utilities and visualization -- **`prepare_dataset.py`** - Dataset preparation and splitting -- **`requirements.txt`** - Python dependencies -- **`dataset.yaml`** - YOLO dataset configuration - -## ๐Ÿ”ฎ Future Enhancements - -1. **Video Processing:** Add video upload and processing endpoints -2. **Model Ensemble:** Combine multiple models for better accuracy -3. **Real-time Streaming:** WebSocket support for live camera feeds -4. **Database Integration:** Store detection results and statistics -5. **Web Interface:** HTML frontend for easier testing -6. **Docker Deployment:** Containerized deployment -7. **Model Versioning:** Support multiple model versions -8. **Batch Processing:** Process multiple images simultaneously - -## ๐Ÿ“„ License - -This project is for educational and training purposes. - -## ๐Ÿค Contributing - -This is a toy project for training purposes. Feel free to experiment and improve! +1. GPU support for faster processing +2. Video input support +3. Real-time streaming capabilities +4. More sophisticated augmentation techniques +5. Model quantization for improved CPU performance diff --git a/VALIDATION_CHECKLIST.md b/VALIDATION_CHECKLIST.md deleted file mode 100644 index 035e2bd..0000000 --- a/VALIDATION_CHECKLIST.md +++ /dev/null @@ -1,189 +0,0 @@ -# Project Validation Checklist - -## โœ… README Requirements Validation - -### Original Requirements from README: -1. **Flask API that processes motherboard images** โœ… -2. **Detects memory modules present on motherboards** โœ… -3. **Returns image with bounding boxes around detected memory modules** โœ… -4. **Image upload via Flask API** โœ… -5. **Hardcoded image for testing purposes** โœ… -6. **Dataset: 20 pictures with memory, 20 without memory** โœ… -7. **Annotation tool suggestion: makesense.ai** โœ… (Already annotated) - -### Additional Features Implemented: -- โœ… **Web Frontend for QA Testing** (Beyond requirements) -- โœ… **Base64 image processing endpoint** -- โœ… **Comprehensive API testing suite** -- โœ… **Automated setup script** -- โœ… **Complete documentation** - -## ๐Ÿ”ง Technical Implementation Validation - -### Algorithm Choice Questions Answered: -1. **Which algorithm for detecting memory modules?** - - โœ… **Answer: YOLOv8 Nano** - - โœ… **Reasoning: State-of-the-art performance, real-time inference, pre-trained weights, easy integration** - -2. **Hardware considerations (CPU vs GPU impact)?** - - โœ… **Training: GPU recommended (5-10 min vs 30-60 min CPU)** - - โœ… **Inference: CPU sufficient for real-time, GPU better for batch processing** - - โœ… **Implementation: Auto-detection with fallback** - -3. **Video input approach?** - - โœ… **Approach described: Frame extraction + batch processing + temporal tracking** - - โœ… **Implementation strategy provided with pseudo-code** - -## ๐Ÿ“ File Structure Validation - -### Required Files: -- โœ… `main.py` - Flask API application -- โœ… `train.py` - YOLOv8 training script -- โœ… `inference_utils.py` - Detection and visualization utilities -- โœ… `prepare_dataset.py` - Dataset preparation script -- โœ… `requirements.txt` - Python dependencies -- โœ… `dataset.yaml` - YOLO dataset configuration -- โœ… `README.md` - Complete documentation - -### Additional Files Created: -- โœ… `test_api.py` - API testing script -- โœ… `setup.py` - Automated setup script -- โœ… `templates/index.html` - Web interface -- โœ… `static/style.css` - Frontend styling -- โœ… `static/script.js` - Frontend functionality -- โœ… `VALIDATION_CHECKLIST.md` - This validation document - -### Dataset Structure: -- โœ… `training/memory/` - 20 images with memory modules + YOLO labels -- โœ… `training/no_memory/` - 20 images without memory modules -- โœ… `training/train/` - Training split (80% = 32 images) -- โœ… `training/val/` - Validation split (20% = 8 images) - -## ๐Ÿš€ API Endpoints Validation - -### Required Endpoints: -1. โœ… **Image upload endpoint** - `POST /detect` -2. โœ… **Hardcoded image endpoint** - `GET /detect/hardcoded` - -### Additional Endpoints: -3. โœ… **API information** - `GET /` (serves frontend) & `GET /api` (JSON) -4. โœ… **Health check** - `GET /health` -5. โœ… **Base64 processing** - `POST /detect/base64` -6. โœ… **Error handlers** - 404, 413, 500 - -## ๐Ÿงช Testing Validation - -### Test Coverage: -- โœ… **API health check testing** -- โœ… **Hardcoded image detection testing** -- โœ… **File upload testing** -- โœ… **Base64 image testing** -- โœ… **Error handling testing** -- โœ… **Web interface testing** - -### Test Scripts: -- โœ… `test_api.py` - Comprehensive API testing -- โœ… Web interface - Interactive QA testing -- โœ… `setup.py` - Automated setup validation - -## ๐Ÿ“ฆ Dependencies Validation - -### Core Dependencies: -- โœ… `ultralytics` - YOLOv8 implementation -- โœ… `torch` & `torchvision` - PyTorch for ML -- โœ… `opencv-python` - Image processing -- โœ… `Pillow` - Image handling -- โœ… `Flask` & `Flask-CORS` - Web framework -- โœ… `numpy` - Numerical operations -- โœ… `PyYAML` - Configuration files - -### Additional Dependencies: -- โœ… `Werkzeug` - Flask utilities -- โœ… `requests` - HTTP testing -- โœ… `tqdm` - Progress bars -- โœ… `matplotlib` & `seaborn` - Visualization (optional) - -## ๐ŸŽฏ Functional Requirements Validation - -### Input Processing: -- โœ… **Accepts PNG, JPG, JPEG, GIF, BMP formats** -- โœ… **File size limit: 16MB** -- โœ… **Drag & drop support in web interface** -- โœ… **Base64 encoding support** -- โœ… **Confidence threshold adjustment** - -### Output Generation: -- โœ… **Bounding boxes around detected memory modules** -- โœ… **Confidence scores for each detection** -- โœ… **Annotated images returned as base64** -- โœ… **JSON response with detection details** -- โœ… **Visual feedback in web interface** - -### Model Performance: -- โœ… **Single class detection: 'memory_module'** -- โœ… **YOLO format annotations** -- โœ… **Transfer learning from COCO dataset** -- โœ… **Configurable confidence and IoU thresholds** - -## ๐ŸŒ Web Interface Validation - -### QA Testing Features: -- โœ… **Real-time API status indicator** -- โœ… **Drag & drop image upload** -- โœ… **Confidence threshold slider** -- โœ… **Multiple testing options** -- โœ… **Interactive results display** -- โœ… **Responsive design** -- โœ… **Error handling and feedback** - -### User Experience: -- โœ… **Intuitive interface design** -- โœ… **Clear visual feedback** -- โœ… **Loading indicators** -- โœ… **Result visualization** -- โœ… **Mobile compatibility** - -## ๐Ÿ“š Documentation Validation - -### README Completeness: -- โœ… **Quick start guide** -- โœ… **Installation instructions** -- โœ… **API documentation** -- โœ… **Usage examples** -- โœ… **Troubleshooting guide** -- โœ… **Technical decisions explained** -- โœ… **Project structure documented** - -### Code Documentation: -- โœ… **Docstrings in all functions** -- โœ… **Inline comments for complex logic** -- โœ… **Type hints where appropriate** -- โœ… **Error handling documented** - -## ๐Ÿ”„ Setup & Deployment Validation - -### Setup Options: -- โœ… **Manual setup with step-by-step instructions** -- โœ… **Automated setup script (`setup.py`)** -- โœ… **Requirements file for dependencies** -- โœ… **Dataset preparation script** - -### Deployment Readiness: -- โœ… **Production-ready Flask configuration** -- โœ… **Error handling and logging** -- โœ… **CORS support for frontend** -- โœ… **File upload security** -- โœ… **Model loading validation** - -## ๐ŸŽ‰ Final Validation Summary - -### โœ… **ALL ORIGINAL REQUIREMENTS MET** -### โœ… **ADDITIONAL FEATURES IMPLEMENTED** -### โœ… **COMPREHENSIVE TESTING SUITE** -### โœ… **PRODUCTION-READY CODE** -### โœ… **EXCELLENT DOCUMENTATION** -### โœ… **QA-FRIENDLY WEB INTERFACE** - -## ๐Ÿš€ Ready for QA Testing! - -The project is complete and ready for quality assurance testing. All original requirements have been met and exceeded with additional features for better usability and testing. diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..8ed80fb --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,14 @@ +from flask import Flask + +def create_app(): + app = Flask(__name__) + + # Register blueprints + from app.routes import main_bp + app.register_blueprint(main_bp) + + # Ensure the static folder is properly set + app.static_folder = 'static' + app.template_folder = 'templates' + + return app diff --git a/app/routes.py b/app/routes.py new file mode 100644 index 0000000..665a52d --- /dev/null +++ b/app/routes.py @@ -0,0 +1,57 @@ +from flask import Blueprint, request, jsonify, send_file, render_template +from app.utils.detector import MemoryDetector +import os +from PIL import Image +import io + +main_bp = Blueprint('main', __name__) +detector = MemoryDetector() + +@main_bp.route('/') +def index(): + return render_template('index.html') + +@main_bp.route('/detect', methods=['POST']) +def detect_memory(): + if 'image' not in request.files: + return jsonify({'error': 'No image provided'}), 400 + + file = request.files['image'] + + # Read the image + img = Image.open(file.stream) + + # Process the image and get annotated image and detections + annotated_img, detections = detector.detect(img) + + # Convert PIL image to bytes + img_byte_arr = io.BytesIO() + annotated_img.save(img_byte_arr, format='PNG') + img_byte_arr.seek(0) + + return send_file( + img_byte_arr, + mimetype='image/png' + ) + +@main_bp.route('/detect/test', methods=['GET']) +def detect_test(): + """Endpoint for testing with a hardcoded image""" + # Using an existing image from the validation set + test_image_path = os.path.join('training', 'val', 'images', 'memory_out19.png') + + if not os.path.exists(test_image_path): + return jsonify({'error': f'Test image not found at {test_image_path}'}), 404 + + img = Image.open(test_image_path) + # Get both the annotated image and detections + annotated_img, detections = detector.detect(img) + + img_byte_arr = io.BytesIO() + annotated_img.save(img_byte_arr, format='PNG') + img_byte_arr.seek(0) + + return send_file( + img_byte_arr, + mimetype='image/png' + ) diff --git a/app/static/css/style.css b/app/static/css/style.css new file mode 100644 index 0000000..ba27398 --- /dev/null +++ b/app/static/css/style.css @@ -0,0 +1,159 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; + line-height: 1.6; + background-color: #f5f5f5; + color: #333; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 2rem; +} + +h1 { + text-align: center; + margin-bottom: 2rem; + color: #2c3e50; +} + +.upload-section { + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; + margin-bottom: 2rem; +} + +.upload-box { + width: 100%; + max-width: 500px; + height: 200px; + border: 2px dashed #3498db; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: border-color 0.3s ease; + background-color: #fff; +} + +.upload-box:hover { + border-color: #2980b9; +} + +.upload-content { + text-align: center; +} + +.upload-icon { + width: 64px; + height: 64px; + margin-bottom: 1rem; +} + +.browse-text { + color: #3498db; + text-decoration: underline; + cursor: pointer; +} + +button { + padding: 0.8rem 1.5rem; + font-size: 1rem; + border: none; + border-radius: 4px; + cursor: pointer; + transition: background-color 0.3s ease; +} + +#detectButton { + background-color: #3498db; + color: white; +} + +#detectButton:disabled { + background-color: #bdc3c7; + cursor: not-allowed; +} + +#testButton { + background-color: #2ecc71; + color: white; +} + +#detectButton:hover:not(:disabled) { + background-color: #2980b9; +} + +#testButton:hover { + background-color: #27ae60; +} + +.results-section { + margin-top: 2rem; +} + +.image-container { + display: flex; + gap: 2rem; + justify-content: center; + flex-wrap: wrap; +} + +.image-box { + flex: 1; + min-width: 300px; + max-width: 500px; + background-color: white; + padding: 1rem; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); +} + +.image-box h3 { + margin-bottom: 1rem; + text-align: center; +} + +.image-box img { + width: 100%; + height: auto; + border-radius: 4px; + display: none; +} + +.loading-spinner { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(255,255,255,0.8); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + z-index: 1000; +} + +.spinner { + width: 50px; + height: 50px; + border: 5px solid #f3f3f3; + border-top: 5px solid #3498db; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} \ No newline at end of file diff --git a/training/train/images/memory_out17.png b/app/static/images/out17.png similarity index 100% rename from training/train/images/memory_out17.png rename to app/static/images/out17.png diff --git a/app/static/images/upload-icon.svg b/app/static/images/upload-icon.svg new file mode 100644 index 0000000..dcd2418 --- /dev/null +++ b/app/static/images/upload-icon.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/static/js/main.js b/app/static/js/main.js new file mode 100644 index 0000000..918c9b3 --- /dev/null +++ b/app/static/js/main.js @@ -0,0 +1,100 @@ +document.addEventListener('DOMContentLoaded', function() { + const dropZone = document.getElementById('dropZone'); + const fileInput = document.getElementById('fileInput'); + const detectButton = document.getElementById('detectButton'); + const testButton = document.getElementById('testButton'); + const originalImage = document.getElementById('originalImage'); + const resultImage = document.getElementById('resultImage'); + const loading = document.getElementById('loading'); + + // Handle drag and drop + dropZone.addEventListener('dragover', (e) => { + e.preventDefault(); + dropZone.style.borderColor = '#2980b9'; + }); + + dropZone.addEventListener('dragleave', (e) => { + e.preventDefault(); + dropZone.style.borderColor = '#3498db'; + }); + + dropZone.addEventListener('drop', (e) => { + e.preventDefault(); + dropZone.style.borderColor = '#3498db'; + + const file = e.dataTransfer.files[0]; + if (file && file.type.startsWith('image/')) { + handleImageSelection(file); + } + }); + + // Handle click to upload + dropZone.addEventListener('click', () => { + fileInput.click(); + }); + + fileInput.addEventListener('change', (e) => { + const file = e.target.files[0]; + if (file) { + handleImageSelection(file); + } + }); + + function handleImageSelection(file) { + const reader = new FileReader(); + reader.onload = function(e) { + originalImage.src = e.target.result; + originalImage.style.display = 'block'; + detectButton.disabled = false; + }; + reader.readAsDataURL(file); + } + + // Handle detect button click + detectButton.addEventListener('click', async () => { + const formData = new FormData(); + formData.append('image', fileInput.files[0]); + + try { + loading.style.display = 'flex'; + const response = await fetch('/detect', { + method: 'POST', + body: formData + }); + + if (response.ok) { + const blob = await response.blob(); + resultImage.src = URL.createObjectURL(blob); + resultImage.style.display = 'block'; + } else { + alert('Error processing image'); + } + } catch (error) { + console.error('Error:', error); + alert('Error processing image'); + } finally { + loading.style.display = 'none'; + } + }); + + // Handle test button click + testButton.addEventListener('click', async () => { + try { + loading.style.display = 'flex'; + const response = await fetch('/detect/test'); + + if (response.ok) { + const blob = await response.blob(); + resultImage.src = URL.createObjectURL(blob); + resultImage.style.display = 'block'; + } else { + alert('Error running test detection'); + } + } catch (error) { + console.error('Error:', error); + alert('Error running test detection'); + } finally { + loading.style.display = 'none'; + } + }); +}); \ No newline at end of file diff --git a/app/templates/index.html b/app/templates/index.html new file mode 100644 index 0000000..de9322e --- /dev/null +++ b/app/templates/index.html @@ -0,0 +1,47 @@ + + + + + + Memory Module Detector + + + +
+

Memory Module Detector

+ +
+
+ +
+ Upload +

Drag and drop an image or browse

+
+
+ + + +
+ +
+
+
+

Original Image

+ Original image will appear here +
+
+

Detected Results

+ Detection results will appear here +
+
+
+ + +
+ + + + \ No newline at end of file diff --git a/training/train/labels/no_memory_out11.txt b/app/utils/__init__.py similarity index 100% rename from training/train/labels/no_memory_out11.txt rename to app/utils/__init__.py diff --git a/app/utils/detector.py b/app/utils/detector.py new file mode 100644 index 0000000..0023d9c --- /dev/null +++ b/app/utils/detector.py @@ -0,0 +1,99 @@ +from ultralytics import YOLO +from PIL import Image +import numpy as np +from typing import Tuple, List, Dict + +class MemoryDetector: + def __init__(self, + model_path='model/weights/best.pt', + conf_threshold=0.25, + iou_threshold=0.45): + """ + Initialize the detector with the trained model. + + Args: + model_path (str): Path to the trained model weights + conf_threshold (float): Confidence threshold for detections + iou_threshold (float): IoU threshold for NMS + """ + self.model = YOLO(model_path) + self.conf_threshold = conf_threshold + self.iou_threshold = iou_threshold + + def detect(self, + image: Image.Image, + conf_threshold: float = None, + iou_threshold: float = None) -> Tuple[Image.Image, List[Dict]]: + """ + Detect memory modules in the given image. + + Args: + image (PIL.Image): Input image to process + conf_threshold (float, optional): Override default confidence threshold + iou_threshold (float, optional): Override default IoU threshold + + Returns: + Tuple[PIL.Image, List[Dict]]: Annotated image and list of detections + """ + # Use provided thresholds or defaults + conf = conf_threshold if conf_threshold is not None else self.conf_threshold + iou = iou_threshold if iou_threshold is not None else self.iou_threshold + + # Run inference + results = self.model.predict( + source=image, + conf=conf, + iou=iou, + max_det=10, + verbose=False + ) + + # Get the annotated image + annotated_img = results[0].plot() + + # Extract detection information + detections = [] + for box in results[0].boxes: + detection = { + 'xyxy': box.xyxy[0].tolist(), # Bounding box coordinates + 'confidence': float(box.conf[0]), # Detection confidence + 'class': int(box.cls[0]) # Class ID + } + detections.append(detection) + + return Image.fromarray(annotated_img), detections + + def optimize_thresholds(self, validation_images: List[Image.Image]) -> Tuple[float, float]: + """ + Find optimal confidence and IoU thresholds using validation images. + + Args: + validation_images (List[Image.Image]): List of validation images + + Returns: + Tuple[float, float]: Optimal confidence and IoU thresholds + """ + best_conf = 0.25 + best_iou = 0.45 + + # Grid search for best parameters + conf_range = [0.15, 0.2, 0.25, 0.3, 0.35] + iou_range = [0.35, 0.4, 0.45, 0.5, 0.55] + + best_score = 0 + + for conf in conf_range: + for iou in iou_range: + total_score = 0 + for img in validation_images: + _, detections = self.detect(img, conf, iou) + # Score based on number of detections and confidence + score = sum([d['confidence'] for d in detections]) + total_score += score + + if total_score > best_score: + best_score = total_score + best_conf = conf + best_iou = iou + + return best_conf, best_iou diff --git a/dataset.yaml b/dataset.yaml deleted file mode 100644 index 0aff621..0000000 --- a/dataset.yaml +++ /dev/null @@ -1,5 +0,0 @@ -path: training # dataset root dir -train: train/images # train images -val: val/images # validation images -nc: 1 # number of classes -names: ['memory_module'] # class names diff --git a/inference_utils.py b/inference_utils.py deleted file mode 100644 index 4c9d2af..0000000 --- a/inference_utils.py +++ /dev/null @@ -1,287 +0,0 @@ -#!/usr/bin/env python3 -""" -Inference utilities for memory module detection. -Contains functions for model loading, inference, and visualization. -""" - -import cv2 -import numpy as np -from PIL import Image, ImageDraw, ImageFont -import os -from ultralytics import YOLO -import torch - -class MemoryModuleDetector: - """Memory module detector using YOLOv8.""" - - def __init__(self, model_path='runs/detect/memory_module_detection/weights/best.pt'): - """ - Initialize the detector. - - Args: - model_path (str): Path to the trained YOLOv8 model - """ - self.model_path = model_path - self.model = None - self.class_names = ['memory_module'] - self.colors = [(0, 255, 0)] # Green for memory modules - - # Load model if it exists - if os.path.exists(model_path): - self.load_model() - else: - print(f"Warning: Model not found at {model_path}") - print("Please train the model first using train.py") - - def load_model(self): - """Load the trained YOLOv8 model.""" - try: - # Fix for PyTorch 2.6+ weights_only issue - import torch - # Use weights_only=False for compatibility - with torch.serialization.safe_globals(['ultralytics.nn.tasks.DetectionModel']): - self.model = YOLO(self.model_path) - print(f"Model loaded successfully from {self.model_path}") - except Exception as e: - try: - # Fallback: try loading with weights_only=False - import torch - original_load = torch.load - torch.load = lambda *args, **kwargs: original_load(*args, **kwargs, weights_only=False) - self.model = YOLO(self.model_path) - torch.load = original_load - print(f"Model loaded successfully from {self.model_path} (fallback method)") - except Exception as e2: - print(f"Error loading model: {e2}") - self.model = None - - def detect(self, image_path, conf_threshold=0.5, iou_threshold=0.45): - """ - Detect memory modules in an image. - - Args: - image_path (str): Path to the input image - conf_threshold (float): Confidence threshold for detections - iou_threshold (float): IoU threshold for NMS - - Returns: - tuple: (detections, annotated_image) - """ - if self.model is None: - raise ValueError("Model not loaded. Please check model path.") - - # Run inference - results = self.model(image_path, conf=conf_threshold, iou=iou_threshold) - - # Extract detections - detections = [] - if len(results) > 0 and results[0].boxes is not None: - boxes = results[0].boxes - for i in range(len(boxes)): - box = boxes.xyxy[i].cpu().numpy() # x1, y1, x2, y2 - conf = boxes.conf[i].cpu().numpy() - cls = int(boxes.cls[i].cpu().numpy()) - - detection = { - 'bbox': box.tolist(), - 'confidence': float(conf), - 'class': int(cls), - 'class_name': self.class_names[cls] if cls < len(self.class_names) else 'unknown' - } - detections.append(detection) - - # Create annotated image - annotated_image = self.draw_detections(image_path, detections) - - return detections, annotated_image - - def draw_detections(self, image_path, detections): - """ - Draw bounding boxes on the image. - - Args: - image_path (str): Path to the input image - detections (list): List of detection dictionaries - - Returns: - PIL.Image: Annotated image - """ - # Load image - image = Image.open(image_path).convert('RGB') - draw = ImageDraw.Draw(image) - - # Try to load a font - try: - font = ImageFont.truetype("arial.ttf", 16) - except: - font = ImageFont.load_default() - - # Draw each detection - for detection in detections: - bbox = detection['bbox'] - confidence = detection['confidence'] - class_name = detection['class_name'] - - # Extract coordinates - x1, y1, x2, y2 = bbox - - # Draw bounding box - color = self.colors[0] # Green for memory modules - draw.rectangle([x1, y1, x2, y2], outline=color, width=3) - - # Draw label - label = f"{class_name}: {confidence:.2f}" - - # Get text size for background - bbox_text = draw.textbbox((0, 0), label, font=font) - text_width = bbox_text[2] - bbox_text[0] - text_height = bbox_text[3] - bbox_text[1] - - # Draw background for text - draw.rectangle([x1, y1 - text_height - 4, x1 + text_width + 4, y1], - fill=color, outline=color) - - # Draw text - draw.text((x1 + 2, y1 - text_height - 2), label, fill=(255, 255, 255), font=font) - - return image - - def detect_from_array(self, image_array, conf_threshold=0.5, iou_threshold=0.45): - """ - Detect memory modules from a numpy array. - - Args: - image_array (np.ndarray): Input image as numpy array - conf_threshold (float): Confidence threshold for detections - iou_threshold (float): IoU threshold for NMS - - Returns: - tuple: (detections, annotated_image) - """ - if self.model is None: - raise ValueError("Model not loaded. Please check model path.") - - # Convert numpy array to PIL Image if needed - if isinstance(image_array, np.ndarray): - if image_array.dtype != np.uint8: - image_array = (image_array * 255).astype(np.uint8) - image = Image.fromarray(image_array) - else: - image = image_array - - # Run inference - results = self.model(image, conf=conf_threshold, iou=iou_threshold) - - # Extract detections - detections = [] - if len(results) > 0 and results[0].boxes is not None: - boxes = results[0].boxes - for i in range(len(boxes)): - box = boxes.xyxy[i].cpu().numpy() # x1, y1, x2, y2 - conf = boxes.conf[i].cpu().numpy() - cls = int(boxes.cls[i].cpu().numpy()) - - detection = { - 'bbox': box.tolist(), - 'confidence': float(conf), - 'class': int(cls), - 'class_name': self.class_names[cls] if cls < len(self.class_names) else 'unknown' - } - detections.append(detection) - - # Create annotated image - annotated_image = self.draw_detections_on_image(image, detections) - - return detections, annotated_image - - def draw_detections_on_image(self, image, detections): - """ - Draw bounding boxes on a PIL Image. - - Args: - image (PIL.Image): Input image - detections (list): List of detection dictionaries - - Returns: - PIL.Image: Annotated image - """ - # Make a copy to avoid modifying the original - annotated_image = image.copy() - draw = ImageDraw.Draw(annotated_image) - - # Try to load a font - try: - font = ImageFont.truetype("arial.ttf", 16) - except: - font = ImageFont.load_default() - - # Draw each detection - for detection in detections: - bbox = detection['bbox'] - confidence = detection['confidence'] - class_name = detection['class_name'] - - # Extract coordinates - x1, y1, x2, y2 = bbox - - # Draw bounding box - color = self.colors[0] # Green for memory modules - draw.rectangle([x1, y1, x2, y2], outline=color, width=3) - - # Draw label - label = f"{class_name}: {confidence:.2f}" - - # Get text size for background - bbox_text = draw.textbbox((0, 0), label, font=font) - text_width = bbox_text[2] - bbox_text[0] - text_height = bbox_text[3] - bbox_text[1] - - # Draw background for text - draw.rectangle([x1, y1 - text_height - 4, x1 + text_width + 4, y1], - fill=color, outline=color) - - # Draw text - draw.text((x1 + 2, y1 - text_height - 2), label, fill=(255, 255, 255), font=font) - - return annotated_image - -def test_inference(image_path, model_path='runs/detect/memory_module_detection/weights/best.pt'): - """ - Test inference on a single image. - - Args: - image_path (str): Path to test image - model_path (str): Path to trained model - """ - detector = MemoryModuleDetector(model_path) - - if detector.model is None: - print("Cannot run inference without a trained model.") - return - - print(f"Running inference on: {image_path}") - detections, annotated_image = detector.detect(image_path) - - print(f"Found {len(detections)} memory modules:") - for i, detection in enumerate(detections): - print(f" {i+1}. {detection['class_name']} (confidence: {detection['confidence']:.3f})") - - # Save annotated image - output_path = f"annotated_{os.path.basename(image_path)}" - annotated_image.save(output_path) - print(f"Annotated image saved as: {output_path}") - - return detections, annotated_image - -if __name__ == "__main__": - import argparse - - parser = argparse.ArgumentParser(description='Test memory module detection') - parser.add_argument('--image', type=str, required=True, help='Path to test image') - parser.add_argument('--model', type=str, default='runs/detect/memory_module_detection/weights/best.pt', - help='Path to trained model') - parser.add_argument('--conf', type=float, default=0.5, help='Confidence threshold') - - args = parser.parse_args() - - test_inference(args.image, args.model) diff --git a/main.py b/main.py deleted file mode 100644 index 4a08f4b..0000000 --- a/main.py +++ /dev/null @@ -1,408 +0,0 @@ -#!/usr/bin/env python3 -""" -Flask API for Memory Module Detection -This API processes motherboard images and detects memory modules using YOLOv8. -""" - -import os -import io -import base64 -from flask import Flask, request, jsonify, send_file, render_template -from flask_cors import CORS -from PIL import Image -import numpy as np -from werkzeug.utils import secure_filename -import tempfile -import logging -from inference_utils import MemoryModuleDetector - -# Configure logging -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) - -# Initialize Flask app -app = Flask(__name__) -CORS(app) - -# Configuration -app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max file size -UPLOAD_FOLDER = 'uploads' -ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'bmp'} - -# Create upload folder if it doesn't exist -os.makedirs(UPLOAD_FOLDER, exist_ok=True) - -# Initialize detector -MODEL_PATH = 'runs/detect/memory_module_detection/weights/best.pt' -detector = MemoryModuleDetector(MODEL_PATH) - -# Hardcoded test image path -HARDCODED_IMAGE_PATH = 'training/memory/out1.png' - -def allowed_file(filename): - """Check if file extension is allowed.""" - return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS - -def image_to_base64(image): - """Convert PIL Image to base64 string.""" - buffer = io.BytesIO() - image.save(buffer, format='PNG') - img_str = base64.b64encode(buffer.getvalue()).decode() - return img_str - -def base64_to_image(base64_string): - """Convert base64 string to PIL Image.""" - img_data = base64.b64decode(base64_string) - image = Image.open(io.BytesIO(img_data)) - return image - -@app.route('/', methods=['GET']) -def home(): - """Home endpoint - serve frontend or API information based on Accept header.""" - # Check if request is from a browser (wants HTML) - if 'text/html' in request.headers.get('Accept', ''): - return render_template('index.html') - - # Otherwise return JSON API information - return jsonify({ - 'message': 'Memory Module Detection API', - 'version': '1.0.0', - 'endpoints': { - '/': 'GET - Frontend interface or API information', - '/api': 'GET - API information (JSON)', - '/detect': 'POST - Upload image for memory module detection', - '/detect/hardcoded': 'GET - Process hardcoded test image', - '/detect/base64': 'POST - Process base64 encoded image', - '/health': 'GET - Health check' - }, - 'model_loaded': detector.model is not None, - 'supported_formats': list(ALLOWED_EXTENSIONS) - }) - -@app.route('/api', methods=['GET']) -def api_info(): - """API information endpoint (always returns JSON).""" - return jsonify({ - 'message': 'Memory Module Detection API', - 'version': '1.0.0', - 'endpoints': { - '/': 'GET - Frontend interface or API information', - '/api': 'GET - API information (JSON)', - '/docs': 'GET - Detailed API documentation', - '/detect': 'POST - Upload image for memory module detection', - '/detect/hardcoded': 'GET - Process hardcoded test image', - '/detect/base64': 'POST - Process base64 encoded image', - '/health': 'GET - Health check' - }, - 'model_loaded': detector.model is not None, - 'supported_formats': list(ALLOWED_EXTENSIONS), - 'documentation': 'Visit /docs for detailed API documentation' - }) - -@app.route('/docs') -def api_docs(): - """Serve API documentation.""" - try: - with open('API_DOCS.md', 'r') as f: - docs_content = f.read() - - # Convert markdown to HTML for better display - html_content = f""" - - - - Memory Module Detection API - Documentation - - - - -
{docs_content}
- - - """ - return html_content - except FileNotFoundError: - return jsonify({ - 'error': 'API documentation file not found', - 'message': 'Please ensure API_DOCS.md exists in the project directory' - }), 404 - -@app.route('/health', methods=['GET']) -def health_check(): - """Health check endpoint.""" - return jsonify({ - 'status': 'healthy', - 'model_loaded': detector.model is not None, - 'model_path': MODEL_PATH - }) - -@app.route('/detect', methods=['POST']) -def detect_memory_modules(): - """ - Detect memory modules in uploaded image. - - Expected input: - - File upload with key 'image' - - Optional: confidence threshold as form data - - Returns: - - JSON with detections and annotated image (base64) - """ - try: - # Check if model is loaded - if detector.model is None: - return jsonify({ - 'error': 'Model not loaded. Please train the model first.', - 'success': False - }), 500 - - # Check if file is present - if 'image' not in request.files: - return jsonify({ - 'error': 'No image file provided', - 'success': False - }), 400 - - file = request.files['image'] - - # Check if file is selected - if file.filename == '': - return jsonify({ - 'error': 'No file selected', - 'success': False - }), 400 - - # Check file extension - if not allowed_file(file.filename): - return jsonify({ - 'error': f'File type not allowed. Supported formats: {ALLOWED_EXTENSIONS}', - 'success': False - }), 400 - - # Get confidence threshold from form data (default 80%) - conf_threshold = float(request.form.get('confidence', 0.8)) - - # Save uploaded file temporarily - filename = secure_filename(file.filename) - temp_path = os.path.join(UPLOAD_FOLDER, filename) - file.save(temp_path) - - try: - # Run detection - detections, annotated_image = detector.detect( - temp_path, - conf_threshold=conf_threshold - ) - - # Convert annotated image to base64 - annotated_base64 = image_to_base64(annotated_image) - - # Prepare response - response_data = { - 'success': True, - 'detections': detections, - 'num_detections': len(detections), - 'annotated_image': annotated_base64, - 'confidence_threshold': conf_threshold, - 'original_filename': filename - } - - logger.info(f"Processed {filename}: found {len(detections)} memory modules") - - return jsonify(response_data) - - finally: - # Clean up temporary file - if os.path.exists(temp_path): - os.remove(temp_path) - - except Exception as e: - logger.error(f"Error processing image: {str(e)}") - return jsonify({ - 'error': f'Error processing image: {str(e)}', - 'success': False - }), 500 - -@app.route('/detect/hardcoded', methods=['GET']) -def detect_hardcoded_image(): - """ - Process hardcoded test image for memory module detection. - - Optional query parameters: - - confidence: confidence threshold (default: 0.8) - - Returns: - - JSON with detections and annotated image (base64) - """ - try: - # Check if model is loaded - if detector.model is None: - return jsonify({ - 'error': 'Model not loaded. Please train the model first.', - 'success': False - }), 500 - - # Check if hardcoded image exists - if not os.path.exists(HARDCODED_IMAGE_PATH): - return jsonify({ - 'error': f'Hardcoded test image not found at {HARDCODED_IMAGE_PATH}', - 'success': False - }), 404 - - # Get confidence threshold from query parameters (default 80%) - conf_threshold = float(request.args.get('confidence', 0.8)) - - # Run detection - detections, annotated_image = detector.detect( - HARDCODED_IMAGE_PATH, - conf_threshold=conf_threshold - ) - - # Convert annotated image to base64 - annotated_base64 = image_to_base64(annotated_image) - - # Prepare response - response_data = { - 'success': True, - 'detections': detections, - 'num_detections': len(detections), - 'annotated_image': annotated_base64, - 'confidence_threshold': conf_threshold, - 'test_image_path': HARDCODED_IMAGE_PATH - } - - logger.info(f"Processed hardcoded image: found {len(detections)} memory modules") - - return jsonify(response_data) - - except Exception as e: - logger.error(f"Error processing hardcoded image: {str(e)}") - return jsonify({ - 'error': f'Error processing hardcoded image: {str(e)}', - 'success': False - }), 500 - - - -@app.route('/detect/base64', methods=['POST']) -def detect_base64_image(): - """ - Detect memory modules in base64 encoded image. - - Expected JSON input: - { - "image": "base64_encoded_image_string", - "confidence": 0.5 // optional - } - - Returns: - - JSON with detections and annotated image (base64) - """ - try: - # Check if model is loaded - if detector.model is None: - return jsonify({ - 'error': 'Model not loaded. Please train the model first.', - 'success': False - }), 500 - - # Get JSON data - data = request.get_json() - - if not data or 'image' not in data: - return jsonify({ - 'error': 'No base64 image data provided', - 'success': False - }), 400 - - # Get confidence threshold (default 80%) - conf_threshold = float(data.get('confidence', 0.8)) - - # Decode base64 image - try: - image = base64_to_image(data['image']) - except Exception as e: - return jsonify({ - 'error': f'Invalid base64 image data: {str(e)}', - 'success': False - }), 400 - - # Run detection - detections, annotated_image = detector.detect_from_array( - np.array(image), - conf_threshold=conf_threshold - ) - - # Convert annotated image to base64 - annotated_base64 = image_to_base64(annotated_image) - - # Prepare response - response_data = { - 'success': True, - 'detections': detections, - 'num_detections': len(detections), - 'annotated_image': annotated_base64, - 'confidence_threshold': conf_threshold - } - - logger.info(f"Processed base64 image: found {len(detections)} memory modules") - - return jsonify(response_data) - - except Exception as e: - logger.error(f"Error processing base64 image: {str(e)}") - return jsonify({ - 'error': f'Error processing base64 image: {str(e)}', - 'success': False - }), 500 - -@app.errorhandler(413) -def too_large(e): - """Handle file too large error.""" - return jsonify({ - 'error': 'File too large. Maximum size is 16MB.', - 'success': False - }), 413 - -@app.errorhandler(404) -def not_found(e): - """Handle 404 errors.""" - return jsonify({ - 'error': 'Endpoint not found', - 'success': False - }), 404 - -@app.errorhandler(500) -def internal_error(e): - """Handle internal server errors.""" - return jsonify({ - 'error': 'Internal server error', - 'success': False - }), 500 - -if __name__ == '__main__': - # Check if model exists - if not os.path.exists(MODEL_PATH): - print(f"Warning: Model not found at {MODEL_PATH}") - print("Please train the model first using: python3 train.py") - print("The API will still start but detection endpoints will return errors.") - - # Start the Flask app - print("Starting Memory Module Detection API...") - print(f"Model path: {MODEL_PATH}") - print(f"Model loaded: {detector.model is not None}") - print(f"Hardcoded test image: {HARDCODED_IMAGE_PATH}") - - app.run(host='0.0.0.0', port=5002, debug=True) diff --git a/requirements.txt b/requirements.txt index d5fd903..96f700b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,30 +1,5 @@ -# Core ML and Computer Vision -ultralytics==8.0.196 -torch>=1.9.0 -torchvision>=0.10.0 -opencv-python==4.8.1.78 -Pillow==10.0.1 - -# Web Framework -Flask==2.3.3 -Flask-CORS==4.0.0 -Flask-RESTX==1.3.0 -Werkzeug==2.3.7 - -# Data Processing -numpy==1.24.3 -pandas==2.0.3 - -# Image Processing and Visualization -matplotlib==3.7.2 -seaborn==0.12.2 - -# Utilities -PyYAML==6.0.1 -requests==2.31.0 -tqdm==4.66.1 -pathlib2>=2.3.0;python_version<"3.4" - -# Optional GPU support (uncomment if using CUDA) -# torch==2.0.1+cu118 --index-url https://download.pytorch.org/whl/cu118 -# torchvision==0.15.2+cu118 --index-url https://download.pytorch.org/whl/cu118 +flask +ultralytics +pillow +numpy +python-multipart \ No newline at end of file diff --git a/run.py b/run.py new file mode 100644 index 0000000..523d51a --- /dev/null +++ b/run.py @@ -0,0 +1,6 @@ +from app import create_app + +app = create_app() + +if __name__ == '__main__': + app.run(debug=True) \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 225e654..0000000 --- a/setup.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env python3 -""" -Setup script for Memory Module Detection Project -This script helps users set up the project quickly. -""" - -import os -import sys -import subprocess -import time - -def run_command(command, description): - """Run a command and handle errors.""" - print(f"๐Ÿ”„ {description}...") - try: - result = subprocess.run(command, shell=True, check=True, capture_output=True, text=True) - print(f"โœ… {description} completed successfully") - return True - except subprocess.CalledProcessError as e: - print(f"โŒ {description} failed:") - print(f" Command: {command}") - print(f" Error: {e.stderr}") - return False - -def check_python_version(): - """Check if Python version is compatible.""" - print("๐Ÿ Checking Python version...") - version = sys.version_info - if version.major == 3 and version.minor >= 8: - print(f"โœ… Python {version.major}.{version.minor}.{version.micro} is compatible") - return True - else: - print(f"โŒ Python {version.major}.{version.minor}.{version.micro} is not compatible") - print(" Please use Python 3.8 or higher") - return False - -def check_files(): - """Check if required files exist.""" - print("๐Ÿ“ Checking project files...") - required_files = [ - 'requirements.txt', - 'main.py', - 'train.py', - 'inference_utils.py', - 'prepare_dataset.py', - 'dataset.yaml', - 'training/memory', - 'training/no_memory' - ] - - missing_files = [] - for file in required_files: - if not os.path.exists(file): - missing_files.append(file) - - if missing_files: - print(f"โŒ Missing files: {missing_files}") - return False - else: - print("โœ… All required files found") - return True - -def install_dependencies(): - """Install Python dependencies.""" - if not run_command("pip install -r requirements.txt", "Installing dependencies"): - print(" Try using: pip3 install -r requirements.txt") - return run_command("pip3 install -r requirements.txt", "Installing dependencies with pip3") - return True - -def prepare_dataset(): - """Prepare the dataset structure.""" - if os.path.exists('training/train/images') and os.path.exists('training/val/images'): - print("โœ… Dataset already prepared") - return True - - return run_command("python3 prepare_dataset.py", "Preparing dataset structure") - -def train_model(): - """Train the YOLOv8 model.""" - model_path = 'runs/detect/memory_module_detection/weights/best.pt' - if os.path.exists(model_path): - print("โœ… Model already trained") - return True - - print("๐Ÿค– Training YOLOv8 model...") - print(" This may take 5-60 minutes depending on your hardware...") - return run_command("python3 train.py --epochs 50 --batch 8", "Training YOLOv8 model") - -def test_setup(): - """Test the setup by running a quick inference.""" - print("๐Ÿงช Testing setup...") - return run_command("python3 test_api.py", "Running API tests") - -def main(): - """Main setup function.""" - print("๐Ÿš€ Memory Module Detection Project Setup") - print("=" * 50) - - # Check prerequisites - if not check_python_version(): - return False - - if not check_files(): - return False - - # Setup steps - steps = [ - ("Install Dependencies", install_dependencies), - ("Prepare Dataset", prepare_dataset), - ("Train Model", train_model) - ] - - for step_name, step_func in steps: - print(f"\n๐Ÿ“‹ Step: {step_name}") - if not step_func(): - print(f"โŒ Setup failed at step: {step_name}") - return False - - print("\n" + "=" * 50) - print("๐ŸŽ‰ Setup completed successfully!") - print("\n๐Ÿ“– Next steps:") - print("1. Start the API:") - print(" python3 main.py") - print("\n2. Test the API (in another terminal):") - print(" python3 test_api.py") - print("\n3. Or test manually:") - print(" curl http://localhost:5000/detect/hardcoded") - - return True - -if __name__ == "__main__": - success = main() - sys.exit(0 if success else 1) diff --git a/static/script.js b/static/script.js deleted file mode 100644 index 1c039a4..0000000 --- a/static/script.js +++ /dev/null @@ -1,505 +0,0 @@ -// Memory Module Detection QA Interface JavaScript - -const API_BASE_URL = 'http://localhost:5002'; -let uploadedFile = null; -let lastDetectionResult = null; - -// Initialize the application -document.addEventListener('DOMContentLoaded', function() { - initializeApp(); - setupEventListeners(); -}); - -function initializeApp() { - checkApiStatus(); - loadApiInfo(); -} - -function setupEventListeners() { - // File input change - document.getElementById('fileInput').addEventListener('change', handleFileSelect); - - // Drag and drop - const uploadArea = document.getElementById('uploadArea'); - uploadArea.addEventListener('dragover', handleDragOver); - uploadArea.addEventListener('dragleave', handleDragLeave); - uploadArea.addEventListener('drop', handleDrop); - - // Click to upload (only on the upload area, not buttons inside it) - uploadArea.addEventListener('click', function(event) { - // Only trigger file input if clicking on the upload area itself, not buttons - if (event.target === uploadArea || (event.target.closest('.upload-content') && !event.target.closest('button'))) { - console.log('Upload area clicked, triggering file input'); - const fileInput = document.getElementById('fileInput'); - if (fileInput) { - fileInput.click(); - } - } - }); -} - -async function checkApiStatus() { - const statusElement = document.getElementById('apiStatus'); - try { - const response = await fetch(`${API_BASE_URL}/health`); - if (response.ok) { - const data = await response.json(); - statusElement.className = 'status-indicator online'; - statusElement.innerHTML = ' API Online'; - } else { - throw new Error('API not responding'); - } - } catch (error) { - statusElement.className = 'status-indicator offline'; - statusElement.innerHTML = ' API Offline'; - } -} - -async function loadApiInfo() { - const apiInfoElement = document.getElementById('apiInfo'); - try { - const response = await fetch(`${API_BASE_URL}/api`); - if (response.ok) { - const data = await response.json(); - displayApiInfo(data); - } else { - throw new Error('Failed to load API info'); - } - } catch (error) { - apiInfoElement.innerHTML = '
Failed to load API information
'; - } -} - -function displayApiInfo(data) { - const apiInfoElement = document.getElementById('apiInfo'); - apiInfoElement.innerHTML = ` -
-

API Status

-

${data.message}

-
-
-

Version

-

${data.version}

-
-
-

Model Status

-

${data.model_loaded ? 'Loaded โœ…' : 'Not Loaded โŒ'}

-
-
-

Supported Formats

-

${data.supported_formats.join(', ')}

-
- `; -} - -function showUploadSection() { - document.getElementById('uploadSection').style.display = 'block'; - document.getElementById('uploadSection').scrollIntoView({ behavior: 'smooth' }); - - // Ensure the upload area is properly initialized - initializeUploadArea(); -} - -function initializeUploadArea() { - const uploadArea = document.getElementById('uploadArea'); - let fileInput = document.getElementById('fileInput'); - - // Completely recreate the file input element - if (fileInput) { - fileInput.remove(); - } - - // Create a brand new file input - const newFileInput = document.createElement('input'); - newFileInput.type = 'file'; - newFileInput.id = 'fileInput'; - newFileInput.accept = 'image/*'; - newFileInput.style.display = 'none'; - newFileInput.multiple = false; - - // Insert the new file input into the DOM - uploadArea.parentNode.insertBefore(newFileInput, uploadArea); - - // Clear any existing event listeners on upload area by cloning - const newUploadArea = uploadArea.cloneNode(true); - uploadArea.parentNode.replaceChild(newUploadArea, uploadArea); - - // Re-attach all event listeners to the new elements - newFileInput.addEventListener('change', handleFileSelect); - - newUploadArea.addEventListener('dragover', handleDragOver); - newUploadArea.addEventListener('dragleave', handleDragLeave); - newUploadArea.addEventListener('drop', handleDrop); - - newUploadArea.addEventListener('click', function(event) { - if (event.target === newUploadArea || (event.target.closest('.upload-content') && !event.target.closest('button'))) { - console.log('Upload area clicked, triggering file input'); - const currentFileInput = document.getElementById('fileInput'); - if (currentFileInput) { - currentFileInput.click(); - } - } - }); - - console.log('Upload area completely reinitialized with fresh file input'); -} - -function handleFileSelect(event) { - console.log('File select event triggered'); - const file = event.target.files[0]; - if (file) { - console.log('File selected:', file.name); - handleFile(file); - } else { - console.log('No file selected'); - } -} - -function handleDragOver(event) { - event.preventDefault(); - event.stopPropagation(); - event.currentTarget.classList.add('dragover'); -} - -function handleDragLeave(event) { - event.preventDefault(); - event.stopPropagation(); - event.currentTarget.classList.remove('dragover'); -} - -function handleDrop(event) { - event.preventDefault(); - event.stopPropagation(); - event.currentTarget.classList.remove('dragover'); - - const files = event.dataTransfer.files; - if (files.length > 0) { - handleFile(files[0]); - } -} - -function handleFile(file) { - if (!file.type.startsWith('image/')) { - alert('Please select an image file'); - return; - } - - uploadedFile = file; - - // Hide test results when new image is uploaded - const testResultsSection = document.getElementById('testResultsSection'); - if (testResultsSection) { - testResultsSection.style.display = 'none'; - } - - // Show file info with change file option - const uploadArea = document.getElementById('uploadArea'); - uploadArea.innerHTML = ` -
- -

File selected: ${file.name}

-

Size: ${(file.size / 1024 / 1024).toFixed(2)} MB

- -
- `; - - // Show controls - document.getElementById('uploadControls').style.display = 'block'; -} - -function resetFileUpload() { - uploadedFile = null; - lastDetectionResult = null; // Reset last detection result - - // Reset upload area HTML - const uploadArea = document.getElementById('uploadArea'); - uploadArea.innerHTML = ` -
- -

Drag and drop an image here or click to select

-

Supported formats: PNG, JPG, JPEG, GIF, BMP

- -
- `; - - // Hide controls - const uploadControls = document.getElementById('uploadControls'); - uploadControls.style.display = 'none'; - - // Remove the "Upload Another" button if it exists - const uploadAnotherBtn = uploadControls.querySelector('.upload-another'); - if (uploadAnotherBtn) { - uploadAnotherBtn.remove(); - } - - // Hide results if showing - document.getElementById('resultsSection').style.display = 'none'; - - // Hide test results when file is reset - const testResultsSection = document.getElementById('testResultsSection'); - if (testResultsSection) { - testResultsSection.style.display = 'none'; - } - - // Reinitialize the upload area with fresh event listeners - initializeUploadArea(); - - console.log('File upload reset completed'); -} - -async function processUploadedImage() { - if (!uploadedFile) { - alert('Please select an image first'); - return; - } - - const confidence = 0.8; // Fixed 80% threshold - showLoading('Processing uploaded image...'); - - try { - const formData = new FormData(); - formData.append('image', uploadedFile); - formData.append('confidence', confidence); - - const response = await fetch(`${API_BASE_URL}/detect`, { - method: 'POST', - body: formData - }); - - const result = await response.json(); - hideLoading(); - - if (result.success) { - // Store the last detection result for Run All Tests - lastDetectionResult = result; - displayResults(result, 'Uploaded Image Detection'); - // Add option to upload another file - addUploadAnotherOption(); - } else { - alert(`Detection failed: ${result.error}`); - } - } catch (error) { - hideLoading(); - alert(`Error: ${error.message}`); - } -} - -function addUploadAnotherOption() { - const uploadControls = document.getElementById('uploadControls'); - if (!uploadControls.querySelector('.upload-another')) { - const uploadAnotherBtn = document.createElement('button'); - uploadAnotherBtn.className = 'btn btn-secondary upload-another'; - uploadAnotherBtn.style.marginLeft = '10px'; - uploadAnotherBtn.innerHTML = ' Upload Another Image'; - uploadAnotherBtn.onclick = resetFileUpload; - uploadControls.appendChild(uploadAnotherBtn); - } -} - -async function testHardcodedImage() { - showLoading('Testing hardcoded image...'); - - try { - console.log(`Making request to: ${API_BASE_URL}/detect/hardcoded?confidence=0.8`); - const response = await fetch(`${API_BASE_URL}/detect/hardcoded?confidence=0.8`); - console.log('Response status:', response.status); - - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - - const result = await response.json(); - console.log('Response data:', result); - hideLoading(); - - if (result.success) { - // Store the last detection result for Run All Tests - lastDetectionResult = result; - displayResults(result, 'Hardcoded Image Test'); - } else { - alert(`Test failed: ${result.error}`); - } - } catch (error) { - hideLoading(); - console.error('Hardcoded test error:', error); - alert(`Error: ${error.message}`); - } -} - -function displayResults(result, title) { - const resultsSection = document.getElementById('resultsSection'); - const resultsContent = document.getElementById('resultsContent'); - - let detectionsHtml = ''; - if (result.detections && result.detections.length > 0) { - detectionsHtml = result.detections.map((detection, index) => ` -
- Detection ${index + 1}: ${detection.class_name} - ${(detection.confidence * 100).toFixed(1)}% -
- `).join(''); - } else { - detectionsHtml = '
No memory modules detected
'; - } - - resultsContent.innerHTML = ` -
-
-

${title}

- ${new Date().toLocaleTimeString()} -
- -
-
-
${result.num_detections}
-
Detections
-
-
-
${(result.confidence_threshold * 100).toFixed(0)}%
-
Confidence
-
-
-
${result.success ? 'Success' : 'Failed'}
-
Status
-
-
- -
-

Detected Memory Modules:

- ${detectionsHtml} -
- - ${result.annotated_image ? ` -
-

Annotated Image:

- Annotated Result -
- ` : ''} -
- `; - - resultsSection.style.display = 'block'; - resultsSection.scrollIntoView({ behavior: 'smooth' }); -} - -async function runAllTests() { - showLoading('Running comprehensive tests...'); - const testResults = []; - - // Test 1: API Health - try { - const response = await fetch(`${API_BASE_URL}/health`); - const result = await response.json(); - testResults.push({ - name: 'API Health Check', - success: response.ok && result.status === 'healthy', - message: response.ok ? 'API is healthy' : 'API health check failed' - }); - } catch (error) { - testResults.push({ - name: 'API Health Check', - success: false, - message: `Error: ${error.message}` - }); - } - - // Test 2: Image with Memory Modules (use last detection result if available) - if (lastDetectionResult) { - // Use the last detection result from uploaded/tested image - testResults.push({ - name: 'Image with Memory Modules', - success: lastDetectionResult.success, - message: lastDetectionResult.success ? - (lastDetectionResult.num_detections > 0 ? - `โœ… Found ${lastDetectionResult.num_detections} memory modules` : - `โŒ No memory modules`) : - `โŒ Error: ${lastDetectionResult.error}` - }); - } else { - // Fallback to hardcoded test if no previous detection - try { - const response = await fetch(`${API_BASE_URL}/detect/hardcoded`); - const result = await response.json(); - lastDetectionResult = result; // Store for future use - testResults.push({ - name: 'Image with Memory Modules', - success: result.success, - message: result.success ? - (result.num_detections > 0 ? - `โœ… Found ${result.num_detections} memory modules` : - `โŒ No memory modules`) : - `โŒ Error: ${result.error}` - }); - } catch (error) { - testResults.push({ - name: 'Image with Memory Modules', - success: false, - message: `โŒ Error: ${error.message}` - }); - } - } - - // Test 3: API Information - try { - const response = await fetch(`${API_BASE_URL}/api`); - const result = await response.json(); - testResults.push({ - name: 'API Information', - success: response.ok && result.message, - message: response.ok ? 'API info loaded successfully' : 'Failed to load API info' - }); - } catch (error) { - testResults.push({ - name: 'API Information', - success: false, - message: `Error: ${error.message}` - }); - } - - hideLoading(); - displayTestResults(testResults); -} - -function displayTestResults(testResults) { - const testResultsSection = document.getElementById('testResultsSection'); - const testResultsContent = document.getElementById('testResults'); - - const successCount = testResults.filter(test => test.success).length; - const totalTests = testResults.length; - - const testsHtml = testResults.map(test => ` -
-

- - ${test.name} -

-

${test.message}

-
- `).join(''); - - testResultsContent.innerHTML = ` -
-

Test Summary: ${successCount}/${totalTests} tests passed

-
- ${testsHtml} - `; - - testResultsSection.style.display = 'block'; - testResultsSection.scrollIntoView({ behavior: 'smooth' }); -} - -function showLoading(message) { - document.getElementById('loadingText').textContent = message; - document.getElementById('loadingOverlay').style.display = 'flex'; -} - -function hideLoading() { - document.getElementById('loadingOverlay').style.display = 'none'; -} - - diff --git a/static/style.css b/static/style.css deleted file mode 100644 index 7ff40b5..0000000 --- a/static/style.css +++ /dev/null @@ -1,413 +0,0 @@ -/* Memory Module Detection QA Interface 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; - color: #333; - display: flex; - flex-direction: column; -} - -.container { - max-width: 1200px; - margin: 0 auto; - padding: 20px; - flex: 1; - display: flex; - flex-direction: column; -} - -header { - text-align: center; - margin-bottom: 30px; - color: white; -} - -header h1 { - font-size: 2.5rem; - margin-bottom: 10px; - text-shadow: 2px 2px 4px rgba(0,0,0,0.3); -} - -header p { - font-size: 1.2rem; - opacity: 0.9; - margin-bottom: 20px; -} - -.status-indicator { - display: inline-flex; - align-items: center; - gap: 8px; - background: rgba(255,255,255,0.2); - padding: 8px 16px; - border-radius: 20px; - backdrop-filter: blur(10px); -} - -.status-indicator.online { - background: rgba(76, 175, 80, 0.3); -} - -.status-indicator.offline { - background: rgba(244, 67, 54, 0.3); -} - -.panel { - background: white; - border-radius: 12px; - padding: 25px; - margin-bottom: 20px; - box-shadow: 0 8px 32px rgba(0,0,0,0.1); - backdrop-filter: blur(10px); -} - -.panel h2 { - color: #333; - margin-bottom: 20px; - font-size: 1.5rem; - display: flex; - align-items: center; - gap: 10px; -} - -.panel h2 i { - color: #667eea; -} - -.api-info { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - gap: 15px; -} - -.info-item { - background: #f8f9fa; - padding: 15px; - border-radius: 8px; - border-left: 4px solid #667eea; -} - -.info-item h3 { - color: #333; - margin-bottom: 8px; - font-size: 1rem; -} - -.info-item p { - color: #666; - font-size: 0.9rem; -} - -.test-options { - display: flex; - gap: 15px; - flex-wrap: wrap; -} - -.btn { - padding: 12px 24px; - border: none; - border-radius: 8px; - cursor: pointer; - font-size: 1rem; - font-weight: 500; - transition: all 0.3s ease; - display: inline-flex; - align-items: center; - gap: 8px; - text-decoration: none; -} - -.btn:hover { - transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0,0,0,0.2); -} - -.btn-primary { - background: #667eea; - color: white; -} - -.btn-secondary { - background: #6c757d; - color: white; -} - -.btn-info { - background: #17a2b8; - color: white; -} - -.btn-success { - background: #28a745; - color: white; -} - -.btn-outline { - background: transparent; - color: #667eea; - border: 2px solid #667eea; -} - -.upload-area { - border: 3px dashed #ddd; - border-radius: 12px; - padding: 40px; - text-align: center; - transition: all 0.3s ease; - cursor: pointer; -} - -.upload-area:hover, -.upload-area.dragover { - border-color: #667eea; - background: rgba(102, 126, 234, 0.05); -} - -.upload-icon { - font-size: 3rem; - color: #667eea; - margin-bottom: 15px; -} - -.upload-hint { - color: #666; - font-size: 0.9rem; - margin-top: 10px; -} - -.upload-controls { - margin-top: 20px; - padding: 20px; - background: #f8f9fa; - border-radius: 8px; -} - -.confidence-info { - margin-bottom: 15px; - padding: 10px; - background: #e8f5e8; - border-radius: 6px; - border-left: 4px solid #28a745; -} - -.confidence-info p { - margin: 0; - color: #155724; - font-size: 0.9rem; -} - -.results-content { - display: grid; - gap: 20px; -} - -.result-item { - background: #f8f9fa; - border-radius: 8px; - padding: 20px; - border-left: 4px solid #28a745; -} - -.result-header { - display: flex; - justify-content: between; - align-items: center; - margin-bottom: 15px; -} - -.result-stats { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); - gap: 15px; - margin-bottom: 20px; -} - -.stat-item { - text-align: center; - padding: 10px; - background: white; - border-radius: 6px; -} - -.stat-value { - font-size: 1.5rem; - font-weight: bold; - color: #667eea; -} - -.stat-label { - font-size: 0.9rem; - color: #666; -} - -.image-container { - text-align: center; - margin-top: 20px; -} - -.result-image { - max-width: 100%; - height: auto; - border-radius: 8px; - box-shadow: 0 4px 12px rgba(0,0,0,0.1); -} - -.detection-list { - margin-top: 15px; -} - -.detection-item { - background: white; - padding: 10px 15px; - margin-bottom: 8px; - border-radius: 6px; - display: flex; - justify-content: space-between; - align-items: center; -} - -.detection-confidence { - background: #28a745; - color: white; - padding: 4px 8px; - border-radius: 12px; - font-size: 0.8rem; - font-weight: bold; -} - -.test-results { - display: grid; - gap: 15px; -} - -.test-item { - background: #f8f9fa; - padding: 15px; - border-radius: 8px; - border-left: 4px solid #ddd; -} - -.test-item.success { - border-left-color: #28a745; -} - -.test-item.error { - border-left-color: #dc3545; -} - -.test-item h3 { - margin-bottom: 8px; - display: flex; - align-items: center; - gap: 8px; -} - -.loading { - text-align: center; - padding: 40px; - color: #666; -} - -.loading-overlay { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0,0,0,0.7); - display: flex; - justify-content: center; - align-items: center; - z-index: 1000; -} - -.loading-content { - background: white; - padding: 30px; - border-radius: 12px; - text-align: center; -} - -.loading-content i { - font-size: 2rem; - color: #667eea; - margin-bottom: 15px; -} - -main { - flex: 1; -} - -.summary-message { - padding: 15px; - margin: 15px 0; - border-radius: 8px; - font-weight: 600; - font-size: 1.1rem; - text-align: center; -} - -.summary-message.success { - background: #d4edda; - color: #155724; - border: 1px solid #c3e6cb; -} - -.summary-message.no-memory { - background: #f8d7da; - color: #721c24; - border: 1px solid #f5c6cb; -} - -.summary-message.error { - background: #f8d7da; - color: #721c24; - border: 1px solid #f5c6cb; -} - -.footer { - text-align: center; - margin-top: auto; - padding: 20px 0; - color: rgba(255,255,255,0.8); - background: rgba(0,0,0,0.1); - backdrop-filter: blur(10px); - border-top: 1px solid rgba(255,255,255,0.1); -} - -/* Responsive Design */ -@media (max-width: 768px) { - .container { - padding: 10px; - } - - header h1 { - font-size: 2rem; - } - - .test-options { - flex-direction: column; - } - - .btn { - width: 100%; - justify-content: center; - } - - .api-info { - grid-template-columns: 1fr; - } - - .result-stats { - grid-template-columns: repeat(2, 1fr); - } -} diff --git a/templates/index.html b/templates/index.html deleted file mode 100644 index b3ccb0d..0000000 --- a/templates/index.html +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - Memory Module Detection - QA Testing Interface - - - - -
-
-

Memory Module Detection

-

AI-Powered Motherboard Memory Module Detection

- -
- -
- -
-

API Information

-
-
Loading API information...
-
-
- - -
-

Test Options

-
- - - - -
-
- - - - - - - - - -
- - -
- - - - - - - diff --git a/test_api.py b/test_api.py deleted file mode 100644 index e2c155b..0000000 --- a/test_api.py +++ /dev/null @@ -1,257 +0,0 @@ -#!/usr/bin/env python3 -""" -Test script for Memory Module Detection API -This script tests all API endpoints and provides usage examples. -""" - -import requests -import json -import base64 -import os -from PIL import Image -import io - -# API base URL -BASE_URL = "http://localhost:5002" - -def test_api_info(): - """Test the API info endpoint.""" - print("๐Ÿ” Testing API Info...") - try: - response = requests.get(f"{BASE_URL}/") - if response.status_code == 200: - data = response.json() - print(f"โœ… API Info: {data['message']}") - print(f" Model loaded: {data['model_loaded']}") - print(f" Supported formats: {data['supported_formats']}") - return True - else: - print(f"โŒ API Info failed: {response.status_code}") - return False - except Exception as e: - print(f"โŒ API Info error: {e}") - return False - -def test_health_check(): - """Test the health check endpoint.""" - print("\n๐Ÿฅ Testing Health Check...") - try: - response = requests.get(f"{BASE_URL}/health") - if response.status_code == 200: - data = response.json() - print(f"โœ… Health: {data['status']}") - print(f" Model loaded: {data['model_loaded']}") - return True - else: - print(f"โŒ Health check failed: {response.status_code}") - return False - except Exception as e: - print(f"โŒ Health check error: {e}") - return False - -def test_hardcoded_detection(): - """Test detection with hardcoded image.""" - print("\n๐Ÿ–ผ๏ธ Testing Hardcoded Image Detection...") - try: - response = requests.get(f"{BASE_URL}/detect/hardcoded?confidence=0.8") - if response.status_code == 200: - data = response.json() - if data['success']: - print(f"โœ… Hardcoded detection successful!") - print(f" Found {data['num_detections']} memory modules") - for i, detection in enumerate(data['detections']): - print(f" Detection {i+1}: {detection['class_name']} " - f"(confidence: {detection['confidence']:.3f})") - - # Save annotated image - if 'annotated_image' in data: - save_base64_image(data['annotated_image'], 'test_hardcoded_result.png') - print(" Annotated image saved as: test_hardcoded_result.png") - - return True - else: - print(f"โŒ Hardcoded detection failed: {data.get('error', 'Unknown error')}") - return False - else: - print(f"โŒ Hardcoded detection failed: {response.status_code}") - if response.text: - print(f" Response: {response.text}") - return False - except Exception as e: - print(f"โŒ Hardcoded detection error: {e}") - return False - -def test_file_upload(): - """Test detection with file upload.""" - print("\n๐Ÿ“ค Testing File Upload Detection...") - - # Find a test image - test_image_path = None - possible_paths = [ - 'training/memory/out1.png', - 'training/memory/out2.png', - 'training/val/images/memory_out8.png' - ] - - for path in possible_paths: - if os.path.exists(path): - test_image_path = path - break - - if not test_image_path: - print("โŒ No test image found. Skipping file upload test.") - return False - - try: - with open(test_image_path, 'rb') as f: - files = {'image': f} - data = {'confidence': '0.8'} - response = requests.post(f"{BASE_URL}/detect", files=files, data=data) - - if response.status_code == 200: - result = response.json() - if result['success']: - print(f"โœ… File upload detection successful!") - print(f" Test image: {test_image_path}") - print(f" Found {result['num_detections']} memory modules") - for i, detection in enumerate(result['detections']): - print(f" Detection {i+1}: {detection['class_name']} " - f"(confidence: {detection['confidence']:.3f})") - - # Save annotated image - if 'annotated_image' in result: - save_base64_image(result['annotated_image'], 'test_upload_result.png') - print(" Annotated image saved as: test_upload_result.png") - - return True - else: - print(f"โŒ File upload detection failed: {result.get('error', 'Unknown error')}") - return False - else: - print(f"โŒ File upload detection failed: {response.status_code}") - if response.text: - print(f" Response: {response.text}") - return False - except Exception as e: - print(f"โŒ File upload detection error: {e}") - return False - -def test_base64_detection(): - """Test detection with base64 encoded image.""" - print("\n๐Ÿ”ข Testing Base64 Detection...") - - # Find a test image - test_image_path = None - possible_paths = [ - 'training/memory/out1.png', - 'training/memory/out2.png' - ] - - for path in possible_paths: - if os.path.exists(path): - test_image_path = path - break - - if not test_image_path: - print("โŒ No test image found. Skipping base64 test.") - return False - - try: - # Convert image to base64 - with open(test_image_path, 'rb') as f: - image_data = f.read() - base64_string = base64.b64encode(image_data).decode('utf-8') - - # Send request - payload = { - 'image': base64_string, - 'confidence': 0.8 - } - - response = requests.post( - f"{BASE_URL}/detect/base64", - json=payload, - headers={'Content-Type': 'application/json'} - ) - - if response.status_code == 200: - result = response.json() - if result['success']: - print(f"โœ… Base64 detection successful!") - print(f" Test image: {test_image_path}") - print(f" Found {result['num_detections']} memory modules") - for i, detection in enumerate(result['detections']): - print(f" Detection {i+1}: {detection['class_name']} " - f"(confidence: {detection['confidence']:.3f})") - - # Save annotated image - if 'annotated_image' in result: - save_base64_image(result['annotated_image'], 'test_base64_result.png') - print(" Annotated image saved as: test_base64_result.png") - - return True - else: - print(f"โŒ Base64 detection failed: {result.get('error', 'Unknown error')}") - return False - else: - print(f"โŒ Base64 detection failed: {response.status_code}") - if response.text: - print(f" Response: {response.text}") - return False - except Exception as e: - print(f"โŒ Base64 detection error: {e}") - return False - -def save_base64_image(base64_string, filename): - """Save base64 encoded image to file.""" - try: - image_data = base64.b64decode(base64_string) - image = Image.open(io.BytesIO(image_data)) - image.save(filename) - except Exception as e: - print(f" Warning: Could not save image {filename}: {e}") - -def main(): - """Run all API tests.""" - print("๐Ÿงช Memory Module Detection API Test Suite") - print("=" * 50) - - # Check if API is running - try: - response = requests.get(f"{BASE_URL}/health", timeout=5) - except requests.exceptions.ConnectionError: - print("โŒ API is not running!") - print(" Please start the API first: python3 main.py") - return - except Exception as e: - print(f"โŒ Cannot connect to API: {e}") - return - - # Run tests - tests = [ - test_api_info, - test_health_check, - test_hardcoded_detection, - test_file_upload, - test_base64_detection - ] - - passed = 0 - total = len(tests) - - for test in tests: - if test(): - passed += 1 - - print("\n" + "=" * 50) - print(f"๐Ÿ Test Results: {passed}/{total} tests passed") - - if passed == total: - print("๐ŸŽ‰ All tests passed! The API is working correctly.") - else: - print("โš ๏ธ Some tests failed. Check the output above for details.") - if passed == 0: - print(" Make sure the model is trained: python3 train.py") - -if __name__ == "__main__": - main() diff --git a/tests/test_memory_detector_comprehensive.py b/tests/test_memory_detector_comprehensive.py new file mode 100644 index 0000000..8adc5f9 --- /dev/null +++ b/tests/test_memory_detector_comprehensive.py @@ -0,0 +1,182 @@ +import pytest +from pathlib import Path +import numpy as np +from PIL import Image +import matplotlib.pyplot as plt +from app.utils.detector import MemoryDetector +import os +import json +from typing import List, Dict, Tuple +import logging + +# Configure logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +class TestMemoryDetector: + @pytest.fixture(scope="class") + def results_dir(self): + """Create and return results directory""" + dir_path = Path("test_results") + dir_path.mkdir(exist_ok=True) + logger.info(f"Created results directory: {dir_path}") + return dir_path + + @pytest.fixture(scope="class") + def detector(self): + """Initialize detector once for all tests""" + logger.info("Initializing MemoryDetector...") + return MemoryDetector() + + @pytest.fixture(scope="class") + def test_images(self): + """Load test images from validation directory""" + val_dir = Path('training/val/images') + assert val_dir.exists(), f"Validation directory not found: {val_dir}" + + logger.info(f"Loading test images from {val_dir}") + images = [] + for img_path in val_dir.glob('memory_*.png'): + images.append({ + 'path': str(img_path), + 'image': Image.open(img_path) + }) + logger.info(f"Loaded {len(images)} test images") + assert len(images) > 0, "No test images found" + return images + + def test_detector_initialization(self, detector): + """Test detector initialization and default parameters""" + logger.info("Testing detector initialization...") + assert detector.conf_threshold == 0.25 + assert detector.iou_threshold == 0.45 + assert detector.model is not None + logger.info("Detector initialization test passed") + + def test_single_image_detection(self, detector, test_images, results_dir): + """Test detection on a single image""" + logger.info("Testing single image detection...") + test_case = test_images[0] + result_img, detections = detector.detect(test_case['image']) + + # Save the result + output_path = results_dir / "single_detection_test.png" + result_img.save(output_path) + logger.info(f"Saved detection result to {output_path}") + + # Verify result type and content + assert isinstance(result_img, Image.Image) + assert isinstance(detections, list) + assert all(isinstance(d, dict) for d in detections) + + # Log detection results + logger.info(f"Number of detections: {len(detections)}") + if len(detections) > 0: + for i, det in enumerate(detections): + logger.info(f"Detection {i+1}: confidence={det['confidence']:.3f}") + + def test_batch_detection(self, detector, test_images, results_dir): + """Test detection on multiple images""" + logger.info("Testing batch detection...") + results = [] + for i, test_case in enumerate(test_images): + logger.info(f"Processing image {i+1}/{len(test_images)}") + result_img, detections = detector.detect(test_case['image']) + + # Save each result + output_path = results_dir / f"batch_detection_{i}.png" + result_img.save(output_path) + + results.append({ + 'path': test_case['path'], + 'detections': len(detections), + 'confidences': [d['confidence'] for d in detections] + }) + + # Save detailed results + results_path = results_dir / "batch_results.json" + with open(results_path, 'w') as f: + json.dump(results, f, indent=2) + logger.info(f"Saved batch results to {results_path}") + + # Log statistics + total_detections = sum(r['detections'] for r in results) + avg_confidence = np.mean([conf for r in results for conf in r['confidences']]) if total_detections > 0 else 0 + + logger.info("\nBatch Detection Statistics:") + logger.info(f"Total images processed: {len(results)}") + logger.info(f"Total detections: {total_detections}") + logger.info(f"Average confidence: {avg_confidence:.3f}") + + assert total_detections > 0, "No detections found in any test image" + + def test_threshold_optimization(self, detector, test_images): + """Test threshold optimization functionality""" + images = [tc['image'] for tc in test_images] + best_conf, best_iou = detector.optimize_thresholds(images) + + # Verify threshold bounds + assert 0 <= best_conf <= 1, f"Invalid confidence threshold: {best_conf}" + assert 0 <= best_iou <= 1, f"Invalid IoU threshold: {best_iou}" + + # Test detection with optimized thresholds + test_case = test_images[0] + result_img, detections = detector.detect( + test_case['image'], + conf_threshold=best_conf, + iou_threshold=best_iou + ) + + print(f"\nOptimized Thresholds:") + print(f"Confidence: {best_conf:.3f}") + print(f"IoU: {best_iou:.3f}") + + @pytest.mark.parametrize("conf_threshold,iou_threshold", [ + (0.1, 0.1), + (0.5, 0.5), + (0.9, 0.9) + ]) + def test_different_thresholds(self, detector, test_images, conf_threshold, iou_threshold): + """Test detection with different threshold combinations""" + test_case = test_images[0] + result_img, detections = detector.detect( + test_case['image'], + conf_threshold=conf_threshold, + iou_threshold=iou_threshold + ) + + print(f"\nThreshold Test (conf={conf_threshold}, iou={iou_threshold}):") + print(f"Detections found: {len(detections)}") + + def test_visualization(self, detector, test_images, results_dir): + """Test detection visualization and save results""" + logger.info("Testing visualization...") + + # Process and visualize a batch of images + fig, axes = plt.subplots(2, 2, figsize=(12, 12)) + axes = axes.ravel() + + for idx, test_case in enumerate(test_images[:4]): + logger.info(f"Processing image {idx+1}/4 for visualization") + result_img, detections = detector.detect(test_case['image']) + + # Save individual result + result_path = results_dir / f"visualization_{idx}.png" + result_img.save(result_path) + logger.info(f"Saved individual result to {result_path}") + + # Plot result + axes[idx].imshow(result_img) + axes[idx].set_title(f"Detections: {len(detections)}") + axes[idx].axis('off') + + # Save summary plot + summary_path = results_dir / "summary.png" + plt.tight_layout() + plt.savefig(summary_path) + plt.close() + logger.info(f"Saved summary visualization to {summary_path}") + +if __name__ == "__main__": + # Run with output capture disabled + pytest.main([__file__, "-v", "-s"]) diff --git a/train.py b/train.py index 6c3faef..794cadd 100644 --- a/train.py +++ b/train.py @@ -1,158 +1,43 @@ -#!/usr/bin/env python3 -""" -YOLOv8 Training Script for Memory Module Detection -This script trains a YOLOv8 nano model to detect memory modules in motherboard images. -""" - -import os -import sys -from pathlib import Path -import yaml from ultralytics import YOLO -import torch -def check_dataset_structure(): - """Verify that the dataset structure is correct.""" - required_paths = [ - 'training/train/images', - 'training/train/labels', - 'training/val/images', - 'training/val/labels', - 'dataset.yaml' - ] +def train_model(): + # Load YOLOv8n (nano) for faster training with decent accuracy + model = YOLO('yolov8n.pt') - for path in required_paths: - if not os.path.exists(path): - raise FileNotFoundError(f"Required path not found: {path}") + # Train with optimized parameters for speed and quality + results = model.train( + data='dataset.yaml', + epochs=50, # Reduced number of epochs + imgsz=640, # Standard image size for faster processing + batch=8, # Smaller batch size for less memory usage + name='memory_detector_fast', + save=True, + device='cpu', + patience=15, # Shorter patience for earlier stopping + save_period=5, # Save every 5 epochs + verbose=True, + + # Effective but lightweight augmentation + degrees=5.0, # Less rotation for speed + scale=0.5, + translate=0.1, + fliplr=0.5, + mosaic=1.0, # Keep mosaic as it's very effective + + # Speed-optimized optimization parameters + lr0=0.01, + lrf=0.01, + momentum=0.937, + weight_decay=0.0005, + warmup_epochs=1.0, # Shorter warmup + + # Performance parameters + workers=0, # Fewer workers for CPU training + cache='disk', # Changed to disk caching for deterministic results + ) - # Check if we have images and labels - train_images = len([f for f in os.listdir('training/train/images') if f.endswith('.png')]) - train_labels = len([f for f in os.listdir('training/train/labels') if f.endswith('.txt')]) - val_images = len([f for f in os.listdir('training/val/images') if f.endswith('.png')]) - val_labels = len([f for f in os.listdir('training/val/labels') if f.endswith('.txt')]) - - print(f"Dataset structure verified:") - print(f" Training: {train_images} images, {train_labels} labels") - print(f" Validation: {val_images} images, {val_labels} labels") - - return True + # Save the trained model + model.save('model/weights/best.pt') -def train_model(epochs=100, imgsz=640, batch_size=16, device='auto'): - """ - Train YOLOv8 nano model on memory module dataset. - - Args: - epochs (int): Number of training epochs - imgsz (int): Image size for training - batch_size (int): Batch size for training - device (str): Device to use ('auto', 'cpu', 'cuda', or specific GPU id) - """ - - # Check dataset structure - check_dataset_structure() - - # Initialize YOLOv8 nano model - print("Initializing YOLOv8 nano model...") - model = YOLO('yolov8n.pt') # Load pretrained YOLOv8 nano model - - # Check available device - if device == 'auto': - device = 'cuda' if torch.cuda.is_available() else 'cpu' - - print(f"Using device: {device}") - print(f"CUDA available: {torch.cuda.is_available()}") - if torch.cuda.is_available(): - print(f"CUDA device: {torch.cuda.get_device_name()}") - - # Training configuration - train_config = { - 'data': 'dataset.yaml', - 'epochs': epochs, - 'imgsz': imgsz, - 'batch': batch_size, - 'device': device, - 'project': 'runs/detect', - 'name': 'memory_module_detection', - 'save': True, - 'save_period': 10, # Save checkpoint every 10 epochs - 'cache': False, # Don't cache images (saves RAM) - 'workers': 4, - 'patience': 50, # Early stopping patience - 'optimizer': 'AdamW', - 'lr0': 0.01, # Initial learning rate - 'lrf': 0.01, # Final learning rate factor - 'momentum': 0.937, - 'weight_decay': 0.0005, - 'warmup_epochs': 3, - 'warmup_momentum': 0.8, - 'warmup_bias_lr': 0.1, - 'box': 7.5, # Box loss gain - 'cls': 0.5, # Class loss gain - 'dfl': 1.5, # DFL loss gain - 'pose': 12.0, # Pose loss gain - 'kobj': 1.0, # Keypoint obj loss gain - 'label_smoothing': 0.0, - 'nbs': 64, # Nominal batch size - 'hsv_h': 0.015, # Image HSV-Hue augmentation - 'hsv_s': 0.7, # Image HSV-Saturation augmentation - 'hsv_v': 0.4, # Image HSV-Value augmentation - 'degrees': 0.0, # Image rotation (+/- deg) - 'translate': 0.1, # Image translation (+/- fraction) - 'scale': 0.5, # Image scale (+/- gain) - 'shear': 0.0, # Image shear (+/- deg) - 'perspective': 0.0, # Image perspective (+/- fraction) - 'flipud': 0.0, # Image flip up-down (probability) - 'fliplr': 0.5, # Image flip left-right (probability) - 'mosaic': 1.0, # Image mosaic (probability) - 'mixup': 0.0, # Image mixup (probability) - 'copy_paste': 0.0, # Segment copy-paste (probability) - } - - print("Starting training...") - print(f"Configuration: {train_config}") - - # Train the model - results = model.train(**train_config) - - # Print training results - print("\nTraining completed!") - print(f"Best model saved at: runs/detect/memory_module_detection/weights/best.pt") - print(f"Last model saved at: runs/detect/memory_module_detection/weights/last.pt") - - return results - -def validate_model(model_path='runs/detect/memory_module_detection/weights/best.pt'): - """Validate the trained model.""" - if not os.path.exists(model_path): - print(f"Model not found at {model_path}") - return None - - print(f"Validating model: {model_path}") - model = YOLO(model_path) - - # Run validation - results = model.val(data='dataset.yaml') - - print("Validation completed!") - return results - -if __name__ == "__main__": - import argparse - - parser = argparse.ArgumentParser(description='Train YOLOv8 for memory module detection') - parser.add_argument('--epochs', type=int, default=100, help='Number of training epochs') - parser.add_argument('--imgsz', type=int, default=640, help='Image size for training') - parser.add_argument('--batch', type=int, default=16, help='Batch size') - parser.add_argument('--device', type=str, default='auto', help='Device to use (auto, cpu, cuda)') - parser.add_argument('--validate', action='store_true', help='Only run validation') - parser.add_argument('--model', type=str, default='runs/detect/memory_module_detection/weights/best.pt', - help='Model path for validation') - - args = parser.parse_args() - - if args.validate: - validate_model(args.model) - else: - train_model(epochs=args.epochs, imgsz=args.imgsz, batch_size=args.batch, device=args.device) - # Also run validation after training - validate_model() +if __name__ == '__main__': + train_model() \ No newline at end of file diff --git a/training/.DS_Store b/training/.DS_Store deleted file mode 100644 index f279437..0000000 Binary files a/training/.DS_Store and /dev/null differ diff --git a/training/memory/out1.txt b/training/memory/out1.txt index 4eb4168..9a01221 100644 --- a/training/memory/out1.txt +++ b/training/memory/out1.txt @@ -1,2 +1,2 @@ -0 0.353333 0.415062 0.164444 0.549136 -0 0.574444 0.426914 0.180000 0.557037 \ No newline at end of file +0 0.331616 0.424054 0.113032 0.395981 +0 0.569149 0.459811 0.093085 0.446809 \ No newline at end of file diff --git a/training/memory/out10.txt b/training/memory/out10.txt index 4aee564..f59b78e 100644 --- a/training/memory/out10.txt +++ b/training/memory/out10.txt @@ -1,2 +1,2 @@ -0 0.353333 0.387407 0.253333 0.454321 -0 0.568889 0.509877 0.244444 0.564938 \ No newline at end of file +0 0.557488 0.563739 0.214812 0.472813 +0 0.372852 0.415530 0.203560 0.361884 \ No newline at end of file diff --git a/training/memory/out11.txt b/training/memory/out11.txt index a423008..a49cba7 100644 --- a/training/memory/out11.txt +++ b/training/memory/out11.txt @@ -1,2 +1,2 @@ -0 0.383333 0.359753 0.233333 0.438519 -0 0.557778 0.559259 0.324444 0.481975 \ No newline at end of file +0 0.557561 0.587639 0.226064 0.445795 +0 0.373290 0.415400 0.212766 0.351233 \ No newline at end of file diff --git a/training/memory/out12.txt b/training/memory/out12.txt index 303cebb..d7cea00 100644 --- a/training/memory/out12.txt +++ b/training/memory/out12.txt @@ -1,2 +1,2 @@ -0 0.368889 0.395309 0.271111 0.391111 -0 0.568889 0.567160 0.324444 0.560988 \ No newline at end of file +0 0.552812 0.583418 0.252660 0.437352 +0 0.378989 0.402736 0.226064 0.366430 \ No newline at end of file diff --git a/training/memory/out13.txt b/training/memory/out13.txt index c8e6301..b439f1f 100644 --- a/training/memory/out13.txt +++ b/training/memory/out13.txt @@ -1,2 +1,2 @@ -0 0.402222 0.316296 0.253333 0.470123 -0 0.550000 0.541481 0.273333 0.509630 \ No newline at end of file +0 0.552527 0.595745 0.299202 0.463357 +0 0.380319 0.388889 0.247340 0.338061 \ No newline at end of file diff --git a/training/memory/out14.txt b/training/memory/out14.txt index 40bae36..d606a9c 100644 --- a/training/memory/out14.txt +++ b/training/memory/out14.txt @@ -1,2 +1,2 @@ -0 0.365556 0.381481 0.304444 0.355556 -0 0.554444 0.571111 0.313333 0.410864 \ No newline at end of file +0 0.397606 0.407801 0.239362 0.342790 +0 0.556516 0.606383 0.299202 0.437352 \ No newline at end of file diff --git a/training/memory/out15.txt b/training/memory/out15.txt index 0442f6d..571b8dc 100644 --- a/training/memory/out15.txt +++ b/training/memory/out15.txt @@ -1,2 +1,2 @@ -0 0.412222 0.369630 0.317778 0.292346 -0 0.572222 0.573086 0.322222 0.454321 \ No newline at end of file +0 0.424867 0.382979 0.238032 0.321513 +0 0.571144 0.601655 0.267287 0.432624 \ No newline at end of file diff --git a/training/memory/out16.txt b/training/memory/out16.txt index 34ac8b3..3bb9f49 100644 --- a/training/memory/out16.txt +++ b/training/memory/out16.txt @@ -1,2 +1,2 @@ -0 0.413333 0.340000 0.315556 0.375309 -0 0.553333 0.594815 0.368889 0.497778 \ No newline at end of file +0 0.417553 0.373522 0.252660 0.335697 +0 0.561170 0.613475 0.284574 0.427896 \ No newline at end of file diff --git a/training/memory/out17.txt b/training/memory/out17.txt index 7e41b05..4e7e27d 100644 --- a/training/memory/out17.txt +++ b/training/memory/out17.txt @@ -1,2 +1,2 @@ -0 0.404444 0.320247 0.368889 0.383210 -0 0.526667 0.616543 0.386667 0.383210 \ No newline at end of file +0 0.404920 0.359338 0.264628 0.373522 +0 0.541223 0.613475 0.303191 0.404255 \ No newline at end of file diff --git a/training/memory/out18.txt b/training/memory/out18.txt index 59fda84..7763821 100644 --- a/training/memory/out18.txt +++ b/training/memory/out18.txt @@ -1,2 +1,2 @@ -0 0.400000 0.365679 0.368889 0.363457 -0 0.513333 0.644198 0.520000 0.454321 \ No newline at end of file +0 0.533245 0.682033 0.372340 0.446809 +0 0.410904 0.375887 0.287234 0.321513 \ No newline at end of file diff --git a/training/memory/out19.txt b/training/memory/out19.txt index 866bd29..9cc7053 100644 --- a/training/memory/out19.txt +++ b/training/memory/out19.txt @@ -1,2 +1,2 @@ -0 0.423333 0.369630 0.393333 0.308148 -0 0.540000 0.673827 0.435556 0.402963 \ No newline at end of file +0 0.443484 0.401891 0.291223 0.293144 +0 0.547872 0.708038 0.356383 0.408983 \ No newline at end of file diff --git a/training/memory/out2.txt b/training/memory/out2.txt index 37e2b8f..d1e00fb 100644 --- a/training/memory/out2.txt +++ b/training/memory/out2.txt @@ -1,2 +1,2 @@ -0 0.332222 0.401235 0.171111 0.513580 -0 0.580000 0.419012 0.217778 0.533333 \ No newline at end of file +0 0.572473 0.470449 0.070479 0.453901 +0 0.336436 0.470449 0.117021 0.458629 \ No newline at end of file diff --git a/training/memory/out20.txt b/training/memory/out20.txt index 7bee07e..5312db0 100644 --- a/training/memory/out20.txt +++ b/training/memory/out20.txt @@ -1,2 +1,2 @@ -0 0.450000 0.343951 0.371111 0.304198 -0 0.535556 0.648148 0.426667 0.414815 \ No newline at end of file +0 0.545878 0.667849 0.341755 0.404255 +0 0.444149 0.390071 0.297872 0.260047 \ No newline at end of file diff --git a/training/memory/out3.txt b/training/memory/out3.txt index 6ffa2e5..6d3c7a8 100644 --- a/training/memory/out3.txt +++ b/training/memory/out3.txt @@ -1,2 +1,2 @@ -0 0.327778 0.401235 0.162222 0.553086 -0 0.552222 0.432840 0.162222 0.616296 \ No newline at end of file +0 0.331782 0.440898 0.126330 0.437352 +0 0.569149 0.471631 0.079787 0.442080 \ No newline at end of file diff --git a/training/memory/out4.txt b/training/memory/out4.txt index fc85a4b..54dbd3d 100644 --- a/training/memory/out4.txt +++ b/training/memory/out4.txt @@ -1,2 +1,2 @@ -0 0.338889 0.424938 0.171111 0.576790 -0 0.541111 0.432840 0.193333 0.553086 \ No newline at end of file +0 0.571809 0.486998 0.079787 0.463357 +0 0.333112 0.456265 0.128989 0.425532 \ No newline at end of file diff --git a/training/memory/out5.txt b/training/memory/out5.txt index bf4eeb8..3c08fac 100644 --- a/training/memory/out5.txt +++ b/training/memory/out5.txt @@ -1,2 +1,2 @@ -0 0.315556 0.411111 0.151111 0.557037 -0 0.550000 0.436790 0.157778 0.553086 \ No newline at end of file +0 0.555851 0.515366 0.095745 0.505910 +0 0.310505 0.465721 0.158245 0.406619 \ No newline at end of file diff --git a/training/memory/out6.txt b/training/memory/out6.txt index 0e87a06..9c80460 100644 --- a/training/memory/out6.txt +++ b/training/memory/out6.txt @@ -1,2 +1,2 @@ -0 0.324444 0.460494 0.200000 0.513580 -0 0.550000 0.478272 0.180000 0.541235 \ No newline at end of file +0 0.543218 0.547281 0.139628 0.517730 +0 0.310505 0.491726 0.168883 0.505910 \ No newline at end of file diff --git a/training/memory/out7.txt b/training/memory/out7.txt index ea8b298..65569c0 100644 --- a/training/memory/out7.txt +++ b/training/memory/out7.txt @@ -1,2 +1,2 @@ -0 0.321111 0.417037 0.197778 0.529383 -0 0.537778 0.474321 0.177778 0.588642 \ No newline at end of file +0 0.533245 0.539007 0.162234 0.520095 +0 0.318484 0.438534 0.174202 0.465721 \ No newline at end of file diff --git a/training/memory/out8.txt b/training/memory/out8.txt index f9e3d81..e03353b 100644 --- a/training/memory/out8.txt +++ b/training/memory/out8.txt @@ -1,2 +1,2 @@ -0 0.307778 0.438765 0.286667 0.414815 -0 0.538889 0.531605 0.273333 0.529383 \ No newline at end of file +0 0.542553 0.554374 0.183511 0.508274 +0 0.327128 0.446809 0.194149 0.444444 \ No newline at end of file diff --git a/training/memory/out9.txt b/training/memory/out9.txt index 62130b6..1eabd88 100644 --- a/training/memory/out9.txt +++ b/training/memory/out9.txt @@ -1,2 +1,2 @@ -0 0.562222 0.541481 0.235556 0.525432 -0 0.346667 0.381481 0.262222 0.489877 \ No newline at end of file +0 0.559840 0.528369 0.210106 0.527187 +0 0.345745 0.407801 0.226064 0.475177 \ No newline at end of file diff --git a/training/train/images/memory_out1.png b/training/train/images/memory_out1.png deleted file mode 100644 index 4a872cb..0000000 Binary files a/training/train/images/memory_out1.png and /dev/null differ diff --git a/training/train/images/memory_out10.png b/training/train/images/memory_out10.png deleted file mode 100644 index 20d3855..0000000 Binary files a/training/train/images/memory_out10.png and /dev/null differ diff --git a/training/train/images/memory_out11.png b/training/train/images/memory_out11.png deleted file mode 100644 index b10a382..0000000 Binary files a/training/train/images/memory_out11.png and /dev/null differ diff --git a/training/train/images/memory_out14.png b/training/train/images/memory_out14.png deleted file mode 100644 index 34d27d9..0000000 Binary files a/training/train/images/memory_out14.png and /dev/null differ diff --git a/training/train/images/memory_out15.png b/training/train/images/memory_out15.png deleted file mode 100644 index 90559ac..0000000 Binary files a/training/train/images/memory_out15.png and /dev/null differ diff --git a/training/train/images/memory_out16.png b/training/train/images/memory_out16.png deleted file mode 100644 index 314d0ee..0000000 Binary files a/training/train/images/memory_out16.png and /dev/null differ diff --git a/training/train/images/memory_out18.png b/training/train/images/memory_out18.png deleted file mode 100644 index b0d3084..0000000 Binary files a/training/train/images/memory_out18.png and /dev/null differ diff --git a/training/train/images/memory_out2.png b/training/train/images/memory_out2.png deleted file mode 100644 index 5019b99..0000000 Binary files a/training/train/images/memory_out2.png and /dev/null differ diff --git a/training/train/images/memory_out20.png b/training/train/images/memory_out20.png deleted file mode 100644 index 1bc07c8..0000000 Binary files a/training/train/images/memory_out20.png and /dev/null differ diff --git a/training/train/images/memory_out3.png b/training/train/images/memory_out3.png deleted file mode 100644 index 47d7331..0000000 Binary files a/training/train/images/memory_out3.png and /dev/null differ diff --git a/training/train/images/memory_out4.png b/training/train/images/memory_out4.png deleted file mode 100644 index a14f93f..0000000 Binary files a/training/train/images/memory_out4.png and /dev/null differ diff --git a/training/train/images/memory_out5.png b/training/train/images/memory_out5.png deleted file mode 100644 index 8a9a367..0000000 Binary files a/training/train/images/memory_out5.png and /dev/null differ diff --git a/training/train/images/memory_out6.png b/training/train/images/memory_out6.png deleted file mode 100644 index d4a1a58..0000000 Binary files a/training/train/images/memory_out6.png and /dev/null differ diff --git a/training/train/images/memory_out7.png b/training/train/images/memory_out7.png deleted file mode 100644 index 006e95d..0000000 Binary files a/training/train/images/memory_out7.png and /dev/null differ diff --git a/training/train/images/memory_out9.png b/training/train/images/memory_out9.png deleted file mode 100644 index 165c2c2..0000000 Binary files a/training/train/images/memory_out9.png and /dev/null differ diff --git a/training/train/images/no_memory_out11.png b/training/train/images/no_memory_out11.png deleted file mode 100644 index e0e3b8c..0000000 Binary files a/training/train/images/no_memory_out11.png and /dev/null differ diff --git a/training/train/images/no_memory_out12.png b/training/train/images/no_memory_out12.png deleted file mode 100644 index 841f02e..0000000 Binary files a/training/train/images/no_memory_out12.png and /dev/null differ diff --git a/training/train/images/no_memory_out13.png b/training/train/images/no_memory_out13.png deleted file mode 100644 index b2ec144..0000000 Binary files a/training/train/images/no_memory_out13.png and /dev/null differ diff --git a/training/train/images/no_memory_out14.png b/training/train/images/no_memory_out14.png deleted file mode 100644 index c98045c..0000000 Binary files a/training/train/images/no_memory_out14.png and /dev/null differ diff --git a/training/train/images/no_memory_out15.png b/training/train/images/no_memory_out15.png deleted file mode 100644 index c190220..0000000 Binary files a/training/train/images/no_memory_out15.png and /dev/null differ diff --git a/training/train/images/no_memory_out16.png b/training/train/images/no_memory_out16.png deleted file mode 100644 index fc75f71..0000000 Binary files a/training/train/images/no_memory_out16.png and /dev/null differ diff --git a/training/train/images/no_memory_out17.png b/training/train/images/no_memory_out17.png deleted file mode 100644 index e5e239b..0000000 Binary files a/training/train/images/no_memory_out17.png and /dev/null differ diff --git a/training/train/images/no_memory_out18.png b/training/train/images/no_memory_out18.png deleted file mode 100644 index dc8f385..0000000 Binary files a/training/train/images/no_memory_out18.png and /dev/null differ diff --git a/training/train/images/no_memory_out19.png b/training/train/images/no_memory_out19.png deleted file mode 100644 index a45fd23..0000000 Binary files a/training/train/images/no_memory_out19.png and /dev/null differ diff --git a/training/train/images/no_memory_out20.png b/training/train/images/no_memory_out20.png deleted file mode 100644 index ed2dcc2..0000000 Binary files a/training/train/images/no_memory_out20.png and /dev/null differ diff --git a/training/train/images/no_memory_out3.png b/training/train/images/no_memory_out3.png deleted file mode 100644 index 69413a3..0000000 Binary files a/training/train/images/no_memory_out3.png and /dev/null differ diff --git a/training/train/images/no_memory_out5.png b/training/train/images/no_memory_out5.png deleted file mode 100644 index a29cf60..0000000 Binary files a/training/train/images/no_memory_out5.png and /dev/null differ diff --git a/training/train/images/no_memory_out6.png b/training/train/images/no_memory_out6.png deleted file mode 100644 index d47234e..0000000 Binary files a/training/train/images/no_memory_out6.png and /dev/null differ diff --git a/training/train/images/no_memory_out7.png b/training/train/images/no_memory_out7.png deleted file mode 100644 index b50435c..0000000 Binary files a/training/train/images/no_memory_out7.png and /dev/null differ diff --git a/training/train/images/no_memory_out8.png b/training/train/images/no_memory_out8.png deleted file mode 100644 index 159046d..0000000 Binary files a/training/train/images/no_memory_out8.png and /dev/null differ diff --git a/training/train/images/no_memory_out9.png b/training/train/images/no_memory_out9.png deleted file mode 100644 index 479d869..0000000 Binary files a/training/train/images/no_memory_out9.png and /dev/null differ diff --git a/training/train/labels/memory_out1.txt b/training/train/labels/memory_out1.txt deleted file mode 100644 index 4eb4168..0000000 --- a/training/train/labels/memory_out1.txt +++ /dev/null @@ -1,2 +0,0 @@ -0 0.353333 0.415062 0.164444 0.549136 -0 0.574444 0.426914 0.180000 0.557037 \ No newline at end of file diff --git a/training/train/labels/memory_out10.txt b/training/train/labels/memory_out10.txt deleted file mode 100644 index 4aee564..0000000 --- a/training/train/labels/memory_out10.txt +++ /dev/null @@ -1,2 +0,0 @@ -0 0.353333 0.387407 0.253333 0.454321 -0 0.568889 0.509877 0.244444 0.564938 \ No newline at end of file diff --git a/training/train/labels/memory_out11.txt b/training/train/labels/memory_out11.txt deleted file mode 100644 index a423008..0000000 --- a/training/train/labels/memory_out11.txt +++ /dev/null @@ -1,2 +0,0 @@ -0 0.383333 0.359753 0.233333 0.438519 -0 0.557778 0.559259 0.324444 0.481975 \ No newline at end of file diff --git a/training/train/labels/memory_out14.txt b/training/train/labels/memory_out14.txt deleted file mode 100644 index 40bae36..0000000 --- a/training/train/labels/memory_out14.txt +++ /dev/null @@ -1,2 +0,0 @@ -0 0.365556 0.381481 0.304444 0.355556 -0 0.554444 0.571111 0.313333 0.410864 \ No newline at end of file diff --git a/training/train/labels/memory_out15.txt b/training/train/labels/memory_out15.txt deleted file mode 100644 index 0442f6d..0000000 --- a/training/train/labels/memory_out15.txt +++ /dev/null @@ -1,2 +0,0 @@ -0 0.412222 0.369630 0.317778 0.292346 -0 0.572222 0.573086 0.322222 0.454321 \ No newline at end of file diff --git a/training/train/labels/memory_out16.txt b/training/train/labels/memory_out16.txt deleted file mode 100644 index 34ac8b3..0000000 --- a/training/train/labels/memory_out16.txt +++ /dev/null @@ -1,2 +0,0 @@ -0 0.413333 0.340000 0.315556 0.375309 -0 0.553333 0.594815 0.368889 0.497778 \ No newline at end of file diff --git a/training/train/labels/memory_out17.txt b/training/train/labels/memory_out17.txt deleted file mode 100644 index 7e41b05..0000000 --- a/training/train/labels/memory_out17.txt +++ /dev/null @@ -1,2 +0,0 @@ -0 0.404444 0.320247 0.368889 0.383210 -0 0.526667 0.616543 0.386667 0.383210 \ No newline at end of file diff --git a/training/train/labels/memory_out18.txt b/training/train/labels/memory_out18.txt deleted file mode 100644 index 59fda84..0000000 --- a/training/train/labels/memory_out18.txt +++ /dev/null @@ -1,2 +0,0 @@ -0 0.400000 0.365679 0.368889 0.363457 -0 0.513333 0.644198 0.520000 0.454321 \ No newline at end of file diff --git a/training/train/labels/memory_out2.txt b/training/train/labels/memory_out2.txt deleted file mode 100644 index 37e2b8f..0000000 --- a/training/train/labels/memory_out2.txt +++ /dev/null @@ -1,2 +0,0 @@ -0 0.332222 0.401235 0.171111 0.513580 -0 0.580000 0.419012 0.217778 0.533333 \ No newline at end of file diff --git a/training/train/labels/memory_out20.txt b/training/train/labels/memory_out20.txt deleted file mode 100644 index 7bee07e..0000000 --- a/training/train/labels/memory_out20.txt +++ /dev/null @@ -1,2 +0,0 @@ -0 0.450000 0.343951 0.371111 0.304198 -0 0.535556 0.648148 0.426667 0.414815 \ No newline at end of file diff --git a/training/train/labels/memory_out3.txt b/training/train/labels/memory_out3.txt deleted file mode 100644 index 6ffa2e5..0000000 --- a/training/train/labels/memory_out3.txt +++ /dev/null @@ -1,2 +0,0 @@ -0 0.327778 0.401235 0.162222 0.553086 -0 0.552222 0.432840 0.162222 0.616296 \ No newline at end of file diff --git a/training/train/labels/memory_out4.txt b/training/train/labels/memory_out4.txt deleted file mode 100644 index fc85a4b..0000000 --- a/training/train/labels/memory_out4.txt +++ /dev/null @@ -1,2 +0,0 @@ -0 0.338889 0.424938 0.171111 0.576790 -0 0.541111 0.432840 0.193333 0.553086 \ No newline at end of file diff --git a/training/train/labels/memory_out5.txt b/training/train/labels/memory_out5.txt deleted file mode 100644 index bf4eeb8..0000000 --- a/training/train/labels/memory_out5.txt +++ /dev/null @@ -1,2 +0,0 @@ -0 0.315556 0.411111 0.151111 0.557037 -0 0.550000 0.436790 0.157778 0.553086 \ No newline at end of file diff --git a/training/train/labels/memory_out6.txt b/training/train/labels/memory_out6.txt deleted file mode 100644 index 0e87a06..0000000 --- a/training/train/labels/memory_out6.txt +++ /dev/null @@ -1,2 +0,0 @@ -0 0.324444 0.460494 0.200000 0.513580 -0 0.550000 0.478272 0.180000 0.541235 \ No newline at end of file diff --git a/training/train/labels/memory_out7.txt b/training/train/labels/memory_out7.txt deleted file mode 100644 index ea8b298..0000000 --- a/training/train/labels/memory_out7.txt +++ /dev/null @@ -1,2 +0,0 @@ -0 0.321111 0.417037 0.197778 0.529383 -0 0.537778 0.474321 0.177778 0.588642 \ No newline at end of file diff --git a/training/train/labels/memory_out9.txt b/training/train/labels/memory_out9.txt deleted file mode 100644 index 62130b6..0000000 --- a/training/train/labels/memory_out9.txt +++ /dev/null @@ -1,2 +0,0 @@ -0 0.562222 0.541481 0.235556 0.525432 -0 0.346667 0.381481 0.262222 0.489877 \ No newline at end of file diff --git a/training/train/labels/no_memory_out12.txt b/training/train/labels/no_memory_out12.txt deleted file mode 100644 index e69de29..0000000 diff --git a/training/train/labels/no_memory_out13.txt b/training/train/labels/no_memory_out13.txt deleted file mode 100644 index e69de29..0000000 diff --git a/training/train/labels/no_memory_out14.txt b/training/train/labels/no_memory_out14.txt deleted file mode 100644 index e69de29..0000000 diff --git a/training/train/labels/no_memory_out15.txt b/training/train/labels/no_memory_out15.txt deleted file mode 100644 index e69de29..0000000 diff --git a/training/train/labels/no_memory_out16.txt b/training/train/labels/no_memory_out16.txt deleted file mode 100644 index e69de29..0000000 diff --git a/training/train/labels/no_memory_out17.txt b/training/train/labels/no_memory_out17.txt deleted file mode 100644 index e69de29..0000000 diff --git a/training/train/labels/no_memory_out18.txt b/training/train/labels/no_memory_out18.txt deleted file mode 100644 index e69de29..0000000 diff --git a/training/train/labels/no_memory_out19.txt b/training/train/labels/no_memory_out19.txt deleted file mode 100644 index e69de29..0000000 diff --git a/training/train/labels/no_memory_out20.txt b/training/train/labels/no_memory_out20.txt deleted file mode 100644 index e69de29..0000000 diff --git a/training/train/labels/no_memory_out3.txt b/training/train/labels/no_memory_out3.txt deleted file mode 100644 index e69de29..0000000 diff --git a/training/train/labels/no_memory_out5.txt b/training/train/labels/no_memory_out5.txt deleted file mode 100644 index e69de29..0000000 diff --git a/training/train/labels/no_memory_out6.txt b/training/train/labels/no_memory_out6.txt deleted file mode 100644 index e69de29..0000000 diff --git a/training/train/labels/no_memory_out7.txt b/training/train/labels/no_memory_out7.txt deleted file mode 100644 index e69de29..0000000 diff --git a/training/train/labels/no_memory_out8.txt b/training/train/labels/no_memory_out8.txt deleted file mode 100644 index e69de29..0000000 diff --git a/training/train/labels/no_memory_out9.txt b/training/train/labels/no_memory_out9.txt deleted file mode 100644 index e69de29..0000000 diff --git a/training/val/images/memory_out12.png b/training/val/images/memory_out12.png deleted file mode 100644 index acec6b2..0000000 Binary files a/training/val/images/memory_out12.png and /dev/null differ diff --git a/training/val/images/memory_out13.png b/training/val/images/memory_out13.png deleted file mode 100644 index 7d88a6f..0000000 Binary files a/training/val/images/memory_out13.png and /dev/null differ diff --git a/training/val/images/memory_out19.png b/training/val/images/memory_out19.png deleted file mode 100644 index cf36055..0000000 Binary files a/training/val/images/memory_out19.png and /dev/null differ diff --git a/training/val/images/memory_out8.png b/training/val/images/memory_out8.png deleted file mode 100644 index 9550e7e..0000000 Binary files a/training/val/images/memory_out8.png and /dev/null differ diff --git a/training/val/images/no_memory_out1.png b/training/val/images/no_memory_out1.png deleted file mode 100644 index 68aa49a..0000000 Binary files a/training/val/images/no_memory_out1.png and /dev/null differ diff --git a/training/val/images/no_memory_out10.png b/training/val/images/no_memory_out10.png deleted file mode 100644 index 5ee0e27..0000000 Binary files a/training/val/images/no_memory_out10.png and /dev/null differ diff --git a/training/val/images/no_memory_out2.png b/training/val/images/no_memory_out2.png deleted file mode 100644 index b97d8e8..0000000 Binary files a/training/val/images/no_memory_out2.png and /dev/null differ diff --git a/training/val/images/no_memory_out4.png b/training/val/images/no_memory_out4.png deleted file mode 100644 index d07fa70..0000000 Binary files a/training/val/images/no_memory_out4.png and /dev/null differ diff --git a/training/val/labels/memory_out12.txt b/training/val/labels/memory_out12.txt deleted file mode 100644 index 303cebb..0000000 --- a/training/val/labels/memory_out12.txt +++ /dev/null @@ -1,2 +0,0 @@ -0 0.368889 0.395309 0.271111 0.391111 -0 0.568889 0.567160 0.324444 0.560988 \ No newline at end of file diff --git a/training/val/labels/memory_out13.txt b/training/val/labels/memory_out13.txt deleted file mode 100644 index c8e6301..0000000 --- a/training/val/labels/memory_out13.txt +++ /dev/null @@ -1,2 +0,0 @@ -0 0.402222 0.316296 0.253333 0.470123 -0 0.550000 0.541481 0.273333 0.509630 \ No newline at end of file diff --git a/training/val/labels/memory_out19.txt b/training/val/labels/memory_out19.txt deleted file mode 100644 index 866bd29..0000000 --- a/training/val/labels/memory_out19.txt +++ /dev/null @@ -1,2 +0,0 @@ -0 0.423333 0.369630 0.393333 0.308148 -0 0.540000 0.673827 0.435556 0.402963 \ No newline at end of file diff --git a/training/val/labels/memory_out8.txt b/training/val/labels/memory_out8.txt deleted file mode 100644 index f9e3d81..0000000 --- a/training/val/labels/memory_out8.txt +++ /dev/null @@ -1,2 +0,0 @@ -0 0.307778 0.438765 0.286667 0.414815 -0 0.538889 0.531605 0.273333 0.529383 \ No newline at end of file diff --git a/training/val/labels/no_memory_out1.txt b/training/val/labels/no_memory_out1.txt deleted file mode 100644 index e69de29..0000000 diff --git a/training/val/labels/no_memory_out10.txt b/training/val/labels/no_memory_out10.txt deleted file mode 100644 index e69de29..0000000 diff --git a/training/val/labels/no_memory_out2.txt b/training/val/labels/no_memory_out2.txt deleted file mode 100644 index e69de29..0000000 diff --git a/training/val/labels/no_memory_out4.txt b/training/val/labels/no_memory_out4.txt deleted file mode 100644 index e69de29..0000000