Making it installabel package and switching to segmentation mode
This commit is contained in:
@@ -2,11 +2,11 @@
|
||||
|
||||
## Project Overview
|
||||
|
||||
A desktop application for detecting organelles and membrane branching structures in microscopy images using YOLOv8s, with comprehensive training, validation, and visualization capabilities.
|
||||
A desktop application for detecting and segmenting organelles and membrane branching structures in microscopy images using YOLOv8s-seg, with comprehensive training, validation, and visualization capabilities including pixel-accurate segmentation masks.
|
||||
|
||||
## Technology Stack
|
||||
|
||||
- **ML Framework**: Ultralytics YOLOv8 (YOLOv8s.pt model)
|
||||
- **ML Framework**: Ultralytics YOLOv8 (YOLOv8s-seg.pt segmentation model)
|
||||
- **GUI Framework**: PySide6 (Qt6 for Python)
|
||||
- **Visualization**: pyqtgraph
|
||||
- **Database**: SQLite3
|
||||
@@ -110,6 +110,7 @@ erDiagram
|
||||
float x_max
|
||||
float y_max
|
||||
float confidence
|
||||
text segmentation_mask
|
||||
datetime detected_at
|
||||
json metadata
|
||||
}
|
||||
@@ -122,6 +123,7 @@ erDiagram
|
||||
float y_min
|
||||
float x_max
|
||||
float y_max
|
||||
text segmentation_mask
|
||||
string annotator
|
||||
datetime created_at
|
||||
boolean verified
|
||||
@@ -139,7 +141,7 @@ Stores information about trained models and their versions.
|
||||
| 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") |
|
||||
| base_model | TEXT | NOT NULL | Base model used (e.g., "yolov8s-seg.pt") |
|
||||
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Model creation timestamp |
|
||||
| training_params | JSON | | Training hyperparameters |
|
||||
| metrics | JSON | | Validation metrics (mAP, precision, recall) |
|
||||
@@ -159,7 +161,7 @@ Stores metadata about microscopy images.
|
||||
| checksum | TEXT | | MD5 hash for integrity verification |
|
||||
|
||||
#### **detections** table
|
||||
Stores object detection results.
|
||||
Stores object detection results with optional segmentation masks.
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
|--------|------|-------------|-------------|
|
||||
@@ -172,11 +174,12 @@ Stores object detection results.
|
||||
| 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) |
|
||||
| segmentation_mask | TEXT | | JSON array of polygon coordinates [[x1,y1], [x2,y2], ...] (normalized 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).
|
||||
Stores manual annotations for training data with optional segmentation masks (future feature).
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
|--------|------|-------------|-------------|
|
||||
@@ -187,6 +190,7 @@ Stores manual annotations for training data (future feature).
|
||||
| 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) |
|
||||
| segmentation_mask | TEXT | | JSON array of polygon coordinates [[x1,y1], [x2,y2], ...] (normalized 0-1) |
|
||||
| annotator | TEXT | | Name of person who created annotation |
|
||||
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Annotation timestamp |
|
||||
| verified | BOOLEAN | DEFAULT 0 | Whether annotation is verified |
|
||||
@@ -245,8 +249,9 @@ graph TB
|
||||
### Key Components
|
||||
|
||||
#### 1. **YOLO Wrapper** ([`src/model/yolo_wrapper.py`](src/model/yolo_wrapper.py))
|
||||
Encapsulates YOLOv8 operations:
|
||||
- Load pre-trained YOLOv8s model
|
||||
Encapsulates YOLOv8-seg operations:
|
||||
- Load pre-trained YOLOv8s-seg segmentation model
|
||||
- Extract pixel-accurate segmentation masks
|
||||
- Fine-tune on custom microscopy dataset
|
||||
- Export trained models
|
||||
- Provide training progress callbacks
|
||||
@@ -255,10 +260,10 @@ Encapsulates YOLOv8 operations:
|
||||
**Key Methods:**
|
||||
```python
|
||||
class YOLOWrapper:
|
||||
def __init__(self, model_path: str = "yolov8s.pt")
|
||||
def __init__(self, model_path: str = "yolov8s-seg.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 predict(self, image_path: str, conf: float) -> list # Returns detections with segmentation masks
|
||||
def export_model(self, format: str, output_path: str)
|
||||
```
|
||||
|
||||
@@ -435,7 +440,7 @@ image_repository:
|
||||
allowed_extensions: [".jpg", ".jpeg", ".png", ".tif", ".tiff"]
|
||||
|
||||
models:
|
||||
default_base_model: "yolov8s.pt"
|
||||
default_base_model: "yolov8s-seg.pt"
|
||||
models_directory: "data/models"
|
||||
|
||||
training:
|
||||
|
||||
178
BUILD.md
Normal file
178
BUILD.md
Normal file
@@ -0,0 +1,178 @@
|
||||
# Building and Publishing Guide
|
||||
|
||||
This guide explains how to build and publish the microscopy-object-detection package.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
```bash
|
||||
pip install build twine
|
||||
```
|
||||
|
||||
## Building the Package
|
||||
|
||||
### 1. Clean Previous Builds
|
||||
|
||||
```bash
|
||||
rm -rf build/ dist/ *.egg-info
|
||||
```
|
||||
|
||||
### 2. Build Distribution Archives
|
||||
|
||||
```bash
|
||||
python -m build
|
||||
```
|
||||
|
||||
This will create both wheel (`.whl`) and source distribution (`.tar.gz`) in the `dist/` directory.
|
||||
|
||||
### 3. Verify the Build
|
||||
|
||||
```bash
|
||||
ls dist/
|
||||
# Should show:
|
||||
# microscopy_object_detection-1.0.0-py3-none-any.whl
|
||||
# microscopy_object_detection-1.0.0.tar.gz
|
||||
```
|
||||
|
||||
## Testing the Package Locally
|
||||
|
||||
### Install in Development Mode
|
||||
|
||||
```bash
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
### Install from Built Package
|
||||
|
||||
```bash
|
||||
pip install dist/microscopy_object_detection-1.0.0-py3-none-any.whl
|
||||
```
|
||||
|
||||
### Test the Installation
|
||||
|
||||
```bash
|
||||
# Test CLI
|
||||
microscopy-detect --version
|
||||
|
||||
# Test GUI launcher
|
||||
microscopy-detect-gui
|
||||
```
|
||||
|
||||
## Publishing to PyPI
|
||||
|
||||
### 1. Configure PyPI Credentials
|
||||
|
||||
Create or update `~/.pypirc`:
|
||||
|
||||
```ini
|
||||
[pypi]
|
||||
username = __token__
|
||||
password = pypi-YOUR-API-TOKEN-HERE
|
||||
```
|
||||
|
||||
### 2. Upload to Test PyPI (Recommended First)
|
||||
|
||||
```bash
|
||||
python -m twine upload --repository testpypi dist/*
|
||||
```
|
||||
|
||||
Then test installation:
|
||||
|
||||
```bash
|
||||
pip install --index-url https://test.pypi.org/simple/ microscopy-object-detection
|
||||
```
|
||||
|
||||
### 3. Upload to PyPI
|
||||
|
||||
```bash
|
||||
python -m twine upload dist/*
|
||||
```
|
||||
|
||||
## Version Management
|
||||
|
||||
Update version in multiple files:
|
||||
- `setup.py`: Update `version` parameter
|
||||
- `pyproject.toml`: Update `version` field
|
||||
- `src/__init__.py`: Update `__version__` variable
|
||||
|
||||
## Git Tags
|
||||
|
||||
After publishing, tag the release:
|
||||
|
||||
```bash
|
||||
git tag -a v1.0.0 -m "Release version 1.0.0"
|
||||
git push origin v1.0.0
|
||||
```
|
||||
|
||||
## Package Structure
|
||||
|
||||
The built package includes:
|
||||
- All Python source files in `src/`
|
||||
- Configuration files in `config/`
|
||||
- Database schema file (`src/database/schema.sql`)
|
||||
- Documentation files (README.md, LICENSE, etc.)
|
||||
- Entry points for CLI and GUI
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Import Errors
|
||||
If you get import errors, ensure:
|
||||
- All `__init__.py` files are present
|
||||
- Package structure follows the setup configuration
|
||||
- Dependencies are listed in `requirements.txt`
|
||||
|
||||
### Missing Files
|
||||
If files are missing in the built package:
|
||||
- Check `MANIFEST.in` includes the required patterns
|
||||
- Check `pyproject.toml` package-data configuration
|
||||
- Rebuild with `python -m build --no-isolation` for debugging
|
||||
|
||||
### Version Conflicts
|
||||
If version conflicts occur:
|
||||
- Ensure version is consistent across all files
|
||||
- Clear build artifacts and rebuild
|
||||
- Check for cached installations: `pip list | grep microscopy`
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
### GitHub Actions Example
|
||||
|
||||
```yaml
|
||||
name: Build and Publish
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install build twine
|
||||
- name: Build package
|
||||
run: python -m build
|
||||
- name: Publish to PyPI
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
|
||||
run: twine upload dist/*
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Version Bumping**: Use semantic versioning (MAJOR.MINOR.PATCH)
|
||||
2. **Testing**: Always test on Test PyPI before publishing to PyPI
|
||||
3. **Documentation**: Update README.md and CHANGELOG.md for each release
|
||||
4. **Git Tags**: Tag releases in git for easy reference
|
||||
5. **Dependencies**: Keep requirements.txt updated and specify version ranges
|
||||
|
||||
## Resources
|
||||
|
||||
- [Python Packaging Guide](https://packaging.python.org/)
|
||||
- [setuptools Documentation](https://setuptools.pypa.io/)
|
||||
- [PyPI Publishing Guide](https://packaging.python.org/tutorials/packaging-projects/)
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Your Name
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
37
MANIFEST.in
Normal file
37
MANIFEST.in
Normal file
@@ -0,0 +1,37 @@
|
||||
# Include documentation files
|
||||
include README.md
|
||||
include LICENSE
|
||||
include ARCHITECTURE.md
|
||||
include IMPLEMENTATION_GUIDE.md
|
||||
include QUICKSTART.md
|
||||
include PLAN_SUMMARY.md
|
||||
|
||||
# Include requirements
|
||||
include requirements.txt
|
||||
|
||||
# Include configuration files
|
||||
recursive-include config *.yaml
|
||||
recursive-include config *.yml
|
||||
|
||||
# Include database schema
|
||||
recursive-include src/database *.sql
|
||||
|
||||
# Include tests
|
||||
recursive-include tests *.py
|
||||
|
||||
# Exclude compiled Python files
|
||||
global-exclude *.pyc
|
||||
global-exclude *.pyo
|
||||
global-exclude __pycache__
|
||||
global-exclude *.so
|
||||
global-exclude .DS_Store
|
||||
|
||||
# Exclude git and IDE files
|
||||
global-exclude .git*
|
||||
global-exclude .vscode
|
||||
global-exclude .idea
|
||||
|
||||
# Exclude build artifacts
|
||||
prune build
|
||||
prune dist
|
||||
prune *.egg-info
|
||||
@@ -38,7 +38,7 @@ This will install:
|
||||
- OpenCV and Pillow (image processing)
|
||||
- And other dependencies
|
||||
|
||||
**Note:** The first run will automatically download the YOLOv8s.pt model (~22MB).
|
||||
**Note:** The first run will automatically download the YOLOv8s-seg.pt segmentation model (~23MB).
|
||||
|
||||
### 4. Verify Installation
|
||||
|
||||
@@ -84,11 +84,11 @@ In the Settings dialog:
|
||||
### Single Image Detection
|
||||
|
||||
1. Go to the **Detection** tab
|
||||
2. Select a model from the dropdown (default: Base Model yolov8s.pt)
|
||||
2. Select a model from the dropdown (default: Base Model yolov8s-seg.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
|
||||
6. View results with segmentation masks overlaid on the image
|
||||
|
||||
### Batch Detection
|
||||
|
||||
@@ -108,9 +108,18 @@ Detection results include:
|
||||
- **Class names**: Types of objects detected (e.g., organelle, membrane_branch)
|
||||
- **Confidence scores**: Detection confidence (0-1)
|
||||
- **Bounding boxes**: Object locations (stored in database)
|
||||
- **Segmentation masks**: Pixel-accurate polygon coordinates for each detected object
|
||||
|
||||
All results are stored in the SQLite database at [`data/detections.db`](data/detections.db).
|
||||
|
||||
### Segmentation Visualization
|
||||
|
||||
The application automatically displays segmentation masks when available:
|
||||
- Semi-transparent colored overlay (30% opacity) showing the exact shape of detected objects
|
||||
- Polygon contours outlining each segmentation
|
||||
- Color-coded by object class
|
||||
- Toggle-able in future versions
|
||||
|
||||
## Database
|
||||
|
||||
The application uses SQLite to store:
|
||||
@@ -176,7 +185,7 @@ sudo apt-get install libxcb-xinerama0
|
||||
### Detection Not Working
|
||||
|
||||
**No models available**
|
||||
- The base YOLOv8s model will be downloaded automatically on first use
|
||||
- The base YOLOv8s-seg segmentation model will be downloaded automatically on first use
|
||||
- Make sure you have internet connection for the first run
|
||||
|
||||
**Images not found**
|
||||
|
||||
62
README.md
62
README.md
@@ -1,6 +1,6 @@
|
||||
# Microscopy Object Detection Application
|
||||
|
||||
A desktop application for detecting organelles and membrane branching structures in microscopy images using YOLOv8, featuring comprehensive training, validation, and visualization capabilities.
|
||||
A desktop application for detecting and segmenting organelles and membrane branching structures in microscopy images using YOLOv8-seg, featuring comprehensive training, validation, and visualization capabilities with pixel-accurate segmentation masks.
|
||||
|
||||

|
||||

|
||||
@@ -8,8 +8,8 @@ A desktop application for detecting organelles and membrane branching structures
|
||||
|
||||
## Features
|
||||
|
||||
- **🎯 Object Detection**: Real-time and batch detection of microscopy objects
|
||||
- **🎓 Model Training**: Fine-tune YOLOv8s on custom microscopy datasets
|
||||
- **🎯 Object Detection & Segmentation**: Real-time and batch detection with pixel-accurate segmentation masks
|
||||
- **🎓 Model Training**: Fine-tune YOLOv8s-seg on custom microscopy datasets
|
||||
- **📊 Validation & Metrics**: Comprehensive model validation with visualization
|
||||
- **💾 Database Storage**: SQLite database for detection results and metadata
|
||||
- **📈 Visualization**: Interactive plots and charts using pyqtgraph
|
||||
@@ -34,14 +34,24 @@ A desktop application for detecting organelles and membrane branching structures
|
||||
|
||||
## Installation
|
||||
|
||||
### 1. Clone the Repository
|
||||
### Option 1: Install from PyPI (Recommended)
|
||||
|
||||
```bash
|
||||
pip install microscopy-object-detection
|
||||
```
|
||||
|
||||
This will install the package and all its dependencies.
|
||||
|
||||
### Option 2: Install from Source
|
||||
|
||||
#### 1. Clone the Repository
|
||||
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd object_detection
|
||||
```
|
||||
|
||||
### 2. Create Virtual Environment
|
||||
#### 2. Create Virtual Environment
|
||||
|
||||
```bash
|
||||
# Linux/Mac
|
||||
@@ -53,25 +63,44 @@ python -m venv venv
|
||||
venv\Scripts\activate
|
||||
```
|
||||
|
||||
### 3. Install Dependencies
|
||||
#### 3. Install in Development Mode
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
# Install in editable mode with dev dependencies
|
||||
pip install -e ".[dev]"
|
||||
|
||||
# Or install just the package
|
||||
pip install .
|
||||
```
|
||||
|
||||
### 4. Download Base Model
|
||||
|
||||
The application will automatically download the YOLOv8s.pt model on first use, or you can download it manually:
|
||||
The application will automatically download the YOLOv8s-seg.pt segmentation model on first use, or you can download it manually:
|
||||
|
||||
```bash
|
||||
# The model will be downloaded automatically by ultralytics
|
||||
# Or download manually from: https://github.com/ultralytics/assets/releases
|
||||
```
|
||||
|
||||
**Note:** YOLOv8s-seg is a segmentation model that provides pixel-accurate masks for detected objects, enabling more precise analysis than standard bounding box detection.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Launch the Application
|
||||
|
||||
After installation, you can launch the application in two ways:
|
||||
|
||||
**Using the GUI launcher:**
|
||||
```bash
|
||||
microscopy-detect-gui
|
||||
```
|
||||
|
||||
**Or using Python directly:**
|
||||
```bash
|
||||
python -m microscopy_object_detection
|
||||
```
|
||||
|
||||
**If installed from source:**
|
||||
```bash
|
||||
python main.py
|
||||
```
|
||||
@@ -85,11 +114,12 @@ python main.py
|
||||
### 3. Perform Detection
|
||||
|
||||
1. Navigate to the **Detection** tab
|
||||
2. Select a model (default: yolov8s.pt)
|
||||
2. Select a model (default: yolov8s-seg.pt)
|
||||
3. Choose an image or folder
|
||||
4. Set confidence threshold
|
||||
5. Click **Detect**
|
||||
6. View results and save to database
|
||||
6. View results with segmentation masks overlaid
|
||||
7. Save results to database
|
||||
|
||||
### 4. Train Custom Model
|
||||
|
||||
@@ -212,8 +242,8 @@ The application uses SQLite with the following main tables:
|
||||
|
||||
- **models**: Stores trained model information and metrics
|
||||
- **images**: Stores image metadata and paths
|
||||
- **detections**: Stores detection results with bounding boxes
|
||||
- **annotations**: Stores manual annotations (future feature)
|
||||
- **detections**: Stores detection results with bounding boxes and segmentation masks (polygon coordinates)
|
||||
- **annotations**: Stores manual annotations with optional segmentation masks (future feature)
|
||||
|
||||
See [`ARCHITECTURE.md`](ARCHITECTURE.md) for detailed schema information.
|
||||
|
||||
@@ -230,7 +260,7 @@ image_repository:
|
||||
allowed_extensions: [".jpg", ".jpeg", ".png", ".tif", ".tiff"]
|
||||
|
||||
models:
|
||||
default_base_model: "yolov8s.pt"
|
||||
default_base_model: "yolov8s-seg.pt"
|
||||
models_directory: "data/models"
|
||||
|
||||
training:
|
||||
@@ -258,7 +288,7 @@ visualization:
|
||||
from src.model.yolo_wrapper import YOLOWrapper
|
||||
|
||||
# Initialize wrapper
|
||||
yolo = YOLOWrapper("yolov8s.pt")
|
||||
yolo = YOLOWrapper("yolov8s-seg.pt")
|
||||
|
||||
# Train model
|
||||
results = yolo.train(
|
||||
@@ -393,10 +423,10 @@ make html
|
||||
|
||||
**Issue**: Model not found error
|
||||
|
||||
**Solution**: Ensure YOLOv8s.pt is downloaded. Run:
|
||||
**Solution**: Ensure YOLOv8s-seg.pt is downloaded. Run:
|
||||
```python
|
||||
from ultralytics import YOLO
|
||||
model = YOLO('yolov8s.pt') # Will auto-download
|
||||
model = YOLO('yolov8s-seg.pt') # Will auto-download
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ image_repository:
|
||||
- .tiff
|
||||
- .bmp
|
||||
models:
|
||||
default_base_model: yolov8s.pt
|
||||
default_base_model: yolov8s-seg.pt
|
||||
models_directory: data/models
|
||||
training:
|
||||
default_epochs: 100
|
||||
|
||||
5
main.py
5
main.py
@@ -6,12 +6,13 @@ Main entry point for the application.
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add src directory to path
|
||||
# Add src directory to path for development mode
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
|
||||
from PySide6.QtWidgets import QApplication
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
from src import __version__
|
||||
from src.gui.main_window import MainWindow
|
||||
from src.utils.logger import setup_logging
|
||||
from src.utils.config_manager import ConfigManager
|
||||
@@ -37,7 +38,7 @@ def main():
|
||||
app = QApplication(sys.argv)
|
||||
app.setApplicationName("Microscopy Object Detection")
|
||||
app.setOrganizationName("MicroscopyLab")
|
||||
app.setApplicationVersion("1.0.0")
|
||||
app.setApplicationVersion(__version__)
|
||||
|
||||
# Set application style
|
||||
app.setStyle("Fusion")
|
||||
|
||||
103
pyproject.toml
Normal file
103
pyproject.toml
Normal file
@@ -0,0 +1,103 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=45", "wheel", "setuptools_scm[toml]>=6.2"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "microscopy-object-detection"
|
||||
version = "1.0.0"
|
||||
description = "Desktop application for detecting and segmenting organelles in microscopy images using YOLOv8-seg"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.8"
|
||||
license = { text = "MIT" }
|
||||
authors = [{ name = "Your Name", email = "your.email@example.com" }]
|
||||
keywords = [
|
||||
"microscopy",
|
||||
"yolov8",
|
||||
"object-detection",
|
||||
"segmentation",
|
||||
"computer-vision",
|
||||
"deep-learning",
|
||||
]
|
||||
classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"Intended Audience :: Science/Research",
|
||||
"Topic :: Scientific/Engineering :: Image Recognition",
|
||||
"Topic :: Scientific/Engineering :: Bio-Informatics",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Operating System :: OS Independent",
|
||||
]
|
||||
|
||||
dependencies = [
|
||||
"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",
|
||||
"pandas>=2.0.0",
|
||||
"openpyxl>=3.1.0",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"pytest>=7.0.0",
|
||||
"pytest-cov>=4.0.0",
|
||||
"black>=23.0.0",
|
||||
"pylint>=2.17.0",
|
||||
"mypy>=1.0.0",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/yourusername/object_detection"
|
||||
Documentation = "https://github.com/yourusername/object_detection/blob/main/README.md"
|
||||
Repository = "https://github.com/yourusername/object_detection"
|
||||
"Bug Tracker" = "https://github.com/yourusername/object_detection/issues"
|
||||
|
||||
[project.scripts]
|
||||
microscopy-detect = "src.cli:main"
|
||||
|
||||
[project.gui-scripts]
|
||||
microscopy-detect-gui = "main:main"
|
||||
|
||||
[tool.setuptools]
|
||||
package-dir = { "" = "." }
|
||||
packages = [
|
||||
"src",
|
||||
"src.database",
|
||||
"src.model",
|
||||
"src.gui",
|
||||
"src.gui.tabs",
|
||||
"src.gui.dialogs",
|
||||
"src.gui.widgets",
|
||||
"src.utils",
|
||||
]
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
src = ["database/*.sql"]
|
||||
"" = ["config/*.yaml"]
|
||||
|
||||
[tool.black]
|
||||
line-length = 88
|
||||
target-version = ['py38', 'py39', 'py310', 'py311']
|
||||
include = '\.pyi?$'
|
||||
|
||||
[tool.pylint.messages_control]
|
||||
max-line-length = 88
|
||||
|
||||
[tool.mypy]
|
||||
python_version = "3.8"
|
||||
warn_return_any = true
|
||||
warn_unused_configs = true
|
||||
disallow_untyped_defs = false
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ["tests"]
|
||||
python_files = ["test_*.py"]
|
||||
python_functions = ["test_*"]
|
||||
addopts = "-v --cov=src --cov-report=term-missing"
|
||||
56
setup.py
Normal file
56
setup.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""Setup script for Microscopy Object Detection Application."""
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
from pathlib import Path
|
||||
|
||||
# Read the contents of README file
|
||||
this_directory = Path(__file__).parent
|
||||
long_description = (this_directory / "README.md").read_text(encoding="utf-8")
|
||||
|
||||
# Read requirements
|
||||
requirements = (this_directory / "requirements.txt").read_text().splitlines()
|
||||
requirements = [
|
||||
req.strip() for req in requirements if req.strip() and not req.startswith("#")
|
||||
]
|
||||
|
||||
setup(
|
||||
name="microscopy-object-detection",
|
||||
version="1.0.0",
|
||||
author="Your Name",
|
||||
author_email="your.email@example.com",
|
||||
description="Desktop application for detecting and segmenting organelles in microscopy images using YOLOv8-seg",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
url="https://github.com/yourusername/object_detection",
|
||||
packages=find_packages(exclude=["tests", "tests.*", "docs"]),
|
||||
include_package_data=True,
|
||||
install_requires=requirements,
|
||||
python_requires=">=3.8",
|
||||
classifiers=[
|
||||
"Development Status :: 4 - Beta",
|
||||
"Intended Audience :: Science/Research",
|
||||
"Topic :: Scientific/Engineering :: Image Recognition",
|
||||
"Topic :: Scientific/Engineering :: Bio-Informatics",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Operating System :: OS Independent",
|
||||
],
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"microscopy-detect=src.cli:main",
|
||||
],
|
||||
"gui_scripts": [
|
||||
"microscopy-detect-gui=main:main",
|
||||
],
|
||||
},
|
||||
keywords="microscopy yolov8 object-detection segmentation computer-vision deep-learning",
|
||||
project_urls={
|
||||
"Bug Reports": "https://github.com/yourusername/object_detection/issues",
|
||||
"Source": "https://github.com/yourusername/object_detection",
|
||||
"Documentation": "https://github.com/yourusername/object_detection/blob/main/README.md",
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,19 @@
|
||||
"""
|
||||
Microscopy Object Detection Application
|
||||
|
||||
A desktop application for detecting and segmenting organelles and membrane
|
||||
branching structures in microscopy images using YOLOv8-seg.
|
||||
"""
|
||||
|
||||
__version__ = "1.0.0"
|
||||
__author__ = "Your Name"
|
||||
__email__ = "your.email@example.com"
|
||||
__license__ = "MIT"
|
||||
|
||||
# Package metadata
|
||||
__all__ = [
|
||||
"__version__",
|
||||
"__author__",
|
||||
"__email__",
|
||||
"__license__",
|
||||
]
|
||||
|
||||
61
src/cli.py
Normal file
61
src/cli.py
Normal file
@@ -0,0 +1,61 @@
|
||||
"""
|
||||
Command-line interface for microscopy object detection application.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
from src import __version__
|
||||
|
||||
|
||||
def main():
|
||||
"""Main CLI entry point."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Microscopy Object Detection Application - CLI Interface",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
# Launch GUI
|
||||
microscopy-detect-gui
|
||||
|
||||
# Show version
|
||||
microscopy-detect --version
|
||||
|
||||
# Get help
|
||||
microscopy-detect --help
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--version",
|
||||
action="version",
|
||||
version=f"microscopy-object-detection {__version__}",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--gui",
|
||||
action="store_true",
|
||||
help="Launch the GUI application (same as microscopy-detect-gui)",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.gui:
|
||||
# Launch GUI
|
||||
try:
|
||||
from main import main as gui_main
|
||||
|
||||
gui_main()
|
||||
except Exception as e:
|
||||
print(f"Error launching GUI: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
else:
|
||||
# Show help if no arguments provided
|
||||
parser.print_help()
|
||||
print("\nTo launch the GUI, use: microscopy-detect-gui")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -6,7 +6,7 @@ Handles all database operations including CRUD operations, queries, and exports.
|
||||
import sqlite3
|
||||
import json
|
||||
from datetime import datetime
|
||||
from typing import List, Dict, Optional, Tuple, Any
|
||||
from typing import List, Dict, Optional, Tuple, Any, Union
|
||||
from pathlib import Path
|
||||
import csv
|
||||
import hashlib
|
||||
@@ -56,7 +56,7 @@ class DatabaseManager:
|
||||
model_name: str,
|
||||
model_version: str,
|
||||
model_path: str,
|
||||
base_model: str = "yolov8s.pt",
|
||||
base_model: str = "yolov8s-seg.pt",
|
||||
training_params: Optional[Dict] = None,
|
||||
metrics: Optional[Dict] = None,
|
||||
) -> int:
|
||||
@@ -243,6 +243,7 @@ class DatabaseManager:
|
||||
class_name: str,
|
||||
bbox: Tuple[float, float, float, float], # (x_min, y_min, x_max, y_max)
|
||||
confidence: float,
|
||||
segmentation_mask: Optional[List[List[float]]] = None,
|
||||
metadata: Optional[Dict] = None,
|
||||
) -> int:
|
||||
"""
|
||||
@@ -254,6 +255,7 @@ class DatabaseManager:
|
||||
class_name: Detected object class
|
||||
bbox: Bounding box coordinates (normalized 0-1)
|
||||
confidence: Detection confidence score
|
||||
segmentation_mask: Polygon coordinates for segmentation [[x1,y1], [x2,y2], ...]
|
||||
metadata: Additional metadata
|
||||
|
||||
Returns:
|
||||
@@ -265,8 +267,8 @@ class DatabaseManager:
|
||||
x_min, y_min, x_max, y_max = bbox
|
||||
cursor.execute(
|
||||
"""
|
||||
INSERT INTO detections (image_id, model_id, class_name, x_min, y_min, x_max, y_max, confidence, metadata)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
INSERT INTO detections (image_id, model_id, class_name, x_min, y_min, x_max, y_max, confidence, segmentation_mask, metadata)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
image_id,
|
||||
@@ -277,6 +279,7 @@ class DatabaseManager:
|
||||
x_max,
|
||||
y_max,
|
||||
confidence,
|
||||
json.dumps(segmentation_mask) if segmentation_mask else None,
|
||||
json.dumps(metadata) if metadata else None,
|
||||
),
|
||||
)
|
||||
@@ -302,8 +305,8 @@ class DatabaseManager:
|
||||
bbox = det["bbox"]
|
||||
cursor.execute(
|
||||
"""
|
||||
INSERT INTO detections (image_id, model_id, class_name, x_min, y_min, x_max, y_max, confidence, metadata)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
INSERT INTO detections (image_id, model_id, class_name, x_min, y_min, x_max, y_max, confidence, segmentation_mask, metadata)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
det["image_id"],
|
||||
@@ -314,6 +317,11 @@ class DatabaseManager:
|
||||
bbox[2],
|
||||
bbox[3],
|
||||
det["confidence"],
|
||||
(
|
||||
json.dumps(det.get("segmentation_mask"))
|
||||
if det.get("segmentation_mask")
|
||||
else None
|
||||
),
|
||||
(
|
||||
json.dumps(det.get("metadata"))
|
||||
if det.get("metadata")
|
||||
@@ -385,9 +393,11 @@ class DatabaseManager:
|
||||
detections = []
|
||||
for row in cursor.fetchall():
|
||||
det = dict(row)
|
||||
# Parse JSON metadata
|
||||
# Parse JSON fields
|
||||
if det.get("metadata"):
|
||||
det["metadata"] = json.loads(det["metadata"])
|
||||
if det.get("segmentation_mask"):
|
||||
det["segmentation_mask"] = json.loads(det["segmentation_mask"])
|
||||
detections.append(det)
|
||||
|
||||
return detections
|
||||
@@ -538,6 +548,7 @@ class DatabaseManager:
|
||||
"x_max",
|
||||
"y_max",
|
||||
"confidence",
|
||||
"segmentation_mask",
|
||||
"detected_at",
|
||||
]
|
||||
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
||||
@@ -545,6 +556,11 @@ class DatabaseManager:
|
||||
|
||||
for det in detections:
|
||||
row = {k: det[k] for k in fieldnames if k in det}
|
||||
# Convert segmentation mask list to JSON string for CSV
|
||||
if row.get("segmentation_mask") and isinstance(
|
||||
row["segmentation_mask"], list
|
||||
):
|
||||
row["segmentation_mask"] = json.dumps(row["segmentation_mask"])
|
||||
writer.writerow(row)
|
||||
|
||||
return True
|
||||
@@ -580,6 +596,7 @@ class DatabaseManager:
|
||||
class_name: str,
|
||||
bbox: Tuple[float, float, float, float],
|
||||
annotator: str,
|
||||
segmentation_mask: Optional[List[List[float]]] = None,
|
||||
verified: bool = False,
|
||||
) -> int:
|
||||
"""Add manual annotation."""
|
||||
@@ -589,10 +606,20 @@ class DatabaseManager:
|
||||
x_min, y_min, x_max, y_max = bbox
|
||||
cursor.execute(
|
||||
"""
|
||||
INSERT INTO annotations (image_id, class_name, x_min, y_min, x_max, y_max, annotator, verified)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
INSERT INTO annotations (image_id, class_name, x_min, y_min, x_max, y_max, segmentation_mask, annotator, verified)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(image_id, class_name, x_min, y_min, x_max, y_max, annotator, verified),
|
||||
(
|
||||
image_id,
|
||||
class_name,
|
||||
x_min,
|
||||
y_min,
|
||||
x_max,
|
||||
y_max,
|
||||
json.dumps(segmentation_mask) if segmentation_mask else None,
|
||||
annotator,
|
||||
verified,
|
||||
),
|
||||
)
|
||||
conn.commit()
|
||||
return cursor.lastrowid
|
||||
|
||||
@@ -5,7 +5,7 @@ These dataclasses represent the database entities.
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import Optional, Dict, Tuple
|
||||
from typing import Optional, Dict, Tuple, List
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -46,6 +46,9 @@ class Detection:
|
||||
class_name: str
|
||||
bbox: Tuple[float, float, float, float] # (x_min, y_min, x_max, y_max)
|
||||
confidence: float
|
||||
segmentation_mask: Optional[
|
||||
List[List[float]]
|
||||
] # List of polygon coordinates [[x1,y1], [x2,y2], ...]
|
||||
detected_at: datetime
|
||||
metadata: Optional[Dict]
|
||||
|
||||
@@ -58,6 +61,9 @@ class Annotation:
|
||||
image_id: int
|
||||
class_name: str
|
||||
bbox: Tuple[float, float, float, float] # (x_min, y_min, x_max, y_max)
|
||||
segmentation_mask: Optional[
|
||||
List[List[float]]
|
||||
] # List of polygon coordinates [[x1,y1], [x2,y2], ...]
|
||||
annotator: str
|
||||
created_at: datetime
|
||||
verified: bool
|
||||
|
||||
@@ -37,6 +37,7 @@ CREATE TABLE IF NOT EXISTS detections (
|
||||
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),
|
||||
segmentation_mask TEXT, -- JSON string of polygon coordinates [[x1,y1], [x2,y2], ...]
|
||||
detected_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
metadata TEXT, -- JSON string for additional metadata
|
||||
FOREIGN KEY (image_id) REFERENCES images (id) ON DELETE CASCADE,
|
||||
@@ -52,6 +53,7 @@ CREATE TABLE IF NOT EXISTS annotations (
|
||||
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),
|
||||
segmentation_mask TEXT, -- JSON string of polygon coordinates [[x1,y1], [x2,y2], ...]
|
||||
annotator TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
verified BOOLEAN DEFAULT 0,
|
||||
|
||||
@@ -121,7 +121,7 @@ class ConfigDialog(QDialog):
|
||||
models_layout.addRow("Models Directory:", self.models_dir_edit)
|
||||
|
||||
self.base_model_edit = QLineEdit()
|
||||
self.base_model_edit.setPlaceholderText("yolov8s.pt")
|
||||
self.base_model_edit.setPlaceholderText("yolov8s-seg.pt")
|
||||
models_layout.addRow("Default Base Model:", self.base_model_edit)
|
||||
|
||||
models_group.setLayout(models_layout)
|
||||
@@ -232,7 +232,7 @@ class ConfigDialog(QDialog):
|
||||
self.config_manager.get("models.models_directory", "data/models")
|
||||
)
|
||||
self.base_model_edit.setText(
|
||||
self.config_manager.get("models.default_base_model", "yolov8s.pt")
|
||||
self.config_manager.get("models.default_base_model", "yolov8s-seg.pt")
|
||||
)
|
||||
|
||||
# Training settings
|
||||
|
||||
@@ -159,7 +159,7 @@ class DetectionTab(QWidget):
|
||||
|
||||
# Add base model option
|
||||
base_model = self.config_manager.get(
|
||||
"models.default_base_model", "yolov8s.pt"
|
||||
"models.default_base_model", "yolov8s-seg.pt"
|
||||
)
|
||||
self.model_combo.addItem(
|
||||
f"Base Model ({base_model})", {"id": 0, "path": base_model}
|
||||
@@ -256,7 +256,7 @@ class DetectionTab(QWidget):
|
||||
if model_id == 0:
|
||||
# Create database entry for base model
|
||||
base_model = self.config_manager.get(
|
||||
"models.default_base_model", "yolov8s.pt"
|
||||
"models.default_base_model", "yolov8s-seg.pt"
|
||||
)
|
||||
model_id = self.db_manager.add_model(
|
||||
model_name="Base Model",
|
||||
|
||||
@@ -87,6 +87,7 @@ class InferenceEngine:
|
||||
"class_name": det["class_name"],
|
||||
"bbox": tuple(bbox_normalized),
|
||||
"confidence": det["confidence"],
|
||||
"segmentation_mask": det.get("segmentation_mask"),
|
||||
"metadata": {"class_id": det["class_id"]},
|
||||
}
|
||||
detection_records.append(record)
|
||||
@@ -160,6 +161,7 @@ class InferenceEngine:
|
||||
conf: float = 0.25,
|
||||
bbox_thickness: int = 2,
|
||||
bbox_colors: Optional[Dict[str, str]] = None,
|
||||
draw_masks: bool = True,
|
||||
) -> tuple:
|
||||
"""
|
||||
Detect objects and return annotated image.
|
||||
@@ -169,6 +171,7 @@ class InferenceEngine:
|
||||
conf: Confidence threshold
|
||||
bbox_thickness: Thickness of bounding boxes
|
||||
bbox_colors: Dictionary mapping class names to hex colors
|
||||
draw_masks: Whether to draw segmentation masks (if available)
|
||||
|
||||
Returns:
|
||||
Tuple of (detections, annotated_image_array)
|
||||
@@ -189,12 +192,8 @@ class InferenceEngine:
|
||||
bbox_colors = {}
|
||||
default_color = self._hex_to_bgr(bbox_colors.get("default", "#00FF00"))
|
||||
|
||||
# Draw bounding boxes
|
||||
# Draw detections
|
||||
for det in detections:
|
||||
# Get absolute coordinates
|
||||
bbox_abs = det["bbox_absolute"]
|
||||
x1, y1, x2, y2 = [int(v) for v in bbox_abs]
|
||||
|
||||
# Get color for this class
|
||||
class_name = det["class_name"]
|
||||
color_hex = bbox_colors.get(
|
||||
@@ -202,7 +201,33 @@ class InferenceEngine:
|
||||
)
|
||||
color = self._hex_to_bgr(color_hex)
|
||||
|
||||
# Draw box
|
||||
# Draw segmentation mask if available and requested
|
||||
if draw_masks and det.get("segmentation_mask"):
|
||||
mask_normalized = det["segmentation_mask"]
|
||||
if mask_normalized and len(mask_normalized) > 0:
|
||||
# Convert normalized coordinates to absolute pixels
|
||||
mask_points = np.array(
|
||||
[
|
||||
[int(pt[0] * width), int(pt[1] * height)]
|
||||
for pt in mask_normalized
|
||||
],
|
||||
dtype=np.int32,
|
||||
)
|
||||
|
||||
# Create a semi-transparent overlay
|
||||
overlay = img.copy()
|
||||
cv2.fillPoly(overlay, [mask_points], color)
|
||||
# Blend with original image (30% opacity)
|
||||
cv2.addWeighted(overlay, 0.3, img, 0.7, 0, img)
|
||||
|
||||
# Draw mask contour
|
||||
cv2.polylines(img, [mask_points], True, color, bbox_thickness)
|
||||
|
||||
# Get absolute coordinates for bounding box
|
||||
bbox_abs = det["bbox_absolute"]
|
||||
x1, y1, x2, y2 = [int(v) for v in bbox_abs]
|
||||
|
||||
# Draw bounding box
|
||||
cv2.rectangle(img, (x1, y1), (x2, y2), color, bbox_thickness)
|
||||
|
||||
# Prepare label
|
||||
|
||||
@@ -16,7 +16,7 @@ logger = get_logger(__name__)
|
||||
class YOLOWrapper:
|
||||
"""Wrapper for YOLOv8 model operations."""
|
||||
|
||||
def __init__(self, model_path: str = "yolov8s.pt"):
|
||||
def __init__(self, model_path: str = "yolov8s-seg.pt"):
|
||||
"""
|
||||
Initialize YOLO model.
|
||||
|
||||
@@ -282,6 +282,10 @@ class YOLOWrapper:
|
||||
boxes = result.boxes
|
||||
image_path = str(result.path)
|
||||
orig_shape = result.orig_shape # (height, width)
|
||||
height, width = orig_shape
|
||||
|
||||
# Check if this is a segmentation model with masks
|
||||
has_masks = hasattr(result, "masks") and result.masks is not None
|
||||
|
||||
for i in range(len(boxes)):
|
||||
# Get normalized coordinates
|
||||
@@ -299,6 +303,33 @@ class YOLOWrapper:
|
||||
float(v) for v in boxes.xyxy[i].cpu().numpy()
|
||||
], # Absolute pixels
|
||||
}
|
||||
|
||||
# Extract segmentation mask if available
|
||||
if has_masks:
|
||||
try:
|
||||
# Get the mask for this detection
|
||||
mask_data = result.masks.xy[
|
||||
i
|
||||
] # Polygon coordinates in absolute pixels
|
||||
|
||||
# Convert to normalized coordinates
|
||||
if len(mask_data) > 0:
|
||||
mask_normalized = []
|
||||
for point in mask_data:
|
||||
x_norm = float(point[0]) / width
|
||||
y_norm = float(point[1]) / height
|
||||
mask_normalized.append([x_norm, y_norm])
|
||||
detection["segmentation_mask"] = mask_normalized
|
||||
else:
|
||||
detection["segmentation_mask"] = None
|
||||
except Exception as mask_error:
|
||||
logger.warning(
|
||||
f"Error extracting mask for detection {i}: {mask_error}"
|
||||
)
|
||||
detection["segmentation_mask"] = None
|
||||
else:
|
||||
detection["segmentation_mask"] = None
|
||||
|
||||
detections.append(detection)
|
||||
|
||||
return detections
|
||||
|
||||
@@ -56,7 +56,7 @@ class ConfigManager:
|
||||
],
|
||||
},
|
||||
"models": {
|
||||
"default_base_model": "yolov8s.pt",
|
||||
"default_base_model": "yolov8s-seg.pt",
|
||||
"models_directory": "data/models",
|
||||
},
|
||||
"training": {
|
||||
|
||||
Reference in New Issue
Block a user