lazylabel-gui 1.0.3__tar.gz → 1.0.4__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.0.3 → lazylabel_gui-1.0.4}/PKG-INFO +1 -1
- {lazylabel_gui-1.0.3 → lazylabel_gui-1.0.4}/pyproject.toml +1 -1
- {lazylabel_gui-1.0.3 → lazylabel_gui-1.0.4}/src/lazylabel/main.py +72 -60
- {lazylabel_gui-1.0.3 → lazylabel_gui-1.0.4}/src/lazylabel_gui.egg-info/PKG-INFO +1 -1
- {lazylabel_gui-1.0.3 → lazylabel_gui-1.0.4}/LICENSE +0 -0
- {lazylabel_gui-1.0.3 → lazylabel_gui-1.0.4}/README.md +0 -0
- {lazylabel_gui-1.0.3 → lazylabel_gui-1.0.4}/setup.cfg +0 -0
- {lazylabel_gui-1.0.3 → lazylabel_gui-1.0.4}/src/lazylabel/controls.py +0 -0
- {lazylabel_gui-1.0.3 → lazylabel_gui-1.0.4}/src/lazylabel/custom_file_system_model.py +0 -0
- {lazylabel_gui-1.0.3 → lazylabel_gui-1.0.4}/src/lazylabel/editable_vertex.py +0 -0
- {lazylabel_gui-1.0.3 → lazylabel_gui-1.0.4}/src/lazylabel/hoverable_polygon_item.py +0 -0
- {lazylabel_gui-1.0.3 → lazylabel_gui-1.0.4}/src/lazylabel/numeric_table_widget_item.py +0 -0
- {lazylabel_gui-1.0.3 → lazylabel_gui-1.0.4}/src/lazylabel/photo_viewer.py +0 -0
- {lazylabel_gui-1.0.3 → lazylabel_gui-1.0.4}/src/lazylabel/reorderable_class_table.py +0 -0
- {lazylabel_gui-1.0.3 → lazylabel_gui-1.0.4}/src/lazylabel/sam_model.py +0 -0
- {lazylabel_gui-1.0.3 → lazylabel_gui-1.0.4}/src/lazylabel/utils.py +0 -0
- {lazylabel_gui-1.0.3 → lazylabel_gui-1.0.4}/src/lazylabel_gui.egg-info/SOURCES.txt +0 -0
- {lazylabel_gui-1.0.3 → lazylabel_gui-1.0.4}/src/lazylabel_gui.egg-info/dependency_links.txt +0 -0
- {lazylabel_gui-1.0.3 → lazylabel_gui-1.0.4}/src/lazylabel_gui.egg-info/entry_points.txt +0 -0
- {lazylabel_gui-1.0.3 → lazylabel_gui-1.0.4}/src/lazylabel_gui.egg-info/requires.txt +0 -0
- {lazylabel_gui-1.0.3 → lazylabel_gui-1.0.4}/src/lazylabel_gui.egg-info/top_level.txt +0 -0
@@ -115,15 +115,24 @@ class MainWindow(QMainWindow):
|
|
115
115
|
)
|
116
116
|
self.control_panel.btn_clear_points.clicked.connect(self.clear_all_points)
|
117
117
|
|
118
|
+
def _get_color_for_class(self, class_id, saturation, value):
|
119
|
+
if class_id is None:
|
120
|
+
return QColor.fromHsv(0, 0, 128)
|
121
|
+
|
122
|
+
hue = int((class_id * 222.4922359) % 360)
|
123
|
+
color = QColor.fromHsv(hue, saturation, value)
|
124
|
+
|
125
|
+
if not color.isValid():
|
126
|
+
return QColor(Qt.GlobalColor.white)
|
127
|
+
return color
|
128
|
+
|
118
129
|
def set_mode(self, mode_name, is_toggle=False):
|
119
|
-
|
120
|
-
if self.mode == "selection" and mode_name != "selection":
|
130
|
+
if self.mode == "selection" and mode_name not in ["selection", "edit"]:
|
121
131
|
self.right_panel.segment_table.clearSelection()
|
122
|
-
|
123
132
|
if self.mode == "edit" and mode_name != "edit":
|
124
133
|
self.display_all_segments()
|
125
134
|
|
126
|
-
if not is_toggle and self.mode not in ["
|
135
|
+
if not is_toggle and self.mode not in ["selection", "edit"]:
|
127
136
|
self.previous_mode = self.mode
|
128
137
|
|
129
138
|
self.mode = mode_name
|
@@ -147,7 +156,7 @@ class MainWindow(QMainWindow):
|
|
147
156
|
if self.mode == new_mode:
|
148
157
|
self.set_mode(self.previous_mode, is_toggle=True)
|
149
158
|
else:
|
150
|
-
if self.mode not in ["
|
159
|
+
if self.mode not in ["selection", "edit"]:
|
151
160
|
self.previous_mode = self.mode
|
152
161
|
self.set_mode(new_mode, is_toggle=True)
|
153
162
|
|
@@ -244,12 +253,13 @@ class MainWindow(QMainWindow):
|
|
244
253
|
self.toggle_pan_mode()
|
245
254
|
elif key == Qt.Key.Key_R:
|
246
255
|
self.toggle_edit_mode()
|
247
|
-
elif key == Qt.Key.Key_C:
|
256
|
+
elif key == Qt.Key.Key_C or key == Qt.Key.Key_Escape:
|
248
257
|
self.clear_all_points()
|
249
258
|
elif key == Qt.Key.Key_V or key == Qt.Key.Key_Backspace:
|
250
259
|
self.delete_selected_segments()
|
251
260
|
elif key == Qt.Key.Key_M:
|
252
261
|
self.assign_selected_to_class()
|
262
|
+
self.right_panel.segment_table.clearSelection()
|
253
263
|
elif key == Qt.Key.Key_Z and mods == Qt.KeyboardModifier.ControlModifier:
|
254
264
|
self.undo_last_action()
|
255
265
|
elif key == Qt.Key.Key_A and mods == Qt.KeyboardModifier.ControlModifier:
|
@@ -393,10 +403,23 @@ class MainWindow(QMainWindow):
|
|
393
403
|
selected_indices = self.get_selected_segment_indices()
|
394
404
|
if not selected_indices:
|
395
405
|
return
|
396
|
-
|
406
|
+
|
407
|
+
existing_class_ids = [
|
408
|
+
self.segments[i]["class_id"]
|
409
|
+
for i in selected_indices
|
410
|
+
if self.segments[i].get("class_id") is not None
|
411
|
+
]
|
412
|
+
|
413
|
+
if existing_class_ids:
|
414
|
+
target_class_id = min(existing_class_ids)
|
415
|
+
else:
|
416
|
+
target_class_id = self.segments[selected_indices[0]].get("class_id")
|
417
|
+
|
397
418
|
for i in selected_indices:
|
398
419
|
self.segments[i]["class_id"] = target_class_id
|
420
|
+
|
399
421
|
self.update_all_lists()
|
422
|
+
self.right_panel.segment_table.clearSelection()
|
400
423
|
self.viewer.setFocus()
|
401
424
|
|
402
425
|
def rasterize_polygon(self, vertices):
|
@@ -418,27 +441,10 @@ class MainWindow(QMainWindow):
|
|
418
441
|
self.segment_items.clear()
|
419
442
|
selected_indices = self.get_selected_segment_indices()
|
420
443
|
|
421
|
-
unique_class_ids = sorted(
|
422
|
-
list(
|
423
|
-
{
|
424
|
-
seg.get("class_id")
|
425
|
-
for seg in self.segments
|
426
|
-
if seg.get("class_id") is not None
|
427
|
-
}
|
428
|
-
)
|
429
|
-
)
|
430
|
-
num_classes = len(unique_class_ids) if unique_class_ids else 1
|
431
|
-
class_id_to_hue_index = {
|
432
|
-
class_id: i for i, class_id in enumerate(unique_class_ids)
|
433
|
-
}
|
434
|
-
|
435
444
|
for i, seg_dict in enumerate(self.segments):
|
436
445
|
self.segment_items[i] = []
|
437
|
-
class_id = seg_dict.get("class_id"
|
438
|
-
|
439
|
-
hue_index = class_id_to_hue_index.get(class_id, 0)
|
440
|
-
hue = int((hue_index * 360 / num_classes)) % 360
|
441
|
-
base_color = QColor.fromHsv(hue, 220, 220)
|
446
|
+
class_id = seg_dict.get("class_id")
|
447
|
+
base_color = self._get_color_for_class(class_id, saturation=220, value=220)
|
442
448
|
|
443
449
|
if seg_dict["type"] == "Polygon":
|
444
450
|
poly_item = HoverablePolygonItem(QPolygonF(seg_dict["vertices"]))
|
@@ -529,27 +535,14 @@ class MainWindow(QMainWindow):
|
|
529
535
|
|
530
536
|
table.setRowCount(len(display_segments))
|
531
537
|
|
532
|
-
unique_class_ids = sorted(
|
533
|
-
list(
|
534
|
-
{
|
535
|
-
s.get("class_id")
|
536
|
-
for s in self.segments
|
537
|
-
if s.get("class_id") is not None
|
538
|
-
}
|
539
|
-
)
|
540
|
-
)
|
541
|
-
num_classes = len(unique_class_ids) if unique_class_ids else 1
|
542
|
-
class_id_to_hue_index = {cid: i for i, cid in enumerate(unique_class_ids)}
|
543
|
-
|
544
538
|
for row, (original_index, seg) in enumerate(display_segments):
|
545
|
-
class_id = seg.get("class_id"
|
546
|
-
|
547
|
-
hue = int((hue_index * 360 / num_classes)) % 360
|
548
|
-
color = QColor.fromHsv(hue, 150, 100)
|
539
|
+
class_id = seg.get("class_id")
|
540
|
+
color = self._get_color_for_class(class_id, saturation=180, value=200)
|
549
541
|
|
542
|
+
class_id_str = str(class_id) if class_id is not None else "N/A"
|
550
543
|
index_item = NumericTableWidgetItem(str(original_index + 1))
|
551
|
-
class_item = NumericTableWidgetItem(
|
552
|
-
type_item = QTableWidgetItem(seg
|
544
|
+
class_item = NumericTableWidgetItem(class_id_str)
|
545
|
+
type_item = QTableWidgetItem(seg.get("type", "N/A"))
|
553
546
|
|
554
547
|
index_item.setFlags(index_item.flags() & ~Qt.ItemFlag.ItemIsEditable)
|
555
548
|
type_item.setFlags(type_item.flags() & ~Qt.ItemFlag.ItemIsEditable)
|
@@ -558,12 +551,15 @@ class MainWindow(QMainWindow):
|
|
558
551
|
table.setItem(row, 0, index_item)
|
559
552
|
table.setItem(row, 1, class_item)
|
560
553
|
table.setItem(row, 2, type_item)
|
554
|
+
|
561
555
|
for col in range(3):
|
562
|
-
table.item(row, col)
|
556
|
+
if table.item(row, col):
|
557
|
+
table.item(row, col).setBackground(QBrush(color))
|
563
558
|
|
564
559
|
table.setSortingEnabled(False)
|
565
560
|
for row in range(table.rowCount()):
|
566
|
-
|
561
|
+
item = table.item(row, 0)
|
562
|
+
if item and item.data(Qt.ItemDataRole.UserRole) in selected_indices:
|
567
563
|
table.selectRow(row)
|
568
564
|
table.setSortingEnabled(True)
|
569
565
|
|
@@ -573,6 +569,8 @@ class MainWindow(QMainWindow):
|
|
573
569
|
def update_class_list(self):
|
574
570
|
class_table = self.right_panel.class_table
|
575
571
|
class_table.blockSignals(True)
|
572
|
+
class_table.clearContents()
|
573
|
+
|
576
574
|
unique_class_ids = sorted(
|
577
575
|
list(
|
578
576
|
{
|
@@ -583,18 +581,16 @@ class MainWindow(QMainWindow):
|
|
583
581
|
)
|
584
582
|
)
|
585
583
|
class_table.setRowCount(len(unique_class_ids))
|
586
|
-
|
587
|
-
class_id_to_hue_index = {
|
588
|
-
class_id: i for i, class_id in enumerate(unique_class_ids)
|
589
|
-
}
|
584
|
+
|
590
585
|
for row, cid in enumerate(unique_class_ids):
|
591
586
|
item = QTableWidgetItem(str(cid))
|
592
587
|
item.setFlags(item.flags() & ~Qt.ItemFlag.ItemIsEditable)
|
593
|
-
|
594
|
-
|
595
|
-
|
588
|
+
|
589
|
+
color = self._get_color_for_class(cid, saturation=180, value=200)
|
590
|
+
|
596
591
|
item.setBackground(QBrush(color))
|
597
592
|
class_table.setItem(row, 0, item)
|
593
|
+
|
598
594
|
class_table.blockSignals(False)
|
599
595
|
|
600
596
|
def update_class_filter_combo(self):
|
@@ -639,19 +635,35 @@ class MainWindow(QMainWindow):
|
|
639
635
|
if item.column() != 1:
|
640
636
|
return
|
641
637
|
table = self.right_panel.segment_table
|
638
|
+
index_item = table.item(item.row(), 0)
|
639
|
+
if not index_item:
|
640
|
+
return
|
641
|
+
|
642
642
|
table.blockSignals(True)
|
643
643
|
try:
|
644
|
-
|
645
|
-
|
644
|
+
new_class_id_text = item.text()
|
645
|
+
if not new_class_id_text.strip():
|
646
|
+
raise ValueError("Class ID cannot be empty.")
|
647
|
+
new_class_id = int(new_class_id_text)
|
648
|
+
original_index = index_item.data(Qt.ItemDataRole.UserRole)
|
649
|
+
|
650
|
+
if original_index is None or original_index >= len(self.segments):
|
651
|
+
raise IndexError("Invalid segment index found in table.")
|
652
|
+
|
646
653
|
self.segments[original_index]["class_id"] = new_class_id
|
647
654
|
if new_class_id >= self.next_class_id:
|
648
655
|
self.next_class_id = new_class_id + 1
|
649
656
|
self.update_all_lists()
|
650
|
-
except (ValueError, TypeError):
|
651
|
-
original_index =
|
652
|
-
|
653
|
-
|
654
|
-
|
657
|
+
except (ValueError, TypeError, AttributeError, IndexError) as e:
|
658
|
+
original_index = index_item.data(Qt.ItemDataRole.UserRole)
|
659
|
+
if original_index is not None and original_index < len(self.segments):
|
660
|
+
original_class_id = self.segments[original_index].get("class_id")
|
661
|
+
item.setText(
|
662
|
+
str(original_class_id) if original_class_id is not None else "N/A"
|
663
|
+
)
|
664
|
+
finally:
|
665
|
+
table.blockSignals(False)
|
666
|
+
self.viewer.setFocus()
|
655
667
|
|
656
668
|
def get_selected_segment_indices(self):
|
657
669
|
table = self.right_panel.segment_table
|
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
|