lazylabel-gui 1.1.5__py3-none-any.whl → 1.1.6__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.5.dist-info → lazylabel_gui-1.1.6.dist-info}/METADATA +2 -1
- {lazylabel_gui-1.1.5.dist-info → lazylabel_gui-1.1.6.dist-info}/RECORD +19 -17
- {lazylabel_gui-1.1.5.dist-info → lazylabel_gui-1.1.6.dist-info}/WHEEL +0 -0
- {lazylabel_gui-1.1.5.dist-info → lazylabel_gui-1.1.6.dist-info}/entry_points.txt +0 -0
- {lazylabel_gui-1.1.5.dist-info → lazylabel_gui-1.1.6.dist-info}/licenses/LICENSE +0 -0
- {lazylabel_gui-1.1.5.dist-info → lazylabel_gui-1.1.6.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 using arrow keys to change images."
|
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 using arrow keys to change images."
|
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()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: lazylabel-gui
|
3
|
-
Version: 1.1.
|
3
|
+
Version: 1.1.6
|
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
|
@@ -139,6 +139,7 @@ Inspired by [LabelMe](https://github.com/wkentaro/labelme?tab=readme-ov-file#ins
|
|
139
139
|
|---|---|
|
140
140
|
| `1` | Enter **Point Mode** (for AI segmentation). |
|
141
141
|
| `2` | Enter **Polygon Drawing Mode**. |
|
142
|
+
| `3` | Enter **Bounding Box Mode**. |
|
142
143
|
| `E` | Toggle **Selection Mode** to select existing segments. |
|
143
144
|
| `R` | Enter **Edit Mode** for selected polygons (drag shape or vertices). |
|
144
145
|
| `Q` | Toggle **Pan Mode** (click and drag the image). |
|
@@ -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=zuszmw41849OXjBwVBc0TnBeuU4HZOJsJEurrzb_qbI,83764
|
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=qxVCiTYMQee95pE_FkFUXMCsmuo85LYdKeQYaCTrRVc,4829
|
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.6.dist-info/licenses/LICENSE,sha256=kSDEIgrWAPd1u2UFGGpC9X71dhzrlzBFs8hbDlENnGE,1092
|
35
|
+
lazylabel_gui-1.1.6.dist-info/METADATA,sha256=6zAAWlFPc61dOus5FGTAeEp4AlwNnbUjQsmHHwSgATQ,9546
|
36
|
+
lazylabel_gui-1.1.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
37
|
+
lazylabel_gui-1.1.6.dist-info/entry_points.txt,sha256=Hd0WwEG9OPTa_ziYjiD0aRh7R6Fupt-wdQ3sspdc1mM,54
|
38
|
+
lazylabel_gui-1.1.6.dist-info/top_level.txt,sha256=YN4uIyrpDBq1wiJaBuZLDipIzyZY0jqJOmmXiPIOUkU,10
|
39
|
+
lazylabel_gui-1.1.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|