imagebaker 0.0.49__py3-none-any.whl → 0.0.50__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.
- imagebaker/__init__.py +5 -1
- imagebaker/core/configs/configs.py +1 -0
- imagebaker/core/defs/defs.py +4 -0
- imagebaker/layers/annotable_layer.py +32 -20
- imagebaker/layers/base_layer.py +132 -1
- imagebaker/layers/canvas_layer.py +52 -10
- imagebaker/list_views/layer_settings.py +31 -2
- imagebaker/models/base_model.py +1 -1
- imagebaker/tabs/baker_tab.py +2 -9
- imagebaker/tabs/layerify_tab.py +1 -0
- imagebaker/utils/__init__.py +3 -0
- imagebaker/utils/state_utils.py +5 -1
- imagebaker/utils/utils.py +26 -0
- imagebaker/utils/vis.py +174 -0
- imagebaker/workers/baker_worker.py +13 -0
- {imagebaker-0.0.49.dist-info → imagebaker-0.0.50.dist-info}/METADATA +2 -2
- {imagebaker-0.0.49.dist-info → imagebaker-0.0.50.dist-info}/RECORD +21 -19
- {imagebaker-0.0.49.dist-info → imagebaker-0.0.50.dist-info}/LICENSE +0 -0
- {imagebaker-0.0.49.dist-info → imagebaker-0.0.50.dist-info}/WHEEL +0 -0
- {imagebaker-0.0.49.dist-info → imagebaker-0.0.50.dist-info}/entry_points.txt +0 -0
- {imagebaker-0.0.49.dist-info → imagebaker-0.0.50.dist-info}/top_level.txt +0 -0
imagebaker/__init__.py
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
from loguru import logger # noqa
|
2
|
+
from importlib.metadata import version, PackageNotFoundError
|
2
3
|
|
3
4
|
logger.info("imagebaker package loaded with loguru logger.")
|
4
5
|
|
5
|
-
|
6
|
+
try:
|
7
|
+
__version__ = version("imagebaker")
|
8
|
+
except PackageNotFoundError:
|
9
|
+
__version__ = "0.0.0"
|
imagebaker/core/defs/defs.py
CHANGED
@@ -76,6 +76,8 @@ class LayerState:
|
|
76
76
|
is_annotable: bool = True
|
77
77
|
status: str = "Ready"
|
78
78
|
drawing_states: list[DrawingState] = field(default_factory=list)
|
79
|
+
edge_opacity: int = 100
|
80
|
+
edge_width: int = 10
|
79
81
|
|
80
82
|
def copy(self):
|
81
83
|
return LayerState(
|
@@ -105,6 +107,8 @@ class LayerState:
|
|
105
107
|
)
|
106
108
|
for d in self.drawing_states
|
107
109
|
],
|
110
|
+
edge_opacity=self.edge_opacity,
|
111
|
+
edge_width=self.edge_width,
|
108
112
|
)
|
109
113
|
|
110
114
|
|
@@ -54,6 +54,7 @@ class AnnotableLayer(BaseLayer):
|
|
54
54
|
self.file_path: Path = Path("Runtime")
|
55
55
|
self.layers: list[BaseLayer] = []
|
56
56
|
self.is_annotable = True
|
57
|
+
self.handle_zoom: float = 1
|
57
58
|
|
58
59
|
def init_ui(self):
|
59
60
|
logger.info(f"Initializing Layer UI of {self.layer_name}")
|
@@ -157,14 +158,14 @@ class AnnotableLayer(BaseLayer):
|
|
157
158
|
self.config.normal_draw_config.brush_alpha,
|
158
159
|
)
|
159
160
|
|
160
|
-
pen = QPen(pen_color, self.config.normal_draw_config.line_width)
|
161
|
+
pen = QPen(pen_color, self.config.normal_draw_config.line_width / self.scale)
|
161
162
|
brush = QBrush(brush_color, Qt.DiagCrossPattern)
|
162
163
|
|
163
164
|
if annotation.selected:
|
164
165
|
painter.setPen(
|
165
166
|
QPen(
|
166
167
|
self.config.selected_draw_config.color,
|
167
|
-
self.config.selected_draw_config.line_width,
|
168
|
+
self.config.selected_draw_config.line_width / self.scale,
|
168
169
|
)
|
169
170
|
)
|
170
171
|
painter.setBrush(
|
@@ -184,8 +185,8 @@ class AnnotableLayer(BaseLayer):
|
|
184
185
|
elif annotation.points:
|
185
186
|
painter.drawEllipse(
|
186
187
|
annotation.points[0],
|
187
|
-
self.config.selected_draw_config.ellipse_size,
|
188
|
-
self.config.selected_draw_config.ellipse_size,
|
188
|
+
self.config.selected_draw_config.ellipse_size / self.scale,
|
189
|
+
self.config.selected_draw_config.ellipse_size / self.scale,
|
189
190
|
)
|
190
191
|
|
191
192
|
if is_temp:
|
@@ -200,8 +201,8 @@ class AnnotableLayer(BaseLayer):
|
|
200
201
|
for point in annotation.points:
|
201
202
|
painter.drawEllipse(
|
202
203
|
point,
|
203
|
-
self.config.normal_draw_config.point_size,
|
204
|
-
self.config.normal_draw_config.point_size,
|
204
|
+
self.config.normal_draw_config.point_size / self.scale,
|
205
|
+
self.config.normal_draw_config.point_size / self.scale,
|
205
206
|
)
|
206
207
|
elif annotation.rectangle:
|
207
208
|
painter.drawRect(annotation.rectangle)
|
@@ -223,28 +224,34 @@ class AnnotableLayer(BaseLayer):
|
|
223
224
|
]
|
224
225
|
painter.save()
|
225
226
|
painter.setPen(
|
226
|
-
QPen(
|
227
|
+
QPen(
|
228
|
+
Qt.black,
|
229
|
+
self.config.normal_draw_config.control_point_size / self.scale,
|
230
|
+
)
|
227
231
|
)
|
228
232
|
painter.setBrush(QBrush(Qt.white))
|
229
233
|
for corner in corners:
|
230
234
|
painter.drawEllipse(
|
231
235
|
corner,
|
232
|
-
self.config.normal_draw_config.point_size,
|
233
|
-
self.config.normal_draw_config.point_size,
|
236
|
+
self.config.normal_draw_config.point_size / self.scale,
|
237
|
+
self.config.normal_draw_config.point_size / self.scale,
|
234
238
|
)
|
235
239
|
painter.restore()
|
236
240
|
|
237
241
|
if annotation.polygon and len(annotation.polygon) > 0:
|
238
242
|
painter.save()
|
239
243
|
painter.setPen(
|
240
|
-
QPen(
|
244
|
+
QPen(
|
245
|
+
Qt.white,
|
246
|
+
self.config.normal_draw_config.control_point_size / self.scale,
|
247
|
+
)
|
241
248
|
)
|
242
249
|
painter.setBrush(QBrush(Qt.darkGray))
|
243
250
|
for point in annotation.polygon:
|
244
251
|
painter.drawEllipse(
|
245
252
|
point,
|
246
|
-
self.config.normal_draw_config.point_size,
|
247
|
-
self.config.normal_draw_config.point_size,
|
253
|
+
self.config.normal_draw_config.point_size / self.scale,
|
254
|
+
self.config.normal_draw_config.point_size / self.scale,
|
248
255
|
)
|
249
256
|
painter.restore()
|
250
257
|
|
@@ -266,7 +273,7 @@ class AnnotableLayer(BaseLayer):
|
|
266
273
|
# Set up font
|
267
274
|
font = painter.font()
|
268
275
|
font.setPixelSize(
|
269
|
-
self.config.normal_draw_config.label_font_size
|
276
|
+
self.config.normal_draw_config.label_font_size * self.scale
|
270
277
|
) # Fixed screen size
|
271
278
|
painter.setFont(font)
|
272
279
|
|
@@ -300,7 +307,10 @@ class AnnotableLayer(BaseLayer):
|
|
300
307
|
painter.save()
|
301
308
|
handle_color = self.config.selected_draw_config.handle_color
|
302
309
|
painter.setPen(
|
303
|
-
QPen(
|
310
|
+
QPen(
|
311
|
+
handle_color,
|
312
|
+
self.config.selected_draw_config.handle_width / self.scale,
|
313
|
+
)
|
304
314
|
)
|
305
315
|
painter.setBrush(QBrush(handle_color))
|
306
316
|
|
@@ -315,8 +325,8 @@ class AnnotableLayer(BaseLayer):
|
|
315
325
|
]:
|
316
326
|
painter.drawEllipse(
|
317
327
|
corner,
|
318
|
-
self.config.selected_draw_config.handle_point_size,
|
319
|
-
self.config.selected_draw_config.handle_point_size,
|
328
|
+
self.config.selected_draw_config.handle_point_size / self.scale,
|
329
|
+
self.config.selected_draw_config.handle_point_size / self.scale,
|
320
330
|
)
|
321
331
|
# Draw edge handles
|
322
332
|
for edge in [
|
@@ -327,8 +337,8 @@ class AnnotableLayer(BaseLayer):
|
|
327
337
|
]:
|
328
338
|
painter.drawEllipse(
|
329
339
|
edge,
|
330
|
-
self.config.selected_draw_config.handle_edge_size,
|
331
|
-
self.config.selected_draw_config.handle_edge_size,
|
340
|
+
self.config.selected_draw_config.handle_edge_size / self.scale,
|
341
|
+
self.config.selected_draw_config.handle_edge_size / self.scale,
|
332
342
|
)
|
333
343
|
|
334
344
|
elif annotation.polygon:
|
@@ -336,8 +346,8 @@ class AnnotableLayer(BaseLayer):
|
|
336
346
|
for point in annotation.polygon:
|
337
347
|
painter.drawEllipse(
|
338
348
|
point,
|
339
|
-
self.config.selected_draw_config.handle_point_size,
|
340
|
-
self.config.selected_draw_config.handle_point_size,
|
349
|
+
self.config.selected_draw_config.handle_point_size / self.scale,
|
350
|
+
self.config.selected_draw_config.handle_point_size / self.scale,
|
341
351
|
)
|
342
352
|
|
343
353
|
painter.restore()
|
@@ -906,6 +916,8 @@ class AnnotableLayer(BaseLayer):
|
|
906
916
|
f"{annotation.label} {annotation.annotation_id} {annotation.annotator}"
|
907
917
|
)
|
908
918
|
|
919
|
+
new_layer._apply_edge_opacity()
|
920
|
+
new_layer.update()
|
909
921
|
self.messageSignal.emit(f"Layerified: {new_layer.layer_name}")
|
910
922
|
logger.info(f"Num annotations: {len(self.annotations)}")
|
911
923
|
|
imagebaker/layers/base_layer.py
CHANGED
@@ -18,6 +18,8 @@ from PySide6.QtWidgets import QWidget
|
|
18
18
|
|
19
19
|
from typing import Optional
|
20
20
|
from pathlib import Path
|
21
|
+
import cv2
|
22
|
+
import numpy as np
|
21
23
|
|
22
24
|
|
23
25
|
class BaseLayer(QWidget):
|
@@ -157,7 +159,8 @@ class BaseLayer(QWidget):
|
|
157
159
|
self.offset: QPointF = QPointF(0, 0)
|
158
160
|
self.pan_start: QPointF = None
|
159
161
|
self.pan_offset: QPointF = None
|
160
|
-
self.
|
162
|
+
self._image = QPixmap()
|
163
|
+
self._original_image = QPixmap()
|
161
164
|
self.annotations: list[Annotation] = []
|
162
165
|
self.current_annotation: Optional[Annotation] = None
|
163
166
|
self.copied_annotation: Optional[Annotation] = None
|
@@ -365,8 +368,120 @@ class BaseLayer(QWidget):
|
|
365
368
|
self.reset_view()
|
366
369
|
self.update()
|
367
370
|
|
371
|
+
self._original_image = self.image.copy() # Store a copy of the original image
|
368
372
|
self.original_size = QSizeF(self.image.size()) # Store original size
|
369
373
|
|
374
|
+
@property
|
375
|
+
def image(self):
|
376
|
+
"""
|
377
|
+
Get the current image of the canvas layer.
|
378
|
+
|
379
|
+
Returns:
|
380
|
+
QPixmap: The current image of the canvas layer.
|
381
|
+
"""
|
382
|
+
return self._image
|
383
|
+
|
384
|
+
@image.setter
|
385
|
+
def image(self, value: QPixmap):
|
386
|
+
"""
|
387
|
+
Set the image of the canvas layer.
|
388
|
+
|
389
|
+
Args:
|
390
|
+
value (QPixmap): The new image for the canvas layer.
|
391
|
+
"""
|
392
|
+
self._image = value
|
393
|
+
|
394
|
+
def _apply_edge_opacity(self):
|
395
|
+
"""
|
396
|
+
Apply edge opacity to the image. This function modifies the edges of the image
|
397
|
+
to have reduced opacity based on the configuration.
|
398
|
+
"""
|
399
|
+
logger.debug("Applying edge opacity to the image.")
|
400
|
+
edge_width = self.edge_width
|
401
|
+
edge_opacity = self.edge_opacity
|
402
|
+
|
403
|
+
# Convert QPixmap to QImage for pixel manipulation
|
404
|
+
image = self._original_image.toImage()
|
405
|
+
image = image.convertToFormat(
|
406
|
+
QImage.Format_ARGB32
|
407
|
+
) # Ensure format supports alpha
|
408
|
+
|
409
|
+
width = image.width()
|
410
|
+
height = image.height()
|
411
|
+
annotation = self.annotations[0] if self.annotations else None
|
412
|
+
if annotation is None:
|
413
|
+
return
|
414
|
+
|
415
|
+
if annotation.rectangle:
|
416
|
+
for x in range(width):
|
417
|
+
for y in range(height):
|
418
|
+
color = image.pixelColor(x, y)
|
419
|
+
if color.alpha() != 0: # If the pixel is not fully transparent
|
420
|
+
# Calculate horizontal and vertical distances to the edges
|
421
|
+
horizontal_distance = min(x, width - x - 1)
|
422
|
+
vertical_distance = min(y, height - y - 1)
|
423
|
+
|
424
|
+
# If the pixel is within the edge region
|
425
|
+
if (
|
426
|
+
horizontal_distance < edge_width
|
427
|
+
or vertical_distance < edge_width
|
428
|
+
):
|
429
|
+
distance_to_edge = min(
|
430
|
+
horizontal_distance, vertical_distance
|
431
|
+
)
|
432
|
+
# Calculate the new alpha based on the distance to the edge
|
433
|
+
factor = (edge_width - distance_to_edge) / edge_width
|
434
|
+
new_alpha = int(
|
435
|
+
color.alpha()
|
436
|
+
* ((1 - factor) + (factor * (edge_opacity / 255.0)))
|
437
|
+
)
|
438
|
+
color.setAlpha(new_alpha)
|
439
|
+
image.setPixelColor(x, y, color)
|
440
|
+
|
441
|
+
elif annotation.polygon:
|
442
|
+
# Extract alpha channel and find contours
|
443
|
+
alpha_image = image.convertToFormat(QImage.Format_Alpha8)
|
444
|
+
bytes_per_line = (
|
445
|
+
alpha_image.bytesPerLine()
|
446
|
+
) # Get the stride (bytes per line)
|
447
|
+
alpha_data = alpha_image.bits().tobytes()
|
448
|
+
|
449
|
+
# Extract only the valid data (remove padding)
|
450
|
+
alpha_array = np.frombuffer(alpha_data, dtype=np.uint8).reshape(
|
451
|
+
(alpha_image.height(), bytes_per_line)
|
452
|
+
)[
|
453
|
+
:, : alpha_image.width()
|
454
|
+
] # Remove padding to match the actual width
|
455
|
+
|
456
|
+
# Use OpenCV to find contours
|
457
|
+
contours, _ = cv2.findContours(
|
458
|
+
alpha_array, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
|
459
|
+
)
|
460
|
+
|
461
|
+
# Iterate over each pixel and apply edge opacity
|
462
|
+
for x in range(width):
|
463
|
+
for y in range(height):
|
464
|
+
color = image.pixelColor(x, y)
|
465
|
+
if color.alpha() != 0: # If the pixel is not fully transparent
|
466
|
+
# Calculate distance to the nearest contour
|
467
|
+
distance_to_edge = cv2.pointPolygonTest(
|
468
|
+
contours[0], (x, y), True
|
469
|
+
) # True for distance calculation
|
470
|
+
|
471
|
+
# If the pixel is within the edge region
|
472
|
+
if 0 <= distance_to_edge < edge_width:
|
473
|
+
# Calculate the new alpha based on the distance to the edge
|
474
|
+
factor = (edge_width - distance_to_edge) / edge_width
|
475
|
+
new_alpha = int(
|
476
|
+
color.alpha()
|
477
|
+
* ((1 - factor) + (factor * (edge_opacity / 255.0)))
|
478
|
+
)
|
479
|
+
color.setAlpha(new_alpha)
|
480
|
+
image.setPixelColor(x, y, color)
|
481
|
+
|
482
|
+
# Convert the modified QImage back to QPixmap
|
483
|
+
self.image = QPixmap.fromImage(image)
|
484
|
+
|
370
485
|
def get_thumbnail(self, annotation: Annotation = None):
|
371
486
|
"""
|
372
487
|
Generate a thumbnail for the layer or a specific annotation.
|
@@ -722,3 +837,19 @@ class BaseLayer(QWidget):
|
|
722
837
|
@drawing_states.setter
|
723
838
|
def drawing_states(self, value: list[DrawingState]):
|
724
839
|
self.layer_state.drawing_states = value
|
840
|
+
|
841
|
+
@property
|
842
|
+
def edge_opacity(self) -> int:
|
843
|
+
return self.layer_state.edge_opacity
|
844
|
+
|
845
|
+
@edge_opacity.setter
|
846
|
+
def edge_opacity(self, value: int):
|
847
|
+
self.layer_state.edge_opacity = value
|
848
|
+
|
849
|
+
@property
|
850
|
+
def edge_width(self) -> int:
|
851
|
+
return self.layer_state.edge_width
|
852
|
+
|
853
|
+
@edge_width.setter
|
854
|
+
def edge_width(self, value: int):
|
855
|
+
self.layer_state.edge_width = value
|
@@ -27,6 +27,7 @@ from PySide6.QtGui import (
|
|
27
27
|
QMouseEvent,
|
28
28
|
QKeyEvent,
|
29
29
|
QTransform,
|
30
|
+
QImage,
|
30
31
|
)
|
31
32
|
from PySide6.QtWidgets import (
|
32
33
|
QApplication,
|
@@ -57,7 +58,6 @@ class CanvasLayer(BaseLayer):
|
|
57
58
|
config (CanvasConfig): Configuration settings for the canvas layer.
|
58
59
|
"""
|
59
60
|
super().__init__(parent, config)
|
60
|
-
self.image = QPixmap()
|
61
61
|
self.is_annotable = False
|
62
62
|
self.last_pan_point = None
|
63
63
|
self.state_thumbnail = dict()
|
@@ -178,6 +178,7 @@ class CanvasLayer(BaseLayer):
|
|
178
178
|
opacity = layer.opacity / 255.0
|
179
179
|
temp_painter.setOpacity(opacity) # Scale opacity to 0.0-1.0
|
180
180
|
temp_painter.drawPixmap(0, 0, layer.image)
|
181
|
+
|
181
182
|
temp_painter.end()
|
182
183
|
|
183
184
|
# Draw the modified pixmap
|
@@ -287,20 +288,20 @@ class CanvasLayer(BaseLayer):
|
|
287
288
|
painter.setPen(
|
288
289
|
QPen(
|
289
290
|
self.config.selected_draw_config.handle_color,
|
290
|
-
self.config.selected_draw_config.handle_width,
|
291
|
+
self.config.selected_draw_config.handle_width / self.scale,
|
291
292
|
)
|
292
293
|
)
|
293
294
|
painter.setBrush(self.config.selected_draw_config.handle_color)
|
294
295
|
painter.drawEllipse(
|
295
296
|
rotation_pos,
|
296
|
-
self.config.selected_draw_config.handle_point_size *
|
297
|
-
self.config.selected_draw_config.handle_point_size *
|
297
|
+
self.config.selected_draw_config.handle_point_size * 1.1 / self.scale,
|
298
|
+
self.config.selected_draw_config.handle_point_size * 1.1 / self.scale,
|
298
299
|
)
|
299
300
|
# now draw rotation symbol
|
300
301
|
painter.setPen(
|
301
302
|
QPen(
|
302
303
|
self.config.selected_draw_config.handle_color,
|
303
|
-
self.config.selected_draw_config.handle_width,
|
304
|
+
self.config.selected_draw_config.handle_width / self.scale,
|
304
305
|
)
|
305
306
|
)
|
306
307
|
painter.drawLine(
|
@@ -323,21 +324,23 @@ class CanvasLayer(BaseLayer):
|
|
323
324
|
# Draw scale handles
|
324
325
|
handle_color = self.config.selected_draw_config.handle_color
|
325
326
|
painter.setPen(
|
326
|
-
QPen(
|
327
|
+
QPen(
|
328
|
+
handle_color, self.config.selected_draw_config.handle_width / self.scale
|
329
|
+
)
|
327
330
|
)
|
328
331
|
painter.setBrush(self.config.selected_draw_config.handle_color)
|
329
332
|
for corner in corners:
|
330
333
|
painter.drawEllipse(
|
331
334
|
corner,
|
332
|
-
self.config.selected_draw_config.handle_point_size,
|
333
|
-
self.config.selected_draw_config.handle_point_size,
|
335
|
+
self.config.selected_draw_config.handle_point_size / self.scale,
|
336
|
+
self.config.selected_draw_config.handle_point_size / self.scale,
|
334
337
|
)
|
335
338
|
for edge in edges:
|
336
339
|
# draw small circles on the edges
|
337
340
|
painter.drawEllipse(
|
338
341
|
edge,
|
339
|
-
self.config.selected_draw_config.handle_edge_size,
|
340
|
-
self.config.selected_draw_config.handle_edge_size,
|
342
|
+
self.config.selected_draw_config.handle_edge_size / self.scale,
|
343
|
+
self.config.selected_draw_config.handle_edge_size / self.scale,
|
341
344
|
)
|
342
345
|
# draw sides
|
343
346
|
painter.drawLine(
|
@@ -874,6 +877,33 @@ class CanvasLayer(BaseLayer):
|
|
874
877
|
"""
|
875
878
|
self.export_current_state(export_to_annotation_tab=True)
|
876
879
|
|
880
|
+
def seek_state(self, step):
|
881
|
+
"""Seek to a specific state using the timeline slider."""
|
882
|
+
self.messageSignal.emit(f"Seeking to step {step}")
|
883
|
+
logger.info(f"Seeking to step {step}")
|
884
|
+
|
885
|
+
# Get the states for the selected step
|
886
|
+
if step in self.states:
|
887
|
+
states = self.states[step]
|
888
|
+
for state in states:
|
889
|
+
layer = self.get_layer(state.layer_id)
|
890
|
+
if layer:
|
891
|
+
# Update the layer's state
|
892
|
+
update_opacities = False
|
893
|
+
logger.debug(
|
894
|
+
f"Updating layer {layer.layer_name} with state: {state}"
|
895
|
+
)
|
896
|
+
|
897
|
+
if (
|
898
|
+
layer.edge_width != state.edge_width
|
899
|
+
or layer.edge_opacity != state.edge_opacity
|
900
|
+
):
|
901
|
+
update_opacities = True
|
902
|
+
layer.layer_state = state
|
903
|
+
if update_opacities:
|
904
|
+
layer._apply_edge_opacity()
|
905
|
+
layer.update()
|
906
|
+
|
877
907
|
def play_states(self):
|
878
908
|
"""Play all the states stored in self.states."""
|
879
909
|
if len(self.states) == 0:
|
@@ -896,7 +926,19 @@ class CanvasLayer(BaseLayer):
|
|
896
926
|
layer = self.get_layer(state.layer_id)
|
897
927
|
if layer:
|
898
928
|
# Update the layer's state
|
929
|
+
update_opacities = False
|
930
|
+
logger.debug(
|
931
|
+
f"Updating layer {layer.layer_name} with state: {state}"
|
932
|
+
)
|
933
|
+
|
934
|
+
if (
|
935
|
+
layer.edge_width != state.edge_width
|
936
|
+
or layer.edge_opacity != state.edge_opacity
|
937
|
+
):
|
938
|
+
update_opacities = True
|
899
939
|
layer.layer_state = state
|
940
|
+
if update_opacities:
|
941
|
+
layer._apply_edge_opacity()
|
900
942
|
layer.update()
|
901
943
|
|
902
944
|
# Update the UI to reflect the changes
|
@@ -20,7 +20,14 @@ class LayerSettings(QDockWidget):
|
|
20
20
|
layerState = Signal(LayerState)
|
21
21
|
messageSignal = Signal(str)
|
22
22
|
|
23
|
-
def __init__(
|
23
|
+
def __init__(
|
24
|
+
self,
|
25
|
+
parent=None,
|
26
|
+
max_xpos=1000,
|
27
|
+
max_ypos=1000,
|
28
|
+
max_scale=100,
|
29
|
+
max_edge_width=10,
|
30
|
+
):
|
24
31
|
super().__init__("BaseLayer Settings", parent)
|
25
32
|
self.selected_layer: BaseLayer = None
|
26
33
|
|
@@ -29,6 +36,7 @@ class LayerSettings(QDockWidget):
|
|
29
36
|
self.max_xpos = max_xpos
|
30
37
|
self.max_ypos = max_ypos
|
31
38
|
self.max_scale = max_scale
|
39
|
+
self.max_edge_width = max_edge_width
|
32
40
|
self.init_ui()
|
33
41
|
self.setFeatures(
|
34
42
|
QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable
|
@@ -67,6 +75,12 @@ class LayerSettings(QDockWidget):
|
|
67
75
|
self.main_layout.addWidget(self.scale_y_slider["widget"])
|
68
76
|
self.rotation_slider = self.create_slider("Rotation:", 0, 360, 0, 1)
|
69
77
|
self.main_layout.addWidget(self.rotation_slider["widget"])
|
78
|
+
self.edge_opacity_slider = self.create_slider("Edge Opacity:", 0, 255, 255, 1)
|
79
|
+
self.main_layout.addWidget(self.edge_opacity_slider["widget"])
|
80
|
+
self.edge_width_slider = self.create_slider(
|
81
|
+
"Edge Width:", 0, self.max_edge_width, 5, 1
|
82
|
+
)
|
83
|
+
self.main_layout.addWidget(self.edge_width_slider["widget"])
|
70
84
|
|
71
85
|
# Add stretch to push content to the top
|
72
86
|
self.main_layout.addStretch()
|
@@ -125,7 +139,6 @@ class LayerSettings(QDockWidget):
|
|
125
139
|
|
126
140
|
try:
|
127
141
|
self._disable_updates = True
|
128
|
-
|
129
142
|
if sender == self.opacity_slider["slider"]:
|
130
143
|
self.selected_layer.opacity = value
|
131
144
|
elif sender == self.x_slider["slider"]:
|
@@ -138,6 +151,12 @@ class LayerSettings(QDockWidget):
|
|
138
151
|
self.selected_layer.scale_y = value / 100.0
|
139
152
|
elif sender == self.rotation_slider["slider"]:
|
140
153
|
self.selected_layer.rotation = value
|
154
|
+
elif sender == self.edge_opacity_slider["slider"]:
|
155
|
+
self.selected_layer.edge_opacity = value
|
156
|
+
self.selected_layer._apply_edge_opacity()
|
157
|
+
elif sender == self.edge_width_slider["slider"]:
|
158
|
+
self.selected_layer.edge_width = value
|
159
|
+
self.selected_layer._apply_edge_opacity()
|
141
160
|
|
142
161
|
self.selected_layer.update() # Trigger a repaint
|
143
162
|
|
@@ -154,6 +173,10 @@ class LayerSettings(QDockWidget):
|
|
154
173
|
rotation=self.selected_layer.rotation,
|
155
174
|
scale_x=self.selected_layer.scale_x,
|
156
175
|
scale_y=self.selected_layer.scale_y,
|
176
|
+
opacity=self.selected_layer.opacity,
|
177
|
+
edge_opacity=self.selected_layer.edge_opacity,
|
178
|
+
edge_width=self.selected_layer.edge_width,
|
179
|
+
visible=self.selected_layer.visible,
|
157
180
|
)
|
158
181
|
logger.info(f"Storing state {bake_settings}")
|
159
182
|
self.messageSignal.emit(f"Stored state for {bake_settings.layer_name}")
|
@@ -211,6 +234,12 @@ class LayerSettings(QDockWidget):
|
|
211
234
|
self.rotation_slider["slider"].setValue(
|
212
235
|
int(self.selected_layer.rotation)
|
213
236
|
)
|
237
|
+
self.edge_opacity_slider["slider"].setValue(
|
238
|
+
int(self.selected_layer.edge_opacity)
|
239
|
+
)
|
240
|
+
self.edge_width_slider["slider"].setValue(
|
241
|
+
int(self.selected_layer.edge_width)
|
242
|
+
)
|
214
243
|
else:
|
215
244
|
self.widget.setEnabled(False)
|
216
245
|
self.layer_name_label.setText("No BaseLayer")
|
imagebaker/models/base_model.py
CHANGED
imagebaker/tabs/baker_tab.py
CHANGED
@@ -66,6 +66,7 @@ class BakerTab(QWidget):
|
|
66
66
|
max_xpos=self.config.max_xpos,
|
67
67
|
max_ypos=self.config.max_ypos,
|
68
68
|
max_scale=self.config.max_scale,
|
69
|
+
max_edge_width=self.config.max_edge_width,
|
69
70
|
)
|
70
71
|
self.layer_list = LayerList(
|
71
72
|
canvas=self.current_canvas,
|
@@ -415,15 +416,7 @@ class BakerTab(QWidget):
|
|
415
416
|
"""Seek to a specific state using the timeline slider."""
|
416
417
|
self.messageSignal.emit(f"Seeking to step {step}")
|
417
418
|
logger.info(f"Seeking to step {step}")
|
418
|
-
|
419
|
-
# Get the states for the selected step
|
420
|
-
if step in self.current_canvas.states:
|
421
|
-
states = self.current_canvas.states[step]
|
422
|
-
for state in states:
|
423
|
-
layer = self.current_canvas.get_layer(state.layer_id)
|
424
|
-
if layer:
|
425
|
-
layer.layer_state = state
|
426
|
-
layer.update()
|
419
|
+
self.current_canvas.seek_state(step)
|
427
420
|
|
428
421
|
# Update the canvas
|
429
422
|
self.current_canvas.update()
|
imagebaker/tabs/layerify_tab.py
CHANGED
@@ -463,6 +463,7 @@ class LayerifyTab(QWidget):
|
|
463
463
|
def handle_model_error(self, error):
|
464
464
|
logger.error(f"Model error: {error}")
|
465
465
|
QMessageBox.critical(self, "Error", f"Model error: {error}")
|
466
|
+
self.loading_dialog.close()
|
466
467
|
|
467
468
|
def save_annotations(self):
|
468
469
|
"""Save annotations to a JSON file."""
|
imagebaker/utils/__init__.py
CHANGED
imagebaker/utils/state_utils.py
CHANGED
@@ -59,9 +59,13 @@ def calculate_intermediate_states(
|
|
59
59
|
visible=current_state.visible,
|
60
60
|
allow_annotation_export=current_state.allow_annotation_export,
|
61
61
|
playing=current_state.playing,
|
62
|
-
selected=
|
62
|
+
selected=False,
|
63
63
|
is_annotable=current_state.is_annotable,
|
64
64
|
status=current_state.status,
|
65
|
+
edge_opacity=previous_state.edge_opacity
|
66
|
+
+ (current_state.edge_opacity - previous_state.edge_opacity) * (i / steps),
|
67
|
+
edge_width=previous_state.edge_width
|
68
|
+
+ (current_state.edge_width - previous_state.edge_width) * (i / steps),
|
65
69
|
)
|
66
70
|
|
67
71
|
# Deep copy the drawing_states from the previous_state
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import numpy as np
|
2
|
+
import cv2
|
3
|
+
|
4
|
+
|
5
|
+
def generate_color_map(num_colors: int = 20):
|
6
|
+
"""Generate a color map for the segmentation masks"""
|
7
|
+
np.random.seed(42) # For reproducible colors
|
8
|
+
|
9
|
+
colors = {}
|
10
|
+
for i in range(num_colors):
|
11
|
+
# Generate distinct colors with good visibility
|
12
|
+
# Using HSV color space for better distribution
|
13
|
+
hue = i / num_colors
|
14
|
+
saturation = 0.8 + np.random.random() * 0.2
|
15
|
+
value = 0.8 + np.random.random() * 0.2
|
16
|
+
|
17
|
+
# Convert HSV to BGR (OpenCV uses BGR)
|
18
|
+
hsv_color = np.array(
|
19
|
+
[[[hue * 180, saturation * 255, value * 255]]], dtype=np.uint8
|
20
|
+
)
|
21
|
+
bgr_color = cv2.cvtColor(hsv_color, cv2.COLOR_HSV2BGR)[0][0]
|
22
|
+
|
23
|
+
# Store as (B, G, R) tuple
|
24
|
+
colors[i] = (int(bgr_color[0]), int(bgr_color[1]), int(bgr_color[2]))
|
25
|
+
|
26
|
+
return colors
|
imagebaker/utils/vis.py
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
from imagebaker.core.defs import PredictionResult
|
2
|
+
|
3
|
+
import cv2
|
4
|
+
import numpy as np
|
5
|
+
from typing import List
|
6
|
+
|
7
|
+
|
8
|
+
def annotate_detection(
|
9
|
+
image: np.ndarray,
|
10
|
+
results: List[PredictionResult],
|
11
|
+
color_map: dict[str, tuple[int, int, int]],
|
12
|
+
box_thickness: int = 2,
|
13
|
+
font_face: int = cv2.FONT_HERSHEY_SIMPLEX,
|
14
|
+
text_scale: float = 0.5,
|
15
|
+
text_thickness: int = 1,
|
16
|
+
) -> np.ndarray:
|
17
|
+
"""
|
18
|
+
Draw bounding boxes and labels on the image
|
19
|
+
|
20
|
+
Args:
|
21
|
+
image: The original image as a numpy array
|
22
|
+
results: List of PredictionResult objects
|
23
|
+
|
24
|
+
Returns:
|
25
|
+
Annotated image as a numpy array
|
26
|
+
"""
|
27
|
+
annotated_image = image.copy()
|
28
|
+
|
29
|
+
for result in results:
|
30
|
+
# Extract data from result
|
31
|
+
box = result.rectangle # [x1, y1, x2, y2]
|
32
|
+
score = result.score
|
33
|
+
class_name = result.class_name
|
34
|
+
|
35
|
+
if not box:
|
36
|
+
continue
|
37
|
+
|
38
|
+
# Get color for this class
|
39
|
+
color = color_map.get(
|
40
|
+
result.class_name, (0, 255, 0)
|
41
|
+
) # Default to green if not found
|
42
|
+
|
43
|
+
# Draw bounding box
|
44
|
+
cv2.rectangle(
|
45
|
+
annotated_image,
|
46
|
+
(box[0], box[1]),
|
47
|
+
(box[2], box[3]),
|
48
|
+
color,
|
49
|
+
box_thickness,
|
50
|
+
)
|
51
|
+
|
52
|
+
# Prepare label text with class name and score
|
53
|
+
label_text = f"{class_name}: {score:.2f}"
|
54
|
+
|
55
|
+
# Calculate text size to create background rectangle
|
56
|
+
(text_width, text_height), baseline = cv2.getTextSize(
|
57
|
+
label_text,
|
58
|
+
font_face,
|
59
|
+
text_scale,
|
60
|
+
text_thickness,
|
61
|
+
)
|
62
|
+
|
63
|
+
# Draw text background
|
64
|
+
cv2.rectangle(
|
65
|
+
annotated_image,
|
66
|
+
(box[0], box[1] - text_height - 5),
|
67
|
+
(box[0] + text_width, box[1]),
|
68
|
+
color,
|
69
|
+
-1, # Fill the rectangle
|
70
|
+
)
|
71
|
+
|
72
|
+
# Draw text
|
73
|
+
cv2.putText(
|
74
|
+
annotated_image,
|
75
|
+
label_text,
|
76
|
+
(box[0], box[1] - 5),
|
77
|
+
font_face,
|
78
|
+
text_scale,
|
79
|
+
(255, 255, 255), # White text
|
80
|
+
text_thickness,
|
81
|
+
)
|
82
|
+
|
83
|
+
return annotated_image
|
84
|
+
|
85
|
+
|
86
|
+
def annotate_segmentation(
|
87
|
+
image: np.ndarray,
|
88
|
+
results: List[PredictionResult],
|
89
|
+
color_map: dict[int, tuple[int, int, int]],
|
90
|
+
contour_thickness: int = 2,
|
91
|
+
mask_opacity: float = 0.5,
|
92
|
+
font_face: int = cv2.FONT_HERSHEY_SIMPLEX,
|
93
|
+
text_scale: float = 0.5,
|
94
|
+
text_thickness: int = 1,
|
95
|
+
) -> np.ndarray:
|
96
|
+
"""
|
97
|
+
Draw segmentation masks and contours on the image
|
98
|
+
"""
|
99
|
+
annotated_image = image.copy()
|
100
|
+
mask_overlay = np.zeros_like(image)
|
101
|
+
|
102
|
+
for i, result in enumerate(results):
|
103
|
+
if (result.polygon is not None) or not result.mask:
|
104
|
+
continue
|
105
|
+
|
106
|
+
# Get color for this mask
|
107
|
+
color_idx = i % len(color_map)
|
108
|
+
color = color_map[color_idx]
|
109
|
+
|
110
|
+
# Create mask from polygons
|
111
|
+
mask = np.zeros((image.shape[0], image.shape[1]), dtype=np.uint8)
|
112
|
+
for poly in result.polygon:
|
113
|
+
poly_np = np.array(poly, dtype=np.int32).reshape((-1, 1, 2))
|
114
|
+
cv2.fillPoly(mask, [poly_np], 1)
|
115
|
+
|
116
|
+
# Apply color to mask overlay
|
117
|
+
color_mask = np.zeros_like(image)
|
118
|
+
color_mask[mask == 1] = color
|
119
|
+
mask_overlay = cv2.addWeighted(mask_overlay, 1.0, color_mask, 1.0, 0)
|
120
|
+
|
121
|
+
# Draw contours
|
122
|
+
for poly in result.polygon:
|
123
|
+
poly_np = np.array(poly, dtype=np.int32).reshape((-1, 1, 2))
|
124
|
+
cv2.polylines(
|
125
|
+
annotated_image,
|
126
|
+
[poly_np],
|
127
|
+
True,
|
128
|
+
color,
|
129
|
+
contour_thickness,
|
130
|
+
)
|
131
|
+
|
132
|
+
# Add label text
|
133
|
+
label_position = (
|
134
|
+
result.polygon[0][0] if result.polygon and result.polygon[0] else [10, 10]
|
135
|
+
)
|
136
|
+
label_text = f"{result.class_id}: {result.score:.2f}"
|
137
|
+
|
138
|
+
# Draw text background
|
139
|
+
(text_width, text_height), baseline = cv2.getTextSize(
|
140
|
+
label_text,
|
141
|
+
font_face,
|
142
|
+
text_scale,
|
143
|
+
text_thickness,
|
144
|
+
)
|
145
|
+
|
146
|
+
cv2.rectangle(
|
147
|
+
annotated_image,
|
148
|
+
(label_position[0], label_position[1] - text_height - 5),
|
149
|
+
(label_position[0] + text_width, label_position[1]),
|
150
|
+
color,
|
151
|
+
-1, # Fill the rectangle
|
152
|
+
)
|
153
|
+
|
154
|
+
# Draw text
|
155
|
+
cv2.putText(
|
156
|
+
annotated_image,
|
157
|
+
label_text,
|
158
|
+
(label_position[0], label_position[1] - 5),
|
159
|
+
font_face,
|
160
|
+
text_scale,
|
161
|
+
(255, 255, 255), # White text
|
162
|
+
text_thickness,
|
163
|
+
)
|
164
|
+
|
165
|
+
# Blend mask overlay with original image
|
166
|
+
annotated_image = cv2.addWeighted(
|
167
|
+
annotated_image,
|
168
|
+
1.0,
|
169
|
+
mask_overlay,
|
170
|
+
mask_opacity,
|
171
|
+
0,
|
172
|
+
)
|
173
|
+
|
174
|
+
return annotated_image
|
@@ -49,10 +49,23 @@ class BakerWorker(QObject):
|
|
49
49
|
top_left = QPointF(sys.maxsize, sys.maxsize)
|
50
50
|
bottom_right = QPointF(-sys.maxsize, -sys.maxsize)
|
51
51
|
|
52
|
+
# contains all states in currenct step
|
52
53
|
for state in states:
|
53
54
|
layer = self._get_layer(state.layer_id)
|
54
55
|
if layer and layer.visible and not layer.image.isNull():
|
56
|
+
update_opacities = False
|
57
|
+
logger.debug(
|
58
|
+
f"Updating layer {layer.layer_name} with state: {state}"
|
59
|
+
)
|
60
|
+
|
61
|
+
if (
|
62
|
+
layer.edge_width != state.edge_width
|
63
|
+
or layer.edge_opacity != state.edge_opacity
|
64
|
+
):
|
65
|
+
update_opacities = True
|
55
66
|
layer.layer_state = state
|
67
|
+
if update_opacities:
|
68
|
+
layer._apply_edge_opacity()
|
56
69
|
layer.update()
|
57
70
|
|
58
71
|
transform = QTransform()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: imagebaker
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.50
|
4
4
|
Summary: A package for baking images.
|
5
5
|
Home-page: https://github.com/q-viper/Image-Baker
|
6
6
|
Author: Ramkrishna Acharya
|
@@ -35,7 +35,7 @@ Requires-Dist: mkdocs-awesome-pages-plugin; extra == "docs"
|
|
35
35
|

|
36
36
|
<!--  -->
|
37
37
|

|
38
|
-
|
38
|
+
[](https://pypi.org/imagebaker/)
|
39
39
|
|
40
40
|
<p align="center">
|
41
41
|
<img src="assets/demo.gif" alt="Centered Demo" />
|
@@ -1,41 +1,43 @@
|
|
1
|
-
imagebaker/__init__.py,sha256=
|
1
|
+
imagebaker/__init__.py,sha256=zrrxwyzuqVNeIu3rVPrOGYf6SCd5kWsoGcdqqUfsYX4,258
|
2
2
|
imagebaker/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
imagebaker/core/configs/__init__.py,sha256=iyR_GOVMFw3XJSm7293YfyTnaLZa7pLQMfn5tGxVofI,31
|
4
|
-
imagebaker/core/configs/configs.py,sha256
|
4
|
+
imagebaker/core/configs/configs.py,sha256=-qy7vmYaaUk3bh49pwsBig8de_3Y2JWjTDeyWcGQods,5130
|
5
5
|
imagebaker/core/defs/__init__.py,sha256=NqV7gYIlRkaS7nx_UTNPSNZbdPrx4w-VurKOKyRLbKY,28
|
6
|
-
imagebaker/core/defs/defs.py,sha256
|
6
|
+
imagebaker/core/defs/defs.py,sha256=-ZItfJdWaK9yFSuFn2LmQ4ncqukAZ_hvYFgE44HUdIo,8394
|
7
7
|
imagebaker/core/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
8
|
imagebaker/core/plugins/base_plugin.py,sha256=ROa1HTwV5LgGL-40CHKk_5MZYI5QAT1MzpYO7Fx-9P0,1084
|
9
9
|
imagebaker/core/plugins/cosine_plugin.py,sha256=IXBfvaoxrvf-hytg_dT1zFmOfDbcWXBZ7NvIFPJ2tWQ,1251
|
10
10
|
imagebaker/layers/__init__.py,sha256=q1kUDHhUXEGBOdu6CHDfqCnE2mraLHRqh0DFHYTbnRY,158
|
11
|
-
imagebaker/layers/annotable_layer.py,sha256=
|
12
|
-
imagebaker/layers/base_layer.py,sha256=
|
13
|
-
imagebaker/layers/canvas_layer.py,sha256=
|
11
|
+
imagebaker/layers/annotable_layer.py,sha256=ejBp6nooLtHs8c_G6lOsTJmSwBT404F_dmPsSkjvdEQ,37167
|
12
|
+
imagebaker/layers/base_layer.py,sha256=1K7Nt6OPITrILj-p4I6Jf0eaesCpdeecOXGj_8oAQb8,30650
|
13
|
+
imagebaker/layers/canvas_layer.py,sha256=7eDo0UHXRRZ5BX3BciPX88JsumzmwVemtS6cBjU9qwM,42115
|
14
14
|
imagebaker/list_views/__init__.py,sha256=Aa9slE6do8eYgZp77wrofpd_mlBDwxgF3adMyHYFanE,144
|
15
15
|
imagebaker/list_views/annotation_list.py,sha256=HGV6lGlkFjvJvvGnCcLuX1kkfXA0GL8wKo8jOXSBXec,7527
|
16
16
|
imagebaker/list_views/canvas_list.py,sha256=JYSYR0peGyJFJ6amL1894KsUHETPUkR3qAWdGL50Lbc,6717
|
17
17
|
imagebaker/list_views/image_list.py,sha256=NInkc893FGU7L6oSxy8KrWql-i6RB0BqvkroPAASjVw,4912
|
18
18
|
imagebaker/list_views/layer_list.py,sha256=fLx3Ry72fas1W5y_V84hSp41ARneogQN3qjfYTOcpxY,14476
|
19
|
-
imagebaker/list_views/layer_settings.py,sha256=
|
19
|
+
imagebaker/list_views/layer_settings.py,sha256=0WVSCm_RSBKo4pCkYU5c2OYjb_sW8x0UUfFC4So26jQ,9752
|
20
20
|
imagebaker/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
21
|
-
imagebaker/models/base_model.py,sha256=
|
21
|
+
imagebaker/models/base_model.py,sha256=4RyS4vShqWFHhdQDwYluwTPnRPEXjpZl9UjYY_w8NL0,4203
|
22
22
|
imagebaker/tabs/__init__.py,sha256=ijg7MA17RvcHA2AuZE4OgRJXWxjecaUAlfASKAoCQ6Q,86
|
23
|
-
imagebaker/tabs/baker_tab.py,sha256=
|
24
|
-
imagebaker/tabs/layerify_tab.py,sha256=
|
25
|
-
imagebaker/utils/__init__.py,sha256=
|
23
|
+
imagebaker/tabs/baker_tab.py,sha256=yFQRiNmLIua5BvgW7Ysj5FrNbTLhGxqGRTRCABxeTtw,20493
|
24
|
+
imagebaker/tabs/layerify_tab.py,sha256=Qsf9w81-43sB0LZy_vIB4wCzUhHHhoIiarZJvKRxeZ0,32340
|
25
|
+
imagebaker/utils/__init__.py,sha256=I1z5VVEf6QPOMvVkgVHDauQ9ew7tcTVguV4Kdi3Lk4Y,130
|
26
26
|
imagebaker/utils/image.py,sha256=fq7g3DqSdjF9okxZ3fe5kF4Hxn32rqhvVqxy8yI5bnI,3067
|
27
|
-
imagebaker/utils/state_utils.py,sha256=
|
27
|
+
imagebaker/utils/state_utils.py,sha256=Y2JVRGVfsoffwfA2lsCcqHwIxH_jOrEJAdH-oWfe2XE,3841
|
28
28
|
imagebaker/utils/transform_mask.py,sha256=k8MfTgM5-_U2TvDHQHRelz-leGFX6OcsllV6-J4BKfw,3651
|
29
|
+
imagebaker/utils/utils.py,sha256=MnJ4flxxwZbjROWJ5iKHnJxPSSMbfWRbF9GKfVcKutA,840
|
30
|
+
imagebaker/utils/vis.py,sha256=f7c44gm6g9ja5hgVeXKfOhHzxHdzXcIUwKiA1RZU_F8,4736
|
29
31
|
imagebaker/window/__init__.py,sha256=FIxtUR1qnbQMYzppQv7tEfv1-ueHhpu0Z7xuWZR794w,44
|
30
32
|
imagebaker/window/app.py,sha256=e6FGO_BnvkiQC9JN3AmqkgbF72zzZS0hc7PFc43QiVc,4725
|
31
33
|
imagebaker/window/main_window.py,sha256=gpJ7DDuPmxhHh_6Rv3YH2J_1AqG7-NM8R3tKNYhFT3E,7030
|
32
34
|
imagebaker/workers/__init__.py,sha256=XfXENwAYyNg9q_zR-gOsYJGjzwg_iIb_gING8ydnp9c,154
|
33
|
-
imagebaker/workers/baker_worker.py,sha256=
|
35
|
+
imagebaker/workers/baker_worker.py,sha256=EJTL4ln09NuntFpu0o-Hfk0vCtDxpKqJxJcmtgTnMwo,11297
|
34
36
|
imagebaker/workers/layerify_worker.py,sha256=EOqKvhdACtf3y5Ljy6M7MvddAjlZW5DNfBFMtNPD-us,3223
|
35
37
|
imagebaker/workers/model_worker.py,sha256=Tlg6_D977iK-kuGCNdQY4OnGiP8QqWY7adpRNXZw4rA,1636
|
36
|
-
imagebaker-0.0.
|
37
|
-
imagebaker-0.0.
|
38
|
-
imagebaker-0.0.
|
39
|
-
imagebaker-0.0.
|
40
|
-
imagebaker-0.0.
|
41
|
-
imagebaker-0.0.
|
38
|
+
imagebaker-0.0.50.dist-info/LICENSE,sha256=1vkysFPOnT7y4LsoFTv9YsopIrQvBc2l6vUOfv4KKLc,1082
|
39
|
+
imagebaker-0.0.50.dist-info/METADATA,sha256=Zsl37tIGiV9wrQ2zfiaubhmPbhOMucos2V_fF5mtbXU,6829
|
40
|
+
imagebaker-0.0.50.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
41
|
+
imagebaker-0.0.50.dist-info/entry_points.txt,sha256=IDjZHJCiiHpH5IUTByT2en0nMbnnnlrJZ5FPFehUvQM,61
|
42
|
+
imagebaker-0.0.50.dist-info/top_level.txt,sha256=Gg-eILTlqJXwVQr0saSwsx3-H4SPdZ2agBZaufe194s,11
|
43
|
+
imagebaker-0.0.50.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|