valetudo-map-parser 0.1.9b4__py3-none-any.whl → 0.1.9b6__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 +448 -0
- valetudo_map_parser/hypfer_draw.py +0 -21
- valetudo_map_parser/hypfer_handler.py +25 -128
- valetudo_map_parser/rand25_handler.py +12 -101
- valetudo_map_parser/reimg_draw.py +0 -21
- {valetudo_map_parser-0.1.9b4.dist-info → valetudo_map_parser-0.1.9b6.dist-info}/METADATA +1 -1
- {valetudo_map_parser-0.1.9b4.dist-info → valetudo_map_parser-0.1.9b6.dist-info}/RECORD +10 -10
- valetudo_map_parser/images_utils.py +0 -398
- {valetudo_map_parser-0.1.9b4.dist-info → valetudo_map_parser-0.1.9b6.dist-info}/LICENSE +0 -0
- {valetudo_map_parser-0.1.9b4.dist-info → valetudo_map_parser-0.1.9b6.dist-info}/NOTICE.txt +0 -0
- {valetudo_map_parser-0.1.9b4.dist-info → valetudo_map_parser-0.1.9b6.dist-info}/WHEEL +0 -0
@@ -1,398 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Image Utils Class for Valetudo Hypfer Image Handling.
|
3
|
-
This class is used to simplify the ImageHandler class.
|
4
|
-
Version: 0.1.6
|
5
|
-
"""
|
6
|
-
|
7
|
-
from __future__ import annotations
|
8
|
-
|
9
|
-
import logging
|
10
|
-
|
11
|
-
from PIL import Image, ImageOps
|
12
|
-
|
13
|
-
_LOGGER = logging.getLogger(__name__)
|
14
|
-
|
15
|
-
|
16
|
-
class ImageUtils:
|
17
|
-
"""Image Utils Class for Valetudo Hypfer Image Handler.
|
18
|
-
It is used to simplify the ImageHandler class."""
|
19
|
-
|
20
|
-
def __init__(self, image_handler):
|
21
|
-
self.img = image_handler
|
22
|
-
self.file_name = self.img.shared.file_name
|
23
|
-
|
24
|
-
def get_vacuum_points(self, rotation_angle: int) -> list[dict[str, int]]:
|
25
|
-
"""Calculate the calibration points based on the rotation angle."""
|
26
|
-
|
27
|
-
# get_calibration_data
|
28
|
-
vacuum_points = [
|
29
|
-
{
|
30
|
-
"x": self.img.crop_area[0] + self.img.offset_x,
|
31
|
-
"y": self.img.crop_area[1] + self.img.offset_y,
|
32
|
-
}, # Top-left corner 0
|
33
|
-
{
|
34
|
-
"x": self.img.crop_area[2] - self.img.offset_x,
|
35
|
-
"y": self.img.crop_area[1] + self.img.offset_y,
|
36
|
-
}, # Top-right corner 1
|
37
|
-
{
|
38
|
-
"x": self.img.crop_area[2] - self.img.offset_x,
|
39
|
-
"y": self.img.crop_area[3] - self.img.offset_y,
|
40
|
-
}, # Bottom-right corner 2
|
41
|
-
{
|
42
|
-
"x": self.img.crop_area[0] + self.img.offset_x,
|
43
|
-
"y": self.img.crop_area[3] - self.img.offset_y,
|
44
|
-
}, # Bottom-left corner (optional)3
|
45
|
-
]
|
46
|
-
|
47
|
-
# Rotate the vacuum points based on the rotation angle
|
48
|
-
if rotation_angle == 90:
|
49
|
-
vacuum_points = [
|
50
|
-
vacuum_points[1],
|
51
|
-
vacuum_points[2],
|
52
|
-
vacuum_points[3],
|
53
|
-
vacuum_points[0],
|
54
|
-
]
|
55
|
-
elif rotation_angle == 180:
|
56
|
-
vacuum_points = [
|
57
|
-
vacuum_points[2],
|
58
|
-
vacuum_points[3],
|
59
|
-
vacuum_points[0],
|
60
|
-
vacuum_points[1],
|
61
|
-
]
|
62
|
-
elif rotation_angle == 270:
|
63
|
-
vacuum_points = [
|
64
|
-
vacuum_points[3],
|
65
|
-
vacuum_points[0],
|
66
|
-
vacuum_points[1],
|
67
|
-
vacuum_points[2],
|
68
|
-
]
|
69
|
-
|
70
|
-
return vacuum_points
|
71
|
-
|
72
|
-
def re_get_vacuum_points(self, rotation_angle: int) -> list[dict[str, int]]:
|
73
|
-
"""Recalculate the calibration points based on the rotation angle.
|
74
|
-
RAND256 Vacuums Calibration Points are in 10th of a mm."""
|
75
|
-
vacuum_points = [
|
76
|
-
{
|
77
|
-
"x": ((self.img.crop_area[0] + self.img.offset_x) * 10),
|
78
|
-
"y": ((self.img.crop_area[1] + self.img.offset_y) * 10),
|
79
|
-
}, # Top-left corner 0
|
80
|
-
{
|
81
|
-
"x": ((self.img.crop_area[2] - self.img.offset_x) * 10),
|
82
|
-
"y": ((self.img.crop_area[1] + self.img.offset_y) * 10),
|
83
|
-
}, # Top-right corner 1
|
84
|
-
{
|
85
|
-
"x": ((self.img.crop_area[2] - self.img.offset_x) * 10),
|
86
|
-
"y": ((self.img.crop_area[3] - self.img.offset_y) * 10),
|
87
|
-
}, # Bottom-right corner 2
|
88
|
-
{
|
89
|
-
"x": ((self.img.crop_area[0] + self.img.offset_x) * 10),
|
90
|
-
"y": ((self.img.crop_area[3] - self.img.offset_y) * 10),
|
91
|
-
}, # Bottom-left corner (optional)3
|
92
|
-
]
|
93
|
-
|
94
|
-
# Rotate the vacuum points based on the rotation angle
|
95
|
-
if rotation_angle == 90:
|
96
|
-
vacuum_points = [
|
97
|
-
vacuum_points[1],
|
98
|
-
vacuum_points[2],
|
99
|
-
vacuum_points[3],
|
100
|
-
vacuum_points[0],
|
101
|
-
]
|
102
|
-
elif rotation_angle == 180:
|
103
|
-
vacuum_points = [
|
104
|
-
vacuum_points[2],
|
105
|
-
vacuum_points[3],
|
106
|
-
vacuum_points[0],
|
107
|
-
vacuum_points[1],
|
108
|
-
]
|
109
|
-
elif rotation_angle == 270:
|
110
|
-
vacuum_points = [
|
111
|
-
vacuum_points[3],
|
112
|
-
vacuum_points[0],
|
113
|
-
vacuum_points[1],
|
114
|
-
vacuum_points[2],
|
115
|
-
]
|
116
|
-
|
117
|
-
return vacuum_points
|
118
|
-
|
119
|
-
def set_image_offset_ratio_1_1(
|
120
|
-
self, width: int, height: int, rand256: bool = False
|
121
|
-
) -> None:
|
122
|
-
"""Set the image offset ratio to 1:1."""
|
123
|
-
|
124
|
-
rotation = self.img.shared.image_rotate
|
125
|
-
if not rand256:
|
126
|
-
if rotation in [0, 180]:
|
127
|
-
self.img.offset_y = self.img.crop_img_size[0] - width
|
128
|
-
self.img.offset_x = (height - self.img.crop_img_size[1]) // 2
|
129
|
-
elif rotation in [90, 270]:
|
130
|
-
self.img.offset_y = width - self.img.crop_img_size[0]
|
131
|
-
self.img.offset_x = (self.img.crop_img_size[1] - height) // 2
|
132
|
-
else:
|
133
|
-
if rotation in [0, 180]:
|
134
|
-
self.img.offset_x = (width - self.img.crop_img_size[0]) // 2
|
135
|
-
self.img.offset_y = height - self.img.crop_img_size[1]
|
136
|
-
elif rotation in [90, 270]:
|
137
|
-
self.img.offset_y = (self.img.crop_img_size[0] - width) // 2
|
138
|
-
self.img.offset_x = self.img.crop_img_size[1] - height
|
139
|
-
_LOGGER.debug(
|
140
|
-
"%s Image Coordinates Offsets (x,y): %s. %s",
|
141
|
-
self.file_name,
|
142
|
-
self.img.offset_x,
|
143
|
-
self.img.offset_y,
|
144
|
-
)
|
145
|
-
|
146
|
-
def set_image_offset_ratio_2_1(
|
147
|
-
self, width: int, height: int, rand256: bool = False
|
148
|
-
) -> None:
|
149
|
-
"""Set the image offset ratio to 2:1."""
|
150
|
-
|
151
|
-
rotation = self.img.shared.image_rotate
|
152
|
-
if not rand256:
|
153
|
-
if rotation in [0, 180]:
|
154
|
-
self.img.offset_y = width - self.img.crop_img_size[0]
|
155
|
-
self.img.offset_x = height - self.img.crop_img_size[1]
|
156
|
-
elif rotation in [90, 270]:
|
157
|
-
self.img.offset_x = width - self.img.crop_img_size[0]
|
158
|
-
self.img.offset_y = height - self.img.crop_img_size[1]
|
159
|
-
else:
|
160
|
-
if rotation in [0, 180]:
|
161
|
-
self.img.offset_y = width - self.img.crop_img_size[0]
|
162
|
-
self.img.offset_x = height - self.img.crop_img_size[1]
|
163
|
-
elif rotation in [90, 270]:
|
164
|
-
self.img.offset_x = width - self.img.crop_img_size[0]
|
165
|
-
self.img.offset_y = height - self.img.crop_img_size[1]
|
166
|
-
|
167
|
-
_LOGGER.debug(
|
168
|
-
"%s Image Coordinates Offsets (x,y): %s. %s",
|
169
|
-
self.file_name,
|
170
|
-
self.img.offset_x,
|
171
|
-
self.img.offset_y,
|
172
|
-
)
|
173
|
-
|
174
|
-
def set_image_offset_ratio_3_2(
|
175
|
-
self, width: int, height: int, rand256: bool = False
|
176
|
-
) -> None:
|
177
|
-
"""Set the image offset ratio to 3:2."""
|
178
|
-
|
179
|
-
rotation = self.img.shared.image_rotate
|
180
|
-
|
181
|
-
if not rand256:
|
182
|
-
if rotation in [0, 180]:
|
183
|
-
self.img.offset_y = width - self.img.crop_img_size[0]
|
184
|
-
self.img.offset_x = ((height - self.img.crop_img_size[1]) // 2) - (
|
185
|
-
self.img.crop_img_size[1] // 10
|
186
|
-
)
|
187
|
-
elif rotation in [90, 270]:
|
188
|
-
self.img.offset_y = (self.img.crop_img_size[0] - width) // 2
|
189
|
-
self.img.offset_x = (self.img.crop_img_size[1] - height) + (
|
190
|
-
(height // 10) // 2
|
191
|
-
)
|
192
|
-
else:
|
193
|
-
if rotation in [0, 180]:
|
194
|
-
self.img.offset_x = (width - self.img.crop_img_size[0]) // 2
|
195
|
-
self.img.offset_y = height - self.img.crop_img_size[1]
|
196
|
-
elif rotation in [90, 270]:
|
197
|
-
self.img.offset_y = (self.img.crop_img_size[0] - width) // 2
|
198
|
-
self.img.offset_x = self.img.crop_img_size[1] - height
|
199
|
-
|
200
|
-
_LOGGER.debug(
|
201
|
-
"%s Image Coordinates Offsets (x,y): %s. %s",
|
202
|
-
self.file_name,
|
203
|
-
self.img.offset_x,
|
204
|
-
self.img.offset_y,
|
205
|
-
)
|
206
|
-
|
207
|
-
def set_image_offset_ratio_5_4(
|
208
|
-
self, width: int, height: int, rand256: bool = False
|
209
|
-
) -> None:
|
210
|
-
"""Set the image offset ratio to 5:4."""
|
211
|
-
|
212
|
-
rotation = self.img.shared.image_rotate
|
213
|
-
if not rand256:
|
214
|
-
if rotation in [0, 180]:
|
215
|
-
self.img.offset_x = ((width - self.img.crop_img_size[0]) // 2) - (
|
216
|
-
self.img.crop_img_size[0] // 2
|
217
|
-
)
|
218
|
-
self.img.offset_y = (self.img.crop_img_size[1] - height) - (
|
219
|
-
self.img.crop_img_size[1] // 2
|
220
|
-
)
|
221
|
-
elif rotation in [90, 270]:
|
222
|
-
self.img.offset_y = ((self.img.crop_img_size[0] - width) // 2) - 10
|
223
|
-
self.img.offset_x = (self.img.crop_img_size[1] - height) + (
|
224
|
-
height // 10
|
225
|
-
)
|
226
|
-
else:
|
227
|
-
if rotation in [0, 180]:
|
228
|
-
self.img.offset_y = (width - self.img.crop_img_size[0]) // 2
|
229
|
-
self.img.offset_x = self.img.crop_img_size[1] - height
|
230
|
-
elif rotation in [90, 270]:
|
231
|
-
self.img.offset_y = (self.img.crop_img_size[0] - width) // 2
|
232
|
-
self.img.offset_x = self.img.crop_img_size[1] - height
|
233
|
-
|
234
|
-
_LOGGER.debug(
|
235
|
-
"%s Image Coordinates Offsets (x,y): %s. %s",
|
236
|
-
self.file_name,
|
237
|
-
self.img.offset_x,
|
238
|
-
self.img.offset_y,
|
239
|
-
)
|
240
|
-
|
241
|
-
def set_image_offset_ratio_9_16(
|
242
|
-
self, width: int, height: int, rand256: bool = False
|
243
|
-
) -> None:
|
244
|
-
"""Set the image offset ratio to 9:16."""
|
245
|
-
|
246
|
-
rotation = self.img.shared.image_rotate
|
247
|
-
if not rand256:
|
248
|
-
if rotation in [0, 180]:
|
249
|
-
self.img.offset_y = width - self.img.crop_img_size[0]
|
250
|
-
self.img.offset_x = height - self.img.crop_img_size[1]
|
251
|
-
elif rotation in [90, 270]:
|
252
|
-
self.img.offset_x = (width - self.img.crop_img_size[0]) + (height // 10)
|
253
|
-
self.img.offset_y = height - self.img.crop_img_size[1]
|
254
|
-
else:
|
255
|
-
if rotation in [0, 180]:
|
256
|
-
self.img.offset_y = width - self.img.crop_img_size[0]
|
257
|
-
self.img.offset_x = height - self.img.crop_img_size[1]
|
258
|
-
elif rotation in [90, 270]:
|
259
|
-
self.img.offset_x = width - self.img.crop_img_size[0]
|
260
|
-
self.img.offset_y = height - self.img.crop_img_size[1]
|
261
|
-
|
262
|
-
_LOGGER.debug(
|
263
|
-
"%s Image Coordinates Offsets (x,y): %s. %s",
|
264
|
-
self.file_name,
|
265
|
-
self.img.offset_x,
|
266
|
-
self.img.offset_y,
|
267
|
-
)
|
268
|
-
|
269
|
-
def set_image_offset_ratio_16_9(
|
270
|
-
self, width: int, height: int, rand256: bool = False
|
271
|
-
) -> None:
|
272
|
-
"""Set the image offset ratio to 16:9."""
|
273
|
-
|
274
|
-
rotation = self.img.shared.image_rotate
|
275
|
-
if not rand256:
|
276
|
-
if rotation in [0, 180]:
|
277
|
-
self.img.offset_y = width - self.img.crop_img_size[0]
|
278
|
-
self.img.offset_x = height - self.img.crop_img_size[1]
|
279
|
-
elif rotation in [90, 270]:
|
280
|
-
self.img.offset_x = width - self.img.crop_img_size[0]
|
281
|
-
self.img.offset_y = height - self.img.crop_img_size[1]
|
282
|
-
else:
|
283
|
-
if rotation in [0, 180]:
|
284
|
-
self.img.offset_y = width - self.img.crop_img_size[0]
|
285
|
-
self.img.offset_x = height - self.img.crop_img_size[1]
|
286
|
-
elif rotation in [90, 270]:
|
287
|
-
self.img.offset_x = width - self.img.crop_img_size[0]
|
288
|
-
self.img.offset_y = height - self.img.crop_img_size[1]
|
289
|
-
|
290
|
-
_LOGGER.debug(
|
291
|
-
"%s Image Coordinates Offsets (x,y): %s. %s",
|
292
|
-
self.file_name,
|
293
|
-
self.img.offset_x,
|
294
|
-
self.img.offset_y,
|
295
|
-
)
|
296
|
-
|
297
|
-
async def async_zone_propriety(self, zones_data) -> dict:
|
298
|
-
"""Get the zone propiety"""
|
299
|
-
zone_properties = {}
|
300
|
-
id_count = 1
|
301
|
-
for zone in zones_data:
|
302
|
-
zone_name = zone.get("name")
|
303
|
-
coordinates = zone.get("coordinates")
|
304
|
-
if coordinates and len(coordinates) > 0:
|
305
|
-
coordinates[0].pop()
|
306
|
-
x1, y1, x2, y2 = coordinates[0]
|
307
|
-
zone_properties[zone_name] = {
|
308
|
-
"zones": coordinates,
|
309
|
-
"name": zone_name,
|
310
|
-
"x": ((x1 + x2) // 2),
|
311
|
-
"y": ((y1 + y2) // 2),
|
312
|
-
}
|
313
|
-
id_count += 1
|
314
|
-
if id_count > 1:
|
315
|
-
_LOGGER.debug("%s: Zones Properties updated.", self.file_name)
|
316
|
-
return zone_properties
|
317
|
-
|
318
|
-
async def async_points_propriety(self, points_data) -> dict:
|
319
|
-
"""Get the point propiety"""
|
320
|
-
point_properties = {}
|
321
|
-
id_count = 1
|
322
|
-
for point in points_data:
|
323
|
-
point_name = point.get("name")
|
324
|
-
coordinates = point.get("coordinates")
|
325
|
-
if coordinates and len(coordinates) > 0:
|
326
|
-
coordinates = point.get("coordinates")
|
327
|
-
x1, y1 = coordinates
|
328
|
-
point_properties[id_count] = {
|
329
|
-
"position": coordinates,
|
330
|
-
"name": point_name,
|
331
|
-
"x": x1,
|
332
|
-
"y": y1,
|
333
|
-
}
|
334
|
-
id_count += 1
|
335
|
-
if id_count > 1:
|
336
|
-
_LOGGER.debug("%s: Point Properties updated.", self.file_name)
|
337
|
-
return point_properties
|
338
|
-
|
339
|
-
|
340
|
-
async def resize_to_aspect_ratio(
|
341
|
-
pil_img: Image.Image,
|
342
|
-
ref_width: int,
|
343
|
-
ref_height: int,
|
344
|
-
aspect_ratio: str = "None",
|
345
|
-
async_map_coordinates_offset=None,
|
346
|
-
) -> tuple:
|
347
|
-
"""
|
348
|
-
Resize the image to match the given aspect ratio, maintaining the camera's aspect ratio.
|
349
|
-
|
350
|
-
Args:
|
351
|
-
pil_img (PIL.Image): The input image to resize.
|
352
|
-
ref_width (int): The reference width for the image.
|
353
|
-
ref_height (int): The reference height for the image.
|
354
|
-
aspect_ratio (str): Aspect ratio in the format "width,height" or "None" for default.
|
355
|
-
async_map_coordinates_offset (callable): Async function to compute coordinate offsets.
|
356
|
-
|
357
|
-
Returns:
|
358
|
-
tuple: A resized image and crop image size as a tuple (PIL.Image, list).
|
359
|
-
"""
|
360
|
-
crop_img_size = [0, 0]
|
361
|
-
|
362
|
-
if aspect_ratio and aspect_ratio != "None":
|
363
|
-
try:
|
364
|
-
# Parse aspect ratio (e.g., "16,9")
|
365
|
-
wsf, hsf = [int(x) for x in aspect_ratio.split(",")]
|
366
|
-
new_aspect_ratio = wsf / hsf
|
367
|
-
|
368
|
-
# Calculate current aspect ratio
|
369
|
-
current_aspect_ratio = ref_width / ref_height
|
370
|
-
|
371
|
-
# Resize based on aspect ratio comparison
|
372
|
-
if current_aspect_ratio > new_aspect_ratio:
|
373
|
-
new_width = int(pil_img.height * new_aspect_ratio)
|
374
|
-
new_height = pil_img.height
|
375
|
-
else:
|
376
|
-
new_width = pil_img.width
|
377
|
-
new_height = int(pil_img.width / new_aspect_ratio)
|
378
|
-
|
379
|
-
# Resize image using padding
|
380
|
-
resized_img = ImageOps.pad(pil_img, (new_width, new_height))
|
381
|
-
|
382
|
-
# Compute crop image size if mapping offset function is provided
|
383
|
-
if async_map_coordinates_offset:
|
384
|
-
(
|
385
|
-
crop_img_size[0],
|
386
|
-
crop_img_size[1],
|
387
|
-
) = await async_map_coordinates_offset(wsf, hsf, new_width, new_height)
|
388
|
-
|
389
|
-
return resized_img, crop_img_size
|
390
|
-
|
391
|
-
except Exception as e:
|
392
|
-
_LOGGER.debug(
|
393
|
-
"Error resizing image with aspect ratio: %s. %s", aspect_ratio, e
|
394
|
-
)
|
395
|
-
raise ValueError("Error resizing image with aspect ratio") from e
|
396
|
-
|
397
|
-
# If no aspect ratio is provided, return the original image and default crop size
|
398
|
-
return pil_img, crop_img_size
|
File without changes
|
File without changes
|
File without changes
|