diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..4391c8e --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,573 @@ +# Microscopy Object Detection Application - Architecture Design + +## Project Overview + +A desktop application for detecting organelles and membrane branching structures in microscopy images using YOLOv8s, with comprehensive training, validation, and visualization capabilities. + +## Technology Stack + +- **ML Framework**: Ultralytics YOLOv8 (YOLOv8s.pt model) +- **GUI Framework**: PySide6 (Qt6 for Python) +- **Visualization**: pyqtgraph +- **Database**: SQLite3 +- **Python Version**: 3.8+ + +## Project Structure + +``` +object_detection/ +├── main.py # Application entry point +├── requirements.txt # Python dependencies +├── config/ +│ └── app_config.yaml # Application configuration +├── src/ +│ ├── __init__.py +│ ├── database/ +│ │ ├── __init__.py +│ │ ├── db_manager.py # Database operations manager +│ │ ├── models.py # SQLAlchemy ORM models +│ │ └── schema.sql # Database schema definition +│ ├── model/ +│ │ ├── __init__.py +│ │ ├── yolo_wrapper.py # YOLO model wrapper +│ │ └── inference.py # Detection inference logic +│ ├── gui/ +│ │ ├── __init__.py +│ │ ├── main_window.py # Main application window +│ │ ├── tabs/ +│ │ │ ├── __init__.py +│ │ │ ├── training_tab.py # Model training interface +│ │ │ ├── validation_tab.py # Validation metrics viewer +│ │ │ ├── detection_tab.py # Real-time/batch detection +│ │ │ ├── results_tab.py # Results viewer and management +│ │ │ └── annotation_tab.py # Annotation tool (future) +│ │ ├── dialogs/ +│ │ │ ├── __init__.py +│ │ │ ├── config_dialog.py # Settings and configuration +│ │ │ └── model_dialog.py # Model management dialog +│ │ └── widgets/ +│ │ ├── __init__.py +│ │ ├── image_viewer.py # Custom image display widget +│ │ └── plot_widget.py # Custom plotting widgets +│ └── utils/ +│ ├── __init__.py +│ ├── file_utils.py # File operations +│ ├── logger.py # Logging configuration +│ └── config_manager.py # Configuration management +├── tests/ +│ ├── __init__.py +│ ├── test_database.py +│ ├── test_model.py +│ └── test_gui.py +├── data/ # Default data directory +│ ├── models/ # Saved models +│ ├── datasets/ # Training datasets +│ └── results/ # Detection results +└── docs/ + ├── USER_GUIDE.md + └── API_REFERENCE.md +``` + +## Database Schema + +### Tables Overview + +```mermaid +erDiagram + MODELS ||--o{ DETECTIONS : produces + IMAGES ||--o{ DETECTIONS : contains + IMAGES ||--o{ ANNOTATIONS : has + + MODELS { + integer id PK + string model_name + string model_version + string model_path + string base_model + datetime created_at + json training_params + json metrics + } + + IMAGES { + integer id PK + string relative_path + string filename + integer width + integer height + datetime captured_at + datetime added_at + string checksum + } + + DETECTIONS { + integer id PK + integer image_id FK + integer model_id FK + string class_name + float x_min + float y_min + float x_max + float y_max + float confidence + datetime detected_at + json metadata + } + + ANNOTATIONS { + integer id PK + integer image_id FK + string class_name + float x_min + float y_min + float x_max + float y_max + string annotator + datetime created_at + boolean verified + } +``` + +### Detailed Schema + +#### **models** table +Stores information about trained models and their versions. + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| id | INTEGER | PRIMARY KEY | Unique model identifier | +| model_name | TEXT | NOT NULL | User-friendly model name | +| model_version | TEXT | NOT NULL | Version string (e.g., "v1.0") | +| model_path | TEXT | NOT NULL | Path to model weights file | +| base_model | TEXT | NOT NULL | Base model used (e.g., "yolov8s.pt") | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Model creation timestamp | +| training_params | JSON | | Training hyperparameters | +| metrics | JSON | | Validation metrics (mAP, precision, recall) | + +#### **images** table +Stores metadata about microscopy images. + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| id | INTEGER | PRIMARY KEY | Unique image identifier | +| relative_path | TEXT | NOT NULL, UNIQUE | Relative path from repository root | +| filename | TEXT | NOT NULL | Image filename | +| width | INTEGER | | Image width in pixels | +| height | INTEGER | | Image height in pixels | +| captured_at | TIMESTAMP | | Original capture timestamp (if available) | +| added_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | When added to database | +| checksum | TEXT | | MD5 hash for integrity verification | + +#### **detections** table +Stores object detection results. + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| id | INTEGER | PRIMARY KEY | Unique detection identifier | +| image_id | INTEGER | FOREIGN KEY | Reference to images table | +| model_id | INTEGER | FOREIGN KEY | Reference to models table | +| class_name | TEXT | NOT NULL | Object class (e.g., "organelle", "membrane_branch") | +| x_min | REAL | NOT NULL | Bounding box left coordinate (normalized 0-1) | +| y_min | REAL | NOT NULL | Bounding box top coordinate (normalized 0-1) | +| x_max | REAL | NOT NULL | Bounding box right coordinate (normalized 0-1) | +| y_max | REAL | NOT NULL | Bounding box bottom coordinate (normalized 0-1) | +| confidence | REAL | NOT NULL | Detection confidence score (0-1) | +| detected_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | When detection was performed | +| metadata | JSON | | Additional metadata (processing time, etc.) | + +#### **annotations** table +Stores manual annotations for training data (future feature). + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| id | INTEGER | PRIMARY KEY | Unique annotation identifier | +| image_id | INTEGER | FOREIGN KEY | Reference to images table | +| class_name | TEXT | NOT NULL | Annotated object class | +| x_min | REAL | NOT NULL | Bounding box left coordinate (normalized) | +| y_min | REAL | NOT NULL | Bounding box top coordinate (normalized) | +| x_max | REAL | NOT NULL | Bounding box right coordinate (normalized) | +| y_max | REAL | NOT NULL | Bounding box bottom coordinate (normalized) | +| annotator | TEXT | | Name of person who created annotation | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Annotation timestamp | +| verified | BOOLEAN | DEFAULT 0 | Whether annotation is verified | + +## Application Architecture + +### Component Diagram + +```mermaid +graph TB + subgraph GUI Layer + MW[Main Window] + TT[Training Tab] + VT[Validation Tab] + DT[Detection Tab] + RT[Results Tab] + AT[Annotation Tab] + end + + subgraph Business Logic + YW[YOLO Wrapper] + INF[Inference Engine] + DBM[Database Manager] + CM[Config Manager] + end + + subgraph Data Layer + DB[(SQLite Database)] + FS[File System] + YOLO[YOLOv8 Model] + end + + MW --> TT + MW --> VT + MW --> DT + MW --> RT + MW --> AT + + TT --> YW + VT --> YW + DT --> INF + RT --> DBM + AT --> DBM + + YW --> YOLO + YW --> DBM + INF --> YOLO + INF --> DBM + DBM --> DB + CM --> FS + + YW -.reads.-> FS + INF -.reads.-> FS +``` + +### Key Components + +#### 1. **YOLO Wrapper** ([`src/model/yolo_wrapper.py`](src/model/yolo_wrapper.py)) +Encapsulates YOLOv8 operations: +- Load pre-trained YOLOv8s model +- Fine-tune on custom microscopy dataset +- Export trained models +- Provide training progress callbacks +- Calculate validation metrics + +**Key Methods:** +```python +class YOLOWrapper: + def __init__(self, model_path: str = "yolov8s.pt") + def train(self, data_yaml: str, epochs: int, callbacks: dict) + def validate(self, data_yaml: str) -> dict + def predict(self, image_path: str, conf: float) -> list + def export_model(self, format: str, output_path: str) +``` + +#### 2. **Database Manager** ([`src/database/db_manager.py`](src/database/db_manager.py)) +Handles all database operations: +- Connection management +- CRUD operations for all tables +- Query builders for filtering and searching +- Transaction management +- Database migrations + +**Key Methods:** +```python +class DatabaseManager: + def __init__(self, db_path: str) + def add_detection(self, detection_data: dict) -> int + def add_image(self, image_data: dict) -> int + def add_model(self, model_data: dict) -> int + def get_detections(self, filters: dict) -> list + def export_detections(self, format: str, output_path: str) +``` + +#### 3. **Inference Engine** ([`src/model/inference.py`](src/model/inference.py)) +Manages detection operations: +- Real-time single image detection +- Batch processing with progress tracking +- Result formatting and storage +- Confidence thresholding + +**Key Methods:** +```python +class InferenceEngine: + def __init__(self, model_path: str, db_manager: DatabaseManager) + def detect_single(self, image_path: str, conf: float) -> dict + def detect_batch(self, image_paths: list, conf: float, callback: callable) + def save_results(self, results: list, model_id: int) +``` + +#### 4. **GUI Components** + +##### **Main Window** ([`src/gui/main_window.py`](src/gui/main_window.py)) +- Tab container for different functionalities +- Menu bar with File, Tools, Help +- Status bar with progress indicators +- Global settings and repository configuration + +##### **Training Tab** ([`src/gui/tabs/training_tab.py`](src/gui/tabs/training_tab.py)) +Features: +- Dataset path selection (YOLO format) +- Training parameters configuration (epochs, batch size, learning rate) +- Start/stop training controls +- Real-time training progress with pyqtgraph plots: + - Loss curves (training and validation) + - mAP progression +- Training logs display + +##### **Validation Tab** ([`src/gui/tabs/validation_tab.py`](src/gui/tabs/validation_tab.py)) +Features: +- Model selection for validation +- Validation dataset selection +- Metrics visualization: + - Confusion matrix + - Precision-Recall curves + - mAP by class +- Results table with per-class metrics + +##### **Detection Tab** ([`src/gui/tabs/detection_tab.py`](src/gui/tabs/detection_tab.py)) +Features: +- Model selection dropdown +- Detection modes: + - Single image detection + - Batch folder processing + - Real-time camera feed (future) +- Confidence threshold slider +- Image viewer with bounding box overlay +- Detection results panel +- Save to database button + +##### **Results Tab** ([`src/gui/tabs/results_tab.py`](src/gui/tabs/results_tab.py)) +Features: +- Searchable detection history table +- Filters by date, model, class, confidence +- Image preview with detections overlay +- Statistics dashboard with pyqtgraph: + - Detections per class histogram + - Confidence distribution + - Detection timeline +- Export options (CSV, JSON, Excel) + +##### **Annotation Tab** ([`src/gui/tabs/annotation_tab.py`](src/gui/tabs/annotation_tab.py)) +Future feature for manual annotation: +- Image browser and viewer +- Drawing tools for bounding boxes +- Class label assignment +- Annotation export to YOLO format +- Annotation statistics + +## Workflow Diagrams + +### Training Workflow + +```mermaid +sequenceDiagram + participant User + participant TrainingTab + participant YOLOWrapper + participant DBManager + participant Database + + User->>TrainingTab: Configure training parameters + User->>TrainingTab: Select dataset (YOLO format) + User->>TrainingTab: Click "Start Training" + TrainingTab->>YOLOWrapper: train(data_yaml, epochs, callbacks) + + loop Every epoch + YOLOWrapper-->>TrainingTab: Progress callback (loss, metrics) + TrainingTab->>TrainingTab: Update plots + end + + YOLOWrapper-->>TrainingTab: Training complete (model_path) + TrainingTab->>DBManager: add_model(model_info) + DBManager->>Database: INSERT INTO models + Database-->>DBManager: model_id + TrainingTab->>User: Show completion message +``` + +### Detection Workflow + +```mermaid +sequenceDiagram + participant User + participant DetectionTab + participant InferenceEngine + participant YOLOWrapper + participant DBManager + participant Database + + User->>DetectionTab: Select model + User->>DetectionTab: Choose image/folder + User->>DetectionTab: Set confidence threshold + User->>DetectionTab: Click "Detect" + + DetectionTab->>InferenceEngine: detect_batch(images, conf) + + loop For each image + InferenceEngine->>YOLOWrapper: predict(image_path, conf) + YOLOWrapper-->>InferenceEngine: detections list + InferenceEngine->>DBManager: add_image(image_info) + DBManager->>Database: INSERT INTO images + Database-->>DBManager: image_id + + loop For each detection + InferenceEngine->>DBManager: add_detection(detection_info) + DBManager->>Database: INSERT INTO detections + end + + InferenceEngine-->>DetectionTab: Progress update + end + + InferenceEngine-->>DetectionTab: All detections complete + DetectionTab->>User: Display results +``` + +## Configuration Management + +### Application Configuration ([`config/app_config.yaml`](config/app_config.yaml)) + +```yaml +database: + path: "data/detections.db" + +image_repository: + base_path: "" # Set by user + allowed_extensions: [".jpg", ".jpeg", ".png", ".tif", ".tiff"] + +models: + default_base_model: "yolov8s.pt" + models_directory: "data/models" + +training: + default_epochs: 100 + default_batch_size: 16 + default_imgsz: 640 + default_patience: 50 + +detection: + default_confidence: 0.25 + max_batch_size: 100 + +visualization: + colors: + organelle: "#FF6B6B" + membrane_branch: "#4ECDC4" + bbox_thickness: 2 + +export: + formats: ["csv", "json", "excel"] + default_format: "csv" + +logging: + level: "INFO" + file: "logs/app.log" +``` + +## Data Flow + +### Image Repository Structure +``` +image_repository/ +├── train/ +│ ├── images/ +│ │ ├── img001.png +│ │ └── ... +│ └── labels/ +│ ├── img001.txt (YOLO format) +│ └── ... +├── val/ +│ ├── images/ +│ └── labels/ +└── test/ + └── images/ +``` + +### YOLO Format Labels +Each label file contains lines with format: +``` + +``` +All coordinates normalized to 0-1 range. + +## Error Handling Strategy + +1. **Database Errors**: Graceful degradation, show error dialog, log to file +2. **Model Loading Errors**: Validate model before loading, clear error messages +3. **File Access Errors**: Check permissions, validate paths, inform user +4. **Training Errors**: Catch ultralytics exceptions, save partial progress +5. **Memory Errors**: Implement batch processing limits, monitor memory usage + +## Performance Considerations + +1. **Database**: + - Indexed columns: image_id, model_id, class_name, detected_at + - Batch inserts for multiple detections + - Connection pooling + +2. **Image Processing**: + - Lazy loading for large datasets + - Thumbnail generation for previews + - Async operations for batch processing + +3. **GUI**: + - Worker threads for long operations (training, batch detection) + - Progress signals for UI updates + - Image caching for faster display + +## Testing Strategy + +1. **Unit Tests**: + - Database operations + - YOLO wrapper methods + - Utility functions + +2. **Integration Tests**: + - End-to-end training workflow + - Detection and storage pipeline + - Export functionality + +3. **GUI Tests**: + - User interaction flows + - Signal/slot connections + - Widget state management + +## Future Enhancements + +1. **Phase 1** (Core Features): + - Basic training, validation, detection + - Results storage and visualization + - Model management + +2. **Phase 2** (Enhanced Features): + - Annotation tool implementation + - Advanced filtering and search + - Model comparison tools + - Batch exports + +3. **Phase 3** (Advanced Features): + - Real-time camera detection + - Multi-model ensemble + - Cloud storage integration + - Collaborative annotation + +## Dependencies + +### Python Packages +``` +ultralytics>=8.0.0 +PySide6>=6.5.0 +pyqtgraph>=0.13.0 +numpy>=1.24.0 +opencv-python>=4.8.0 +Pillow>=10.0.0 +pyyaml>=6.0 +sqlalchemy>=2.0.0 +pandas>=2.0.0 +openpyxl>=3.1.0 +``` + +### System Requirements +- Python 3.8+ +- CUDA-capable GPU (recommended for training) +- 8GB RAM minimum +- 2GB disk space for models and data \ No newline at end of file diff --git a/IMPLEMENTATION_GUIDE.md b/IMPLEMENTATION_GUIDE.md new file mode 100644 index 0000000..6c2cc18 --- /dev/null +++ b/IMPLEMENTATION_GUIDE.md @@ -0,0 +1,1223 @@ +# Implementation Guide - Microscopy Object Detection Application + +This guide provides detailed implementation specifications for each component of the application. + +## Table of Contents +1. [Development Setup](#development-setup) +2. [Database Implementation](#database-implementation) +3. [Model Wrapper Implementation](#model-wrapper-implementation) +4. [GUI Components](#gui-components) +5. [Testing Strategy](#testing-strategy) +6. [Deployment](#deployment) + +--- + +## Development Setup + +### Prerequisites +```bash +# Python 3.8 or higher +python3 --version + +# pip package manager +pip3 --version + +# Git for version control +git --version +``` + +### Project Initialization + +**Step 1: Create virtual environment** +```bash +cd /home/martin/code/object_detection +python3 -m venv venv +source venv/bin/activate # On Linux/Mac +# or +venv\Scripts\activate # On Windows +``` + +**Step 2: Create requirements.txt** +``` +# Core ML and Detection +ultralytics>=8.0.0 +torch>=2.0.0 +torchvision>=0.15.0 + +# GUI Framework +PySide6>=6.5.0 +pyqtgraph>=0.13.0 + +# Image Processing +opencv-python>=4.8.0 +Pillow>=10.0.0 +numpy>=1.24.0 + +# Database +sqlalchemy>=2.0.0 + +# Data Export +pandas>=2.0.0 +openpyxl>=3.1.0 + +# Configuration +pyyaml>=6.0 + +# Testing +pytest>=7.4.0 +pytest-qt>=4.2.0 +pytest-cov>=4.1.0 + +# Development +black>=23.0.0 +pylint>=2.17.0 +mypy>=1.4.0 +``` + +**Step 3: Install dependencies** +```bash +pip install -r requirements.txt +``` + +### Directory Structure Creation + +Create the complete directory structure: +```bash +mkdir -p src/{database,model,gui/{tabs,dialogs,widgets},utils} +mkdir -p config data/{models,datasets,results} tests docs logs +touch src/__init__.py +touch src/database/__init__.py +touch src/model/__init__.py +touch src/gui/__init__.py +touch src/gui/tabs/__init__.py +touch src/gui/dialogs/__init__.py +touch src/gui/widgets/__init__.py +touch src/utils/__init__.py +touch tests/__init__.py +``` + +--- + +## Database Implementation + +### 1. Database Schema (`src/database/schema.sql`) + +```sql +-- Models table: stores trained model information +CREATE TABLE IF NOT EXISTS models ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + model_name TEXT NOT NULL, + model_version TEXT NOT NULL, + model_path TEXT NOT NULL, + base_model TEXT NOT NULL DEFAULT 'yolov8s.pt', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + training_params TEXT, -- JSON string + metrics TEXT, -- JSON string + UNIQUE(model_name, model_version) +); + +-- Images table: stores image metadata +CREATE TABLE IF NOT EXISTS images ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + relative_path TEXT NOT NULL UNIQUE, + filename TEXT NOT NULL, + width INTEGER, + height INTEGER, + captured_at TIMESTAMP, + added_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + checksum TEXT +); + +-- Detections table: stores detection results +CREATE TABLE IF NOT EXISTS detections ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + image_id INTEGER NOT NULL, + model_id INTEGER NOT NULL, + class_name TEXT NOT NULL, + x_min REAL NOT NULL CHECK(x_min >= 0 AND x_min <= 1), + y_min REAL NOT NULL CHECK(y_min >= 0 AND y_min <= 1), + x_max REAL NOT NULL CHECK(x_max >= 0 AND x_max <= 1), + y_max REAL NOT NULL CHECK(y_max >= 0 AND y_max <= 1), + confidence REAL NOT NULL CHECK(confidence >= 0 AND confidence <= 1), + detected_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + metadata TEXT, -- JSON string + FOREIGN KEY (image_id) REFERENCES images (id) ON DELETE CASCADE, + FOREIGN KEY (model_id) REFERENCES models (id) ON DELETE CASCADE +); + +-- Annotations table: stores manual annotations (future feature) +CREATE TABLE IF NOT EXISTS annotations ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + image_id INTEGER NOT NULL, + class_name TEXT NOT NULL, + x_min REAL NOT NULL CHECK(x_min >= 0 AND x_min <= 1), + y_min REAL NOT NULL CHECK(y_min >= 0 AND y_min <= 1), + x_max REAL NOT NULL CHECK(x_max >= 0 AND x_max <= 1), + y_max REAL NOT NULL CHECK(y_max >= 0 AND y_max <= 1), + annotator TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + verified BOOLEAN DEFAULT 0, + FOREIGN KEY (image_id) REFERENCES images (id) ON DELETE CASCADE +); + +-- Create indexes for performance +CREATE INDEX IF NOT EXISTS idx_detections_image_id ON detections(image_id); +CREATE INDEX IF NOT EXISTS idx_detections_model_id ON detections(model_id); +CREATE INDEX IF NOT EXISTS idx_detections_class_name ON detections(class_name); +CREATE INDEX IF NOT EXISTS idx_detections_detected_at ON detections(detected_at); +CREATE INDEX IF NOT EXISTS idx_images_relative_path ON images(relative_path); +CREATE INDEX IF NOT EXISTS idx_annotations_image_id ON annotations(image_id); +``` + +### 2. Database Manager (`src/database/db_manager.py`) + +**Key Components:** + +```python +import sqlite3 +import json +from datetime import datetime +from typing import List, Dict, Optional, Tuple +from pathlib import Path +import hashlib + + +class DatabaseManager: + """Manages all database operations for the application.""" + + def __init__(self, db_path: str = "data/detections.db"): + """ + Initialize database manager. + + Args: + db_path: Path to SQLite database file + """ + self.db_path = db_path + self._ensure_database_exists() + + def _ensure_database_exists(self) -> None: + """Create database and tables if they don't exist.""" + # Implementation: Read schema.sql and execute + pass + + def get_connection(self) -> sqlite3.Connection: + """Get database connection with proper settings.""" + conn = sqlite3.Connection(self.db_path) + conn.row_factory = sqlite3.Row # Enable column access by name + conn.execute("PRAGMA foreign_keys = ON") # Enable foreign keys + return conn + + # ==================== Model Operations ==================== + + def add_model( + self, + model_name: str, + model_version: str, + model_path: str, + base_model: str = "yolov8s.pt", + training_params: Optional[Dict] = None, + metrics: Optional[Dict] = None + ) -> int: + """ + Add a new model to the database. + + Args: + model_name: Name of the model + model_version: Version string + model_path: Path to model weights file + base_model: Base model used for training + training_params: Dictionary of training parameters + metrics: Dictionary of validation metrics + + Returns: + ID of the inserted model + """ + pass + + def get_models(self, filters: Optional[Dict] = None) -> List[Dict]: + """ + Retrieve models from database. + + Args: + filters: Optional filters (e.g., {'model_name': 'my_model'}) + + Returns: + List of model dictionaries + """ + pass + + def get_model_by_id(self, model_id: int) -> Optional[Dict]: + """Get model by ID.""" + pass + + # ==================== Image Operations ==================== + + def add_image( + self, + relative_path: str, + filename: str, + width: int, + height: int, + captured_at: Optional[datetime] = None + ) -> int: + """ + Add a new image to the database. + + Args: + relative_path: Path relative to image repository + filename: Image filename + width: Image width in pixels + height: Image height in pixels + captured_at: When image was captured (if known) + + Returns: + ID of the inserted image + """ + pass + + def get_image_by_path(self, relative_path: str) -> Optional[Dict]: + """Get image by relative path.""" + pass + + def get_or_create_image( + self, + relative_path: str, + filename: str, + width: int, + height: int + ) -> int: + """Get existing image or create new one.""" + pass + + # ==================== Detection Operations ==================== + + def add_detection( + self, + image_id: int, + model_id: int, + class_name: str, + bbox: Tuple[float, float, float, float], # (x_min, y_min, x_max, y_max) + confidence: float, + metadata: Optional[Dict] = None + ) -> int: + """ + Add a new detection to the database. + + Args: + image_id: ID of the image + model_id: ID of the model used + class_name: Detected object class + bbox: Bounding box coordinates (normalized 0-1) + confidence: Detection confidence score + metadata: Additional metadata + + Returns: + ID of the inserted detection + """ + pass + + def add_detections_batch(self, detections: List[Dict]) -> int: + """ + Add multiple detections in a single transaction. + + Args: + detections: List of detection dictionaries + + Returns: + Number of detections inserted + """ + pass + + def get_detections( + self, + filters: Optional[Dict] = None, + limit: Optional[int] = None, + offset: int = 0 + ) -> List[Dict]: + """ + Retrieve detections from database. + + Args: + filters: Optional filters for querying + limit: Maximum number of results + offset: Number of results to skip + + Returns: + List of detection dictionaries with joined data + """ + pass + + def get_detections_for_image( + self, + image_id: int, + model_id: Optional[int] = None + ) -> List[Dict]: + """Get all detections for a specific image.""" + pass + + def delete_detections_for_model(self, model_id: int) -> int: + """Delete all detections for a specific model.""" + pass + + # ==================== Statistics Operations ==================== + + def get_detection_statistics( + self, + start_date: Optional[datetime] = None, + end_date: Optional[datetime] = None + ) -> Dict: + """ + Get detection statistics for a date range. + + Returns: + Dictionary with statistics (count by class, confidence distribution, etc.) + """ + pass + + def get_class_distribution(self, model_id: Optional[int] = None) -> Dict[str, int]: + """Get count of detections per class.""" + pass + + # ==================== Export Operations ==================== + + def export_detections_to_csv( + self, + output_path: str, + filters: Optional[Dict] = None + ) -> bool: + """Export detections to CSV file.""" + pass + + def export_detections_to_json( + self, + output_path: str, + filters: Optional[Dict] = None + ) -> bool: + """Export detections to JSON file.""" + pass + + # ==================== Annotation Operations ==================== + + def add_annotation( + self, + image_id: int, + class_name: str, + bbox: Tuple[float, float, float, float], + annotator: str, + verified: bool = False + ) -> int: + """Add manual annotation.""" + pass + + def get_annotations_for_image(self, image_id: int) -> List[Dict]: + """Get all annotations for an image.""" + pass +``` + +### 3. Data Models (`src/database/models.py`) + +```python +from dataclasses import dataclass +from datetime import datetime +from typing import Optional, Dict, Tuple + + +@dataclass +class Model: + """Represents a trained model.""" + id: Optional[int] + model_name: str + model_version: str + model_path: str + base_model: str + created_at: datetime + training_params: Optional[Dict] + metrics: Optional[Dict] + + +@dataclass +class Image: + """Represents an image in the database.""" + id: Optional[int] + relative_path: str + filename: str + width: int + height: int + captured_at: Optional[datetime] + added_at: datetime + checksum: Optional[str] + + +@dataclass +class Detection: + """Represents a detection result.""" + id: Optional[int] + image_id: int + model_id: int + class_name: str + bbox: Tuple[float, float, float, float] # (x_min, y_min, x_max, y_max) + confidence: float + detected_at: datetime + metadata: Optional[Dict] + + +@dataclass +class Annotation: + """Represents a manual annotation.""" + id: Optional[int] + image_id: int + class_name: str + bbox: Tuple[float, float, float, float] + annotator: str + created_at: datetime + verified: bool +``` + +--- + +## Model Wrapper Implementation + +### YOLO Wrapper (`src/model/yolo_wrapper.py`) + +```python +from ultralytics import YOLO +from pathlib import Path +from typing import Optional, List, Dict, Callable +import torch + + +class YOLOWrapper: + """Wrapper for YOLOv8 model operations.""" + + def __init__(self, model_path: str = "yolov8s.pt"): + """ + Initialize YOLO model. + + Args: + model_path: Path to model weights (.pt file) + """ + self.model_path = model_path + self.model = None + self.device = "cuda" if torch.cuda.is_available() else "cpu" + + def load_model(self) -> None: + """Load YOLO model from path.""" + self.model = YOLO(self.model_path) + self.model.to(self.device) + + def train( + self, + data_yaml: str, + epochs: int = 100, + imgsz: int = 640, + batch: int = 16, + patience: int = 50, + save_dir: str = "data/models", + name: str = "custom_model", + callbacks: Optional[Dict[str, Callable]] = None, + **kwargs + ) -> Dict: + """ + Train the YOLO model. + + Args: + data_yaml: Path to data.yaml configuration file + epochs: Number of training epochs + imgsz: Input image size + batch: Batch size + patience: Early stopping patience + save_dir: Directory to save trained model + name: Name for the training run + callbacks: Dictionary of callback functions + **kwargs: Additional training arguments + + Returns: + Dictionary with training results + """ + if self.model is None: + self.load_model() + + # Train the model + results = self.model.train( + data=data_yaml, + epochs=epochs, + imgsz=imgsz, + batch=batch, + patience=patience, + project=save_dir, + name=name, + device=self.device, + **kwargs + ) + + return self._format_training_results(results) + + def validate(self, data_yaml: str, **kwargs) -> Dict: + """ + Validate the model. + + Args: + data_yaml: Path to data.yaml configuration file + **kwargs: Additional validation arguments + + Returns: + Dictionary with validation metrics + """ + if self.model is None: + self.load_model() + + results = self.model.val(data=data_yaml, **kwargs) + return self._format_validation_results(results) + + def predict( + self, + source: str, + conf: float = 0.25, + iou: float = 0.45, + save: bool = False, + **kwargs + ) -> List[Dict]: + """ + Perform inference on image(s). + + Args: + source: Path to image or directory + conf: Confidence threshold + iou: IoU threshold for NMS + save: Whether to save annotated images + **kwargs: Additional prediction arguments + + Returns: + List of detection dictionaries + """ + if self.model is None: + self.load_model() + + results = self.model.predict( + source=source, + conf=conf, + iou=iou, + save=save, + device=self.device, + **kwargs + ) + + return self._format_prediction_results(results) + + def export( + self, + format: str = "onnx", + output_path: Optional[str] = None + ) -> str: + """ + Export model to different format. + + Args: + format: Export format (onnx, torchscript, etc.) + output_path: Path for exported model + + Returns: + Path to exported model + """ + if self.model is None: + self.load_model() + + export_path = self.model.export(format=format) + return str(export_path) + + def _format_training_results(self, results) -> Dict: + """Format training results into dictionary.""" + return { + 'final_epoch': results.epoch, + 'metrics': { + 'mAP50': float(results.results_dict.get('metrics/mAP50(B)', 0)), + 'mAP50-95': float(results.results_dict.get('metrics/mAP50-95(B)', 0)), + 'precision': float(results.results_dict.get('metrics/precision(B)', 0)), + 'recall': float(results.results_dict.get('metrics/recall(B)', 0)), + }, + 'best_model_path': str(results.save_dir / 'weights' / 'best.pt') + } + + def _format_validation_results(self, results) -> Dict: + """Format validation results into dictionary.""" + return { + 'mAP50': float(results.box.map50), + 'mAP50-95': float(results.box.map), + 'precision': float(results.box.mp), + 'recall': float(results.box.mr), + 'class_metrics': { + name: { + 'ap': float(ap), + 'precision': float(p), + 'recall': float(r) + } + for name, ap, p, r in zip( + results.names.values(), + results.box.ap, + results.box.p, + results.box.r + ) + } + } + + def _format_prediction_results(self, results) -> List[Dict]: + """Format prediction results into list of dictionaries.""" + detections = [] + + for result in results: + boxes = result.boxes + + for i in range(len(boxes)): + detection = { + 'image_path': str(result.path), + 'class_id': int(boxes.cls[i]), + 'class_name': result.names[int(boxes.cls[i])], + 'confidence': float(boxes.conf[i]), + 'bbox': boxes.xywhn[i].tolist(), # Normalized [x_center, y_center, width, height] + 'bbox_xyxy': boxes.xyxy[i].tolist(), # Absolute [x1, y1, x2, y2] + } + detections.append(detection) + + return detections + + @staticmethod + def convert_bbox_format( + bbox: List[float], + format_from: str = "xywh", + format_to: str = "xyxy" + ) -> List[float]: + """ + Convert bounding box between formats. + + Formats: + - xywh: [x_center, y_center, width, height] + - xyxy: [x_min, y_min, x_max, y_max] + """ + if format_from == "xywh" and format_to == "xyxy": + x, y, w, h = bbox + return [x - w/2, y - h/2, x + w/2, y + h/2] + elif format_from == "xyxy" and format_to == "xywh": + x1, y1, x2, y2 = bbox + return [(x1 + x2)/2, (y1 + y2)/2, x2 - x1, y2 - y1] + else: + return bbox +``` + +### Inference Engine (`src/model/inference.py`) + +```python +from typing import List, Dict, Optional, Callable +from pathlib import Path +import cv2 +from PIL import Image + +from .yolo_wrapper import YOLOWrapper +from ..database.db_manager import DatabaseManager + + +class InferenceEngine: + """Handles detection inference and result storage.""" + + def __init__( + self, + model_path: str, + db_manager: DatabaseManager, + model_id: int + ): + """ + Initialize inference engine. + + Args: + model_path: Path to YOLO model weights + db_manager: Database manager instance + model_id: ID of the model in database + """ + self.yolo = YOLOWrapper(model_path) + self.yolo.load_model() + self.db_manager = db_manager + self.model_id = model_id + + def detect_single( + self, + image_path: str, + relative_path: str, + conf: float = 0.25, + save_to_db: bool = True + ) -> Dict: + """ + Detect objects in a single image. + + Args: + image_path: Absolute path to image file + relative_path: Relative path from repository root + conf: Confidence threshold + save_to_db: Whether to save results to database + + Returns: + Dictionary with detection results + """ + # Get image dimensions + img = Image.open(image_path) + width, height = img.size + + # Perform detection + detections = self.yolo.predict(image_path, conf=conf) + + # Add/get image in database + image_id = self.db_manager.get_or_create_image( + relative_path=relative_path, + filename=Path(image_path).name, + width=width, + height=height + ) + + # Save detections to database + if save_to_db and detections: + detection_records = [] + for det in detections: + # Convert bbox to xyxy normalized format + bbox_xyxy = YOLOWrapper.convert_bbox_format( + det['bbox'], 'xywh', 'xyxy' + ) + + record = { + 'image_id': image_id, + 'model_id': self.model_id, + 'class_name': det['class_name'], + 'bbox': tuple(bbox_xyxy), + 'confidence': det['confidence'], + 'metadata': {'class_id': det['class_id']} + } + detection_records.append(record) + + self.db_manager.add_detections_batch(detection_records) + + return { + 'image_path': image_path, + 'image_id': image_id, + 'detections': detections, + 'count': len(detections) + } + + def detect_batch( + self, + image_paths: List[str], + repository_root: str, + conf: float = 0.25, + progress_callback: Optional[Callable[[int, int, str], None]] = None + ) -> List[Dict]: + """ + Detect objects in multiple images. + + Args: + image_paths: List of absolute image paths + repository_root: Root directory for relative paths + conf: Confidence threshold + progress_callback: Optional callback(current, total, message) + + Returns: + List of detection result dictionaries + """ + results = [] + total = len(image_paths) + + for i, image_path in enumerate(image_paths, 1): + # Calculate relative path + rel_path = str(Path(image_path).relative_to(repository_root)) + + # Perform detection + result = self.detect_single(image_path, rel_path, conf) + results.append(result) + + # Update progress + if progress_callback: + progress_callback(i, total, f"Processed {rel_path}") + + return results + + def detect_with_visualization( + self, + image_path: str, + conf: float = 0.25 + ) -> tuple: + """ + Detect objects and return annotated image. + + Returns: + Tuple of (detections, annotated_image_array) + """ + detections = self.yolo.predict(image_path, conf=conf, save=False) + + # Load image + img = cv2.imread(image_path) + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + + # Draw bounding boxes + for det in detections: + x1, y1, x2, y2 = [int(v) for v in det['bbox_xyxy']] + label = f"{det['class_name']} {det['confidence']:.2f}" + + # Draw box + cv2.rectangle(img, (x1, y1), (x2, y2), (255, 0, 0), 2) + + # Draw label background + (label_w, label_h), _ = cv2.getTextSize( + label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1 + ) + cv2.rectangle( + img, (x1, y1 - label_h - 5), (x1 + label_w, y1), (255, 0, 0), -1 + ) + + # Draw label text + cv2.putText( + img, label, (x1, y1 - 5), + cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1 + ) + + return detections, img +``` + +--- + +## GUI Components + +### Main Application (`main.py`) + +```python +import sys +from PySide6.QtWidgets import QApplication +from src.gui.main_window import MainWindow +from src.utils.logger import setup_logging + + +def main(): + """Application entry point.""" + # Setup logging + setup_logging() + + # Create Qt application + app = QApplication(sys.argv) + app.setApplicationName("Microscopy Object Detection") + app.setOrganizationName("YourOrganization") + + # Create and show main window + window = MainWindow() + window.show() + + # Run application + sys.exit(app.exec()) + + +if __name__ == "__main__": + main() +``` + +### Main Window (`src/gui/main_window.py`) + +```python +from PySide6.QtWidgets import ( + QMainWindow, QTabWidget, QMenuBar, QMenu, + QStatusBar, QMessageBox +) +from PySide6.QtCore import Qt +from PySide6.QtGui import QAction + +from .tabs.training_tab import TrainingTab +from .tabs.validation_tab import ValidationTab +from .tabs.detection_tab import DetectionTab +from .tabs.results_tab import ResultsTab +from .tabs.annotation_tab import AnnotationTab +from .dialogs.config_dialog import ConfigDialog +from ..database.db_manager import DatabaseManager +from ..utils.config_manager import ConfigManager + + +class MainWindow(QMainWindow): + """Main application window.""" + + def __init__(self): + super().__init__() + + # Initialize managers + self.config_manager = ConfigManager() + self.db_manager = DatabaseManager( + self.config_manager.get('database.path', 'data/detections.db') + ) + + # Setup UI + self.setWindowTitle("Microscopy Object Detection") + self.setMinimumSize(1200, 800) + + self._create_menu_bar() + self._create_tab_widget() + self._create_status_bar() + + def _create_menu_bar(self): + """Create application menu bar.""" + menubar = self.menuBar() + + # File menu + file_menu = menubar.addMenu("&File") + + settings_action = QAction("&Settings", self) + settings_action.triggered.connect(self._show_settings) + file_menu.addAction(settings_action) + + file_menu.addSeparator() + + exit_action = QAction("E&xit", self) + exit_action.setShortcut("Ctrl+Q") + exit_action.triggered.connect(self.close) + file_menu.addAction(exit_action) + + # Tools menu + tools_menu = menubar.addMenu("&Tools") + + # Help menu + help_menu = menubar.addMenu("&Help") + + about_action = QAction("&About", self) + about_action.triggered.connect(self._show_about) + help_menu.addAction(about_action) + + def _create_tab_widget(self): + """Create main tab widget with all tabs.""" + self.tab_widget = QTabWidget() + + # Create tabs + self.training_tab = TrainingTab(self.db_manager, self.config_manager) + self.validation_tab = ValidationTab(self.db_manager, self.config_manager) + self.detection_tab = DetectionTab(self.db_manager, self.config_manager) + self.results_tab = ResultsTab(self.db_manager, self.config_manager) + self.annotation_tab = AnnotationTab(self.db_manager, self.config_manager) + + # Add tabs + self.tab_widget.addTab(self.detection_tab, "Detection") + self.tab_widget.addTab(self.training_tab, "Training") + self.tab_widget.addTab(self.validation_tab, "Validation") + self.tab_widget.addTab(self.results_tab, "Results") + self.tab_widget.addTab(self.annotation_tab, "Annotation") + + self.setCentralWidget(self.tab_widget) + + def _create_status_bar(self): + """Create status bar.""" + self.status_bar = QStatusBar() + self.setStatusBar(self.status_bar) + self.status_bar.showMessage("Ready") + + def _show_settings(self): + """Show settings dialog.""" + dialog = ConfigDialog(self.config_manager, self) + if dialog.exec(): + self._apply_settings() + + def _apply_settings(self): + """Apply changed settings.""" + # Reload configuration in all tabs + pass + + def _show_about(self): + """Show about dialog.""" + QMessageBox.about( + self, + "About", + "Microscopy Object Detection Application\n\n" + "Version 1.0\n\n" + "Powered by YOLOv8 and PySide6" + ) +``` + +### Tab Structure + +Each tab should follow this pattern: + +```python +from PySide6.QtWidgets import QWidget, QVBoxLayout +from ..database.db_manager import DatabaseManager +from ..utils.config_manager import ConfigManager + + +class TabName(QWidget): + """Tab description.""" + + def __init__( + self, + db_manager: DatabaseManager, + config_manager: ConfigManager, + parent=None + ): + super().__init__(parent) + self. db_manager = db_manager + self.config_manager = config_manager + + self._setup_ui() + self._connect_signals() + + def _setup_ui(self): + """Setup user interface.""" + layout = QVBoxLayout() + # Add widgets + self.setLayout(layout) + + def _connect_signals(self): + """Connect signals and slots.""" + pass +``` + +--- + +## Testing Strategy + +### Unit Tests Example (`tests/test_database.py`) + +```python +import pytest +from src.database.db_manager import DatabaseManager +import tempfile +import os + + +@pytest.fixture +def db_manager(): + """Create temporary database for testing.""" + fd, path = tempfile.mkstemp(suffix='.db') + os.close(fd) + + manager = DatabaseManager(path) + yield manager + + os.unlink(path) + + +def test_add_model(db_manager): + """Test adding a model to database.""" + model_id = db_manager.add_model( + model_name="test_model", + model_version="v1.0", + model_path="/path/to/model.pt", + base_model="yolov8s.pt" + ) + + assert model_id > 0 + + model = db_manager.get_model_by_id(model_id) + assert model['model_name'] == "test_model" + assert model['model_version'] == "v1.0" + + +def test_add_detection(db_manager): + """Test adding detection with foreign key constraints.""" + # First add model and image + model_id = db_manager.add_model( + "test", "v1", "/path", "yolov8s.pt" + ) + image_id = db_manager.add_image( + "test.jpg", "test.jpg", 1024, 768 + ) + + # Add detection + det_id = db_manager.add_detection( + image_id=image_id, + model_id=model_id, + class_name="organelle", + bbox=(0.1, 0.2, 0.3, 0.4), + confidence=0.95 + ) + + assert det_id > 0 +``` + +--- + +## Configuration Files + +### Application Config (`config/app_config.yaml`) + +```yaml +database: + path: "data/detections.db" + +image_repository: + base_path: "" + allowed_extensions: + - ".jpg" + - ".jpeg" + - ".png" + - ".tif" + - ".tiff" + +models: + default_base_model: "yolov8s.pt" + models_directory: "data/models" + +training: + default_epochs: 100 + default_batch_size: 16 + default_imgsz: 640 + default_patience: 50 + default_lr0: 0.01 + +detection: + default_confidence: 0.25 + default_iou: 0.45 + max_batch_size: 100 + +visualization: + bbox_colors: + organelle: "#FF6B6B" + membrane_branch: "#4ECDC4" + bbox_thickness: 2 + font_size: 12 + +export: + formats: + - csv + - json + - excel + default_format: "csv" + +logging: + level: "INFO" + file: "logs/app.log" + format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s" +``` + +### Dataset Config Example (`data.yaml`) + +```yaml +# YOLOv8 dataset configuration +path: /path/to/dataset # Root directory +train: train/images # Training images relative to path +val: val/images # Validation images relative to path +test: test/images # Test images (optional) + +# Classes +names: + 0: organelle + 1: membrane_branch + +# Number of classes +nc: 2 +``` + +--- + +## Deployment Checklist + +- [ ] Install all dependencies from [`requirements.txt`](requirements.txt) +- [ ] Create necessary directories ([`data/`](data/), [`logs/`](logs/), [`config/`](config/)) +- [ ] Initialize database with schema +- [ ] Download YOLOv8s.pt base model +- [ ] Configure [`app_config.yaml`](config/app_config.yaml) +- [ ] Set image repository path +- [ ] Test database operations +- [ ] Test model loading and inference +- [ ] Run unit tests +- [ ] Build application icon and resources +- [ ] Create user documentation +- [ ] Package application (PyInstaller or similar) + +--- + +This implementation guide provides detailed specifications for building each component of the application. The actual implementation in Code mode will follow these specifications to create a fully functional microscopy object detection system. \ No newline at end of file diff --git a/QUICKSTART.md b/QUICKSTART.md new file mode 100644 index 0000000..e211216 --- /dev/null +++ b/QUICKSTART.md @@ -0,0 +1,261 @@ +# Quick Start Guide + +This guide will help you get the Microscopy Object Detection Application up and running quickly. + +## Prerequisites + +- Python 3.8 or higher +- pip package manager +- (Optional) CUDA-capable GPU for faster training and inference + +## Installation + +### 1. Clone or Navigate to Project Directory + +```bash +cd /home/martin/code/object_detection +``` + +### 2. Create Virtual Environment + +```bash +python3 -m venv venv +source venv/bin/activate # On Linux/Mac +# or +venv\Scripts\activate # On Windows +``` + +### 3. Install Dependencies + +```bash +pip install -r requirements.txt +``` + +This will install: +- ultralytics (YOLOv8) +- PySide6 (GUI framework) +- pyqtgraph (visualization) +- OpenCV and Pillow (image processing) +- And other dependencies + +**Note:** The first run will automatically download the YOLOv8s.pt model (~22MB). + +### 4. Verify Installation + +```bash +python -c "import PySide6; import ultralytics; print('Installation successful!')" +``` + +## First Run + +### Launch the Application + +```bash +python main.py +``` + +The application window should open with 5 tabs: +- **Detection**: For running object detection +- **Training**: For training custom models (placeholder) +- **Validation**: For validating models (placeholder) +- **Results**: For viewing detection history (placeholder) +- **Annotation**: Future feature (placeholder) + +## Configuration + +### Set Up Image Repository + +1. Go to **File → Settings** +2. Under "General" tab, click "Browse..." next to "Base Path" +3. Select your microscopy images directory +4. Click "Save" + +This tells the application where your images are stored. + +### Adjust Detection Settings + +In the Settings dialog: +- **Detection tab**: Adjust default confidence threshold (0.25 default) +- **Training tab**: Configure default training parameters +- All settings are saved to [`config/app_config.yaml`](config/app_config.yaml) + +## Running Detection + +### Single Image Detection + +1. Go to the **Detection** tab +2. Select a model from the dropdown (default: Base Model yolov8s.pt) +3. Adjust confidence threshold with the slider +4. Click "Detect Single Image" +5. Select an image file +6. View results in the results panel + +### Batch Detection + +1. Go to the **Detection** tab +2. Select a model +3. Click "Detect Batch (Folder)" +4. Select a folder containing images +5. Confirm the number of images to process +6. Wait for processing to complete +7. Results are automatically saved to the database + +## Understanding the Results + +Detection results include: +- **Image path**: Location of the processed image +- **Detections**: Number of objects found +- **Class names**: Types of objects detected (e.g., organelle, membrane_branch) +- **Confidence scores**: Detection confidence (0-1) +- **Bounding boxes**: Object locations (stored in database) + +All results are stored in the SQLite database at [`data/detections.db`](data/detections.db). + +## Database + +The application uses SQLite to store: +- **Models**: Information about trained models +- **Images**: Metadata about processed images +- **Detections**: All detection results with bounding boxes +- **Annotations**: Manual annotations (future feature) + +### View Database Statistics + +Go to **Tools → Database Statistics** to see: +- Total number of detections +- Detections per class +- Average confidence scores + +## Next Steps + +### Training Custom Models (Coming Soon) + +The Training tab will allow you to: +1. Select a YOLO-format dataset +2. Configure training parameters +3. Monitor training progress +4. Save trained models + +### Preparing Your Dataset + +For training, you'll need data in YOLO format: +``` +dataset/ +├── train/ +│ ├── images/ +│ │ ├── img001.png +│ │ └── ... +│ └── labels/ +│ ├── img001.txt +│ └── ... +├── val/ +│ ├── images/ +│ └── labels/ +└── data.yaml +``` + +See [`README.md`](README.md) for detailed dataset format information. + +## Troubleshooting + +### Application Won't Start + +**Error: Module not found** +```bash +# Make sure virtual environment is activated +source venv/bin/activate +pip install -r requirements.txt +``` + +**Error: Qt platform plugin** +```bash +# Install system Qt dependencies (Linux) +sudo apt-get install libxcb-xinerama0 +``` + +### Detection Not Working + +**No models available** +- The base YOLOv8s model will be downloaded automatically on first use +- Make sure you have internet connection for the first run + +**Images not found** +- Verify the image repository path in Settings +- Check that image files have supported extensions (.jpg, .png, .tif, etc.) + +### Performance Issues + +**Slow detection** +- Use a GPU if available (CUDA will be auto-detected) +- Reduce batch size in settings +- Process fewer images at once + +**Out of memory** +- Reduce batch size +- Use CPU instead of GPU for large images +- Process images sequentially rather than in batch + +## File Locations + +- **Database**: `data/detections.db` +- **Configuration**: `config/app_config.yaml` +- **Logs**: `logs/app.log` +- **Models**: `data/models/` +- **Results**: Stored in database + +## Command Line Tips + +### Check GPU Availability + +```bash +python -c "import torch; print(f'CUDA available: {torch.cuda.is_available()}')" +``` + +### View Logs + +```bash +tail -f logs/app.log +``` + +### Backup Database + +```bash +cp data/detections.db data/detections_backup_$(date +%Y%m%d).db +``` + +## Getting Help + +- Read the full documentation in [`README.md`](README.md) +- Check architecture details in [`ARCHITECTURE.md`](ARCHITECTURE.md) +- Review implementation guide in [`IMPLEMENTATION_GUIDE.md`](IMPLEMENTATION_GUIDE.md) +- Check logs in `logs/app.log` for error messages + +## What's Implemented + +✅ **Core Infrastructure** +- Project structure and configuration +- Database schema and operations +- YOLO model wrapper +- Inference engine with batch processing +- Configuration management +- Logging system + +✅ **GUI Components** +- Main window with menu bar +- Settings dialog +- Detection tab with single/batch detection +- Placeholder tabs for future features + +🚧 **In Progress / Planned** +- Training tab implementation +- Validation tab with metrics +- Results browser and visualization +- Annotation tool +- Advanced filtering and export +- Real-time camera detection + +--- + +**Happy detecting! 🔬🔍** + +For questions or issues, please refer to the documentation or check the application logs. \ No newline at end of file