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,248 @@
1
+ from PyImageLabeling.model.Core import Core
2
+ from PyQt6.QtWidgets import QGraphicsRectItem
3
+ from PyQt6.QtGui import QPen, QCursor, QBrush
4
+ from PyQt6.QtCore import Qt, QPointF, QRectF, QSizeF
5
+ import math
6
+
7
+ HANDLE_SIZE = 8
8
+ HANDLE_DETECTION_DISTANCE = 15
9
+ MIN_RECT_SIZE = 10
10
+
11
+
12
+ class RectangleItem(QGraphicsRectItem):
13
+ HANDLE_TYPES = {
14
+ 'top_left': Qt.CursorShape.SizeFDiagCursor,
15
+ 'top_right': Qt.CursorShape.SizeBDiagCursor,
16
+ 'bottom_left': Qt.CursorShape.SizeBDiagCursor,
17
+ 'bottom_right': Qt.CursorShape.SizeFDiagCursor,
18
+ 'rotation': Qt.CursorShape.OpenHandCursor,
19
+ }
20
+
21
+ def __init__(self, x, y, width, height, color=Qt.GlobalColor.red):
22
+ super().__init__(x, y, width, height)
23
+
24
+ pen = QPen(color, 2, Qt.PenStyle.SolidLine)
25
+ self.setPen(pen)
26
+
27
+ self.setFlags(
28
+ QGraphicsRectItem.GraphicsItemFlag.ItemIsSelectable
29
+ | QGraphicsRectItem.GraphicsItemFlag.ItemIsMovable
30
+ | QGraphicsRectItem.GraphicsItemFlag.ItemSendsGeometryChanges
31
+ )
32
+ self.setAcceptHoverEvents(True)
33
+
34
+ self.handles = {}
35
+ self.handle_selected = None
36
+ self.handles_visible = False
37
+ self.initial_rotation = 0
38
+ self.initial_angle = 0
39
+
40
+ self.update_handles()
41
+
42
+ def update_handles(self):
43
+ rect = self.rect()
44
+ self.handles = {
45
+ 'top_left': QRectF(rect.topLeft() - QPointF(HANDLE_SIZE / 2, HANDLE_SIZE / 2), QSizeF(HANDLE_SIZE, HANDLE_SIZE)),
46
+ 'top_right': QRectF(rect.topRight() - QPointF(HANDLE_SIZE / 2, HANDLE_SIZE / 2), QSizeF(HANDLE_SIZE, HANDLE_SIZE)),
47
+ 'bottom_left': QRectF(rect.bottomLeft() - QPointF(HANDLE_SIZE / 2, HANDLE_SIZE / 2), QSizeF(HANDLE_SIZE, HANDLE_SIZE)),
48
+ 'bottom_right': QRectF(rect.bottomRight() - QPointF(HANDLE_SIZE / 2, HANDLE_SIZE / 2), QSizeF(HANDLE_SIZE, HANDLE_SIZE)),
49
+ 'rotation': QRectF(rect.center() - QPointF(HANDLE_SIZE / 2, HANDLE_SIZE / 2), QSizeF(HANDLE_SIZE, HANDLE_SIZE)),
50
+ }
51
+
52
+ def check_handle_proximity(self, pos):
53
+ near_handle = any(
54
+ self.distance_to_rect(pos, rect) < HANDLE_DETECTION_DISTANCE
55
+ for rect in self.handles.values()
56
+ )
57
+ if self.isSelected():
58
+ near_handle = True
59
+
60
+ if near_handle != self.handles_visible:
61
+ self.handles_visible = near_handle
62
+ self.update()
63
+
64
+ @staticmethod
65
+ def distance_to_rect(point, rect):
66
+ center = rect.center()
67
+ return math.hypot(point.x() - center.x(), point.y() - center.y())
68
+
69
+ def update_cursor(self, pos):
70
+ if not self.handles_visible:
71
+ return
72
+ for name, rect in self.handles.items():
73
+ if rect.contains(pos):
74
+ self.setCursor(self.HANDLE_TYPES.get(name, Qt.CursorShape.ArrowCursor))
75
+ return
76
+ self.setCursor(Qt.CursorShape.SizeAllCursor)
77
+
78
+ def hoverEnterEvent(self, event):
79
+ self.check_handle_proximity(event.pos())
80
+ super().hoverEnterEvent(event)
81
+
82
+ def hoverMoveEvent(self, event):
83
+ self.check_handle_proximity(event.pos())
84
+ self.update_cursor(event.pos())
85
+ super().hoverMoveEvent(event)
86
+
87
+ def hoverLeaveEvent(self, event):
88
+ self.handles_visible = False
89
+ self.setCursor(Qt.CursorShape.ArrowCursor)
90
+ self.update()
91
+ super().hoverLeaveEvent(event)
92
+
93
+ def paint(self, painter, option, widget=None):
94
+ super().paint(painter, option, widget)
95
+ if not self.handles_visible:
96
+ return
97
+
98
+ # Draw resize handles
99
+ painter.setPen(QPen(Qt.GlobalColor.black, 1))
100
+ painter.setBrush(QBrush(Qt.GlobalColor.white))
101
+ for name, handle in self.handles.items():
102
+ if name == "rotation":
103
+ painter.setPen(QPen(Qt.GlobalColor.blue, 2))
104
+ painter.setBrush(QBrush(Qt.GlobalColor.blue))
105
+ painter.drawEllipse(handle)
106
+ else:
107
+ painter.drawRect(handle)
108
+
109
+ def mousePressEvent(self, event):
110
+ self.handle_selected = None
111
+ for name, rect in self.handles.items():
112
+ if rect.contains(event.pos()):
113
+ self.handle_selected = name
114
+ if name == "rotation":
115
+ rect_center = self.rect().center()
116
+ self.setTransformOriginPoint(rect_center)
117
+
118
+ # Save starting angle
119
+ rect_center_scene = self.mapToScene(rect_center)
120
+ mouse_scene_pos = self.mapToScene(event.pos())
121
+ self.initial_rotation = math.atan2(
122
+ mouse_scene_pos.y() - rect_center_scene.y(),
123
+ mouse_scene_pos.x() - rect_center_scene.x(),
124
+ )
125
+ self.initial_angle = self.rotation()
126
+ break
127
+ super().mousePressEvent(event)
128
+
129
+ def mouseMoveEvent(self, event):
130
+ if self.handle_selected == 'rotation':
131
+ self.rotate_item(event)
132
+ elif self.handle_selected:
133
+ self.resize_item(event)
134
+ else:
135
+ super().mouseMoveEvent(event)
136
+
137
+ def mouseReleaseEvent(self, event):
138
+ if self.handle_selected == 'rotation':
139
+ self.setCursor(Qt.CursorShape.OpenHandCursor)
140
+ event.accept()
141
+ self.handle_selected = None
142
+ self.handles_visible = True
143
+ self.update()
144
+ if not event.isAccepted():
145
+ super().mouseReleaseEvent(event)
146
+
147
+ def rotate_item(self, event):
148
+ self.setCursor(Qt.CursorShape.ClosedHandCursor)
149
+ rect_center_scene = self.mapToScene(self.rect().center())
150
+ mouse_scene_pos = self.mapToScene(event.pos())
151
+
152
+ current_angle = math.atan2(
153
+ mouse_scene_pos.y() - rect_center_scene.y(),
154
+ mouse_scene_pos.x() - rect_center_scene.x(),
155
+ )
156
+ angle_diff = math.degrees(current_angle - self.initial_rotation)
157
+ self.setRotation(self.initial_angle + angle_diff)
158
+
159
+ self.update_handles()
160
+ self.update()
161
+
162
+ def resize_item(self, event):
163
+ rect = self.rect()
164
+ pos = event.pos()
165
+
166
+ if self.handle_selected == 'top_left':
167
+ rect.setTopLeft(pos)
168
+ elif self.handle_selected == 'top_right':
169
+ rect.setTopRight(pos)
170
+ elif self.handle_selected == 'bottom_left':
171
+ rect.setBottomLeft(pos)
172
+ elif self.handle_selected == 'bottom_right':
173
+ rect.setBottomRight(pos)
174
+
175
+ # Enforce minimum size
176
+ if rect.width() < MIN_RECT_SIZE:
177
+ rect.setWidth(MIN_RECT_SIZE)
178
+ if rect.height() < MIN_RECT_SIZE:
179
+ rect.setHeight(MIN_RECT_SIZE)
180
+
181
+ self.setRect(rect)
182
+ self.update_handles()
183
+ self.update()
184
+
185
+
186
+ class Rectangle(Core):
187
+ def __init__(self):
188
+ super().__init__()
189
+ self.first_click_pos = None
190
+ self.current_rectangle = None
191
+ self.is_drawing = False
192
+ self.selected_rectangle = None
193
+
194
+ def rectangle(self):
195
+ self.checked_button = self.rectangle.__name__
196
+ self.zoomable_graphics_view.scene.selectionChanged.connect(self.update_selected_rectangle)
197
+
198
+ def cleanup_temporary_rectangles(self):
199
+ if self.current_rectangle and self.current_rectangle in self.zoomable_graphics_view.scene.items():
200
+ self.zoomable_graphics_view.scene.removeItem(self.current_rectangle)
201
+ self.current_rectangle = None
202
+
203
+ def start_rectangle_tool(self, current_position):
204
+ self.zoomable_graphics_view.change_cursor("rectangle")
205
+ self.cleanup_temporary_rectangles()
206
+
207
+ self.first_click_pos = QPointF(current_position)
208
+ self.color = self.get_labeling_overlay().get_color()
209
+ self.is_drawing = True
210
+
211
+ self.current_rectangle = QGraphicsRectItem(self.first_click_pos.x(), self.first_click_pos.y(), 1, 1)
212
+ self.current_rectangle.setPen(QPen(self.color, 2, Qt.PenStyle.DashLine))
213
+ self.current_rectangle.setZValue(2)
214
+ self.zoomable_graphics_view.scene.addItem(self.current_rectangle)
215
+
216
+ def move_rectangle_tool(self, current_position):
217
+ if not (self.is_drawing and self.current_rectangle):
218
+ return
219
+ current_pos = QPointF(current_position)
220
+ x, y = min(self.first_click_pos.x(), current_pos.x()), min(self.first_click_pos.y(), current_pos.y())
221
+ w, h = abs(current_pos.x() - self.first_click_pos.x()), abs(current_pos.y() - self.first_click_pos.y())
222
+ self.current_rectangle.setRect(x, y, w, h)
223
+
224
+ def end_rectangle_tool(self):
225
+ if not (self.is_drawing and self.current_rectangle):
226
+ return
227
+
228
+ rect = self.current_rectangle.rect()
229
+ self.cleanup_temporary_rectangles()
230
+
231
+ if rect.width() > 5 and rect.height() > 5:
232
+ final_rectangle = RectangleItem(rect.x(), rect.y(), rect.width(), rect.height(), self.color)
233
+ final_rectangle.setZValue(2)
234
+ final_rectangle.setFlag(QGraphicsRectItem.GraphicsItemFlag.ItemIsSelectable, True)
235
+ self.zoomable_graphics_view.scene.addItem(final_rectangle)
236
+ self.selected_rectangle = final_rectangle
237
+
238
+ self.first_click_pos = None
239
+ self.is_drawing = False
240
+
241
+ def update_selected_rectangle(self):
242
+ items = self.zoomable_graphics_view.scene.selectedItems()
243
+ self.selected_rectangle = next((i for i in reversed(items) if isinstance(i, RectangleItem)), None)
244
+
245
+ def clear_rectangle(self):
246
+ if self.selected_rectangle and self.selected_rectangle in self.zoomable_graphics_view.scene.items():
247
+ self.zoomable_graphics_view.scene.removeItem(self.selected_rectangle)
248
+ self.selected_rectangle = None
@@ -0,0 +1,12 @@
1
+ from PyQt6.QtGui import QPainter, QBitmap, QImage, QPixmap, QPen
2
+ from PyQt6.QtCore import Qt, QSize
3
+ from PyImageLabeling.model.Core import Core
4
+
5
+ class Undo(Core):
6
+ def __init__(self):
7
+ super().__init__()
8
+
9
+ def undo(self):
10
+ self.get_current_image_item().get_labeling_overlay().undo()
11
+
12
+
@@ -0,0 +1,40 @@
1
+ # Model
2
+
3
+
4
+ from PyImageLabeling.model.File.Files import Files
5
+ from PyImageLabeling.model.File.NextImage import NextImage
6
+ from PyImageLabeling.model.File.PreviousImage import PreviousImage
7
+
8
+ from PyImageLabeling.model.Labeling.ContourFilling import ContourFilling
9
+ from PyImageLabeling.model.Labeling.MagicPen import MagicPen
10
+ from PyImageLabeling.model.Labeling.PaintBrush import PaintBrush
11
+ from PyImageLabeling.model.Labeling.Polygon import Polygon
12
+ from PyImageLabeling.model.Labeling.Rectangle import Rectangle
13
+ from PyImageLabeling.model.Labeling.ClearAll import ClearAll
14
+ from PyImageLabeling.model.Labeling.Eraser import Eraser
15
+ from PyImageLabeling.model.Labeling.Undo import Undo
16
+ from PyImageLabeling.model.Labeling.Ellipse import Ellipse
17
+
18
+ from PyImageLabeling.model.Image.MoveImage import MoveImage
19
+ from PyImageLabeling.model.Image.ZoomMinus import ZoomMinus
20
+ from PyImageLabeling.model.Image.ZoomPlus import ZoomPlus
21
+ from PyImageLabeling.model.Image.ResetMoveZoomImage import ResetMoveZoomImage
22
+
23
+
24
+ class Model(Files, NextImage, PreviousImage, ClearAll, Eraser, Undo, ContourFilling, MagicPen, PaintBrush, Polygon, Rectangle, Ellipse, MoveImage, ZoomMinus, ZoomPlus, ResetMoveZoomImage):
25
+ def __init__(self, view, controller, config):
26
+ super().__init__()
27
+ self.config = config
28
+ self.set_view(view)
29
+ self.set_controller(controller)
30
+
31
+
32
+ def set_view(self, view):
33
+ super().set_view(view)
34
+ self.view = view
35
+
36
+ def set_controller(self, controller):
37
+ super().set_controller(controller)
38
+ self.controller = controller
39
+
40
+
@@ -0,0 +1,40 @@
1
+
2
+ import os
3
+ import json
4
+
5
+ class Utils():
6
+
7
+ def __init__(self):
8
+ pass
9
+
10
+ def get_icon_path(icon_name):
11
+ # Assuming icons are stored in an 'icons' folder next to the script
12
+ icon_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..'+os.sep+'icons')
13
+ icon_path = os.path.join(icon_dir, f"{icon_name}.png")
14
+ if not os.path.exists(icon_path):
15
+ raise FileNotFoundError("The icon is not found: ", icon_path)
16
+ return icon_path
17
+
18
+ def get_style_css():
19
+ return open(os.path.dirname(os.path.abspath(__file__))+os.sep+".."+os.sep+"style.css").read()
20
+
21
+ def get_config():
22
+ with open(os.path.dirname(os.path.abspath(__file__))+os.sep+".."+os.sep+"config.json", 'r', encoding='utf-8') as file:
23
+ data = json.load(file)
24
+ return data
25
+
26
+ def load_parameters():
27
+ with open(os.path.dirname(os.path.abspath(__file__))+os.sep+".."+os.sep+"parameters.json", 'r', encoding='utf-8') as file:
28
+ data = json.load(file)
29
+ return data
30
+
31
+ def save_parameters(data):
32
+ with open(os.path.dirname(os.path.abspath(__file__))+os.sep+".."+os.sep+"parameters.json", 'w') as fp:
33
+ json.dump(data, fp)
34
+
35
+
36
+ def color_to_stylesheet(color):
37
+ return f"background-color: rgb({color.red()}, {color.green()}, {color.blue()}); color: {'white' if color.lightness() < 128 else 'black'};"
38
+
39
+ def compute_diagonal(x_1, y_1, x_2, y_2):
40
+ return ((x_1 - x_2) ** 2 + (y_1 - y_2) ** 2) ** 0.5
@@ -0,0 +1,6 @@
1
+ {
2
+ "retert": {
3
+ "color": "#ff0000",
4
+ "thickness": 2
5
+ }
6
+ }