labelimgplusplus 2.3.0__tar.gz → 2.3.1__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.
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/HISTORY.rst +16 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/PKG-INFO +1 -1
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/labelImgPlusPlus.py +32 -9
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/labelimgplusplus.egg-info/PKG-INFO +1 -1
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/__init__.py +2 -2
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/core/commands.py +76 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/formats/yolo_seg_io.py +4 -2
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/resources.py +43 -43
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/widgets/canvas.py +81 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/pyproject.toml +1 -1
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/CONTRIBUTING.rst +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/LICENSE +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/MANIFEST.in +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/README.rst +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/data/predefined_classes.txt +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/labelimgplusplus.egg-info/SOURCES.txt +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/labelimgplusplus.egg-info/dependency_links.txt +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/labelimgplusplus.egg-info/entry_points.txt +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/labelimgplusplus.egg-info/requires.txt +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/labelimgplusplus.egg-info/top_level.txt +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/core/__init__.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/core/keypoint_config.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/core/settings.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/core/shape.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/core/shortcut_config.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/formats/__init__.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/formats/coco_io.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/formats/create_ml_io.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/formats/labelFile.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/formats/pascal_voc_io.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/formats/yolo_io.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/tools/__init__.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/tools/dataset_splitter.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/tools/label_checker.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/utils/__init__.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/utils/constants.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/utils/hashableQListWidgetItem.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/utils/stringBundle.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/utils/styles.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/utils/ustr.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/utils/utils.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/widgets/__init__.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/widgets/batchVerifyDialog.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/widgets/colorDialog.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/widgets/combobox.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/widgets/default_label_combobox.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/widgets/galleryWidget.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/widgets/keypointPanel.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/widgets/labelCheckerDialog.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/widgets/labelDialog.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/widgets/lightWidget.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/widgets/shortcutsDialog.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/widgets/splitDialog.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/widgets/statsWidget.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/widgets/toolBar.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/libs/widgets/zoomWidget.py +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/resources.qrc +0 -0
- {labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/setup.cfg +0 -0
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
History
|
|
2
2
|
=======
|
|
3
3
|
|
|
4
|
+
2.3.1 (2026-05-14)
|
|
5
|
+
------------------
|
|
6
|
+
|
|
7
|
+
Patch release fixing five blocker-tier issues identified in the v2.3.0 review.
|
|
8
|
+
|
|
9
|
+
* Restore Ctrl-Z for polygon vertex insert, vertex delete, vertex drag-move, and
|
|
10
|
+
keypoint placement — these previously silently undid the prior rectangle action
|
|
11
|
+
* Clear polygon/keypoint mode state when loading a new file, preventing
|
|
12
|
+
stale-reference crashes after switching images mid-annotation
|
|
13
|
+
* Exit keypoint mode safely when the active keypoint shape is deleted
|
|
14
|
+
* Defer format switch in COCO and YOLO-seg loaders until the reader succeeds,
|
|
15
|
+
so a malformed annotation file no longer silently flips the saved format
|
|
16
|
+
* Clamp YOLO-seg normalized coordinates to ``[0.0, 1.0]`` to prevent
|
|
17
|
+
out-of-range values when a vertex is dragged past the image edge
|
|
18
|
+
|
|
19
|
+
|
|
4
20
|
2.3.0 (2026-03-27)
|
|
5
21
|
------------------
|
|
6
22
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: labelimgplusplus
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.1
|
|
4
4
|
Summary: labelImg++ is an enhanced graphical image annotation tool with Gallery Mode for browsing and labeling images
|
|
5
5
|
Author-email: Abhik Sarkar <abhiksark@gmail.com>
|
|
6
6
|
License: Copyright (c) <2015-Present> Tzutalin
|
|
@@ -59,7 +59,10 @@ from libs.widgets.keypointPanel import KeypointPanel
|
|
|
59
59
|
# Core
|
|
60
60
|
from libs.core.shape import Shape, ShapeType, DEFAULT_LINE_COLOR, DEFAULT_FILL_COLOR
|
|
61
61
|
from libs.core.settings import Settings
|
|
62
|
-
from libs.core.commands import
|
|
62
|
+
from libs.core.commands import (
|
|
63
|
+
UndoStack, CreateShapeCommand, DeleteShapeCommand, MoveShapeCommand,
|
|
64
|
+
EditLabelCommand, EditPolygonVerticesCommand, EditKeypointsCommand,
|
|
65
|
+
)
|
|
63
66
|
from libs.core.shortcut_config import ShortcutConfig
|
|
64
67
|
|
|
65
68
|
# Formats
|
|
@@ -593,6 +596,9 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
|
593
596
|
self.canvas.newShape.connect(self.new_shape)
|
|
594
597
|
self.canvas.shapeMoved.connect(self.set_dirty)
|
|
595
598
|
self.canvas.shapeMoved.connect(self._on_shape_moved_keypoints)
|
|
599
|
+
self.canvas.polygonVerticesEdited.connect(
|
|
600
|
+
self._on_polygon_vertices_edited)
|
|
601
|
+
self.canvas.keypointsEdited.connect(self._on_keypoints_edited)
|
|
596
602
|
self.canvas.selectionChanged.connect(self.shape_selection_changed)
|
|
597
603
|
self.canvas.drawingPolygon.connect(self.toggle_drawing_sensitive)
|
|
598
604
|
|
|
@@ -1692,6 +1698,21 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
|
1692
1698
|
self.keypoint_panel.set_current_index(
|
|
1693
1699
|
self.canvas._keypoint_index)
|
|
1694
1700
|
|
|
1701
|
+
def _on_polygon_vertices_edited(self, shape, old_points):
|
|
1702
|
+
"""Capture polygon vertex edits for undo support."""
|
|
1703
|
+
cmd = EditPolygonVerticesCommand(
|
|
1704
|
+
self, shape, old_points, list(shape.points))
|
|
1705
|
+
self.undo_stack.push(cmd)
|
|
1706
|
+
self.set_dirty()
|
|
1707
|
+
|
|
1708
|
+
def _on_keypoints_edited(self, shape, old_keypoints):
|
|
1709
|
+
"""Capture keypoint mutations for undo support."""
|
|
1710
|
+
cmd = EditKeypointsCommand(
|
|
1711
|
+
self, shape, old_keypoints,
|
|
1712
|
+
list(shape.keypoints) if shape.keypoints else None)
|
|
1713
|
+
self.undo_stack.push(cmd)
|
|
1714
|
+
self.set_dirty()
|
|
1715
|
+
|
|
1695
1716
|
def toggle_drawing_sensitive(self, drawing=True):
|
|
1696
1717
|
"""In the middle of drawing, toggling between modes should be disabled."""
|
|
1697
1718
|
self.actions.editMode.setEnabled(not drawing)
|
|
@@ -3443,19 +3464,20 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
|
3443
3464
|
if not os.path.isfile(json_path):
|
|
3444
3465
|
return
|
|
3445
3466
|
|
|
3446
|
-
self.set_format(FORMAT_COCO)
|
|
3447
|
-
|
|
3448
3467
|
image_filename = os.path.basename(file_path)
|
|
3449
3468
|
try:
|
|
3450
3469
|
reader = COCOReader(json_path, image_filename)
|
|
3451
3470
|
shapes = reader.get_shapes()
|
|
3452
|
-
self.load_labels(shapes)
|
|
3453
|
-
self.canvas.verified = reader.verified
|
|
3454
3471
|
except Exception as e:
|
|
3455
3472
|
self.error_message(
|
|
3456
3473
|
'Annotation Error',
|
|
3457
3474
|
f'Error loading COCO annotations from '
|
|
3458
3475
|
f'{os.path.basename(json_path)}: {e}')
|
|
3476
|
+
return
|
|
3477
|
+
|
|
3478
|
+
self.set_format(FORMAT_COCO)
|
|
3479
|
+
self.load_labels(shapes)
|
|
3480
|
+
self.canvas.verified = reader.verified
|
|
3459
3481
|
|
|
3460
3482
|
def load_yolo_seg_by_filename(self, txt_path):
|
|
3461
3483
|
"""Load annotations from a YOLO-seg text file.
|
|
@@ -3468,8 +3490,6 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
|
3468
3490
|
if not os.path.isfile(txt_path):
|
|
3469
3491
|
return
|
|
3470
3492
|
|
|
3471
|
-
self.set_format(FORMAT_YOLO_SEG)
|
|
3472
|
-
|
|
3473
3493
|
try:
|
|
3474
3494
|
if hasattr(self, '_original_image_size') and self._original_image_size is not None:
|
|
3475
3495
|
class MockImage:
|
|
@@ -3488,13 +3508,16 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
|
3488
3508
|
else:
|
|
3489
3509
|
reader = YOLOSegReader(txt_path, self.image)
|
|
3490
3510
|
shapes = reader.get_shapes()
|
|
3491
|
-
self.load_labels(shapes)
|
|
3492
|
-
self.canvas.verified = reader.verified
|
|
3493
3511
|
except Exception as e:
|
|
3494
3512
|
self.error_message(
|
|
3495
3513
|
'Annotation Error',
|
|
3496
3514
|
f'Error loading YOLO-seg annotations for '
|
|
3497
3515
|
f'{os.path.basename(txt_path)}: {e}')
|
|
3516
|
+
return
|
|
3517
|
+
|
|
3518
|
+
self.set_format(FORMAT_YOLO_SEG)
|
|
3519
|
+
self.load_labels(shapes)
|
|
3520
|
+
self.canvas.verified = reader.verified
|
|
3498
3521
|
|
|
3499
3522
|
def copy_previous_bounding_boxes(self):
|
|
3500
3523
|
current_index = self._path_to_idx.get(self.file_path, 0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: labelimgplusplus
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.1
|
|
4
4
|
Summary: labelImg++ is an enhanced graphical image annotation tool with Gallery Mode for browsing and labeling images
|
|
5
5
|
Author-email: Abhik Sarkar <abhiksark@gmail.com>
|
|
6
6
|
License: Copyright (c) <2015-Present> Tzutalin
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# libs/__init__.py
|
|
2
2
|
"""LabelImg++ library package."""
|
|
3
3
|
|
|
4
|
-
__version_info__ = ('2', '
|
|
5
|
-
__version__ = '2.
|
|
4
|
+
__version_info__ = ('2', '3', '1')
|
|
5
|
+
__version__ = '2.3.1'
|
|
6
6
|
|
|
7
7
|
# Re-export subpackages for convenient access
|
|
8
8
|
from libs import core, formats, widgets, utils
|
|
@@ -189,6 +189,82 @@ class EditLabelCommand(Command):
|
|
|
189
189
|
return f"Edit label '{self.old_label}' -> '{self.new_label}'"
|
|
190
190
|
|
|
191
191
|
|
|
192
|
+
class EditPolygonVerticesCommand(Command):
|
|
193
|
+
"""Command for polygon vertex insert / remove / move operations.
|
|
194
|
+
|
|
195
|
+
Snapshots the full points list before and after the mutation;
|
|
196
|
+
undo restores the snapshot.
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
def __init__(self, main_window, shape, old_points, new_points):
|
|
200
|
+
"""Initialize with shape and its point lists.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
main_window: The MainWindow instance.
|
|
204
|
+
shape: The Shape object being edited.
|
|
205
|
+
old_points: List of QPointF before the mutation.
|
|
206
|
+
new_points: List of QPointF after the mutation.
|
|
207
|
+
"""
|
|
208
|
+
self.main_window = main_window
|
|
209
|
+
self.shape = shape
|
|
210
|
+
self.old_points = [QPointF(p.x(), p.y()) for p in old_points]
|
|
211
|
+
self.new_points = [QPointF(p.x(), p.y()) for p in new_points]
|
|
212
|
+
|
|
213
|
+
def execute(self):
|
|
214
|
+
"""Apply the post-mutation point list."""
|
|
215
|
+
self.shape.points = [QPointF(p.x(), p.y()) for p in self.new_points]
|
|
216
|
+
self.main_window.canvas.update()
|
|
217
|
+
|
|
218
|
+
def undo(self):
|
|
219
|
+
"""Restore the pre-mutation point list."""
|
|
220
|
+
self.shape.points = [QPointF(p.x(), p.y()) for p in self.old_points]
|
|
221
|
+
self.main_window.canvas.update()
|
|
222
|
+
|
|
223
|
+
@property
|
|
224
|
+
def description(self):
|
|
225
|
+
return (
|
|
226
|
+
f"Edit polygon vertices "
|
|
227
|
+
f"({len(self.old_points)} -> {len(self.new_points)})"
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class EditKeypointsCommand(Command):
|
|
232
|
+
"""Command for keypoint placement / clear operations.
|
|
233
|
+
|
|
234
|
+
Snapshots the keypoints list before and after; undo restores.
|
|
235
|
+
"""
|
|
236
|
+
|
|
237
|
+
def __init__(self, main_window, shape, old_keypoints, new_keypoints):
|
|
238
|
+
"""Initialize with shape and keypoint snapshots.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
main_window: The MainWindow instance.
|
|
242
|
+
shape: The Shape object being edited.
|
|
243
|
+
old_keypoints: List of keypoint tuples before mutation (or None).
|
|
244
|
+
new_keypoints: List of keypoint tuples after mutation (or None).
|
|
245
|
+
"""
|
|
246
|
+
self.main_window = main_window
|
|
247
|
+
self.shape = shape
|
|
248
|
+
self.old_keypoints = list(old_keypoints) if old_keypoints else None
|
|
249
|
+
self.new_keypoints = list(new_keypoints) if new_keypoints else None
|
|
250
|
+
|
|
251
|
+
def execute(self):
|
|
252
|
+
"""Apply the post-mutation keypoint list."""
|
|
253
|
+
self.shape.keypoints = (
|
|
254
|
+
list(self.new_keypoints) if self.new_keypoints else None)
|
|
255
|
+
self.main_window.canvas.update()
|
|
256
|
+
|
|
257
|
+
def undo(self):
|
|
258
|
+
"""Restore the pre-mutation keypoint list."""
|
|
259
|
+
self.shape.keypoints = (
|
|
260
|
+
list(self.old_keypoints) if self.old_keypoints else None)
|
|
261
|
+
self.main_window.canvas.update()
|
|
262
|
+
|
|
263
|
+
@property
|
|
264
|
+
def description(self):
|
|
265
|
+
return f"Edit keypoints on '{self.shape.label}'"
|
|
266
|
+
|
|
267
|
+
|
|
192
268
|
class UndoStack:
|
|
193
269
|
"""Manages command history for undo/redo operations.
|
|
194
270
|
|
|
@@ -53,8 +53,10 @@ class YOLOSegWriter:
|
|
|
53
53
|
|
|
54
54
|
coords = []
|
|
55
55
|
for x, y in entry['points']:
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
nx = max(0.0, min(1.0, x / w))
|
|
57
|
+
ny = max(0.0, min(1.0, y / h))
|
|
58
|
+
coords.append('%.6f' % nx)
|
|
59
|
+
coords.append('%.6f' % ny)
|
|
58
60
|
|
|
59
61
|
f.write('%d %s\n' % (class_idx, ' '.join(coords)))
|
|
60
62
|
|
|
@@ -4195,91 +4195,91 @@ qt_resource_struct_v2 = b"\
|
|
|
4195
4195
|
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x2b\x00\x00\x00\x01\
|
|
4196
4196
|
\x00\x00\x00\x00\x00\x00\x00\x00\
|
|
4197
4197
|
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
|
|
4198
|
-
\x00\x00\x01\
|
|
4198
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4199
4199
|
\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x01\x00\x00\x7a\x97\
|
|
4200
|
-
\x00\x00\x01\
|
|
4200
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4201
4201
|
\x00\x00\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x7b\xd5\
|
|
4202
|
-
\x00\x00\x01\
|
|
4202
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4203
4203
|
\x00\x00\x00\x24\x00\x00\x00\x00\x00\x01\x00\x00\x7e\x0a\
|
|
4204
|
-
\x00\x00\x01\
|
|
4204
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4205
4205
|
\x00\x00\x00\x32\x00\x00\x00\x00\x00\x01\x00\x00\x7f\x3f\
|
|
4206
|
-
\x00\x00\x01\
|
|
4206
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4207
4207
|
\x00\x00\x00\x40\x00\x00\x00\x00\x00\x01\x00\x00\x80\x1c\
|
|
4208
|
-
\x00\x00\x01\
|
|
4208
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4209
4209
|
\x00\x00\x00\x4e\x00\x00\x00\x00\x00\x01\x00\x00\x81\x2d\
|
|
4210
|
-
\x00\x00\x01\
|
|
4210
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4211
4211
|
\x00\x00\x00\x5c\x00\x00\x00\x00\x00\x01\x00\x00\x82\x50\
|
|
4212
|
-
\x00\x00\x01\
|
|
4212
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4213
4213
|
\x00\x00\x00\x6a\x00\x00\x00\x00\x00\x01\x00\x00\x83\x84\
|
|
4214
|
-
\x00\x00\x01\
|
|
4214
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4215
4215
|
\x00\x00\x00\x78\x00\x00\x00\x00\x00\x01\x00\x00\x85\x23\
|
|
4216
|
-
\x00\x00\x01\
|
|
4216
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4217
4217
|
\x00\x00\x00\x86\x00\x00\x00\x00\x00\x01\x00\x00\x86\x00\
|
|
4218
|
-
\x00\x00\x01\
|
|
4218
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4219
4219
|
\x00\x00\x00\x94\x00\x00\x00\x00\x00\x01\x00\x00\x87\x11\
|
|
4220
|
-
\x00\x00\x01\
|
|
4220
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4221
4221
|
\x00\x00\x00\xa2\x00\x00\x00\x00\x00\x01\x00\x00\x87\xef\
|
|
4222
|
-
\x00\x00\x01\
|
|
4222
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4223
4223
|
\x00\x00\x00\xb0\x00\x00\x00\x00\x00\x01\x00\x00\x88\xfe\
|
|
4224
|
-
\x00\x00\x01\
|
|
4224
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4225
4225
|
\x00\x00\x00\xbe\x00\x00\x00\x00\x00\x01\x00\x00\x8a\x36\
|
|
4226
|
-
\x00\x00\x01\
|
|
4226
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4227
4227
|
\x00\x00\x00\xcc\x00\x00\x00\x00\x00\x01\x00\x00\x8b\x86\
|
|
4228
|
-
\x00\x00\x01\
|
|
4228
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4229
4229
|
\x00\x00\x00\xda\x00\x00\x00\x00\x00\x01\x00\x00\x8c\x8f\
|
|
4230
|
-
\x00\x00\x01\
|
|
4230
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4231
4231
|
\x00\x00\x00\xe8\x00\x00\x00\x00\x00\x01\x00\x00\x8d\x95\
|
|
4232
|
-
\x00\x00\x01\
|
|
4232
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4233
4233
|
\x00\x00\x00\xf8\x00\x00\x00\x00\x00\x01\x00\x00\x8e\x99\
|
|
4234
|
-
\x00\x00\x01\
|
|
4234
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4235
4235
|
\x00\x00\x01\x08\x00\x00\x00\x00\x00\x01\x00\x00\x8f\x84\
|
|
4236
|
-
\x00\x00\x01\
|
|
4236
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4237
4237
|
\x00\x00\x01\x1c\x00\x00\x00\x00\x00\x01\x00\x00\x90\xd6\
|
|
4238
|
-
\x00\x00\x01\
|
|
4238
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xc2\
|
|
4239
4239
|
\x00\x00\x01\x3c\x00\x00\x00\x00\x00\x01\x00\x00\x9b\x2a\
|
|
4240
|
-
\x00\x00\x01\
|
|
4240
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xc2\
|
|
4241
4241
|
\x00\x00\x01\x5c\x00\x00\x00\x00\x00\x01\x00\x00\xa4\x77\
|
|
4242
|
-
\x00\x00\x01\
|
|
4242
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xc2\
|
|
4243
4243
|
\x00\x00\x01\x7c\x00\x00\x00\x00\x00\x01\x00\x00\xb0\xcb\
|
|
4244
|
-
\x00\x00\x01\
|
|
4244
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4245
4245
|
\x00\x00\x01\x96\x00\x00\x00\x00\x00\x01\x00\x00\xb3\xe1\
|
|
4246
|
-
\x00\x00\x01\
|
|
4246
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4247
4247
|
\x00\x00\x01\xb0\x00\x00\x00\x00\x00\x01\x00\x00\xb5\x2d\
|
|
4248
|
-
\x00\x00\x01\
|
|
4248
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4249
4249
|
\x00\x00\x01\xc6\x00\x00\x00\x00\x00\x01\x00\x00\xb6\x59\
|
|
4250
|
-
\x00\x00\x01\
|
|
4250
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4251
4251
|
\x00\x00\x01\xe0\x00\x00\x00\x00\x00\x01\x00\x00\xb7\x56\
|
|
4252
|
-
\x00\x00\x01\
|
|
4252
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4253
4253
|
\x00\x00\x01\xf2\x00\x00\x00\x00\x00\x01\x00\x00\xb8\xd9\
|
|
4254
|
-
\x00\x00\x01\
|
|
4254
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4255
4255
|
\x00\x00\x02\x04\x00\x00\x00\x00\x00\x01\x00\x00\xba\xe5\
|
|
4256
|
-
\x00\x00\x01\
|
|
4256
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4257
4257
|
\x00\x00\x02\x24\x00\x00\x00\x00\x00\x01\x00\x00\xbd\x1a\
|
|
4258
|
-
\x00\x00\x01\
|
|
4258
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4259
4259
|
\x00\x00\x02\x36\x00\x00\x00\x00\x00\x01\x00\x00\xbe\x78\
|
|
4260
|
-
\x00\x00\x01\
|
|
4260
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4261
4261
|
\x00\x00\x02\x4e\x00\x00\x00\x00\x00\x01\x00\x00\xbf\x9b\
|
|
4262
|
-
\x00\x00\x01\
|
|
4262
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4263
4263
|
\x00\x00\x02\x60\x00\x00\x00\x00\x00\x01\x00\x00\xc0\xad\
|
|
4264
|
-
\x00\x00\x01\
|
|
4264
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4265
4265
|
\x00\x00\x02\x7c\x00\x00\x00\x00\x00\x01\x00\x00\xc3\x54\
|
|
4266
|
-
\x00\x00\x01\
|
|
4266
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4267
4267
|
\x00\x00\x02\x90\x00\x00\x00\x00\x00\x01\x00\x00\xc4\x8e\
|
|
4268
|
-
\x00\x00\x01\
|
|
4268
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4269
4269
|
\x00\x00\x02\xac\x00\x00\x00\x00\x00\x01\x00\x00\xc6\xc3\
|
|
4270
|
-
\x00\x00\x01\
|
|
4270
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xc2\
|
|
4271
4271
|
\x00\x00\x02\xc0\x00\x00\x00\x00\x00\x01\x00\x00\xd9\x78\
|
|
4272
|
-
\x00\x00\x01\
|
|
4272
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4273
4273
|
\x00\x00\x02\xe4\x00\x00\x00\x00\x00\x01\x00\x00\xe9\xb8\
|
|
4274
|
-
\x00\x00\x01\
|
|
4274
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4275
4275
|
\x00\x00\x03\x02\x00\x00\x00\x00\x00\x01\x00\x00\xea\x96\
|
|
4276
|
-
\x00\x00\x01\
|
|
4276
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4277
4277
|
\x00\x00\x03\x20\x00\x00\x00\x00\x00\x01\x00\x00\xeb\x8b\
|
|
4278
|
-
\x00\x00\x01\
|
|
4278
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4279
4279
|
\x00\x00\x03\x36\x00\x00\x00\x00\x00\x01\x00\x00\xec\xdd\
|
|
4280
|
-
\x00\x00\x01\
|
|
4280
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4281
4281
|
\x00\x00\x03\x4c\x00\x00\x00\x00\x00\x01\x00\x00\xf0\xa0\
|
|
4282
|
-
\x00\x00\x01\
|
|
4282
|
+
\x00\x00\x01\x9e\x3c\x48\x08\xbe\
|
|
4283
4283
|
"
|
|
4284
4284
|
|
|
4285
4285
|
qt_version = [int(v) for v in QtCore.qVersion().split('.')]
|
|
@@ -64,6 +64,12 @@ class Canvas(QWidget):
|
|
|
64
64
|
selectionChanged = pyqtSignal(bool)
|
|
65
65
|
shapeMoved = pyqtSignal()
|
|
66
66
|
drawingPolygon = pyqtSignal(bool)
|
|
67
|
+
# Emitted on polygon vertex insert / remove / drag-move with the
|
|
68
|
+
# pre-mutation points list so MainWindow can push an undo command.
|
|
69
|
+
polygonVerticesEdited = pyqtSignal(object, object) # (shape, old_points)
|
|
70
|
+
# Emitted on keypoint placement / mutation with the pre-mutation
|
|
71
|
+
# keypoints list (may be None) for undo support.
|
|
72
|
+
keypointsEdited = pyqtSignal(object, object) # (shape, old_keypoints)
|
|
67
73
|
|
|
68
74
|
CREATE, EDIT, CREATE_POLYGON, KEYPOINT_MODE = list(range(4))
|
|
69
75
|
|
|
@@ -128,6 +134,11 @@ class Canvas(QWidget):
|
|
|
128
134
|
self._keypoint_template_name = None
|
|
129
135
|
self._keypoint_template = None
|
|
130
136
|
|
|
137
|
+
# Snapshot of points list captured at the start of a polygon
|
|
138
|
+
# vertex drag, so we can emit polygonVerticesEdited on release
|
|
139
|
+
# if the drag actually changed any vertex position.
|
|
140
|
+
self._polygon_drag_old_points = None
|
|
141
|
+
|
|
131
142
|
def set_drawing_color(self, qcolor):
|
|
132
143
|
self.drawing_line_color = qcolor
|
|
133
144
|
self.drawing_rect_color = qcolor
|
|
@@ -445,15 +456,27 @@ class Canvas(QWidget):
|
|
|
445
456
|
def mousePressEvent(self, ev):
|
|
446
457
|
pos = self.transform_pos(ev.pos())
|
|
447
458
|
|
|
459
|
+
# Snapshot polygon points before any potential vertex drag.
|
|
460
|
+
# Emitted on release if any point actually changed.
|
|
461
|
+
self._polygon_drag_old_points = None
|
|
462
|
+
if (ev.button() == Qt.LeftButton
|
|
463
|
+
and self.selected_shape
|
|
464
|
+
and self.selected_shape.shape_type == ShapeType.POLYGON):
|
|
465
|
+
self._polygon_drag_old_points = list(self.selected_shape.points)
|
|
466
|
+
|
|
448
467
|
# Keypoint placement
|
|
449
468
|
if self.mode == self.KEYPOINT_MODE and self._keypoint_shape:
|
|
450
469
|
kp_count = self._keypoint_count()
|
|
451
470
|
if ev.button() == Qt.LeftButton and self._keypoint_index < kp_count:
|
|
452
471
|
if not self.out_of_pixmap(pos):
|
|
472
|
+
old_kps = (list(self._keypoint_shape.keypoints)
|
|
473
|
+
if self._keypoint_shape.keypoints else None)
|
|
453
474
|
if self._keypoint_shape.keypoints is None:
|
|
454
475
|
self._keypoint_shape.keypoints = [None] * kp_count
|
|
455
476
|
self._keypoint_shape.keypoints[self._keypoint_index] = (
|
|
456
477
|
pos.x(), pos.y(), 2)
|
|
478
|
+
self._emit_keypoints_edit(
|
|
479
|
+
self._keypoint_shape, old_kps)
|
|
457
480
|
self._keypoint_index = self._next_unplaced_keypoint(
|
|
458
481
|
self._keypoint_index + 1)
|
|
459
482
|
if self._keypoint_index >= kp_count:
|
|
@@ -463,10 +486,14 @@ class Canvas(QWidget):
|
|
|
463
486
|
return
|
|
464
487
|
elif ev.button() == Qt.RightButton and self._keypoint_index < kp_count:
|
|
465
488
|
if not self.out_of_pixmap(pos):
|
|
489
|
+
old_kps = (list(self._keypoint_shape.keypoints)
|
|
490
|
+
if self._keypoint_shape.keypoints else None)
|
|
466
491
|
if self._keypoint_shape.keypoints is None:
|
|
467
492
|
self._keypoint_shape.keypoints = [None] * kp_count
|
|
468
493
|
self._keypoint_shape.keypoints[self._keypoint_index] = (
|
|
469
494
|
pos.x(), pos.y(), 1)
|
|
495
|
+
self._emit_keypoints_edit(
|
|
496
|
+
self._keypoint_shape, old_kps)
|
|
470
497
|
self._keypoint_index = self._next_unplaced_keypoint(
|
|
471
498
|
self._keypoint_index + 1)
|
|
472
499
|
if self._keypoint_index >= kp_count:
|
|
@@ -494,7 +521,15 @@ class Canvas(QWidget):
|
|
|
494
521
|
mid_idx = self.selected_shape.nearest_midpoint(pos, self.epsilon)
|
|
495
522
|
if mid_idx is not None:
|
|
496
523
|
mid = self.selected_shape.midpoint_of_edge(mid_idx)
|
|
524
|
+
old_points = list(self.selected_shape.points)
|
|
497
525
|
self.selected_shape.insert_point(mid_idx + 1, mid)
|
|
526
|
+
self._emit_polygon_edit(
|
|
527
|
+
self.selected_shape, old_points)
|
|
528
|
+
# Reseat the drag baseline to the post-insert state so a
|
|
529
|
+
# subsequent drag-while-held emits a second edit covering
|
|
530
|
+
# only the move, not the insert (already emitted above).
|
|
531
|
+
self._polygon_drag_old_points = list(
|
|
532
|
+
self.selected_shape.points)
|
|
498
533
|
self.h_vertex = mid_idx + 1
|
|
499
534
|
self.h_shape = self.selected_shape
|
|
500
535
|
self.selected_shape.highlight_vertex(mid_idx + 1, Shape.MOVE_VERTEX)
|
|
@@ -526,8 +561,28 @@ class Canvas(QWidget):
|
|
|
526
561
|
self.current.add_point(snapped)
|
|
527
562
|
self.finalise()
|
|
528
563
|
self._freehand_points = []
|
|
564
|
+
self._polygon_drag_old_points = None
|
|
529
565
|
return
|
|
530
566
|
|
|
567
|
+
# If a polygon was selected on press and any vertex moved during
|
|
568
|
+
# the drag, emit polygonVerticesEdited so MainWindow can push an
|
|
569
|
+
# undo command. Compares pre-press snapshot to current points.
|
|
570
|
+
if (ev.button() == Qt.LeftButton
|
|
571
|
+
and self._polygon_drag_old_points is not None
|
|
572
|
+
and self.selected_shape
|
|
573
|
+
and self.selected_shape.shape_type == ShapeType.POLYGON):
|
|
574
|
+
new_pts = self.selected_shape.points
|
|
575
|
+
old_pts = self._polygon_drag_old_points
|
|
576
|
+
moved = (
|
|
577
|
+
len(new_pts) != len(old_pts)
|
|
578
|
+
or any((a.x(), a.y()) != (b.x(), b.y())
|
|
579
|
+
for a, b in zip(old_pts, new_pts))
|
|
580
|
+
)
|
|
581
|
+
if moved:
|
|
582
|
+
self._emit_polygon_edit(self.selected_shape, old_pts)
|
|
583
|
+
if ev.button() == Qt.LeftButton:
|
|
584
|
+
self._polygon_drag_old_points = None
|
|
585
|
+
|
|
531
586
|
if ev.button() == Qt.RightButton:
|
|
532
587
|
# Check if right-clicking a polygon vertex
|
|
533
588
|
if self.selected_shape and self.selected_shape.shape_type == ShapeType.POLYGON:
|
|
@@ -540,7 +595,10 @@ class Canvas(QWidget):
|
|
|
540
595
|
delete_action.setEnabled(False)
|
|
541
596
|
action = vertex_menu.exec_(self.mapToGlobal(ev.pos()))
|
|
542
597
|
if action == delete_action:
|
|
598
|
+
old_points = list(self.selected_shape.points)
|
|
543
599
|
self.selected_shape.remove_point(idx)
|
|
600
|
+
self._emit_polygon_edit(
|
|
601
|
+
self.selected_shape, old_points)
|
|
544
602
|
self.shapeMoved.emit()
|
|
545
603
|
self.update()
|
|
546
604
|
return
|
|
@@ -812,6 +870,16 @@ class Canvas(QWidget):
|
|
|
812
870
|
return True
|
|
813
871
|
return False
|
|
814
872
|
|
|
873
|
+
def _emit_polygon_edit(self, shape, old_points):
|
|
874
|
+
"""Emit polygonVerticesEdited with a deep-copy snapshot of old_points."""
|
|
875
|
+
snapshot = [QPointF(p.x(), p.y()) for p in old_points]
|
|
876
|
+
self.polygonVerticesEdited.emit(shape, snapshot)
|
|
877
|
+
|
|
878
|
+
def _emit_keypoints_edit(self, shape, old_keypoints):
|
|
879
|
+
"""Emit keypointsEdited with a deep-copy snapshot."""
|
|
880
|
+
snapshot = list(old_keypoints) if old_keypoints else None
|
|
881
|
+
self.keypointsEdited.emit(shape, snapshot)
|
|
882
|
+
|
|
815
883
|
def de_select_shape(self):
|
|
816
884
|
if self.selected_shape:
|
|
817
885
|
self.selected_shape.selected = False
|
|
@@ -823,6 +891,10 @@ class Canvas(QWidget):
|
|
|
823
891
|
def delete_selected(self):
|
|
824
892
|
if self.selected_shape:
|
|
825
893
|
shape = self.selected_shape
|
|
894
|
+
# If this shape is the active keypoint subject, exit keypoint mode
|
|
895
|
+
# before removing — otherwise the next click crashes on a stale ref.
|
|
896
|
+
if self._keypoint_shape is shape:
|
|
897
|
+
self.exit_keypoint_mode()
|
|
826
898
|
self.un_highlight(shape)
|
|
827
899
|
self.shapes.remove(self.selected_shape)
|
|
828
900
|
self.selected_shape = None
|
|
@@ -1240,6 +1312,15 @@ class Canvas(QWidget):
|
|
|
1240
1312
|
self.un_highlight()
|
|
1241
1313
|
self.selected_shape_copy = None
|
|
1242
1314
|
|
|
1315
|
+
# Clear per-file drawing/keypoint state to avoid stale references.
|
|
1316
|
+
self._keypoint_shape = None
|
|
1317
|
+
self._keypoint_index = 0
|
|
1318
|
+
self._keypoint_template = None
|
|
1319
|
+
self._freehand_active = False
|
|
1320
|
+
self._freehand_points = []
|
|
1321
|
+
self.current = None
|
|
1322
|
+
self.mode = self.EDIT
|
|
1323
|
+
|
|
1243
1324
|
self.restore_cursor()
|
|
1244
1325
|
self.pixmap = None
|
|
1245
1326
|
self.update()
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "labelimgplusplus"
|
|
7
|
-
version = "2.3.
|
|
7
|
+
version = "2.3.1"
|
|
8
8
|
description = "labelImg++ is an enhanced graphical image annotation tool with Gallery Mode for browsing and labeling images"
|
|
9
9
|
readme = "README.rst"
|
|
10
10
|
license = {file = "LICENSE"}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/labelimgplusplus.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{labelimgplusplus-2.3.0 → labelimgplusplus-2.3.1}/labelimgplusplus.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|