valetudo-map-parser 0.1.9b9__py3-none-any.whl → 0.1.9b10__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.
- valetudo_map_parser/config/utils.py +164 -115
- valetudo_map_parser/rand25_handler.py +1 -1
- {valetudo_map_parser-0.1.9b9.dist-info → valetudo_map_parser-0.1.9b10.dist-info}/METADATA +1 -1
- {valetudo_map_parser-0.1.9b9.dist-info → valetudo_map_parser-0.1.9b10.dist-info}/RECORD +7 -7
- {valetudo_map_parser-0.1.9b9.dist-info → valetudo_map_parser-0.1.9b10.dist-info}/LICENSE +0 -0
- {valetudo_map_parser-0.1.9b9.dist-info → valetudo_map_parser-0.1.9b10.dist-info}/NOTICE.txt +0 -0
- {valetudo_map_parser-0.1.9b9.dist-info → valetudo_map_parser-0.1.9b10.dist-info}/WHEEL +0 -0
@@ -1,16 +1,43 @@
|
|
1
1
|
"""Utility code for the valetudo map parser."""
|
2
2
|
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from functools import partial
|
3
5
|
import hashlib
|
4
6
|
import json
|
5
7
|
from logging import getLogger
|
6
8
|
|
7
|
-
from PIL import ImageOps
|
9
|
+
from PIL import ImageOps, Image
|
8
10
|
|
9
11
|
from .types import ChargerPosition, ImageSize, NumpyArray, RobotPosition
|
12
|
+
from .shared import CameraShared
|
10
13
|
|
11
14
|
_LOGGER = getLogger(__name__)
|
12
15
|
|
13
16
|
|
17
|
+
@dataclass
|
18
|
+
class ResizeParams:
|
19
|
+
"""Resize the image to the given dimensions and aspect ratio."""
|
20
|
+
|
21
|
+
pil_img: Image # PIL image object
|
22
|
+
width: int
|
23
|
+
height: int
|
24
|
+
aspect_ratio: str = None
|
25
|
+
crop_size: list = None
|
26
|
+
is_rand: bool = False
|
27
|
+
offset_func: callable = None # Function reference for offset calculation
|
28
|
+
|
29
|
+
|
30
|
+
@dataclass
|
31
|
+
class OffsetParams:
|
32
|
+
"""Map parameters."""
|
33
|
+
|
34
|
+
wsf: int
|
35
|
+
hsf: int
|
36
|
+
width: int
|
37
|
+
height: int
|
38
|
+
rand256: bool = False
|
39
|
+
|
40
|
+
|
14
41
|
class BaseHandler:
|
15
42
|
"""Avoid Code duplication"""
|
16
43
|
|
@@ -30,9 +57,16 @@ class BaseHandler:
|
|
30
57
|
self.crop_img_size = [0, 0]
|
31
58
|
self.offset_x = 0
|
32
59
|
self.offset_y = 0
|
33
|
-
self.shared =
|
60
|
+
self.shared = CameraShared(self.file_name)
|
34
61
|
self.crop_area = [0, 0, 0, 0]
|
35
62
|
self.zooming = False
|
63
|
+
self.async_resize_image = partial(
|
64
|
+
async_resize_image,
|
65
|
+
width=self.shared.image_ref_width,
|
66
|
+
height=self.shared.image_ref_height,
|
67
|
+
crop_size=self.crop_img_size,
|
68
|
+
offset_func=self.async_map_coordinates_offset,
|
69
|
+
)
|
36
70
|
|
37
71
|
def get_frame_number(self) -> int:
|
38
72
|
"""Return the frame number of the image."""
|
@@ -57,97 +91,15 @@ class BaseHandler:
|
|
57
91
|
def check_zoom_and_aspect_ratio(self) -> bool:
|
58
92
|
"""Check if the image is zoomed and has an aspect ratio."""
|
59
93
|
return (
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
94
|
+
self.shared.image_auto_zoom
|
95
|
+
and self.shared.vacuum_state == "cleaning"
|
96
|
+
and self.zooming
|
97
|
+
and self.shared.image_zoom_lock_ratio
|
98
|
+
or self.shared.image_aspect_ratio != "None"
|
65
99
|
)
|
66
100
|
|
67
|
-
|
68
|
-
self,
|
69
|
-
):
|
70
|
-
"""Resize the image to the given dimensions and aspect ratio."""
|
71
|
-
width = self.shared.image_ref_width
|
72
|
-
height = self.shared.image_ref_height
|
73
|
-
if aspect_ratio:
|
74
|
-
wsf, hsf = [int(x) for x in aspect_ratio.split(",")]
|
75
|
-
if wsf == 0 or hsf == 0:
|
76
|
-
return pil_img
|
77
|
-
new_aspect_ratio = wsf / hsf
|
78
|
-
if width / height > new_aspect_ratio:
|
79
|
-
new_width = int(pil_img.height * new_aspect_ratio)
|
80
|
-
new_height = pil_img.height
|
81
|
-
else:
|
82
|
-
new_width = pil_img.width
|
83
|
-
new_height = int(pil_img.width / new_aspect_ratio)
|
84
|
-
_LOGGER.debug(
|
85
|
-
"%s: Image Aspect Ratio: %s, %s",
|
86
|
-
self.file_name,
|
87
|
-
str(wsf),
|
88
|
-
str(hsf),
|
89
|
-
)
|
90
|
-
(
|
91
|
-
self.crop_img_size[0],
|
92
|
-
self.crop_img_size[1],
|
93
|
-
) = await self.async_map_coordinates_offset(
|
94
|
-
wsf, hsf, new_width, new_height, is_rand
|
95
|
-
)
|
96
|
-
return ImageOps.pad(pil_img, (new_width, new_height))
|
97
|
-
return ImageOps.pad(pil_img, (width, height))
|
98
|
-
|
99
|
-
async def async_map_coordinates_offset(
|
100
|
-
self, wsf: int, hsf: int, width: int, height: int, rand256: bool = False
|
101
|
-
) -> tuple[int, int]:
|
102
|
-
"""
|
103
|
-
Offset the coordinates to the map.
|
104
|
-
"""
|
105
|
-
|
106
|
-
if wsf == 1 and hsf == 1:
|
107
|
-
self.set_image_offset_ratio_1_1(width, height, rand256)
|
108
|
-
elif wsf == 2 and hsf == 1:
|
109
|
-
self.set_image_offset_ratio_2_1(width, height, rand256)
|
110
|
-
elif wsf == 3 and hsf == 2:
|
111
|
-
self.set_image_offset_ratio_3_2(width, height, rand256)
|
112
|
-
elif wsf == 5 and hsf == 4:
|
113
|
-
self.set_image_offset_ratio_5_4(width, height, rand256)
|
114
|
-
elif wsf == 9 and hsf == 16:
|
115
|
-
self.set_image_offset_ratio_9_16(width, height, rand256)
|
116
|
-
elif wsf == 16 and hsf == 9:
|
117
|
-
self.set_image_offset_ratio_16_9(width, height, rand256)
|
118
|
-
return width, height
|
119
|
-
|
120
|
-
@staticmethod
|
121
|
-
async def calculate_array_hash(layers: dict, active: list[int] = None) -> str or None:
|
122
|
-
"""Calculate the hash of the image based on layers and active zones."""
|
123
|
-
if layers and active:
|
124
|
-
data_to_hash = {
|
125
|
-
"layers": len(layers["wall"][0]),
|
126
|
-
"active_segments": tuple(active),
|
127
|
-
}
|
128
|
-
data_json = json.dumps(data_to_hash, sort_keys=True)
|
129
|
-
return hashlib.sha256(data_json.encode()).hexdigest()
|
130
|
-
return None
|
131
|
-
|
132
|
-
@staticmethod
|
133
|
-
async def async_copy_array(original_array: NumpyArray) -> NumpyArray:
|
134
|
-
"""Copy the array."""
|
135
|
-
return NumpyArray.copy(original_array)
|
136
|
-
|
137
|
-
def get_map_points(self) -> list[dict[str, int] | dict[str, int] | dict[str, int] | dict[str, int]]:
|
138
|
-
"""Return the map points."""
|
139
|
-
return [
|
140
|
-
{"x": 0, "y": 0}, # Top-left corner 0
|
141
|
-
{"x": self.crop_img_size[0], "y": 0}, # Top-right corner 1
|
142
|
-
{
|
143
|
-
"x": self.crop_img_size[0],
|
144
|
-
"y": self.crop_img_size[1],
|
145
|
-
}, # Bottom-right corner 2
|
146
|
-
{"x": 0, "y": self.crop_img_size[1]}, # Bottom-left corner (optional) 3
|
147
|
-
]
|
148
|
-
|
149
|
-
def set_image_offset_ratio_1_1(
|
150
|
-
self, width: int, height: int, rand256: bool = False
|
101
|
+
def _set_image_offset_ratio_1_1(
|
102
|
+
self, width: int, height: int, rand256: bool = False
|
151
103
|
) -> None:
|
152
104
|
"""Set the image offset ratio to 1:1."""
|
153
105
|
|
@@ -173,8 +125,8 @@ class BaseHandler:
|
|
173
125
|
self.offset_y,
|
174
126
|
)
|
175
127
|
|
176
|
-
def
|
177
|
-
|
128
|
+
def _set_image_offset_ratio_2_1(
|
129
|
+
self, width: int, height: int, rand256: bool = False
|
178
130
|
) -> None:
|
179
131
|
"""Set the image offset ratio to 2:1."""
|
180
132
|
|
@@ -201,8 +153,8 @@ class BaseHandler:
|
|
201
153
|
self.offset_y,
|
202
154
|
)
|
203
155
|
|
204
|
-
def
|
205
|
-
|
156
|
+
def _set_image_offset_ratio_3_2(
|
157
|
+
self, width: int, height: int, rand256: bool = False
|
206
158
|
) -> None:
|
207
159
|
"""Set the image offset ratio to 3:2."""
|
208
160
|
|
@@ -212,13 +164,11 @@ class BaseHandler:
|
|
212
164
|
if rotation in [0, 180]:
|
213
165
|
self.offset_y = width - self.crop_img_size[0]
|
214
166
|
self.offset_x = ((height - self.crop_img_size[1]) // 2) - (
|
215
|
-
|
167
|
+
self.crop_img_size[1] // 10
|
216
168
|
)
|
217
169
|
elif rotation in [90, 270]:
|
218
170
|
self.offset_y = (self.crop_img_size[0] - width) // 2
|
219
|
-
self.offset_x = (self.crop_img_size[1] - height) + (
|
220
|
-
(height // 10) // 2
|
221
|
-
)
|
171
|
+
self.offset_x = (self.crop_img_size[1] - height) + ((height // 10) // 2)
|
222
172
|
else:
|
223
173
|
if rotation in [0, 180]:
|
224
174
|
self.offset_x = (width - self.crop_img_size[0]) // 2
|
@@ -234,8 +184,8 @@ class BaseHandler:
|
|
234
184
|
self.offset_y,
|
235
185
|
)
|
236
186
|
|
237
|
-
def
|
238
|
-
|
187
|
+
def _set_image_offset_ratio_5_4(
|
188
|
+
self, width: int, height: int, rand256: bool = False
|
239
189
|
) -> None:
|
240
190
|
"""Set the image offset ratio to 5:4."""
|
241
191
|
|
@@ -243,16 +193,14 @@ class BaseHandler:
|
|
243
193
|
if not rand256:
|
244
194
|
if rotation in [0, 180]:
|
245
195
|
self.offset_x = ((width - self.crop_img_size[0]) // 2) - (
|
246
|
-
|
196
|
+
self.crop_img_size[0] // 2
|
247
197
|
)
|
248
198
|
self.offset_y = (self.crop_img_size[1] - height) - (
|
249
|
-
|
199
|
+
self.crop_img_size[1] // 2
|
250
200
|
)
|
251
201
|
elif rotation in [90, 270]:
|
252
202
|
self.offset_y = ((self.crop_img_size[0] - width) // 2) - 10
|
253
|
-
self.offset_x = (self.crop_img_size[1] - height) + (
|
254
|
-
height // 10
|
255
|
-
)
|
203
|
+
self.offset_x = (self.crop_img_size[1] - height) + (height // 10)
|
256
204
|
else:
|
257
205
|
if rotation in [0, 180]:
|
258
206
|
self.offset_y = (width - self.crop_img_size[0]) // 2
|
@@ -268,8 +216,8 @@ class BaseHandler:
|
|
268
216
|
self.offset_y,
|
269
217
|
)
|
270
218
|
|
271
|
-
def
|
272
|
-
|
219
|
+
def _set_image_offset_ratio_9_16(
|
220
|
+
self, width: int, height: int, rand256: bool = False
|
273
221
|
) -> None:
|
274
222
|
"""Set the image offset ratio to 9:16."""
|
275
223
|
|
@@ -296,8 +244,8 @@ class BaseHandler:
|
|
296
244
|
self.offset_y,
|
297
245
|
)
|
298
246
|
|
299
|
-
def
|
300
|
-
|
247
|
+
def _set_image_offset_ratio_16_9(
|
248
|
+
self, width: int, height: int, rand256: bool = False
|
301
249
|
) -> None:
|
302
250
|
"""Set the image offset ratio to 16:9."""
|
303
251
|
|
@@ -324,6 +272,71 @@ class BaseHandler:
|
|
324
272
|
self.offset_y,
|
325
273
|
)
|
326
274
|
|
275
|
+
async def async_map_coordinates_offset(
|
276
|
+
self, params: OffsetParams
|
277
|
+
) -> tuple[int, int]:
|
278
|
+
"""
|
279
|
+
Offset the coordinates to the map.
|
280
|
+
"""
|
281
|
+
if params.wsf == 1 and params.hsf == 1:
|
282
|
+
self._set_image_offset_ratio_1_1(
|
283
|
+
params.width, params.height, params.rand256
|
284
|
+
)
|
285
|
+
elif params.wsf == 2 and params.hsf == 1:
|
286
|
+
self._set_image_offset_ratio_2_1(
|
287
|
+
params.width, params.height, params.rand256
|
288
|
+
)
|
289
|
+
elif params.wsf == 3 and params.hsf == 2:
|
290
|
+
self._set_image_offset_ratio_3_2(
|
291
|
+
params.width, params.height, params.rand256
|
292
|
+
)
|
293
|
+
elif params.wsf == 5 and params.hsf == 4:
|
294
|
+
self._set_image_offset_ratio_5_4(
|
295
|
+
params.width, params.height, params.rand256
|
296
|
+
)
|
297
|
+
elif params.wsf == 9 and params.hsf == 16:
|
298
|
+
self._set_image_offset_ratio_9_16(
|
299
|
+
params.width, params.height, params.rand256
|
300
|
+
)
|
301
|
+
elif params.wsf == 16 and params.hsf == 9:
|
302
|
+
self._set_image_offset_ratio_16_9(
|
303
|
+
params.width, params.height, params.rand256
|
304
|
+
)
|
305
|
+
return params.width, params.height
|
306
|
+
|
307
|
+
@staticmethod
|
308
|
+
async def calculate_array_hash(
|
309
|
+
layers: dict, active: list[int] = None
|
310
|
+
) -> str or None:
|
311
|
+
"""Calculate the hash of the image based on layers and active zones."""
|
312
|
+
if layers and active:
|
313
|
+
data_to_hash = {
|
314
|
+
"layers": len(layers["wall"][0]),
|
315
|
+
"active_segments": tuple(active),
|
316
|
+
}
|
317
|
+
data_json = json.dumps(data_to_hash, sort_keys=True)
|
318
|
+
return hashlib.sha256(data_json.encode()).hexdigest()
|
319
|
+
return None
|
320
|
+
|
321
|
+
@staticmethod
|
322
|
+
async def async_copy_array(original_array: NumpyArray) -> NumpyArray:
|
323
|
+
"""Copy the array."""
|
324
|
+
return NumpyArray.copy(original_array)
|
325
|
+
|
326
|
+
def get_map_points(
|
327
|
+
self,
|
328
|
+
) -> list[dict[str, int] | dict[str, int] | dict[str, int] | dict[str, int]]:
|
329
|
+
"""Return the map points."""
|
330
|
+
return [
|
331
|
+
{"x": 0, "y": 0}, # Top-left corner 0
|
332
|
+
{"x": self.crop_img_size[0], "y": 0}, # Top-right corner 1
|
333
|
+
{
|
334
|
+
"x": self.crop_img_size[0],
|
335
|
+
"y": self.crop_img_size[1],
|
336
|
+
}, # Bottom-right corner 2
|
337
|
+
{"x": 0, "y": self.crop_img_size[1]}, # Bottom-left corner (optional) 3
|
338
|
+
]
|
339
|
+
|
327
340
|
def get_vacuum_points(self, rotation_angle: int) -> list[dict[str, int]]:
|
328
341
|
"""Calculate the calibration points based on the rotation angle."""
|
329
342
|
|
@@ -462,11 +475,47 @@ class BaseHandler:
|
|
462
475
|
return point_properties
|
463
476
|
|
464
477
|
@staticmethod
|
465
|
-
def get_corners(
|
478
|
+
def get_corners(
|
479
|
+
x_max: int, x_min: int, y_max: int, y_min: int
|
480
|
+
) -> list[tuple[int, int]]:
|
466
481
|
"""Return the corners of the image."""
|
467
482
|
return [
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
483
|
+
(x_min, y_min),
|
484
|
+
(x_max, y_min),
|
485
|
+
(x_max, y_max),
|
486
|
+
(x_min, y_max),
|
472
487
|
]
|
488
|
+
|
489
|
+
|
490
|
+
async def async_resize_image(params: ResizeParams):
|
491
|
+
"""Resize the image to the given dimensions and aspect ratio."""
|
492
|
+
if params.aspect_ratio:
|
493
|
+
wsf, hsf = [int(x) for x in params.aspect_ratio.split(",")]
|
494
|
+
if wsf == 0 or hsf == 0:
|
495
|
+
return params.pil_img
|
496
|
+
new_aspect_ratio = wsf / hsf
|
497
|
+
if params.width / params.height > new_aspect_ratio:
|
498
|
+
new_width = int(params.pil_img.height * new_aspect_ratio)
|
499
|
+
new_height = params.pil_img.height
|
500
|
+
else:
|
501
|
+
new_width = params.pil_img.width
|
502
|
+
new_height = int(params.pil_img.width / new_aspect_ratio)
|
503
|
+
|
504
|
+
_LOGGER.debug(
|
505
|
+
"Image Aspect Ratio: %s, %s",
|
506
|
+
str(wsf),
|
507
|
+
str(hsf),
|
508
|
+
)
|
509
|
+
|
510
|
+
if params.crop_size is not None:
|
511
|
+
params.crop_size[0], params.crop_size[1] = await params.offset_func(
|
512
|
+
wsf=wsf,
|
513
|
+
hsf=hsf,
|
514
|
+
width=new_width,
|
515
|
+
height=new_height,
|
516
|
+
rand256=params.is_rand,
|
517
|
+
)
|
518
|
+
|
519
|
+
return ImageOps.pad(params.pil_img, (new_width, new_height))
|
520
|
+
|
521
|
+
return ImageOps.pad(params.pil_img, (params.width, params.height))
|
@@ -256,7 +256,7 @@ class ReImageHandler(BaseHandler):
|
|
256
256
|
return img_np_array
|
257
257
|
|
258
258
|
async def _finalize_image(self, pil_img):
|
259
|
-
if self.check_zoom_and_aspect_ratio()
|
259
|
+
if self.check_zoom_and_aspect_ratio():
|
260
260
|
pil_img = await self.async_resize_image(
|
261
261
|
pil_img, self.shared.image_aspect_ratio, True
|
262
262
|
)
|
@@ -6,15 +6,15 @@ valetudo_map_parser/config/drawable.py,sha256=hsrEJCMVOrjs5sJfr26SeqJD0VNlYWwxcV
|
|
6
6
|
valetudo_map_parser/config/rand25_parser.py,sha256=fehyF18hRWRWbXbojocQCIaIch21Lbh1wtl2XdKRSl0,16447
|
7
7
|
valetudo_map_parser/config/shared.py,sha256=LQV5K8tbVhEKUkby9ssjEmh_T4Ai-Euzsbag_HWYVRc,9448
|
8
8
|
valetudo_map_parser/config/types.py,sha256=sdjqhcxpRSxNFLVHIyCg-RDL9JIjtQHoR2C2WpocWBc,16107
|
9
|
-
valetudo_map_parser/config/utils.py,sha256=
|
9
|
+
valetudo_map_parser/config/utils.py,sha256=mCmTRxiy5REBPvm3S8_b3nCce4pFAjRyCUnAvz3omLw,18536
|
10
10
|
valetudo_map_parser/hypfer_draw.py,sha256=s58ak9IBYLjJyoddfDC99PfQ12HTjkSfJYXqCi4vZKs,14931
|
11
11
|
valetudo_map_parser/hypfer_handler.py,sha256=lA9rC4gO0cNas4ValRikw4X6Uc0sqo2-Roj7EQi1MBA,13471
|
12
12
|
valetudo_map_parser/map_data.py,sha256=6FbQfgxFB6E4kcOWokReJOVSekVaE1kStyhTQhAhiOg,19469
|
13
13
|
valetudo_map_parser/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
|
-
valetudo_map_parser/rand25_handler.py,sha256=
|
14
|
+
valetudo_map_parser/rand25_handler.py,sha256=u3N-_qjpYQM3WO4cnhqHIPa7TMquJ9XYqhoUYFKoc8I,15276
|
15
15
|
valetudo_map_parser/reimg_draw.py,sha256=dtdbYKKxmQnbOaHBHayWEF07OdSnTKo2CPSOW0qpgH0,12506
|
16
|
-
valetudo_map_parser-0.1.
|
17
|
-
valetudo_map_parser-0.1.
|
18
|
-
valetudo_map_parser-0.1.
|
19
|
-
valetudo_map_parser-0.1.
|
20
|
-
valetudo_map_parser-0.1.
|
16
|
+
valetudo_map_parser-0.1.9b10.dist-info/LICENSE,sha256=Lh-qBbuRV0-jiCIBhfV7NgdwFxQFOXH3BKOzK865hRs,10480
|
17
|
+
valetudo_map_parser-0.1.9b10.dist-info/METADATA,sha256=ha_vys80z9E1XW1dDtgbHMigATO4vb2w-7gDRqJ1_mM,1029
|
18
|
+
valetudo_map_parser-0.1.9b10.dist-info/NOTICE.txt,sha256=5lTOuWiU9aiEnJ2go8sc7lTJ7ntMBx0g0GFnNrswCY4,2533
|
19
|
+
valetudo_map_parser-0.1.9b10.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
20
|
+
valetudo_map_parser-0.1.9b10.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|