lazylabel-gui 1.1.8__tar.gz → 1.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lazylabel_gui-1.1.8/src/lazylabel_gui.egg-info → lazylabel_gui-1.2.0}/PKG-INFO +56 -96
- lazylabel_gui-1.2.0/README.md +145 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/pyproject.toml +1 -1
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/config/hotkeys.py +2 -2
- lazylabel_gui-1.2.0/src/lazylabel/core/model_manager.py +183 -0
- lazylabel_gui-1.2.0/src/lazylabel/models/sam2_model.py +223 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/models/sam_model.py +25 -3
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/ui/control_panel.py +920 -861
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/ui/main_window.py +3489 -3007
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/ui/widgets/__init__.py +2 -0
- lazylabel_gui-1.2.0/src/lazylabel/ui/widgets/fft_threshold_widget.py +392 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0/src/lazylabel_gui.egg-info}/PKG-INFO +56 -96
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel_gui.egg-info/SOURCES.txt +2 -0
- lazylabel_gui-1.1.8/README.md +0 -185
- lazylabel_gui-1.1.8/src/lazylabel/core/model_manager.py +0 -108
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/LICENSE +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/setup.cfg +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/__init__.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/__main__.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/config/__init__.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/config/paths.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/config/settings.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/core/__init__.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/core/file_manager.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/core/segment_manager.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/main.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/models/__init__.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/ui/__init__.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/ui/editable_vertex.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/ui/hotkey_dialog.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/ui/hoverable_pixelmap_item.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/ui/hoverable_polygon_item.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/ui/numeric_table_widget_item.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/ui/photo_viewer.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/ui/reorderable_class_table.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/ui/right_panel.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/ui/widgets/adjustments_widget.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/ui/widgets/border_crop_widget.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/ui/widgets/channel_threshold_widget.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/ui/widgets/fragment_threshold_widget.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/ui/widgets/model_selection_widget.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/ui/widgets/settings_widget.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/ui/widgets/status_bar.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/utils/__init__.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/utils/custom_file_system_model.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/utils/logger.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel/utils/utils.py +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel_gui.egg-info/dependency_links.txt +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel_gui.egg-info/entry_points.txt +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel_gui.egg-info/requires.txt +0 -0
- {lazylabel_gui-1.1.8 → lazylabel_gui-1.2.0}/src/lazylabel_gui.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: lazylabel-gui
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.2.0
|
4
4
|
Summary: An image segmentation GUI for generating ML ready mask tensors and annotations.
|
5
5
|
Author-email: "Deniz N. Cakan" <deniz.n.cakan@gmail.com>
|
6
6
|
License: MIT License
|
@@ -57,7 +57,7 @@ Dynamic: license-file
|
|
57
57
|
|
58
58
|
**AI-Assisted Image Segmentation Made Simple**
|
59
59
|
|
60
|
-
LazyLabel combines Meta's Segment Anything Model (SAM) with intuitive editing tools for fast, precise image labeling. Perfect for machine learning datasets
|
60
|
+
LazyLabel combines Meta's Segment Anything Model (SAM) with intuitive editing tools for fast, precise image labeling. Perfect for machine learning datasets and computer vision research.
|
61
61
|
|
62
62
|

|
63
63
|
|
@@ -71,67 +71,57 @@ pip install lazylabel-gui
|
|
71
71
|
lazylabel-gui
|
72
72
|
```
|
73
73
|
|
74
|
+
### Optional: SAM-2 Support
|
75
|
+
For advanced SAM-2 models, install manually:
|
76
|
+
```bash
|
77
|
+
pip install git+https://github.com/facebookresearch/sam2.git
|
78
|
+
```
|
79
|
+
*Note: SAM-2 is optional - LazyLabel works with SAM 1.0 models by default*
|
80
|
+
|
74
81
|
### Usage
|
75
82
|
1. **Open Folder** → Select your image directory
|
76
83
|
2. **Click on image** → AI generates instant masks
|
77
|
-
3. **Fine-tune** → Edit polygons, merge segments
|
84
|
+
3. **Fine-tune** → Edit polygons, merge segments
|
78
85
|
4. **Export** → Clean `.npz` files ready for ML training
|
79
86
|
|
80
87
|
---
|
81
88
|
|
82
89
|
## ✨ Key Features
|
83
90
|
|
84
|
-
|
85
|
-
-
|
86
|
-
-
|
87
|
-
-
|
88
|
-
-
|
89
|
-
|
90
|
-
|
91
|
-
-
|
92
|
-
-
|
93
|
-
-
|
94
|
-
-
|
95
|
-
|
96
|
-
### **⚡ Productivity Tools**
|
97
|
-
- **Image adjustments** (brightness, contrast, gamma)
|
98
|
-
- **Customizable hotkeys** for all functions
|
99
|
-
- **Undo/redo** with full history
|
100
|
-
- **Auto-save** and session persistence
|
101
|
-
|
102
|
-
### **📊 ML-Ready Outputs**
|
103
|
-
- **One-hot encoded** `.npz` format
|
104
|
-
- **Clean class separation** with shape `(H, W, Classes)`
|
105
|
-
- **Batch processing** support
|
106
|
-
- **Existing mask loading** for iterative work
|
91
|
+
- **🧠 One-click AI segmentation** with Meta's SAM and SAM2 models
|
92
|
+
- **🎨 Manual polygon drawing** with full vertex control
|
93
|
+
- **⚡ Smart editing tools** - merge segments, adjust class names, and class order
|
94
|
+
- **📊 ML-ready exports** - One-hot encoded `.npz` format and `.json` for YOLO format
|
95
|
+
- **🔧 Image enhancement** - brightness, contrast, gamma adjustment
|
96
|
+
- **🔍 Image viewer** - zoom, pan, brightness, contrast, and gamma adjustment
|
97
|
+
- **✂️ Edge cropping** - define custom crop areas to focus on specific regions
|
98
|
+
- **🔄 Undo/Redo** - full history of all actions
|
99
|
+
- **💾 Auto-saving** - Automatic saving of your labels when navigating between images
|
100
|
+
- **🎛️ Advanced filtering** - FFT thresholding and color channel thresholding
|
101
|
+
- **⌨️ Customizable hotkeys** for all functions
|
107
102
|
|
108
103
|
---
|
109
104
|
|
110
|
-
## ⌨️ Essential
|
111
|
-
|
112
|
-
|
|
113
|
-
|
114
|
-
| **AI
|
115
|
-
| | `
|
116
|
-
| | `
|
117
|
-
| | `Space` |
|
118
|
-
| **
|
119
|
-
| | `
|
120
|
-
| | `
|
121
|
-
| **
|
122
|
-
|
123
|
-
|
124
|
-
| **Navigation** | `Q` | Pan mode |
|
125
|
-
| | `W/A/S/D` | Pan image |
|
126
|
-
| | `Scroll` | Zoom in/out |
|
127
|
-
|
128
|
-
**💡 All hotkeys are customizable** - Click "Hotkeys" button to personalize shortcuts
|
105
|
+
## ⌨️ Essential Hotkeys
|
106
|
+
|
107
|
+
| Action | Key | Description |
|
108
|
+
|--------|-----|-------------|
|
109
|
+
| **AI Mode** | `1` | Point-click segmentation |
|
110
|
+
| **Draw Mode** | `2` | Manual polygon drawing |
|
111
|
+
| **Edit Mode** | `E` | Select and modify shapes |
|
112
|
+
| **Save Segment** | `Space` | Confirm current mask |
|
113
|
+
| **Merge** | `M` | Combine selected segments |
|
114
|
+
| **Pan** | `Q` + drag | Navigate large images |
|
115
|
+
| **Positive Point** | `Left Click` | Add to segment |
|
116
|
+
| **Negative Point** | `Right Click` | Remove from segment |
|
117
|
+
|
118
|
+
💡 **All hotkeys customizable** - Click "Hotkeys" button to personalize
|
129
119
|
|
130
120
|
---
|
131
121
|
|
132
122
|
## 📦 Output Format
|
133
123
|
|
134
|
-
|
124
|
+
Perfect for ML training - clean, structured data:
|
135
125
|
|
136
126
|
```python
|
137
127
|
import numpy as np
|
@@ -141,41 +131,24 @@ data = np.load('your_image.npz')
|
|
141
131
|
mask = data['mask'] # Shape: (height, width, num_classes)
|
142
132
|
|
143
133
|
# Each channel is a binary mask for one class
|
144
|
-
class_0_mask = mask[:, :, 0] #
|
145
|
-
class_1_mask = mask[:, :, 1] #
|
146
|
-
#
|
134
|
+
class_0_mask = mask[:, :, 0] # Background
|
135
|
+
class_1_mask = mask[:, :, 1] # Object type 1
|
136
|
+
class_2_mask = mask[:, :, 2] # Object type 2
|
147
137
|
```
|
148
138
|
|
149
|
-
|
150
|
-
|
151
|
-
-
|
139
|
+
|
140
|
+
**Ideal for:**
|
141
|
+
- Semantic segmentation datasets
|
142
|
+
- Instance segmentation training
|
152
143
|
- Computer vision research
|
153
144
|
- Automated annotation pipelines
|
154
145
|
|
155
146
|
---
|
156
147
|
|
157
|
-
## 🛠️
|
158
|
-
|
159
|
-
### **Image Enhancement**
|
160
|
-
- **Brightness/Contrast** adjustment sliders
|
161
|
-
- **Gamma correction** for better visibility
|
162
|
-
- **Live preview** of adjustments
|
163
|
-
- **SAM integration** with adjusted images
|
164
|
-
|
165
|
-
### **Smart Filtering**
|
166
|
-
- **Fragment threshold** removes small segments
|
167
|
-
- **Size-based filtering** (0-100% of largest segment)
|
168
|
-
- **Quality control** for clean annotations
|
169
|
-
|
170
|
-
### **Professional Workflow**
|
171
|
-
- **Class management** with custom aliases
|
172
|
-
- **Segment organization** with sortable tables
|
173
|
-
- **Batch export** for large datasets
|
174
|
-
- **Model switching** without restart
|
148
|
+
## 🛠️ Development
|
175
149
|
|
176
|
-
|
177
|
-
|
178
|
-
## 🏗️ Development
|
150
|
+
**Requirements:** Python 3.10+
|
151
|
+
**2.5GB** disk space for SAM model (auto-downloaded)
|
179
152
|
|
180
153
|
### Installation from Source
|
181
154
|
```bash
|
@@ -185,38 +158,26 @@ pip install -e .
|
|
185
158
|
lazylabel-gui
|
186
159
|
```
|
187
160
|
|
188
|
-
###
|
161
|
+
### Testing & Quality
|
189
162
|
```bash
|
190
|
-
#
|
191
|
-
ruff check . && ruff format .
|
192
|
-
|
193
|
-
# Run tests with coverage
|
163
|
+
# Run full test suite
|
194
164
|
python -m pytest --cov=lazylabel --cov-report=html
|
195
165
|
|
196
|
-
#
|
166
|
+
# Code formatting & linting
|
167
|
+
ruff check . && ruff format .
|
197
168
|
```
|
198
169
|
|
199
170
|
### Architecture
|
200
|
-
- **Modular design** with clean separation
|
201
|
-
- **Signal-based communication** between
|
171
|
+
- **Modular design** with clean component separation
|
172
|
+
- **Signal-based communication** between UI elements
|
202
173
|
- **Extensible model system** for new SAM variants
|
203
|
-
- **Comprehensive test suite**
|
204
|
-
|
205
|
-
---
|
206
|
-
|
207
|
-
## 📋 Requirements
|
208
|
-
|
209
|
-
- **Python 3.10+**
|
210
|
-
- **OpenCV** for image processing
|
211
|
-
- **PyQt6** for GUI
|
212
|
-
- **NumPy** for data handling
|
213
|
-
- **2.5GB** disk space for SAM model (auto-downloaded)
|
174
|
+
- **Comprehensive test suite** (150+ tests, 60%+ coverage)
|
214
175
|
|
215
176
|
---
|
216
177
|
|
217
178
|
## 🤝 Contributing
|
218
179
|
|
219
|
-
LazyLabel welcomes contributions! Check out
|
180
|
+
LazyLabel welcomes contributions! Check out:
|
220
181
|
- [Architecture Guide](src/lazylabel/ARCHITECTURE.md) for technical details
|
221
182
|
- [Hotkey System](src/lazylabel/HOTKEY_FEATURE.md) for customization
|
222
183
|
- Issues page for feature requests and bug reports
|
@@ -225,9 +186,8 @@ LazyLabel welcomes contributions! Check out our:
|
|
225
186
|
|
226
187
|
## 🙏 Acknowledgments
|
227
188
|
|
228
|
-
|
229
|
-
- [
|
230
|
-
- [Segment-Anything-UI](https://github.com/branislavhesko/segment-anything-ui) - Early SAM integration concepts
|
189
|
+
- [LabelMe](https://github.com/wkentaro/labelme)
|
190
|
+
- [Segment-Anything-UI](https://github.com/branislavhesko/segment-anything-ui)
|
231
191
|
|
232
192
|
---
|
233
193
|
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# <img src="https://raw.githubusercontent.com/dnzckn/LazyLabel/main/src/lazylabel/demo_pictures/logo2.png" alt="LazyLabel Logo" style="height:60px; vertical-align:middle;" /> <img src="https://raw.githubusercontent.com/dnzckn/LazyLabel/main/src/lazylabel/demo_pictures/logo_black.png" alt="LazyLabel Cursive" style="height:60px; vertical-align:middle;" />
|
2
|
+
|
3
|
+
**AI-Assisted Image Segmentation Made Simple**
|
4
|
+
|
5
|
+
LazyLabel combines Meta's Segment Anything Model (SAM) with intuitive editing tools for fast, precise image labeling. Perfect for machine learning datasets and computer vision research.
|
6
|
+
|
7
|
+

|
8
|
+
|
9
|
+
---
|
10
|
+
|
11
|
+
## 🚀 Quick Start
|
12
|
+
|
13
|
+
### Installation
|
14
|
+
```bash
|
15
|
+
pip install lazylabel-gui
|
16
|
+
lazylabel-gui
|
17
|
+
```
|
18
|
+
|
19
|
+
### Optional: SAM-2 Support
|
20
|
+
For advanced SAM-2 models, install manually:
|
21
|
+
```bash
|
22
|
+
pip install git+https://github.com/facebookresearch/sam2.git
|
23
|
+
```
|
24
|
+
*Note: SAM-2 is optional - LazyLabel works with SAM 1.0 models by default*
|
25
|
+
|
26
|
+
### Usage
|
27
|
+
1. **Open Folder** → Select your image directory
|
28
|
+
2. **Click on image** → AI generates instant masks
|
29
|
+
3. **Fine-tune** → Edit polygons, merge segments
|
30
|
+
4. **Export** → Clean `.npz` files ready for ML training
|
31
|
+
|
32
|
+
---
|
33
|
+
|
34
|
+
## ✨ Key Features
|
35
|
+
|
36
|
+
- **🧠 One-click AI segmentation** with Meta's SAM and SAM2 models
|
37
|
+
- **🎨 Manual polygon drawing** with full vertex control
|
38
|
+
- **⚡ Smart editing tools** - merge segments, adjust class names, and class order
|
39
|
+
- **📊 ML-ready exports** - One-hot encoded `.npz` format and `.json` for YOLO format
|
40
|
+
- **🔧 Image enhancement** - brightness, contrast, gamma adjustment
|
41
|
+
- **🔍 Image viewer** - zoom, pan, brightness, contrast, and gamma adjustment
|
42
|
+
- **✂️ Edge cropping** - define custom crop areas to focus on specific regions
|
43
|
+
- **🔄 Undo/Redo** - full history of all actions
|
44
|
+
- **💾 Auto-saving** - Automatic saving of your labels when navigating between images
|
45
|
+
- **🎛️ Advanced filtering** - FFT thresholding and color channel thresholding
|
46
|
+
- **⌨️ Customizable hotkeys** for all functions
|
47
|
+
|
48
|
+
---
|
49
|
+
|
50
|
+
## ⌨️ Essential Hotkeys
|
51
|
+
|
52
|
+
| Action | Key | Description |
|
53
|
+
|--------|-----|-------------|
|
54
|
+
| **AI Mode** | `1` | Point-click segmentation |
|
55
|
+
| **Draw Mode** | `2` | Manual polygon drawing |
|
56
|
+
| **Edit Mode** | `E` | Select and modify shapes |
|
57
|
+
| **Save Segment** | `Space` | Confirm current mask |
|
58
|
+
| **Merge** | `M` | Combine selected segments |
|
59
|
+
| **Pan** | `Q` + drag | Navigate large images |
|
60
|
+
| **Positive Point** | `Left Click` | Add to segment |
|
61
|
+
| **Negative Point** | `Right Click` | Remove from segment |
|
62
|
+
|
63
|
+
💡 **All hotkeys customizable** - Click "Hotkeys" button to personalize
|
64
|
+
|
65
|
+
---
|
66
|
+
|
67
|
+
## 📦 Output Format
|
68
|
+
|
69
|
+
Perfect for ML training - clean, structured data:
|
70
|
+
|
71
|
+
```python
|
72
|
+
import numpy as np
|
73
|
+
|
74
|
+
# Load your labeled data
|
75
|
+
data = np.load('your_image.npz')
|
76
|
+
mask = data['mask'] # Shape: (height, width, num_classes)
|
77
|
+
|
78
|
+
# Each channel is a binary mask for one class
|
79
|
+
class_0_mask = mask[:, :, 0] # Background
|
80
|
+
class_1_mask = mask[:, :, 1] # Object type 1
|
81
|
+
class_2_mask = mask[:, :, 2] # Object type 2
|
82
|
+
```
|
83
|
+
|
84
|
+
|
85
|
+
**Ideal for:**
|
86
|
+
- Semantic segmentation datasets
|
87
|
+
- Instance segmentation training
|
88
|
+
- Computer vision research
|
89
|
+
- Automated annotation pipelines
|
90
|
+
|
91
|
+
---
|
92
|
+
|
93
|
+
## 🛠️ Development
|
94
|
+
|
95
|
+
**Requirements:** Python 3.10+
|
96
|
+
**2.5GB** disk space for SAM model (auto-downloaded)
|
97
|
+
|
98
|
+
### Installation from Source
|
99
|
+
```bash
|
100
|
+
git clone https://github.com/dnzckn/LazyLabel.git
|
101
|
+
cd LazyLabel
|
102
|
+
pip install -e .
|
103
|
+
lazylabel-gui
|
104
|
+
```
|
105
|
+
|
106
|
+
### Testing & Quality
|
107
|
+
```bash
|
108
|
+
# Run full test suite
|
109
|
+
python -m pytest --cov=lazylabel --cov-report=html
|
110
|
+
|
111
|
+
# Code formatting & linting
|
112
|
+
ruff check . && ruff format .
|
113
|
+
```
|
114
|
+
|
115
|
+
### Architecture
|
116
|
+
- **Modular design** with clean component separation
|
117
|
+
- **Signal-based communication** between UI elements
|
118
|
+
- **Extensible model system** for new SAM variants
|
119
|
+
- **Comprehensive test suite** (150+ tests, 60%+ coverage)
|
120
|
+
|
121
|
+
---
|
122
|
+
|
123
|
+
## 🤝 Contributing
|
124
|
+
|
125
|
+
LazyLabel welcomes contributions! Check out:
|
126
|
+
- [Architecture Guide](src/lazylabel/ARCHITECTURE.md) for technical details
|
127
|
+
- [Hotkey System](src/lazylabel/HOTKEY_FEATURE.md) for customization
|
128
|
+
- Issues page for feature requests and bug reports
|
129
|
+
|
130
|
+
---
|
131
|
+
|
132
|
+
## 🙏 Acknowledgments
|
133
|
+
|
134
|
+
- [LabelMe](https://github.com/wkentaro/labelme)
|
135
|
+
- [Segment-Anything-UI](https://github.com/branislavhesko/segment-anything-ui)
|
136
|
+
|
137
|
+
---
|
138
|
+
|
139
|
+
## ☕ Support
|
140
|
+
|
141
|
+
If LazyLabel saves you time on annotation tasks, [consider supporting the project!](https://buymeacoffee.com/dnzckn)
|
142
|
+
|
143
|
+
---
|
144
|
+
|
145
|
+
**Made with ❤️ for the computer vision community**
|
@@ -44,7 +44,7 @@ class HotkeyManager:
|
|
44
44
|
),
|
45
45
|
HotkeyAction("fit_view", "Fit View", ".", category="Navigation"),
|
46
46
|
# Modes
|
47
|
-
HotkeyAction("sam_mode", "
|
47
|
+
HotkeyAction("sam_mode", "AI Mode (Points + Box)", "1", category="Modes"),
|
48
48
|
HotkeyAction("polygon_mode", "Polygon Mode", "2", category="Modes"),
|
49
49
|
HotkeyAction("bbox_mode", "Bounding Box Mode", "3", category="Modes"),
|
50
50
|
HotkeyAction("selection_mode", "Selection Mode", "E", category="Modes"),
|
@@ -95,7 +95,7 @@ class HotkeyManager:
|
|
95
95
|
# Mouse-related (cannot be reassigned)
|
96
96
|
HotkeyAction(
|
97
97
|
"left_click",
|
98
|
-
"
|
98
|
+
"AI: Point (click) / Box (drag) / Select",
|
99
99
|
"Left Click",
|
100
100
|
category="Mouse",
|
101
101
|
mouse_related=True,
|
@@ -0,0 +1,183 @@
|
|
1
|
+
"""Model management functionality."""
|
2
|
+
|
3
|
+
import os
|
4
|
+
from collections.abc import Callable
|
5
|
+
|
6
|
+
from ..config import Paths
|
7
|
+
from ..models.sam_model import SamModel
|
8
|
+
from ..utils.logger import logger
|
9
|
+
|
10
|
+
# Optional SAM-2 support
|
11
|
+
try:
|
12
|
+
from ..models.sam2_model import Sam2Model
|
13
|
+
|
14
|
+
SAM2_AVAILABLE = True
|
15
|
+
except ImportError:
|
16
|
+
logger.info(
|
17
|
+
"SAM-2 not available. Install with: pip install git+https://github.com/facebookresearch/sam2.git"
|
18
|
+
)
|
19
|
+
Sam2Model = None
|
20
|
+
SAM2_AVAILABLE = False
|
21
|
+
|
22
|
+
|
23
|
+
class ModelManager:
|
24
|
+
"""Manages SAM model loading and selection."""
|
25
|
+
|
26
|
+
def __init__(self, paths: Paths):
|
27
|
+
self.paths = paths
|
28
|
+
self.sam_model: SamModel | None = None
|
29
|
+
self.current_models_folder: str | None = None
|
30
|
+
self.on_model_changed: Callable[[str], None] | None = None
|
31
|
+
|
32
|
+
def initialize_default_model(self, model_type: str = "vit_h") -> SamModel | None:
|
33
|
+
"""Initialize the default SAM model.
|
34
|
+
|
35
|
+
Returns:
|
36
|
+
SamModel instance if successful, None if failed
|
37
|
+
"""
|
38
|
+
try:
|
39
|
+
logger.info(f"Step 4/8: Loading {model_type.upper()} model...")
|
40
|
+
self.sam_model = SamModel(model_type=model_type)
|
41
|
+
self.current_models_folder = str(self.paths.models_dir)
|
42
|
+
return self.sam_model
|
43
|
+
except Exception as e:
|
44
|
+
logger.error(f"Step 4/8: Failed to initialize default model: {e}")
|
45
|
+
self.sam_model = None
|
46
|
+
return None
|
47
|
+
|
48
|
+
def get_available_models(self, folder_path: str) -> list[tuple[str, str]]:
|
49
|
+
"""Get list of available .pth models in folder.
|
50
|
+
|
51
|
+
Returns:
|
52
|
+
List of (display_name, full_path) tuples
|
53
|
+
"""
|
54
|
+
pth_files = []
|
55
|
+
for root, _dirs, files in os.walk(folder_path):
|
56
|
+
for file in files:
|
57
|
+
if file.lower().endswith(".pth") or file.lower().endswith(".pt"):
|
58
|
+
full_path = os.path.join(root, file)
|
59
|
+
rel_path = os.path.relpath(full_path, folder_path)
|
60
|
+
pth_files.append((rel_path, full_path))
|
61
|
+
|
62
|
+
return sorted(pth_files, key=lambda x: x[0])
|
63
|
+
|
64
|
+
def detect_model_type(self, model_path: str) -> str:
|
65
|
+
"""Detect model type from filename."""
|
66
|
+
filename = os.path.basename(model_path).lower()
|
67
|
+
|
68
|
+
# Check if it's a SAM2 model
|
69
|
+
if self._is_sam2_model(model_path):
|
70
|
+
if "tiny" in filename or "_t" in filename:
|
71
|
+
return "sam2_tiny"
|
72
|
+
elif "small" in filename or "_s" in filename:
|
73
|
+
return "sam2_small"
|
74
|
+
elif "base_plus" in filename or "_b+" in filename:
|
75
|
+
return "sam2_base_plus"
|
76
|
+
elif "large" in filename or "_l" in filename:
|
77
|
+
return "sam2_large"
|
78
|
+
else:
|
79
|
+
return "sam2_large" # default for SAM2
|
80
|
+
else:
|
81
|
+
# Original SAM model types
|
82
|
+
if "vit_l" in filename or "large" in filename:
|
83
|
+
return "vit_l"
|
84
|
+
elif "vit_b" in filename or "base" in filename:
|
85
|
+
return "vit_b"
|
86
|
+
elif "vit_h" in filename or "huge" in filename:
|
87
|
+
return "vit_h"
|
88
|
+
return "vit_h" # default for SAM1
|
89
|
+
|
90
|
+
def _is_sam2_model(self, model_path: str) -> bool:
|
91
|
+
"""Check if the model is a SAM2 model based on filename patterns."""
|
92
|
+
filename = os.path.basename(model_path).lower()
|
93
|
+
sam2_indicators = ["sam2", "sam2.1", "hiera", "_t.", "_s.", "_b+.", "_l."]
|
94
|
+
return any(indicator in filename for indicator in sam2_indicators)
|
95
|
+
|
96
|
+
def load_custom_model(self, model_path: str) -> bool:
|
97
|
+
"""Load a custom model from path.
|
98
|
+
|
99
|
+
Returns:
|
100
|
+
True if successful, False otherwise
|
101
|
+
"""
|
102
|
+
if not os.path.exists(model_path):
|
103
|
+
return False
|
104
|
+
|
105
|
+
model_type = self.detect_model_type(model_path)
|
106
|
+
|
107
|
+
try:
|
108
|
+
# Clear existing model from memory
|
109
|
+
if self.sam_model is not None:
|
110
|
+
del self.sam_model
|
111
|
+
import torch
|
112
|
+
|
113
|
+
torch.cuda.empty_cache() if torch.cuda.is_available() else None
|
114
|
+
|
115
|
+
# Create appropriate model instance
|
116
|
+
if self._is_sam2_model(model_path):
|
117
|
+
if not SAM2_AVAILABLE:
|
118
|
+
logger.warning(
|
119
|
+
f"SAM-2 model detected but SAM-2 not installed: {model_path}"
|
120
|
+
)
|
121
|
+
logger.info(
|
122
|
+
"Install SAM-2 with: pip install git+https://github.com/facebookresearch/sam2.git"
|
123
|
+
)
|
124
|
+
return False
|
125
|
+
|
126
|
+
logger.info(f"Loading SAM2 model: {model_type}")
|
127
|
+
self.sam_model = Sam2Model(model_path)
|
128
|
+
else:
|
129
|
+
logger.info(f"Loading SAM1 model: {model_type}")
|
130
|
+
# Convert SAM2 model types back to SAM1 types for compatibility
|
131
|
+
sam1_model_type = model_type
|
132
|
+
if model_type.startswith("sam2_"):
|
133
|
+
type_mapping = {
|
134
|
+
"sam2_tiny": "vit_b",
|
135
|
+
"sam2_small": "vit_b",
|
136
|
+
"sam2_base_plus": "vit_l",
|
137
|
+
"sam2_large": "vit_h",
|
138
|
+
}
|
139
|
+
sam1_model_type = type_mapping.get(model_type, "vit_h")
|
140
|
+
|
141
|
+
# Create SAM1 model with custom path
|
142
|
+
self.sam_model = SamModel(
|
143
|
+
model_type=sam1_model_type, custom_model_path=model_path
|
144
|
+
)
|
145
|
+
|
146
|
+
success = self.sam_model.is_loaded
|
147
|
+
|
148
|
+
if success and self.on_model_changed:
|
149
|
+
model_name = os.path.basename(model_path)
|
150
|
+
self.on_model_changed(f"Current: {model_name}")
|
151
|
+
|
152
|
+
return success
|
153
|
+
|
154
|
+
except Exception as e:
|
155
|
+
logger.error(f"Failed to load custom model: {e}")
|
156
|
+
self.sam_model = None
|
157
|
+
return False
|
158
|
+
|
159
|
+
def set_models_folder(self, folder_path: str) -> None:
|
160
|
+
"""Set the current models folder."""
|
161
|
+
self.current_models_folder = folder_path
|
162
|
+
|
163
|
+
def get_models_folder(self) -> str | None:
|
164
|
+
"""Get the current models folder."""
|
165
|
+
return self.current_models_folder
|
166
|
+
|
167
|
+
def is_model_available(self) -> bool:
|
168
|
+
"""Check if a SAM model is loaded and available."""
|
169
|
+
return self.sam_model is not None and getattr(
|
170
|
+
self.sam_model, "is_loaded", False
|
171
|
+
)
|
172
|
+
|
173
|
+
def set_image_from_path(self, image_path: str) -> bool:
|
174
|
+
"""Set image for SAM model from file path."""
|
175
|
+
if not self.is_model_available():
|
176
|
+
return False
|
177
|
+
return self.sam_model.set_image_from_path(image_path)
|
178
|
+
|
179
|
+
def set_image_from_array(self, image_array) -> bool:
|
180
|
+
"""Set image for SAM model from numpy array."""
|
181
|
+
if not self.is_model_available():
|
182
|
+
return False
|
183
|
+
return self.sam_model.set_image_from_array(image_array)
|