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.
@@ -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