PyImageLabeling 1.0.0__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.
Files changed (99) hide show
  1. PyImageLabeling/__init__.py +22 -0
  2. PyImageLabeling/config.json +289 -0
  3. PyImageLabeling/controller/Controller.py +25 -0
  4. PyImageLabeling/controller/Events.py +147 -0
  5. PyImageLabeling/controller/FileEvents.py +69 -0
  6. PyImageLabeling/controller/ImageEvents.py +32 -0
  7. PyImageLabeling/controller/LabelEvents.py +219 -0
  8. PyImageLabeling/controller/LabelingEvents.py +123 -0
  9. PyImageLabeling/controller/settings/ContourFillinSetting.py +93 -0
  10. PyImageLabeling/controller/settings/CoutourFillingApplyCancel.py +37 -0
  11. PyImageLabeling/controller/settings/EraserSetting.py +73 -0
  12. PyImageLabeling/controller/settings/LabelSetting.py +91 -0
  13. PyImageLabeling/controller/settings/MagicPenSetting.py +125 -0
  14. PyImageLabeling/controller/settings/OpacitySetting.py +66 -0
  15. PyImageLabeling/controller/settings/PaintBrushSetting.py +66 -0
  16. PyImageLabeling/icons/apply.png +0 -0
  17. PyImageLabeling/icons/asterisk-green.png +0 -0
  18. PyImageLabeling/icons/asterisk-red.png +0 -0
  19. PyImageLabeling/icons/back.png +0 -0
  20. PyImageLabeling/icons/border.png +0 -0
  21. PyImageLabeling/icons/cancel.png +0 -0
  22. PyImageLabeling/icons/cleaner.png +0 -0
  23. PyImageLabeling/icons/close.png +0 -0
  24. PyImageLabeling/icons/down.png +0 -0
  25. PyImageLabeling/icons/ellipse.png +0 -0
  26. PyImageLabeling/icons/eraser.png +0 -0
  27. PyImageLabeling/icons/filling.png +0 -0
  28. PyImageLabeling/icons/logoMAIA.png +0 -0
  29. PyImageLabeling/icons/magic.png +0 -0
  30. PyImageLabeling/icons/maia.png +0 -0
  31. PyImageLabeling/icons/maia1.png +0 -0
  32. PyImageLabeling/icons/maia3.ico +0 -0
  33. PyImageLabeling/icons/maia_icon.png +0 -0
  34. PyImageLabeling/icons/move.png +0 -0
  35. PyImageLabeling/icons/opacity.png +0 -0
  36. PyImageLabeling/icons/open_image.png +0 -0
  37. PyImageLabeling/icons/open_layer.png +0 -0
  38. PyImageLabeling/icons/paint.png +0 -0
  39. PyImageLabeling/icons/plus.png +0 -0
  40. PyImageLabeling/icons/polygon.png +0 -0
  41. PyImageLabeling/icons/rectangle.png +0 -0
  42. PyImageLabeling/icons/reset.png +0 -0
  43. PyImageLabeling/icons/save.png +0 -0
  44. PyImageLabeling/icons/setting.png +0 -0
  45. PyImageLabeling/icons/transparency.png:Zone.Identifier +4 -0
  46. PyImageLabeling/icons/up.png +0 -0
  47. PyImageLabeling/icons/visibility.png +0 -0
  48. PyImageLabeling/icons/zoom_minus.png +0 -0
  49. PyImageLabeling/icons/zoom_plus.png +0 -0
  50. PyImageLabeling/model/Core.py +795 -0
  51. PyImageLabeling/model/File/Files.py +166 -0
  52. PyImageLabeling/model/File/NextImage.py +36 -0
  53. PyImageLabeling/model/File/PreviousImage.py +19 -0
  54. PyImageLabeling/model/Image/MoveImage.py +32 -0
  55. PyImageLabeling/model/Image/ResetMoveZoomImage.py +16 -0
  56. PyImageLabeling/model/Image/ZoomMinus.py +25 -0
  57. PyImageLabeling/model/Image/ZoomPlus.py +16 -0
  58. PyImageLabeling/model/Labeling/ClearAll.py +22 -0
  59. PyImageLabeling/model/Labeling/ContourFilling.py +135 -0
  60. PyImageLabeling/model/Labeling/Ellipse.py +350 -0
  61. PyImageLabeling/model/Labeling/Eraser.py +131 -0
  62. PyImageLabeling/model/Labeling/MagicPen.py +131 -0
  63. PyImageLabeling/model/Labeling/PaintBrush.py +207 -0
  64. PyImageLabeling/model/Labeling/Polygon.py +279 -0
  65. PyImageLabeling/model/Labeling/Rectangle.py +248 -0
  66. PyImageLabeling/model/Labeling/Undo.py +12 -0
  67. PyImageLabeling/model/Model.py +40 -0
  68. PyImageLabeling/model/Utils.py +40 -0
  69. PyImageLabeling/old_version/label_rectangle_properties.json +6 -0
  70. PyImageLabeling/old_version/main.py +2073 -0
  71. PyImageLabeling/old_version/models/EraseSettingsDialog.py +51 -0
  72. PyImageLabeling/old_version/models/LabeledRectangle.py +80 -0
  73. PyImageLabeling/old_version/models/MagicSettingsDialog.py +119 -0
  74. PyImageLabeling/old_version/models/OverlayOpacityDialog.py +63 -0
  75. PyImageLabeling/old_version/models/PaintSettingsDialog.py +289 -0
  76. PyImageLabeling/old_version/models/PointItem.py +66 -0
  77. PyImageLabeling/old_version/models/ProcessWorker.py +52 -0
  78. PyImageLabeling/old_version/models/ZoomableGraphicsView.py +1214 -0
  79. PyImageLabeling/old_version/models/tools/ContourTool.py +279 -0
  80. PyImageLabeling/old_version/models/tools/EraserTool.py +290 -0
  81. PyImageLabeling/old_version/models/tools/MagicPenTool.py +199 -0
  82. PyImageLabeling/old_version/models/tools/OverlayTool.py +179 -0
  83. PyImageLabeling/old_version/models/tools/PaintTool.py +68 -0
  84. PyImageLabeling/old_version/models/tools/PolygonTool.py +786 -0
  85. PyImageLabeling/old_version/models/tools/RectangleTool.py +1036 -0
  86. PyImageLabeling/parameters.json +1 -0
  87. PyImageLabeling/style.css +611 -0
  88. PyImageLabeling/view/Builder.py +333 -0
  89. PyImageLabeling/view/QBackgroundItem.py +30 -0
  90. PyImageLabeling/view/QWidgets.py +10 -0
  91. PyImageLabeling/view/View.py +226 -0
  92. PyImageLabeling/view/ZoomableGraphicsView.py +91 -0
  93. PyImageLabeling/view/__init__.py +0 -0
  94. pyimagelabeling-1.0.0.dist-info/METADATA +55 -0
  95. pyimagelabeling-1.0.0.dist-info/RECORD +99 -0
  96. pyimagelabeling-1.0.0.dist-info/WHEEL +5 -0
  97. pyimagelabeling-1.0.0.dist-info/licenses/LICENCE +22 -0
  98. pyimagelabeling-1.0.0.dist-info/top_level.txt +2 -0
  99. pypi/publish_pypi.py +18 -0
@@ -0,0 +1,219 @@
1
+
2
+ from controller.Events import Events
3
+
4
+ from PyQt6.QtWidgets import QMessageBox, QFileDialog, QDialog, QColorDialog
5
+ from PyQt6.QtGui import QPixmap, QImage
6
+
7
+ from PyImageLabeling.controller.settings.OpacitySetting import OpacitySetting
8
+ from PyImageLabeling.controller.settings.LabelSetting import LabelSetting
9
+ from PyImageLabeling.model.Utils import Utils
10
+
11
+ class LabelEvents(Events):
12
+ def __init__(self):
13
+ super().__init__()
14
+
15
+
16
+ def new_label(self):
17
+ self.all_events(self.new_label.__name__)
18
+ label_setting = LabelSetting(self.view.zoomable_graphics_view)
19
+
20
+ if label_setting.exec():
21
+ if self.model.name_already_exists(label_setting.name):
22
+ self.error_message("Error", f"The label name '{label_setting.name}' already exists!")
23
+ return
24
+
25
+ # Uncheck all activation buttons
26
+ for label_id in self.view.buttons_label_bar_temporary:
27
+ self.view.buttons_label_bar_temporary[label_id]["activation"].setChecked(False)
28
+
29
+ # Create a new label
30
+ label = self.model.new_label(label_setting.name, label_setting.labeling_mode, label_setting.color)
31
+
32
+ # Display the new label bar
33
+ self.view.builder.build_new_layer_label_bar(label.get_label_id(), label_setting.name, label_setting.labeling_mode, label_setting.color)
34
+
35
+ # Update the model with the new labal
36
+ self.model.update_labeling_overlays(label.get_label_id())
37
+
38
+ # Put the good labeling buttons according to the mode
39
+ self.view.update_labeling_buttons(label_setting.labeling_mode)
40
+
41
+ def select_label(self, label_id):
42
+ self.all_events(label_id)
43
+
44
+ # Uncheck all activation buttons except the selected label
45
+ for id in self.view.buttons_label_bar_temporary:
46
+ if id == label_id:
47
+ self.view.buttons_label_bar_temporary[id]["activation"].setChecked(True)
48
+ else:
49
+ self.view.buttons_label_bar_temporary[id]["activation"].setChecked(False)
50
+
51
+
52
+ # Active or deactivate the good labeling buttons
53
+ self.view.update_labeling_buttons(self.model.get_label_items()[label_id].get_labeling_mode())
54
+
55
+ label_item = self.model.get_label_items()[label_id]
56
+ if not label_item.get_visible():
57
+ # Set the label as visible
58
+ label_item.set_visible(True)
59
+
60
+ # Call the model part to change the labeling overlay
61
+ self.model.update_labeling_overlays(label_id)
62
+ #self.model.select_labeling_overlay(label_id)
63
+
64
+ # Ensure that the visibility button of this label is checked
65
+ self.view.buttons_label_bar_temporary[label_id]["visibility"].setChecked(True)
66
+
67
+ def color(self, label_id):
68
+ self.all_events(self.color.__name__)
69
+ label_item = self.model.get_label_items()[label_id]
70
+
71
+ color = QColorDialog.getColor(label_item.get_color())
72
+ if label_item.get_color() != color:
73
+ label_item.set_color(color)
74
+ self.model.update_color(label_id)
75
+ print("label_id:", label_id)
76
+ print("color:", color)
77
+ print("color:", type(color))
78
+
79
+ self.view.buttons_label_bar_temporary[label_id]["color"].setStyleSheet(Utils.color_to_stylesheet(color))
80
+
81
+ def visibility(self, label_id):
82
+ self.all_events(self.visibility.__name__)
83
+
84
+ if self.model.get_current_label_item().get_label_id() == label_id:
85
+ # Do nothing
86
+ self.view.buttons_label_bar_temporary[label_id]["visibility"].setChecked(True)
87
+ else:
88
+ # Get the label visibility state
89
+ label_item = self.model.get_label_items()[label_id]
90
+ new_visibility = not label_item.get_visible()
91
+ label_item.set_visible(new_visibility)
92
+ self.model.get_current_image_item().update_scene()
93
+ # Apply to all loaded images
94
+ # image_items = self.model.get_image_items()
95
+ # for file_path, image_item in image_items.items():
96
+ # if image_item is not None and label_id in image_item.labeling_overlays:
97
+ # overlay = image_item.labeling_overlays[label_id]
98
+ # if overlay.is_displayed_in_scene:
99
+ # overlay.set_visible(new_visibility)
100
+
101
+ def opacity(self):
102
+ self.all_events(self.opacity.__name__)
103
+ opacity_setting = OpacitySetting(self.view.zoomable_graphics_view)
104
+ if opacity_setting.exec():
105
+ self.model.set_opacity(opacity_setting.opacity/100) # To normalize
106
+
107
+
108
+ def label_setting(self, label_id):
109
+ self.all_events(self.label_setting.__name__)
110
+
111
+ label_item = self.model.get_label_items()[label_id]
112
+ #name = self.model.labeling_overlays[label_id].get_name()
113
+ #labeling_mode = self.model.labeling_overlays[label_id].get_labeling_mode()
114
+ #color = self.model.labeling_overlays[label_id].get_color()
115
+
116
+ label_setting = LabelSetting(self.view.zoomable_graphics_view,
117
+ label_item.get_name(),
118
+ label_item.get_labeling_mode(),
119
+ label_item.get_color())
120
+
121
+ if label_setting.exec():
122
+ if label_item.get_name() != label_setting.name:
123
+ if self.model.name_already_exists(label_setting.name):
124
+ self.error_message("Error", f"The label name '{label_setting.name}' already exists!")
125
+ return
126
+
127
+ # Change the name in the model
128
+ label_item.set_name(label_setting.name)
129
+
130
+ # Change the name in the view
131
+ self.view.buttons_label_bar_temporary[label_id]["activation"].setText(label_setting.name)
132
+
133
+ if label_item.get_labeling_mode() != label_setting.labeling_mode:
134
+ msgBox = QMessageBox(self.view.zoomable_graphics_view)
135
+ msgBox.setWindowTitle("Labeling Mode")
136
+ msgBox.setText("Are you sure you want to change the labeling mode ?")
137
+ msgBox.setInformativeText("All previous works done with this label will be erased.")
138
+ msgBox.setStandardButtons(QMessageBox.StandardButton.No | QMessageBox.StandardButton.Yes)
139
+ msgBox.setDefaultButton(QMessageBox.StandardButton.No)
140
+ msgBox.setModal(True)
141
+ result = msgBox.exec()
142
+ if result == QMessageBox.StandardButton.Yes:
143
+ # Change in the model
144
+ label_item.set_labeling_mode(label_setting.labeling_mode)
145
+
146
+ # Reset the labeling overlay
147
+ for file_path in self.model.file_paths:
148
+ image_item = self.model.image_items[file_path]
149
+ if image_item is not None and label_id in image_item.labeling_overlays:
150
+ image_item.labeling_overlays[label_id].reset()
151
+
152
+ # Put the good labeling buttons according to the mode
153
+ self.view.update_labeling_buttons(label_setting.labeling_mode)
154
+
155
+ if label_item.get_color() != label_setting.color:
156
+ label_item.set_color(label_setting.color)
157
+ self.model.update_color(label_id)
158
+ self.view.buttons_label_bar_temporary[label_id]["color"].setStyleSheet(Utils.color_to_stylesheet(label_setting.color))
159
+
160
+
161
+ def remove_label(self, label_id):
162
+ self.all_events(self.remove_label.__name__)
163
+ msgBox = QMessageBox(self.view.zoomable_graphics_view)
164
+ msgBox.setWindowTitle("Remove Label")
165
+ msgBox.setText("Are you sure you want to delete this label ?")
166
+ msgBox.setInformativeText("All previous works done with this label will be erased on all images.")
167
+ msgBox.setStandardButtons(QMessageBox.StandardButton.No | QMessageBox.StandardButton.Yes)
168
+ msgBox.setDefaultButton(QMessageBox.StandardButton.No)
169
+ for button in msgBox.buttons():
170
+ button.setObjectName("dialog")
171
+
172
+ msgBox.setModal(True)
173
+ result = msgBox.exec()
174
+ if result == QMessageBox.StandardButton.Yes:
175
+ # Remove from all images (replace the old single overlay removal)
176
+ image_items = self.model.get_image_items()
177
+ for file_path, image_item in image_items.items():
178
+ if image_item is not None and label_id in image_item.labeling_overlays:
179
+ image_item.labeling_overlays[label_id].remove()
180
+ del image_item.labeling_overlays[label_id]
181
+
182
+ # Remove from the model's label_items
183
+ if label_id in self.model.get_label_items():
184
+ del self.model.get_label_items()[label_id]
185
+
186
+ # Remove from the view
187
+ if label_id in self.view.container_label_bar_temporary:
188
+ widget, separator = self.view.container_label_bar_temporary[label_id]
189
+
190
+ widget.hide()
191
+ self.view.label_bar_layout.removeWidget(widget)
192
+ separator.hide()
193
+ self.view.label_bar_layout.removeWidget(separator)
194
+
195
+ # Clean up the view dictionaries
196
+ del self.view.container_label_bar_temporary[label_id]
197
+ if label_id in self.view.buttons_label_bar_temporary:
198
+ del self.view.buttons_label_bar_temporary[label_id]
199
+
200
+ # Check if there are no more labels
201
+ if len(self.model.get_label_items()) == 0:
202
+ for button_key in self.view.buttons_labeling_bar.keys():
203
+ self.view.buttons_labeling_bar[button_key].setEnabled(False)
204
+ self.move_image() # To deactivate the last used tool, we active the move button :)
205
+ self.model.remove_contour()
206
+ self.model.set_current_label_item(None)
207
+ # Select another label if the deleted one was selected (this condition should never be true now)
208
+ elif self.model.get_current_label_item() is not None and self.model.get_current_label_item().get_label_id() == label_id:
209
+ first_id = list(self.model.get_label_items().keys())[0]
210
+ self.select_label(first_id)
211
+
212
+
213
+
214
+
215
+
216
+
217
+
218
+
219
+
@@ -0,0 +1,123 @@
1
+ from PyImageLabeling.controller.Events import Events
2
+ from PyImageLabeling.controller.settings.MagicPenSetting import MagicPenSetting
3
+ from PyImageLabeling.controller.settings.PaintBrushSetting import PaintBrushSetting
4
+ from PyImageLabeling.controller.settings.EraserSetting import EraserSetting
5
+ from PyImageLabeling.controller.settings.ContourFillinSetting import ContourFillingSetting
6
+ from PyQt6.QtWidgets import QDialog
7
+
8
+ class LabelingEvents(Events):
9
+ def __init__(self):
10
+ super().__init__()
11
+
12
+ ### Buttons events
13
+
14
+ def apply(self):
15
+ print("apply")
16
+
17
+ def cancel(self):
18
+ print("cancel")
19
+
20
+
21
+ def contour_filling(self):
22
+ self.model.remove_contour()
23
+ self.desactivate_buttons_labeling_image_bar(self.contour_filling.__name__)
24
+ self.all_events(self.contour_filling.__name__)
25
+ self.view.zoomable_graphics_view.change_cursor("filling")
26
+ self.model.contour_filling()
27
+ self.model.start_contour_filling()
28
+
29
+ def paintbrush(self):
30
+ self.model.remove_contour()
31
+
32
+ self.desactivate_buttons_labeling_image_bar(self.paintbrush.__name__)
33
+ self.all_events(self.paintbrush.__name__)
34
+ self.view.zoomable_graphics_view.change_cursor("paint")
35
+ self.model.paint_brush()
36
+
37
+ def magic_pen(self):
38
+ self.model.remove_contour()
39
+
40
+ self.desactivate_buttons_labeling_image_bar(self.magic_pen.__name__)
41
+ self.all_events(self.magic_pen.__name__)
42
+ self.view.zoomable_graphics_view.change_cursor("magic")
43
+ self.model.magic_pen()
44
+
45
+ def ellipse(self):
46
+ self.desactivate_buttons_labeling_image_bar(self.ellipse.__name__)
47
+ self.all_events(self.ellipse.__name__)
48
+ self.view.zoomable_graphics_view.change_cursor("ellipse")
49
+ self.model.ellipse()
50
+
51
+ def rectangle(self):
52
+ self.desactivate_buttons_labeling_image_bar(self.rectangle.__name__)
53
+ self.all_events(self.rectangle.__name__)
54
+ self.view.zoomable_graphics_view.change_cursor("rectangle")
55
+ self.model.rectangle()
56
+
57
+ def polygon(self):
58
+ self.desactivate_buttons_labeling_image_bar(self.polygon.__name__)
59
+ self.all_events(self.polygon.__name__)
60
+ self.view.zoomable_graphics_view.change_cursor("polygon")
61
+ self.model.polygon()
62
+
63
+ def undo(self):
64
+ self.all_events(self.undo.__name__)
65
+ self.model.undo()
66
+
67
+ def eraser(self):
68
+ self.model.remove_contour()
69
+
70
+ self.desactivate_buttons_labeling_image_bar(self.eraser.__name__)
71
+ self.all_events(self.eraser.__name__)
72
+ self.view.zoomable_graphics_view.change_cursor("eraser")
73
+ self.model.eraser()
74
+
75
+ def clear_all(self):
76
+ self.all_events(self.clear_all.__name__)
77
+ self.model.clear_all()
78
+
79
+ ### Setting Buttons events
80
+
81
+
82
+ def contour_filling_setting(self):
83
+ self.all_events(self.contour_filling_setting.__name__)
84
+ contourfillingsetting = ContourFillingSetting(self.view.zoomable_graphics_view)
85
+ if contourfillingsetting.exec():
86
+ self.view.desactivate_buttons(self.contour_filling.__name__, [self.view.buttons_labeling_bar, self.view.buttons_image_bar])
87
+ self.contour_filling()
88
+
89
+ def paintbrush_setting(self):
90
+
91
+ self.all_events(self.paintbrush_setting.__name__)
92
+ paintbrushsetting = PaintBrushSetting(self.view.zoomable_graphics_view, self.model)
93
+ if paintbrushsetting.exec():
94
+ self.view.desactivate_buttons(self.paintbrush.__name__, [self.view.buttons_labeling_bar, self.view.buttons_image_bar])
95
+ self.paintbrush()
96
+
97
+ def magic_pen_setting(self):
98
+
99
+ self.all_events(self.magic_pen_setting.__name__)
100
+ magicpensetting = MagicPenSetting(self.view.zoomable_graphics_view)
101
+ if magicpensetting.exec():
102
+ self.view.desactivate_buttons(self.magic_pen.__name__, [self.view.buttons_labeling_bar, self.view.buttons_image_bar])
103
+ self.magic_pen()
104
+
105
+ def ellipse_setting(self):
106
+ self.all_events(self.ellipse_setting.__name__)
107
+ print("ellipse_setting")
108
+
109
+ def rectangle_setting(self):
110
+ self.all_events(self.rectangle_setting.__name__)
111
+ print("rectangle_setting")
112
+
113
+ def polygon_setting(self):
114
+ self.all_events(self.polygon_setting.__name__)
115
+ print("polygon_setting")
116
+
117
+ def eraser_setting(self):
118
+ self.all_events(self.eraser_setting.__name__)
119
+ erasersetting = EraserSetting(self.view.zoomable_graphics_view)
120
+ if erasersetting.exec():
121
+ self.view.desactivate_buttons(self.eraser.__name__, [self.view.buttons_labeling_bar, self.view.buttons_image_bar])
122
+ self.eraser()
123
+
@@ -0,0 +1,93 @@
1
+ from PyQt6.QtWidgets import QDialog, QSlider, QFormLayout, QDialogButtonBox, QSpinBox, QLabel, QVBoxLayout
2
+ from PyQt6.QtCore import Qt
3
+
4
+ from PyImageLabeling.model.Utils import Utils
5
+
6
+ class ContourFillingSetting(QDialog):
7
+ def __init__(self, parent):
8
+ super().__init__(parent)
9
+ self.setWindowTitle("Contour Filling Settings")
10
+ self.resize(500, 100)
11
+
12
+ self.tolerance = Utils.load_parameters()["contour_filling"]["tolerance"]
13
+
14
+ layout = QVBoxLayout()
15
+ form_layout = QFormLayout()
16
+
17
+ # Tolerance slider and spinbox
18
+ self.tolerance_slider = QSlider(Qt.Orientation.Horizontal)
19
+ self.tolerance_slider.setRange(1, 10)
20
+ self.tolerance_slider.setValue(self.tolerance)
21
+ self.tolerance_slider.setTickPosition(QSlider.TickPosition.TicksBelow)
22
+ self.tolerance_slider.setTickInterval(1)
23
+
24
+ self.tolerance_spinbox = QSpinBox()
25
+ self.tolerance_spinbox.setRange(1, 10)
26
+ self.tolerance_spinbox.setValue(self.tolerance)
27
+
28
+ # Connect both ways to keep them synchronized
29
+ self.tolerance_spinbox.valueChanged.connect(self.tolerance_slider.setValue)
30
+ self.tolerance_slider.valueChanged.connect(self.tolerance_spinbox.setValue)
31
+
32
+ # Update internal values when sliders change
33
+ self.tolerance_slider.valueChanged.connect(self.update_tolerance)
34
+ self.tolerance_spinbox.valueChanged.connect(self.update_tolerance)
35
+
36
+ form_layout.addRow("Tolerance:", self.tolerance_slider)
37
+ form_layout.addRow("Value:", self.tolerance_spinbox)
38
+
39
+ # Label to display the tolerance description
40
+ self.tolerance_description_label = QLabel()
41
+ self.update_tolerance_description()
42
+ form_layout.addRow("Description:", self.tolerance_description_label)
43
+
44
+ layout.addLayout(form_layout)
45
+
46
+ # Buttons
47
+ self.buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
48
+ self.buttons.accepted.connect(self.accept)
49
+ self.buttons.rejected.connect(self.reject)
50
+ layout.addWidget(self.buttons)
51
+
52
+ self.setLayout(layout)
53
+
54
+ def update_tolerance(self, value):
55
+ """Update internal tolerance value when slider changes"""
56
+ self.tolerance = value
57
+ self.update_tolerance_description()
58
+
59
+ def update_tolerance_description(self):
60
+ """Update the tolerance description label based on the current tolerance level"""
61
+ descriptions = {
62
+ 1: "Most Precise - Only very clear, well-defined contours",
63
+ 2: "Very Precise - Clear contours with minimal noise",
64
+ 3: "Precise - Well-defined contours, some fine details",
65
+ 4: "Moderately Precise - Good balance, cleaner results",
66
+ 5: "Balanced - Default setting, good for most images",
67
+ 6: "Moderately Tolerant - Captures more subtle edges",
68
+ 7: "Tolerant - Finds more contours, including faint ones",
69
+ 8: "Very Tolerant - Detects subtle features and textures",
70
+ 9: "Highly Tolerant - Captures most edge information",
71
+ 10: "Most Tolerant - Maximum sensitivity, may include noise"
72
+ }
73
+ description = descriptions.get(self.tolerance, "Unknown tolerance level")
74
+ self.tolerance_description_label.setText(description)
75
+
76
+ def get_settings(self):
77
+ """Return current settings from the UI controls"""
78
+ # Get values directly from the controls to ensure we have the latest values
79
+ tolerance = self.tolerance_slider.value()
80
+
81
+ # Also update internal variables for consistency
82
+ self.tolerance = tolerance
83
+ return tolerance
84
+
85
+ def accept(self):
86
+ """Override accept to ensure settings are updated before closing"""
87
+ # Update internal values one final time
88
+ self.tolerance = self.tolerance_slider.value()
89
+ data = Utils.load_parameters()
90
+ data["contour_filling"]["tolerance"] = self.tolerance
91
+ Utils.save_parameters(data)
92
+ return super().accept()
93
+
@@ -0,0 +1,37 @@
1
+ from PyQt6.QtWidgets import QDialog, QSlider, QFormLayout, QDialogButtonBox, QSpinBox, QLabel, QVBoxLayout
2
+ from PyQt6.QtCore import Qt, QRect, QPoint
3
+
4
+ from PyImageLabeling.model.Utils import Utils
5
+
6
+ class ContourFillingApplyCancel(QDialog):
7
+ def __init__(self, parent):
8
+ super().__init__(parent)
9
+ self.setWindowFlag(Qt.WindowType.FramelessWindowHint)
10
+ #self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
11
+ self.setObjectName("apply_cancel_bar")
12
+ #self.setGeometry(QRect(100,100, 300, 70))
13
+ self.move(parent.mapToGlobal(QPoint(10,10)))
14
+ layout = QVBoxLayout()
15
+ form_layout = QFormLayout()
16
+
17
+ # Help text
18
+ tolerance_help = QLabel("Click on the area to fill and apply")
19
+ form_layout.addRow("", tolerance_help)
20
+
21
+ layout.addLayout(form_layout)
22
+
23
+ # Buttons
24
+ self.buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
25
+ for button in self.buttons.buttons():
26
+ button.setObjectName("dialog")
27
+ self.buttons.accepted.connect(self.accept)
28
+ self.buttons.rejected.connect(self.reject)
29
+ layout.addWidget(self.buttons)
30
+
31
+ self.setLayout(layout)
32
+
33
+ def accept(self):
34
+ """Override accept to ensure settings are updated before closing"""
35
+ # Update internal values one final time
36
+ return super().accept()
37
+
@@ -0,0 +1,73 @@
1
+ from PyQt6.QtWidgets import QCheckBox, QDialog, QSlider, QFormLayout, QDialogButtonBox, QSpinBox, QLabel, QHBoxLayout, QVBoxLayout
2
+ from PyQt6.QtCore import Qt
3
+
4
+ from PyImageLabeling.model.Utils import Utils
5
+
6
+ class EraserSetting(QDialog):
7
+ def __init__(self, parent):
8
+ super().__init__(parent)
9
+ self.setWindowTitle("Eraser Settings")
10
+ self.resize(500, 100)
11
+
12
+ self.radius = Utils.load_parameters()["eraser"]["size"]
13
+ self.absolute_mode = Utils.load_parameters()["eraser"].get("absolute_mode", 0)
14
+
15
+ layout = QVBoxLayout()
16
+ form_layout = QFormLayout()
17
+
18
+ # Tolerance slider and spinbox
19
+ self.radius_slider = QSlider(Qt.Orientation.Horizontal)
20
+ self.radius_slider.setRange(0, 100)
21
+ self.radius_slider.setValue(self.radius)
22
+ self.radius_slider.setTickPosition(QSlider.TickPosition.TicksBelow)
23
+ self.radius_slider.setTickInterval(10)
24
+
25
+ self.radius_spinbox = QSpinBox()
26
+ self.radius_spinbox.setRange(0, 100)
27
+ self.radius_spinbox.setValue(self.radius)
28
+
29
+ self.absolute_checkbox = QCheckBox("Absolute mode")
30
+ self.absolute_checkbox.setChecked(self.absolute_mode == 1)
31
+
32
+ # Connect both ways to keep them synchronized
33
+ self.radius_spinbox.valueChanged.connect(self.radius_slider.setValue)
34
+ self.radius_slider.valueChanged.connect(self.radius_spinbox.setValue)
35
+
36
+ # Update internal values when sliders change
37
+ self.radius_slider.valueChanged.connect(self.update_radius)
38
+ self.radius_spinbox.valueChanged.connect(self.update_radius)
39
+ self.absolute_checkbox.stateChanged.connect(self.update_absolute_mode)
40
+
41
+ form_layout.addRow("Radius:", self.radius_slider)
42
+ form_layout.addRow("Value:", self.radius_spinbox)
43
+ form_layout.addRow("", self.absolute_checkbox)
44
+
45
+
46
+ layout.addLayout(form_layout)
47
+
48
+ # Buttons
49
+ self.buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
50
+ self.buttons.accepted.connect(self.accept)
51
+ self.buttons.rejected.connect(self.reject)
52
+ layout.addWidget(self.buttons)
53
+
54
+ self.setLayout(layout)
55
+
56
+ def update_radius(self, value):
57
+ """Update internal tolerance value when slider changes"""
58
+ self.radius = value
59
+
60
+ def update_absolute_mode(self, state):
61
+ """Update internal absolute mode value when checkbox changes"""
62
+ self.absolute_mode = 1 if state == Qt.CheckState.Checked.value else 0
63
+
64
+ def accept(self):
65
+ """Override accept to ensure settings are updated before closing"""
66
+ # Update internal values one final time
67
+ self.radius= self.radius_slider.value()
68
+ self.radius = self.radius_slider.value()
69
+ data = Utils.load_parameters()
70
+ data["eraser"]["size"] = self.radius
71
+ data["eraser"]["absolute_mode"] = self.absolute_mode
72
+ Utils.save_parameters(data)
73
+ return super().accept()
@@ -0,0 +1,91 @@
1
+ from PyQt6.QtWidgets import QComboBox, QPushButton, QHBoxLayout, QColorDialog, QDialog, QSlider, QFormLayout, QDialogButtonBox, QSpinBox
2
+
3
+ from PyQt6.QtGui import QColor
4
+ from PyQt6.QtCore import Qt
5
+ import random
6
+
7
+ class LabelSetting(QDialog):
8
+
9
+ def __init__(self, parent, name=None, labeling_mode=None, color=None):
10
+ super().__init__(parent)
11
+
12
+ labeling_mode_pixel = parent.view.config["labeling_bar"]["pixel"]["name_view"]
13
+ labeling_mode_geometric = parent.view.config["labeling_bar"]["geometric"]["name_view"]
14
+ automatic_name = "label "+str(parent.view.controller.model.get_static_label_id()+1)
15
+
16
+ self.name = automatic_name if name is None else name
17
+ self.color = QColor(random.choice(QColor.colorNames())) if color is None else color
18
+ self.labeling_mode = labeling_mode_pixel if labeling_mode is None else labeling_mode
19
+
20
+ self.setWindowTitle("Label Setting")
21
+
22
+ layout = QFormLayout()
23
+
24
+ label_layout = QHBoxLayout()
25
+
26
+ self.label_combo = QComboBox()
27
+ self.label_combo.setEditable(True)
28
+ self.label_combo.setPlaceholderText("Enter new label or select existing")
29
+
30
+ # Populate combo box with saved labels
31
+ self.label_combo.addItem("") # Empty option for new labels
32
+ #for label in self.label_manager.get_all_labels():
33
+ # self.label_combo.addItem(label)
34
+
35
+ # Set current label if provided
36
+ #if self.label:
37
+ # index = self.label_combo.findText(self.label)
38
+ # if index >= 0:
39
+ # self.label_combo.setCurrentIndex(index)
40
+ # else:
41
+ # self.label_combo.setCurrentText(self.label)
42
+ self.label_combo.setCurrentText(self.name)
43
+
44
+ self.label_combo.currentTextChanged.connect(self.name_update)
45
+ label_layout.addWidget(self.label_combo)
46
+
47
+ layout.addRow("Label:", label_layout)
48
+
49
+ self.mode_combo = QComboBox()
50
+ self.mode_combo.addItems([labeling_mode_pixel]) #add later: labeling_mode_geometric
51
+ self.mode_combo.setCurrentText(self.labeling_mode)
52
+ self.mode_combo.currentTextChanged.connect(self.mode_update)
53
+ layout.addRow("Labeling Mode:", self.mode_combo)
54
+
55
+ # Color selection
56
+ self.color_button = QPushButton("Choose Color")
57
+ self.color_button.clicked.connect(self.select_color)
58
+ self.color_update(self.color)
59
+ layout.addRow("Color:", self.color_button)
60
+
61
+ self.buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
62
+ self.buttons.accepted.connect(self.accept)
63
+ self.buttons.rejected.connect(self.reject)
64
+ layout.addRow(self.buttons)
65
+
66
+ self.setLayout(layout)
67
+
68
+ def name_update(self, name):
69
+ self.name = name
70
+
71
+ def mode_update(self, labeling_mode):
72
+ self.labeling_mode = labeling_mode
73
+
74
+ def color_update(self, color):
75
+ """Update color button appearance to show current color"""
76
+ self.color = color
77
+ color_style = f"background-color: rgb({self.color.red()}, {self.color.green()}, {self.color.blue()}); color: {'white' if self.color.lightness() < 128 else 'black'};"
78
+ self.color_button.setStyleSheet(color_style)
79
+ self.color_button.setText(f"Color: {self.color.name()}")
80
+
81
+ def select_color(self):
82
+ color = QColorDialog.getColor(initial=self.color)
83
+ if color.isValid(): self.color_update(color)
84
+
85
+ def accept(self):
86
+ """Override accept to ensure settings are updated before closing"""
87
+ self.name = self.label_combo.currentText()
88
+ self.labeling_mode = self.mode_combo.currentText()
89
+ return super().accept()
90
+
91
+