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.
- PyImageLabeling/__init__.py +22 -0
- PyImageLabeling/config.json +289 -0
- PyImageLabeling/controller/Controller.py +25 -0
- PyImageLabeling/controller/Events.py +147 -0
- PyImageLabeling/controller/FileEvents.py +69 -0
- PyImageLabeling/controller/ImageEvents.py +32 -0
- PyImageLabeling/controller/LabelEvents.py +219 -0
- PyImageLabeling/controller/LabelingEvents.py +123 -0
- PyImageLabeling/controller/settings/ContourFillinSetting.py +93 -0
- PyImageLabeling/controller/settings/CoutourFillingApplyCancel.py +37 -0
- PyImageLabeling/controller/settings/EraserSetting.py +73 -0
- PyImageLabeling/controller/settings/LabelSetting.py +91 -0
- PyImageLabeling/controller/settings/MagicPenSetting.py +125 -0
- PyImageLabeling/controller/settings/OpacitySetting.py +66 -0
- PyImageLabeling/controller/settings/PaintBrushSetting.py +66 -0
- PyImageLabeling/icons/apply.png +0 -0
- PyImageLabeling/icons/asterisk-green.png +0 -0
- PyImageLabeling/icons/asterisk-red.png +0 -0
- PyImageLabeling/icons/back.png +0 -0
- PyImageLabeling/icons/border.png +0 -0
- PyImageLabeling/icons/cancel.png +0 -0
- PyImageLabeling/icons/cleaner.png +0 -0
- PyImageLabeling/icons/close.png +0 -0
- PyImageLabeling/icons/down.png +0 -0
- PyImageLabeling/icons/ellipse.png +0 -0
- PyImageLabeling/icons/eraser.png +0 -0
- PyImageLabeling/icons/filling.png +0 -0
- PyImageLabeling/icons/logoMAIA.png +0 -0
- PyImageLabeling/icons/magic.png +0 -0
- PyImageLabeling/icons/maia.png +0 -0
- PyImageLabeling/icons/maia1.png +0 -0
- PyImageLabeling/icons/maia3.ico +0 -0
- PyImageLabeling/icons/maia_icon.png +0 -0
- PyImageLabeling/icons/move.png +0 -0
- PyImageLabeling/icons/opacity.png +0 -0
- PyImageLabeling/icons/open_image.png +0 -0
- PyImageLabeling/icons/open_layer.png +0 -0
- PyImageLabeling/icons/paint.png +0 -0
- PyImageLabeling/icons/plus.png +0 -0
- PyImageLabeling/icons/polygon.png +0 -0
- PyImageLabeling/icons/rectangle.png +0 -0
- PyImageLabeling/icons/reset.png +0 -0
- PyImageLabeling/icons/save.png +0 -0
- PyImageLabeling/icons/setting.png +0 -0
- PyImageLabeling/icons/transparency.png:Zone.Identifier +4 -0
- PyImageLabeling/icons/up.png +0 -0
- PyImageLabeling/icons/visibility.png +0 -0
- PyImageLabeling/icons/zoom_minus.png +0 -0
- PyImageLabeling/icons/zoom_plus.png +0 -0
- PyImageLabeling/model/Core.py +795 -0
- PyImageLabeling/model/File/Files.py +166 -0
- PyImageLabeling/model/File/NextImage.py +36 -0
- PyImageLabeling/model/File/PreviousImage.py +19 -0
- PyImageLabeling/model/Image/MoveImage.py +32 -0
- PyImageLabeling/model/Image/ResetMoveZoomImage.py +16 -0
- PyImageLabeling/model/Image/ZoomMinus.py +25 -0
- PyImageLabeling/model/Image/ZoomPlus.py +16 -0
- PyImageLabeling/model/Labeling/ClearAll.py +22 -0
- PyImageLabeling/model/Labeling/ContourFilling.py +135 -0
- PyImageLabeling/model/Labeling/Ellipse.py +350 -0
- PyImageLabeling/model/Labeling/Eraser.py +131 -0
- PyImageLabeling/model/Labeling/MagicPen.py +131 -0
- PyImageLabeling/model/Labeling/PaintBrush.py +207 -0
- PyImageLabeling/model/Labeling/Polygon.py +279 -0
- PyImageLabeling/model/Labeling/Rectangle.py +248 -0
- PyImageLabeling/model/Labeling/Undo.py +12 -0
- PyImageLabeling/model/Model.py +40 -0
- PyImageLabeling/model/Utils.py +40 -0
- PyImageLabeling/old_version/label_rectangle_properties.json +6 -0
- PyImageLabeling/old_version/main.py +2073 -0
- PyImageLabeling/old_version/models/EraseSettingsDialog.py +51 -0
- PyImageLabeling/old_version/models/LabeledRectangle.py +80 -0
- PyImageLabeling/old_version/models/MagicSettingsDialog.py +119 -0
- PyImageLabeling/old_version/models/OverlayOpacityDialog.py +63 -0
- PyImageLabeling/old_version/models/PaintSettingsDialog.py +289 -0
- PyImageLabeling/old_version/models/PointItem.py +66 -0
- PyImageLabeling/old_version/models/ProcessWorker.py +52 -0
- PyImageLabeling/old_version/models/ZoomableGraphicsView.py +1214 -0
- PyImageLabeling/old_version/models/tools/ContourTool.py +279 -0
- PyImageLabeling/old_version/models/tools/EraserTool.py +290 -0
- PyImageLabeling/old_version/models/tools/MagicPenTool.py +199 -0
- PyImageLabeling/old_version/models/tools/OverlayTool.py +179 -0
- PyImageLabeling/old_version/models/tools/PaintTool.py +68 -0
- PyImageLabeling/old_version/models/tools/PolygonTool.py +786 -0
- PyImageLabeling/old_version/models/tools/RectangleTool.py +1036 -0
- PyImageLabeling/parameters.json +1 -0
- PyImageLabeling/style.css +611 -0
- PyImageLabeling/view/Builder.py +333 -0
- PyImageLabeling/view/QBackgroundItem.py +30 -0
- PyImageLabeling/view/QWidgets.py +10 -0
- PyImageLabeling/view/View.py +226 -0
- PyImageLabeling/view/ZoomableGraphicsView.py +91 -0
- PyImageLabeling/view/__init__.py +0 -0
- pyimagelabeling-1.0.0.dist-info/METADATA +55 -0
- pyimagelabeling-1.0.0.dist-info/RECORD +99 -0
- pyimagelabeling-1.0.0.dist-info/WHEEL +5 -0
- pyimagelabeling-1.0.0.dist-info/licenses/LICENCE +22 -0
- pyimagelabeling-1.0.0.dist-info/top_level.txt +2 -0
- 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
|
+
|