lazylabel-gui 1.1.5__py3-none-any.whl → 1.1.7__py3-none-any.whl
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/__main__.py +6 -0
- lazylabel/config/settings.py +72 -65
- lazylabel/core/file_manager.py +123 -122
- lazylabel/core/model_manager.py +96 -95
- lazylabel/main.py +39 -37
- lazylabel/models/sam_model.py +211 -200
- lazylabel/ui/control_panel.py +283 -245
- lazylabel/ui/hotkey_dialog.py +417 -416
- lazylabel/ui/main_window.py +2020 -1882
- lazylabel/ui/photo_viewer.py +55 -1
- lazylabel/ui/widgets/adjustments_widget.py +372 -108
- lazylabel/ui/widgets/settings_widget.py +125 -113
- lazylabel/utils/logger.py +45 -0
- lazylabel_gui-1.1.7.dist-info/METADATA +240 -0
- {lazylabel_gui-1.1.5.dist-info → lazylabel_gui-1.1.7.dist-info}/RECORD +19 -17
- lazylabel_gui-1.1.5.dist-info/METADATA +0 -243
- {lazylabel_gui-1.1.5.dist-info → lazylabel_gui-1.1.7.dist-info}/WHEEL +0 -0
- {lazylabel_gui-1.1.5.dist-info → lazylabel_gui-1.1.7.dist-info}/entry_points.txt +0 -0
- {lazylabel_gui-1.1.5.dist-info → lazylabel_gui-1.1.7.dist-info}/licenses/LICENSE +0 -0
- {lazylabel_gui-1.1.5.dist-info → lazylabel_gui-1.1.7.dist-info}/top_level.txt +0 -0
@@ -1,113 +1,125 @@
|
|
1
|
-
"""Settings widget for save options."""
|
2
|
-
|
3
|
-
from PyQt6.QtCore import pyqtSignal
|
4
|
-
from PyQt6.QtWidgets import QCheckBox, QGroupBox, QVBoxLayout, QWidget
|
5
|
-
|
6
|
-
|
7
|
-
class SettingsWidget(QWidget):
|
8
|
-
"""Widget for application settings."""
|
9
|
-
|
10
|
-
settings_changed = pyqtSignal()
|
11
|
-
|
12
|
-
def __init__(self, parent=None):
|
13
|
-
super().__init__(parent)
|
14
|
-
self._setup_ui()
|
15
|
-
self._connect_signals()
|
16
|
-
|
17
|
-
def _setup_ui(self):
|
18
|
-
"""Setup the UI layout."""
|
19
|
-
group = QGroupBox("Settings")
|
20
|
-
layout = QVBoxLayout(group)
|
21
|
-
|
22
|
-
# Auto-save
|
23
|
-
self.chk_auto_save = QCheckBox("Auto-Save on Navigate")
|
24
|
-
self.chk_auto_save.setToolTip(
|
25
|
-
"Automatically save work when
|
26
|
-
)
|
27
|
-
self.chk_auto_save.setChecked(True)
|
28
|
-
layout.addWidget(self.chk_auto_save)
|
29
|
-
|
30
|
-
# Save NPZ
|
31
|
-
self.chk_save_npz = QCheckBox("Save .npz")
|
32
|
-
self.chk_save_npz.setChecked(True)
|
33
|
-
self.chk_save_npz.setToolTip(
|
34
|
-
"Save the final mask as a compressed NumPy NPZ file."
|
35
|
-
)
|
36
|
-
layout.addWidget(self.chk_save_npz)
|
37
|
-
|
38
|
-
# Save TXT
|
39
|
-
self.chk_save_txt = QCheckBox("Save .txt")
|
40
|
-
self.chk_save_txt.setChecked(True)
|
41
|
-
self.chk_save_txt.setToolTip(
|
42
|
-
"Save bounding box annotations in YOLO TXT format."
|
43
|
-
)
|
44
|
-
layout.addWidget(self.chk_save_txt)
|
45
|
-
|
46
|
-
# YOLO with aliases
|
47
|
-
self.chk_yolo_use_alias = QCheckBox("Save YOLO with Class Aliases")
|
48
|
-
self.chk_yolo_use_alias.setToolTip(
|
49
|
-
"If checked, saves YOLO .txt files using class alias names instead of numeric IDs.\n"
|
50
|
-
"This is useful when a separate .yaml or .names file defines the classes."
|
51
|
-
)
|
52
|
-
self.chk_yolo_use_alias.setChecked(True)
|
53
|
-
layout.addWidget(self.chk_yolo_use_alias)
|
54
|
-
|
55
|
-
# Save class aliases
|
56
|
-
self.chk_save_class_aliases = QCheckBox("Save Class Aliases (.json)")
|
57
|
-
self.chk_save_class_aliases.setToolTip(
|
58
|
-
"Save class aliases to a companion JSON file."
|
59
|
-
)
|
60
|
-
self.chk_save_class_aliases.setChecked(False)
|
61
|
-
layout.addWidget(self.chk_save_class_aliases)
|
62
|
-
|
63
|
-
#
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
def
|
106
|
-
"""
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
1
|
+
"""Settings widget for save options."""
|
2
|
+
|
3
|
+
from PyQt6.QtCore import pyqtSignal
|
4
|
+
from PyQt6.QtWidgets import QCheckBox, QGroupBox, QVBoxLayout, QWidget
|
5
|
+
|
6
|
+
|
7
|
+
class SettingsWidget(QWidget):
|
8
|
+
"""Widget for application settings."""
|
9
|
+
|
10
|
+
settings_changed = pyqtSignal()
|
11
|
+
|
12
|
+
def __init__(self, parent=None):
|
13
|
+
super().__init__(parent)
|
14
|
+
self._setup_ui()
|
15
|
+
self._connect_signals()
|
16
|
+
|
17
|
+
def _setup_ui(self):
|
18
|
+
"""Setup the UI layout."""
|
19
|
+
group = QGroupBox("Settings")
|
20
|
+
layout = QVBoxLayout(group)
|
21
|
+
|
22
|
+
# Auto-save
|
23
|
+
self.chk_auto_save = QCheckBox("Auto-Save on Navigate")
|
24
|
+
self.chk_auto_save.setToolTip(
|
25
|
+
"Automatically save work when switching to any new image (navigation keys, double-click, etc.)"
|
26
|
+
)
|
27
|
+
self.chk_auto_save.setChecked(True)
|
28
|
+
layout.addWidget(self.chk_auto_save)
|
29
|
+
|
30
|
+
# Save NPZ
|
31
|
+
self.chk_save_npz = QCheckBox("Save .npz")
|
32
|
+
self.chk_save_npz.setChecked(True)
|
33
|
+
self.chk_save_npz.setToolTip(
|
34
|
+
"Save the final mask as a compressed NumPy NPZ file."
|
35
|
+
)
|
36
|
+
layout.addWidget(self.chk_save_npz)
|
37
|
+
|
38
|
+
# Save TXT
|
39
|
+
self.chk_save_txt = QCheckBox("Save .txt")
|
40
|
+
self.chk_save_txt.setChecked(True)
|
41
|
+
self.chk_save_txt.setToolTip(
|
42
|
+
"Save bounding box annotations in YOLO TXT format."
|
43
|
+
)
|
44
|
+
layout.addWidget(self.chk_save_txt)
|
45
|
+
|
46
|
+
# YOLO with aliases
|
47
|
+
self.chk_yolo_use_alias = QCheckBox("Save YOLO with Class Aliases")
|
48
|
+
self.chk_yolo_use_alias.setToolTip(
|
49
|
+
"If checked, saves YOLO .txt files using class alias names instead of numeric IDs.\n"
|
50
|
+
"This is useful when a separate .yaml or .names file defines the classes."
|
51
|
+
)
|
52
|
+
self.chk_yolo_use_alias.setChecked(True)
|
53
|
+
layout.addWidget(self.chk_yolo_use_alias)
|
54
|
+
|
55
|
+
# Save class aliases
|
56
|
+
self.chk_save_class_aliases = QCheckBox("Save Class Aliases (.json)")
|
57
|
+
self.chk_save_class_aliases.setToolTip(
|
58
|
+
"Save class aliases to a companion JSON file."
|
59
|
+
)
|
60
|
+
self.chk_save_class_aliases.setChecked(False)
|
61
|
+
layout.addWidget(self.chk_save_class_aliases)
|
62
|
+
|
63
|
+
# Operate on View
|
64
|
+
self.chk_operate_on_view = QCheckBox("Operate On View")
|
65
|
+
self.chk_operate_on_view.setToolTip(
|
66
|
+
"If checked, SAM model will operate on the currently displayed (adjusted) image.\n"
|
67
|
+
"Otherwise, it operates on the original image."
|
68
|
+
)
|
69
|
+
self.chk_operate_on_view.setChecked(False)
|
70
|
+
layout.addWidget(self.chk_operate_on_view)
|
71
|
+
|
72
|
+
# Main layout
|
73
|
+
main_layout = QVBoxLayout(self)
|
74
|
+
main_layout.setContentsMargins(0, 0, 0, 0)
|
75
|
+
main_layout.addWidget(group)
|
76
|
+
|
77
|
+
def _connect_signals(self):
|
78
|
+
"""Connect internal signals."""
|
79
|
+
self.chk_save_npz.stateChanged.connect(self._handle_save_checkbox_change)
|
80
|
+
self.chk_save_txt.stateChanged.connect(self._handle_save_checkbox_change)
|
81
|
+
|
82
|
+
# Connect all checkboxes to settings changed signal
|
83
|
+
for checkbox in [
|
84
|
+
self.chk_auto_save,
|
85
|
+
self.chk_save_npz,
|
86
|
+
self.chk_save_txt,
|
87
|
+
self.chk_yolo_use_alias,
|
88
|
+
self.chk_save_class_aliases,
|
89
|
+
self.chk_operate_on_view,
|
90
|
+
]:
|
91
|
+
checkbox.stateChanged.connect(self.settings_changed)
|
92
|
+
|
93
|
+
def _handle_save_checkbox_change(self):
|
94
|
+
"""Ensure at least one save format is selected."""
|
95
|
+
is_npz_checked = self.chk_save_npz.isChecked()
|
96
|
+
is_txt_checked = self.chk_save_txt.isChecked()
|
97
|
+
|
98
|
+
if not is_npz_checked and not is_txt_checked:
|
99
|
+
sender = self.sender()
|
100
|
+
if sender == self.chk_save_npz:
|
101
|
+
self.chk_save_txt.setChecked(True)
|
102
|
+
else:
|
103
|
+
self.chk_save_npz.setChecked(True)
|
104
|
+
|
105
|
+
def get_settings(self):
|
106
|
+
"""Get current settings as dictionary."""
|
107
|
+
return {
|
108
|
+
"auto_save": self.chk_auto_save.isChecked(),
|
109
|
+
"save_npz": self.chk_save_npz.isChecked(),
|
110
|
+
"save_txt": self.chk_save_txt.isChecked(),
|
111
|
+
"yolo_use_alias": self.chk_yolo_use_alias.isChecked(),
|
112
|
+
"save_class_aliases": self.chk_save_class_aliases.isChecked(),
|
113
|
+
"operate_on_view": self.chk_operate_on_view.isChecked(),
|
114
|
+
}
|
115
|
+
|
116
|
+
def set_settings(self, settings):
|
117
|
+
"""Set settings from dictionary."""
|
118
|
+
self.chk_auto_save.setChecked(settings.get("auto_save", True))
|
119
|
+
self.chk_save_npz.setChecked(settings.get("save_npz", True))
|
120
|
+
self.chk_save_txt.setChecked(settings.get("save_txt", True))
|
121
|
+
self.chk_yolo_use_alias.setChecked(settings.get("yolo_use_alias", True))
|
122
|
+
self.chk_save_class_aliases.setChecked(
|
123
|
+
settings.get("save_class_aliases", False)
|
124
|
+
)
|
125
|
+
self.chk_operate_on_view.setChecked(settings.get("operate_on_view", False))
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import logging
|
2
|
+
import os
|
3
|
+
|
4
|
+
|
5
|
+
def setup_logging(log_file="lazylabel.log", level=logging.INFO):
|
6
|
+
"""
|
7
|
+
Sets up the logging configuration for the application.
|
8
|
+
|
9
|
+
Args:
|
10
|
+
log_file (str): The name of the log file.
|
11
|
+
level (int): The logging level (e.g., logging.INFO, logging.DEBUG).
|
12
|
+
"""
|
13
|
+
log_dir = os.path.join(os.path.expanduser("~"), ".lazylabel", "logs")
|
14
|
+
os.makedirs(log_dir, exist_ok=True)
|
15
|
+
log_path = os.path.join(log_dir, log_file)
|
16
|
+
|
17
|
+
# Create a logger
|
18
|
+
logger = logging.getLogger("lazylabel")
|
19
|
+
logger.setLevel(level)
|
20
|
+
|
21
|
+
# Create handlers
|
22
|
+
# Console handler
|
23
|
+
c_handler = logging.StreamHandler()
|
24
|
+
c_handler.setLevel(level)
|
25
|
+
|
26
|
+
# File handler
|
27
|
+
f_handler = logging.FileHandler(log_path)
|
28
|
+
f_handler.setLevel(level)
|
29
|
+
|
30
|
+
# Create formatters and add it to handlers
|
31
|
+
c_format = logging.Formatter("%(levelname)s: %(message)s")
|
32
|
+
f_format = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
33
|
+
c_handler.setFormatter(c_format)
|
34
|
+
f_handler.setFormatter(f_format)
|
35
|
+
|
36
|
+
# Add handlers to the logger
|
37
|
+
if not logger.handlers: # Avoid adding handlers multiple times
|
38
|
+
logger.addHandler(c_handler)
|
39
|
+
logger.addHandler(f_handler)
|
40
|
+
|
41
|
+
return logger
|
42
|
+
|
43
|
+
|
44
|
+
# Initialize logger for the application
|
45
|
+
logger = setup_logging()
|
@@ -0,0 +1,240 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: lazylabel-gui
|
3
|
+
Version: 1.1.7
|
4
|
+
Summary: An image segmentation GUI for generating ML ready mask tensors and annotations.
|
5
|
+
Author-email: "Deniz N. Cakan" <deniz.n.cakan@gmail.com>
|
6
|
+
License: MIT License
|
7
|
+
|
8
|
+
Copyright (c) 2025 Deniz N. Cakan
|
9
|
+
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
12
|
+
in the Software without restriction, including without limitation the rights
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
15
|
+
furnished to do so, subject to the following conditions:
|
16
|
+
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
18
|
+
copies or substantial portions of the Software.
|
19
|
+
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
26
|
+
SOFTWARE.
|
27
|
+
|
28
|
+
Project-URL: Homepage, https://github.com/dnzckn/lazylabel
|
29
|
+
Project-URL: Bug Tracker, https://github.com/dnzckn/lazylabel/issues
|
30
|
+
Classifier: Programming Language :: Python :: 3
|
31
|
+
Classifier: License :: OSI Approved :: MIT License
|
32
|
+
Classifier: Operating System :: OS Independent
|
33
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
34
|
+
Classifier: Environment :: X11 Applications :: Qt
|
35
|
+
Requires-Python: >=3.10
|
36
|
+
Description-Content-Type: text/markdown
|
37
|
+
License-File: LICENSE
|
38
|
+
Requires-Dist: PyQt6>=6.9.0
|
39
|
+
Requires-Dist: pyqtdarktheme==2.1.0
|
40
|
+
Requires-Dist: torch>=2.7.1
|
41
|
+
Requires-Dist: torchvision>=0.22.1
|
42
|
+
Requires-Dist: segment-anything==1.0
|
43
|
+
Requires-Dist: numpy>=2.1.2
|
44
|
+
Requires-Dist: opencv-python>=4.11.0.86
|
45
|
+
Requires-Dist: scipy>=1.15.3
|
46
|
+
Requires-Dist: requests>=2.32.4
|
47
|
+
Requires-Dist: tqdm>=4.67.1
|
48
|
+
Provides-Extra: dev
|
49
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
50
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
51
|
+
Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
|
52
|
+
Requires-Dist: pytest-qt>=4.2.0; extra == "dev"
|
53
|
+
Requires-Dist: ruff>=0.8.0; extra == "dev"
|
54
|
+
Dynamic: license-file
|
55
|
+
|
56
|
+
# <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;" />
|
57
|
+
|
58
|
+
**AI-Assisted Image Segmentation Made Simple**
|
59
|
+
|
60
|
+
LazyLabel combines Meta's Segment Anything Model (SAM) with intuitive editing tools for fast, precise image labeling. Perfect for machine learning datasets, computer vision research, and annotation workflows.
|
61
|
+
|
62
|
+

|
63
|
+
|
64
|
+
---
|
65
|
+
|
66
|
+
## 🚀 Quick Start
|
67
|
+
|
68
|
+
### Installation
|
69
|
+
```bash
|
70
|
+
pip install lazylabel-gui
|
71
|
+
lazylabel-gui
|
72
|
+
```
|
73
|
+
|
74
|
+
### Usage
|
75
|
+
1. **Open Folder** → Select your image directory
|
76
|
+
2. **Click on image** → AI generates instant masks
|
77
|
+
3. **Fine-tune** → Edit polygons, merge segments, adjust classes
|
78
|
+
4. **Export** → Clean `.npz` files ready for ML training
|
79
|
+
|
80
|
+
---
|
81
|
+
|
82
|
+
## ✨ Key Features
|
83
|
+
|
84
|
+
### **🧠 AI-Powered Segmentation**
|
85
|
+
- **One-click masking** with Meta's SAM model
|
86
|
+
- **Smart prompting** via positive/negative points
|
87
|
+
- **Fragment filtering** to remove small artifacts
|
88
|
+
- **Multiple model support** (VIT-H, VIT-L, VIT-B)
|
89
|
+
|
90
|
+
### **🎨 Advanced Editing**
|
91
|
+
- **Polygon drawing** with full vertex control
|
92
|
+
- **Bounding box** annotation mode
|
93
|
+
- **Shape merging** and class assignment
|
94
|
+
- **Edit mode** for precision adjustments
|
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
|
107
|
+
|
108
|
+
---
|
109
|
+
|
110
|
+
## ⌨️ Essential Controls
|
111
|
+
|
112
|
+
| Mode | Key | Action |
|
113
|
+
|------|-----|--------|
|
114
|
+
| **AI Segmentation** | `1` | Point mode for SAM |
|
115
|
+
| | `Left Click` | Add positive point |
|
116
|
+
| | `Right Click` | Add negative point |
|
117
|
+
| | `Space` | Save segment |
|
118
|
+
| **Manual Drawing** | `2` | Polygon mode |
|
119
|
+
| | `Left Click` | Add vertex |
|
120
|
+
| | `Enter` | Close polygon |
|
121
|
+
| **Editing** | `E` | Selection mode |
|
122
|
+
| | `R` | Edit selected shapes |
|
123
|
+
| | `M` | Merge selected segments |
|
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
|
129
|
+
|
130
|
+
---
|
131
|
+
|
132
|
+
## 📦 Output Format
|
133
|
+
|
134
|
+
LazyLabel exports clean, ML-ready data:
|
135
|
+
|
136
|
+
```python
|
137
|
+
import numpy as np
|
138
|
+
|
139
|
+
# Load your labeled data
|
140
|
+
data = np.load('your_image.npz')
|
141
|
+
mask = data['mask'] # Shape: (height, width, num_classes)
|
142
|
+
|
143
|
+
# Each channel is a binary mask for one class
|
144
|
+
class_0_mask = mask[:, :, 0] # Binary mask for class 0
|
145
|
+
class_1_mask = mask[:, :, 1] # Binary mask for class 1
|
146
|
+
# ... and so on
|
147
|
+
```
|
148
|
+
|
149
|
+
**Perfect for:**
|
150
|
+
- Semantic segmentation training
|
151
|
+
- Instance segmentation datasets
|
152
|
+
- Computer vision research
|
153
|
+
- Automated annotation pipelines
|
154
|
+
|
155
|
+
---
|
156
|
+
|
157
|
+
## 🛠️ Advanced Features
|
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
|
175
|
+
|
176
|
+
---
|
177
|
+
|
178
|
+
## 🏗️ Development
|
179
|
+
|
180
|
+
### Installation from Source
|
181
|
+
```bash
|
182
|
+
git clone https://github.com/dnzckn/LazyLabel.git
|
183
|
+
cd LazyLabel
|
184
|
+
pip install -e .
|
185
|
+
lazylabel-gui
|
186
|
+
```
|
187
|
+
|
188
|
+
### Code Quality & Testing
|
189
|
+
```bash
|
190
|
+
# Linting & formatting
|
191
|
+
ruff check . && ruff format .
|
192
|
+
|
193
|
+
# Run tests with coverage
|
194
|
+
python -m pytest --cov=lazylabel --cov-report=html
|
195
|
+
|
196
|
+
# All tests pass with 60%+ coverage
|
197
|
+
```
|
198
|
+
|
199
|
+
### Architecture
|
200
|
+
- **Modular design** with clean separation of concerns
|
201
|
+
- **Signal-based communication** between components
|
202
|
+
- **Extensible model system** for new SAM variants
|
203
|
+
- **Comprehensive test suite** with 95% speed optimization
|
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)
|
214
|
+
|
215
|
+
---
|
216
|
+
|
217
|
+
## 🤝 Contributing
|
218
|
+
|
219
|
+
LazyLabel welcomes contributions! Check out our:
|
220
|
+
- [Architecture Guide](src/lazylabel/ARCHITECTURE.md) for technical details
|
221
|
+
- [Hotkey System](src/lazylabel/HOTKEY_FEATURE.md) for customization
|
222
|
+
- Issues page for feature requests and bug reports
|
223
|
+
|
224
|
+
---
|
225
|
+
|
226
|
+
## 🙏 Acknowledgments
|
227
|
+
|
228
|
+
LazyLabel was inspired by and builds upon the excellent work of:
|
229
|
+
- [LabelMe](https://github.com/wkentaro/labelme) - The pioneering open-source image annotation tool
|
230
|
+
- [Segment-Anything-UI](https://github.com/branislavhesko/segment-anything-ui) - Early SAM integration concepts
|
231
|
+
|
232
|
+
---
|
233
|
+
|
234
|
+
## ☕ Support
|
235
|
+
|
236
|
+
If LazyLabel saves you time on annotation tasks, [consider supporting the project!](https://buymeacoffee.com/dnzckn)
|
237
|
+
|
238
|
+
---
|
239
|
+
|
240
|
+
**Made with ❤️ for the computer vision community**
|
@@ -1,37 +1,39 @@
|
|
1
1
|
lazylabel/__init__.py,sha256=0yitHZYNNuF4Rqj7cqE0TrfDV8zhzKD3A5W1vOymHK4,199
|
2
|
-
lazylabel/
|
2
|
+
lazylabel/__main__.py,sha256=5IWklQrNkzeMH6rchzZxxoeSnZlkz-HRiPWXqjjj2yA,139
|
3
|
+
lazylabel/main.py,sha256=8NkDYawiSRavXv35Lh2nYAz88EYVt8jrJXYZsk6EE7M,849
|
3
4
|
lazylabel/config/__init__.py,sha256=TnDXH0yx0zy97FfjpiVPHleMgMrM5YwWrUbvevSNJjg,263
|
4
5
|
lazylabel/config/hotkeys.py,sha256=bbHPpCNokwgH0EX74vGcNdn3RoyVZNVr-is6us-7J3M,7990
|
5
6
|
lazylabel/config/paths.py,sha256=ZVKbtaNOxmYO4l6JgsY-8DXaE_jaJfDg2RQJJn3-5nw,1275
|
6
|
-
lazylabel/config/settings.py,sha256=
|
7
|
+
lazylabel/config/settings.py,sha256=0muOT64zBr9Tn-JbyYRPf9xX760-X84ov93m56SpSHA,1898
|
7
8
|
lazylabel/core/__init__.py,sha256=FmRjop_uIBSJwKMGhaZ-3Iwu34LkoxTuD-hnq5vbTSY,232
|
8
|
-
lazylabel/core/file_manager.py,sha256=
|
9
|
-
lazylabel/core/model_manager.py,sha256=
|
9
|
+
lazylabel/core/file_manager.py,sha256=I8josIoPtRYQXcaBZbC5fb5zqCxbFlTVitkbKjyOz70,4715
|
10
|
+
lazylabel/core/model_manager.py,sha256=PZj8YPLOacn_TnFcnPDY3stqapsT4B9mUYqJ500gCtM,3326
|
10
11
|
lazylabel/core/segment_manager.py,sha256=M6kHcYeiub3WqL01NElCvKOc2GNmf72LUM1W8XwSaxc,6465
|
11
12
|
lazylabel/models/__init__.py,sha256=fIlk_0DuZfiClcm0XlZdimeHzunQwBmTMI4PcGsaymw,91
|
12
|
-
lazylabel/models/sam_model.py,sha256=
|
13
|
+
lazylabel/models/sam_model.py,sha256=anh4XMkoX8U5MCZHV5HqN0x8iVnRYi50rcXFt6LdFCQ,8020
|
13
14
|
lazylabel/ui/__init__.py,sha256=4qDIh9y6tABPmD8MAMGZn_G7oSRyrcHt2HkjoWgbGH4,268
|
14
|
-
lazylabel/ui/control_panel.py,sha256=
|
15
|
+
lazylabel/ui/control_panel.py,sha256=ZYclv2G1-HR_pg-b-4heU8hsKfE35jggLvcmJ3clHZA,10471
|
15
16
|
lazylabel/ui/editable_vertex.py,sha256=nAFC2UuFfbvMbGBbAiLWA77cS5-Hn3a08xe1_QLz2yk,2449
|
16
|
-
lazylabel/ui/hotkey_dialog.py,sha256=
|
17
|
+
lazylabel/ui/hotkey_dialog.py,sha256=U_B76HLOxWdWkfA4d2XgRUaZTJPAAE_m5fmwf7Rh-5Y,14743
|
17
18
|
lazylabel/ui/hoverable_pixelmap_item.py,sha256=kJFOp7WXiyHpNf7l73TZjiob85jgP30b5MZvu_z5L3c,728
|
18
19
|
lazylabel/ui/hoverable_polygon_item.py,sha256=aclUwd0P8H8xbcep6GwhnfaVs1zSkqeZKAL-xeDyMiU,1222
|
19
|
-
lazylabel/ui/main_window.py,sha256=
|
20
|
+
lazylabel/ui/main_window.py,sha256=7i0SQ3gX8RsRcnyrX2F9vaiKMigLU4iWApCQcVKgIqo,83728
|
20
21
|
lazylabel/ui/numeric_table_widget_item.py,sha256=dQUlIFu9syCxTGAHVIlmbgkI7aJ3f3wmDPBz1AGK9Bg,283
|
21
|
-
lazylabel/ui/photo_viewer.py,sha256=
|
22
|
+
lazylabel/ui/photo_viewer.py,sha256=f93Mn9ajR2CYakJbbhhHvD5blKrwiGq3ZYgro-k2npc,4217
|
22
23
|
lazylabel/ui/reorderable_class_table.py,sha256=sxHhQre5O_MXLDFgKnw43QnvXXoqn5xRKMGitgO7muI,2371
|
23
24
|
lazylabel/ui/right_panel.py,sha256=-PeXcu7Lr-xhZniBMvWLDPiFb_RAHYAcILyw8fPJs6I,13139
|
24
25
|
lazylabel/ui/widgets/__init__.py,sha256=bYjLRTqWdi4hcPfSSXmXuT-0c5Aee7HnnQurv_k5bfY,314
|
25
|
-
lazylabel/ui/widgets/adjustments_widget.py,sha256=
|
26
|
+
lazylabel/ui/widgets/adjustments_widget.py,sha256=CJXtq19hfy12ggCA3_RMwETKNnVAQsn-rT_lIYxpAT4,13458
|
26
27
|
lazylabel/ui/widgets/model_selection_widget.py,sha256=kMPaBMfdfnEVH-Be1d5OxLEuioO0c04FZT1Hr904axM,3497
|
27
|
-
lazylabel/ui/widgets/settings_widget.py,sha256=
|
28
|
+
lazylabel/ui/widgets/settings_widget.py,sha256=ShTaLJeXxwrSuTV4kmtV2JiWjfREil2D1nvPUIfAgDs,4859
|
28
29
|
lazylabel/ui/widgets/status_bar.py,sha256=wTbMQNEOBfmtNj8EVFZS_lxgaemu-CbRXeZzEQDaVz8,4014
|
29
30
|
lazylabel/utils/__init__.py,sha256=V6IR5Gim-39HgM2NyTVT-n8gy3mjilCSFW9y0owN5nc,179
|
30
31
|
lazylabel/utils/custom_file_system_model.py,sha256=-3EimlybvevH6bvqBE0qdFnLADVtayylmkntxPXK0Bk,4869
|
32
|
+
lazylabel/utils/logger.py,sha256=R7z6ifgA-NY-9ZbLlNH0i19zzwXndJ_gkG2J1zpVEhg,1306
|
31
33
|
lazylabel/utils/utils.py,sha256=sYSCoXL27OaLgOZaUkCAhgmKZ7YfhR3Cc5F8nDIa3Ig,414
|
32
|
-
lazylabel_gui-1.1.
|
33
|
-
lazylabel_gui-1.1.
|
34
|
-
lazylabel_gui-1.1.
|
35
|
-
lazylabel_gui-1.1.
|
36
|
-
lazylabel_gui-1.1.
|
37
|
-
lazylabel_gui-1.1.
|
34
|
+
lazylabel_gui-1.1.7.dist-info/licenses/LICENSE,sha256=kSDEIgrWAPd1u2UFGGpC9X71dhzrlzBFs8hbDlENnGE,1092
|
35
|
+
lazylabel_gui-1.1.7.dist-info/METADATA,sha256=9C9ppy_0pAwHmlNKlP5_0i1mkqNLABpskyuRtRjxEwo,8064
|
36
|
+
lazylabel_gui-1.1.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
37
|
+
lazylabel_gui-1.1.7.dist-info/entry_points.txt,sha256=Hd0WwEG9OPTa_ziYjiD0aRh7R6Fupt-wdQ3sspdc1mM,54
|
38
|
+
lazylabel_gui-1.1.7.dist-info/top_level.txt,sha256=YN4uIyrpDBq1wiJaBuZLDipIzyZY0jqJOmmXiPIOUkU,10
|
39
|
+
lazylabel_gui-1.1.7.dist-info/RECORD,,
|