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
+
+
+
+
+
+
 }})
+
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 = `
-
-
-
-
-
-
${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:
-

-
- ` : ''}
-
- `;
-
- 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
-
-
-
-
-
-
-
-
-
-
- API Information
-
-
Loading API information...
-
-
-
-
-
- Test Options
-
-
-
-
-
-
-
-
-
-
- Upload Image
-
-
-
-
Drag and drop an image here or click to select
-
Supported formats: PNG, JPG, JPEG, GIF, BMP
-
-
-
-
-
-
-
-
Confidence Threshold: 80% (High Precision Mode)
-
-
-
-
-
-
-
- Detection Results
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
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