valetudo-map-parser 0.1.10rc6__py3-none-any.whl → 0.1.11b1__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/__init__.py +8 -10
- valetudo_map_parser/config/drawable.py +91 -329
- valetudo_map_parser/config/drawable_elements.py +0 -2
- valetudo_map_parser/config/rand256_parser.py +162 -44
- valetudo_map_parser/config/shared.py +12 -10
- valetudo_map_parser/config/status_text/status_text.py +1 -0
- valetudo_map_parser/config/types.py +12 -3
- valetudo_map_parser/config/utils.py +43 -133
- valetudo_map_parser/hypfer_draw.py +0 -2
- valetudo_map_parser/hypfer_handler.py +14 -22
- valetudo_map_parser/map_data.py +17 -11
- valetudo_map_parser/rand256_handler.py +79 -53
- valetudo_map_parser/reimg_draw.py +13 -18
- valetudo_map_parser/rooms_handler.py +10 -10
- {valetudo_map_parser-0.1.10rc6.dist-info → valetudo_map_parser-0.1.11b1.dist-info}/METADATA +2 -2
- valetudo_map_parser-0.1.11b1.dist-info/RECORD +32 -0
- valetudo_map_parser/config/auto_crop.py +0 -452
- valetudo_map_parser/config/color_utils.py +0 -105
- valetudo_map_parser/config/enhanced_drawable.py +0 -324
- valetudo_map_parser/hypfer_rooms_handler.py +0 -599
- valetudo_map_parser-0.1.10rc6.dist-info/RECORD +0 -36
- {valetudo_map_parser-0.1.10rc6.dist-info → valetudo_map_parser-0.1.11b1.dist-info}/WHEEL +0 -0
- {valetudo_map_parser-0.1.10rc6.dist-info → valetudo_map_parser-0.1.11b1.dist-info}/licenses/LICENSE +0 -0
- {valetudo_map_parser-0.1.10rc6.dist-info → valetudo_map_parser-0.1.11b1.dist-info}/licenses/NOTICE.txt +0 -0
@@ -269,8 +269,6 @@ class ImageDraw:
|
|
269
269
|
zone_clean = self.img_h.data.find_zone_entities(m_json)
|
270
270
|
except (ValueError, KeyError):
|
271
271
|
zone_clean = None
|
272
|
-
else:
|
273
|
-
_LOGGER.info("%s: Got zones.", self.file_name)
|
274
272
|
|
275
273
|
if zone_clean:
|
276
274
|
# Process zones sequentially to avoid memory-intensive array copies
|
@@ -8,25 +8,22 @@ Version: 0.1.10
|
|
8
8
|
from __future__ import annotations
|
9
9
|
|
10
10
|
import asyncio
|
11
|
-
import numpy as np
|
12
11
|
|
12
|
+
import numpy as np
|
13
|
+
from mvcrender.autocrop import AutoCrop
|
13
14
|
from PIL import Image
|
14
15
|
|
15
16
|
from .config.async_utils import AsyncPIL
|
16
|
-
|
17
|
-
# from .config.auto_crop import AutoCrop
|
18
|
-
from mvcrender.autocrop import AutoCrop
|
19
17
|
from .config.drawable_elements import DrawableElement
|
20
18
|
from .config.shared import CameraShared
|
21
|
-
|
22
19
|
from .config.types import (
|
23
20
|
COLORS,
|
24
21
|
LOGGER,
|
25
22
|
CalibrationPoints,
|
26
23
|
Colors,
|
24
|
+
JsonType,
|
27
25
|
RoomsProperties,
|
28
26
|
RoomStore,
|
29
|
-
JsonType,
|
30
27
|
)
|
31
28
|
from .config.utils import (
|
32
29
|
BaseHandler,
|
@@ -49,9 +46,7 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
|
|
49
46
|
self.calibration_data = None # camera shared data.
|
50
47
|
self.data = ImageData # imported Image Data Module.
|
51
48
|
# Initialize drawing configuration using the shared utility function
|
52
|
-
self.drawing_config, self.draw
|
53
|
-
self
|
54
|
-
)
|
49
|
+
self.drawing_config, self.draw = initialize_drawing_config(self)
|
55
50
|
|
56
51
|
self.go_to = None # vacuum go to data
|
57
52
|
self.img_hash = None # hash of the image calculated to check differences.
|
@@ -60,7 +55,6 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
|
|
60
55
|
None # persistent working buffer to avoid per-frame allocations
|
61
56
|
)
|
62
57
|
self.active_zones = [] # vacuum active zones.
|
63
|
-
self.svg_wait = False # SVG image creation wait.
|
64
58
|
self.imd = ImDraw(self) # Image Draw class.
|
65
59
|
self.color_grey = (128, 128, 128, 255)
|
66
60
|
self.file_name = self.shared.file_name # file name of the vacuum.
|
@@ -79,7 +73,7 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
|
|
79
73
|
json_data
|
80
74
|
)
|
81
75
|
if room_properties:
|
82
|
-
|
76
|
+
_ = RoomStore(self.file_name, room_properties)
|
83
77
|
# Convert room_properties to the format expected by async_get_robot_in_room
|
84
78
|
self.rooms_pos = []
|
85
79
|
for room_id, room_data in room_properties.items():
|
@@ -260,7 +254,12 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
|
|
260
254
|
)
|
261
255
|
LOGGER.info("%s: Completed base Layers", self.file_name)
|
262
256
|
# Copy the new array in base layer.
|
257
|
+
# Delete old base layer before creating new one to free memory
|
258
|
+
if self.img_base_layer is not None:
|
259
|
+
del self.img_base_layer
|
263
260
|
self.img_base_layer = await self.async_copy_array(img_np_array)
|
261
|
+
# Delete source array after copying to free memory
|
262
|
+
del img_np_array
|
264
263
|
|
265
264
|
self.shared.frame_number = self.frame_number
|
266
265
|
self.frame_number += 1
|
@@ -274,6 +273,9 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
|
|
274
273
|
or self.img_work_layer.shape != self.img_base_layer.shape
|
275
274
|
or self.img_work_layer.dtype != self.img_base_layer.dtype
|
276
275
|
):
|
276
|
+
# Delete old buffer before creating new one to free memory
|
277
|
+
if self.img_work_layer is not None:
|
278
|
+
del self.img_work_layer
|
277
279
|
self.img_work_layer = np.empty_like(self.img_base_layer)
|
278
280
|
|
279
281
|
# Copy the base layer into the persistent working buffer (no new allocation per frame)
|
@@ -348,21 +350,11 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
|
|
348
350
|
robot_state=self.shared.vacuum_state,
|
349
351
|
)
|
350
352
|
|
351
|
-
# Update element map for robot position
|
352
|
-
if (
|
353
|
-
hasattr(self.shared, "element_map")
|
354
|
-
and self.shared.element_map is not None
|
355
|
-
):
|
356
|
-
update_element_map_with_robot(
|
357
|
-
self.shared.element_map,
|
358
|
-
robot_position,
|
359
|
-
DrawableElement.ROBOT,
|
360
|
-
)
|
361
353
|
# Synchronize zooming state from ImageDraw to handler before auto-crop
|
362
354
|
self.zooming = self.imd.img_h.zooming
|
363
355
|
|
364
356
|
# Resize the image
|
365
|
-
img_np_array = self.
|
357
|
+
img_np_array = self.auto_trim_and_zoom_image(
|
366
358
|
img_np_array,
|
367
359
|
colors["background"],
|
368
360
|
int(self.shared.margins),
|
valetudo_map_parser/map_data.py
CHANGED
@@ -8,22 +8,22 @@ Version: v0.1.10
|
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
10
|
|
11
|
-
import
|
11
|
+
from dataclasses import asdict, dataclass, field
|
12
12
|
from typing import (
|
13
|
-
List,
|
14
|
-
Sequence,
|
15
|
-
TypeVar,
|
16
13
|
Any,
|
17
|
-
TypedDict,
|
18
|
-
NotRequired,
|
19
14
|
Literal,
|
15
|
+
NotRequired,
|
20
16
|
Optional,
|
17
|
+
Sequence,
|
18
|
+
TypedDict,
|
19
|
+
TypeVar,
|
21
20
|
)
|
22
21
|
|
23
|
-
|
22
|
+
import numpy as np
|
24
23
|
|
25
24
|
from .config.types import ImageSize, JsonType
|
26
25
|
|
26
|
+
|
27
27
|
T = TypeVar("T")
|
28
28
|
|
29
29
|
# --- Common Nested Structures ---
|
@@ -373,6 +373,11 @@ class ImageData:
|
|
373
373
|
Else:
|
374
374
|
(min_x_mm, min_y_mm, max_x_mm, max_y_mm)
|
375
375
|
"""
|
376
|
+
|
377
|
+
def to_mm(coord):
|
378
|
+
"""Convert pixel coordinates to millimeters."""
|
379
|
+
return round(coord * pixel_size * 10)
|
380
|
+
|
376
381
|
if not pixels:
|
377
382
|
raise ValueError("Pixels list cannot be empty.")
|
378
383
|
|
@@ -393,7 +398,6 @@ class ImageData:
|
|
393
398
|
min_y = min(min_y, y)
|
394
399
|
|
395
400
|
if rand:
|
396
|
-
to_mm = lambda v: v * pixel_size * 10
|
397
401
|
return (to_mm(max_x), to_mm(max_y)), (to_mm(min_x), to_mm(min_y))
|
398
402
|
|
399
403
|
return (
|
@@ -539,16 +543,18 @@ class RandImageData:
|
|
539
543
|
return None
|
540
544
|
|
541
545
|
@staticmethod
|
542
|
-
def get_rrm_currently_cleaned_zones(json_data: JsonType) -> dict:
|
546
|
+
def get_rrm_currently_cleaned_zones(json_data: JsonType) -> list[dict[str, Any]]:
|
543
547
|
"""Get the currently cleaned zones from the json."""
|
544
548
|
re_zones = json_data.get("currently_cleaned_zones", [])
|
545
549
|
formatted_zones = RandImageData._rrm_valetudo_format_zone(re_zones)
|
546
550
|
return formatted_zones
|
547
551
|
|
548
552
|
@staticmethod
|
549
|
-
def get_rrm_forbidden_zones(json_data: JsonType) -> dict:
|
553
|
+
def get_rrm_forbidden_zones(json_data: JsonType) -> list[dict[str, Any]]:
|
550
554
|
"""Get the forbidden zones from the json."""
|
551
|
-
re_zones = json_data.get("forbidden_zones", [])
|
555
|
+
re_zones = json_data.get("forbidden_zones", []) + json_data.get(
|
556
|
+
"forbidden_mop_zones", []
|
557
|
+
)
|
552
558
|
formatted_zones = RandImageData._rrm_valetudo_format_zone(re_zones)
|
553
559
|
return formatted_zones
|
554
560
|
|
@@ -2,27 +2,26 @@
|
|
2
2
|
Image Handler Module for Valetudo Re Vacuums.
|
3
3
|
It returns the PIL PNG image frame relative to the Map Data extrapolated from the vacuum json.
|
4
4
|
It also returns calibration, rooms data to the card and other images information to the camera.
|
5
|
-
Version: 0.1.
|
5
|
+
Version: 0.1.10
|
6
6
|
"""
|
7
7
|
|
8
8
|
from __future__ import annotations
|
9
9
|
|
10
|
-
import logging
|
11
10
|
import uuid
|
12
11
|
from typing import Any
|
13
12
|
|
14
13
|
import numpy as np
|
14
|
+
from mvcrender.autocrop import AutoCrop
|
15
15
|
|
16
16
|
from .config.async_utils import AsyncPIL
|
17
|
-
|
18
|
-
# from .config.auto_crop import AutoCrop
|
19
|
-
from mvcrender.autocrop import AutoCrop
|
20
17
|
from .config.drawable_elements import DrawableElement
|
21
18
|
from .config.types import (
|
22
19
|
COLORS,
|
23
20
|
DEFAULT_IMAGE_SIZE,
|
24
21
|
DEFAULT_PIXEL_SIZE,
|
22
|
+
LOGGER,
|
25
23
|
Colors,
|
24
|
+
Destinations,
|
26
25
|
JsonType,
|
27
26
|
PilPNG,
|
28
27
|
RobotPosition,
|
@@ -39,9 +38,6 @@ from .reimg_draw import ImageDraw
|
|
39
38
|
from .rooms_handler import RandRoomsHandler
|
40
39
|
|
41
40
|
|
42
|
-
_LOGGER = logging.getLogger(__name__)
|
43
|
-
|
44
|
-
|
45
41
|
# noinspection PyTypeChecker
|
46
42
|
class ReImageHandler(BaseHandler, AutoCrop):
|
47
43
|
"""
|
@@ -59,11 +55,10 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
59
55
|
self.data = RandImageData # Image Data
|
60
56
|
|
61
57
|
# Initialize drawing configuration using the shared utility function
|
62
|
-
self.drawing_config, self.draw
|
63
|
-
self
|
64
|
-
)
|
58
|
+
self.drawing_config, self.draw = initialize_drawing_config(self)
|
65
59
|
self.go_to = None # Go to position data
|
66
60
|
self.img_base_layer = None # Base image layer
|
61
|
+
self.img_work_layer = None # Persistent working buffer (reused across frames)
|
67
62
|
self.img_rotate = shared_data.image_rotate # Image rotation
|
68
63
|
self.room_propriety = None # Room propriety data
|
69
64
|
self.active_zones = None # Active zones
|
@@ -74,7 +69,9 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
74
69
|
) # Room data handler
|
75
70
|
|
76
71
|
async def extract_room_properties(
|
77
|
-
self,
|
72
|
+
self,
|
73
|
+
json_data: JsonType,
|
74
|
+
destinations: Destinations | None = None,
|
78
75
|
) -> RoomsProperties:
|
79
76
|
"""Extract the room properties."""
|
80
77
|
# unsorted_id = RandImageData.get_rrm_segments_ids(json_data)
|
@@ -89,9 +86,10 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
89
86
|
json_data, size_x, size_y, top, left, True
|
90
87
|
)
|
91
88
|
|
92
|
-
|
93
|
-
|
94
|
-
|
89
|
+
|
90
|
+
dest_json = destinations if destinations else {}
|
91
|
+
zones_data = dest_json.get("zones", [])
|
92
|
+
points_data = dest_json.get("spots", [])
|
95
93
|
|
96
94
|
# Use the RandRoomsHandler to extract room properties
|
97
95
|
room_properties = await self.rooms_handler.async_extract_room_properties(
|
@@ -100,39 +98,35 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
100
98
|
|
101
99
|
# Update self.rooms_pos from room_properties for compatibility with other methods
|
102
100
|
self.rooms_pos = []
|
103
|
-
room_ids = [] # Collect room IDs for shared.map_rooms
|
104
101
|
for room_id, room_data in room_properties.items():
|
105
102
|
self.rooms_pos.append(
|
106
103
|
{"name": room_data["name"], "outline": room_data["outline"]}
|
107
104
|
)
|
108
|
-
# Store the room number (segment ID) for MQTT active zone mapping
|
109
|
-
room_ids.append(room_data["number"])
|
110
|
-
|
111
|
-
# Update shared.map_rooms with the room IDs for MQTT active zone mapping
|
112
|
-
self.shared.map_rooms = room_ids
|
113
105
|
|
106
|
+
# Update shared.map_rooms with the full room properties (consistent with Hypfer)
|
107
|
+
self.shared.map_rooms = room_properties
|
114
108
|
# get the zones and points data
|
115
|
-
|
109
|
+
self.shared.map_pred_zones = await self.async_zone_propriety(zones_data)
|
116
110
|
# get the points data
|
117
|
-
|
111
|
+
self.shared.map_pred_points = await self.async_points_propriety(points_data)
|
118
112
|
|
119
|
-
if not (room_properties or
|
113
|
+
if not (room_properties or self.shared.map_pred_zones):
|
120
114
|
self.rooms_pos = None
|
121
115
|
|
122
|
-
|
123
|
-
return room_properties
|
116
|
+
_ = RoomStore(self.file_name, room_properties)
|
117
|
+
return room_properties
|
124
118
|
except (RuntimeError, ValueError) as e:
|
125
|
-
|
119
|
+
LOGGER.warning(
|
126
120
|
"No rooms Data or Error in extract_room_properties: %s",
|
127
121
|
e,
|
128
122
|
exc_info=True,
|
129
123
|
)
|
130
|
-
return None
|
124
|
+
return None
|
131
125
|
|
132
126
|
async def get_image_from_rrm(
|
133
127
|
self,
|
134
128
|
m_json: JsonType, # json data
|
135
|
-
destinations: None = None, # MQTT destinations for labels
|
129
|
+
destinations: Destinations | None = None, # MQTT destinations for labels
|
136
130
|
) -> PilPNG | None:
|
137
131
|
"""Generate Images from the json data.
|
138
132
|
@param m_json: The JSON data to use to draw the image.
|
@@ -146,12 +140,12 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
146
140
|
|
147
141
|
try:
|
148
142
|
if (m_json is not None) and (not isinstance(m_json, tuple)):
|
149
|
-
|
143
|
+
LOGGER.info("%s: Composing the image for the camera.", self.file_name)
|
150
144
|
self.json_data = m_json
|
151
145
|
size_x, size_y = self.data.get_rrm_image_size(m_json)
|
152
146
|
self.img_size = DEFAULT_IMAGE_SIZE
|
153
147
|
self.json_id = str(uuid.uuid4()) # image id
|
154
|
-
|
148
|
+
LOGGER.info("Vacuum Data ID: %s", self.json_id)
|
155
149
|
|
156
150
|
(
|
157
151
|
img_np_array,
|
@@ -163,10 +157,24 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
163
157
|
|
164
158
|
# Increment frame number
|
165
159
|
self.frame_number += 1
|
166
|
-
img_np_array = await self.async_copy_array(self.img_base_layer)
|
167
160
|
if self.frame_number > 5:
|
168
161
|
self.frame_number = 0
|
169
162
|
|
163
|
+
# Ensure persistent working buffer exists and matches base (allocate only when needed)
|
164
|
+
if (
|
165
|
+
self.img_work_layer is None
|
166
|
+
or self.img_work_layer.shape != self.img_base_layer.shape
|
167
|
+
or self.img_work_layer.dtype != self.img_base_layer.dtype
|
168
|
+
):
|
169
|
+
# Delete old buffer before creating new one to free memory
|
170
|
+
if self.img_work_layer is not None:
|
171
|
+
del self.img_work_layer
|
172
|
+
self.img_work_layer = np.empty_like(self.img_base_layer)
|
173
|
+
|
174
|
+
# Copy the base layer into the persistent working buffer (no new allocation per frame)
|
175
|
+
np.copyto(self.img_work_layer, self.img_base_layer)
|
176
|
+
img_np_array = self.img_work_layer
|
177
|
+
|
170
178
|
# Draw map elements
|
171
179
|
img_np_array = await self._draw_map_elements(
|
172
180
|
img_np_array, m_json, colors, robot_position, robot_position_angle
|
@@ -174,11 +182,11 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
174
182
|
|
175
183
|
# Return PIL Image using async utilities
|
176
184
|
pil_img = await AsyncPIL.async_fromarray(img_np_array, mode="RGBA")
|
177
|
-
|
185
|
+
# Note: Don't delete img_np_array here as it's the persistent work buffer
|
178
186
|
return await self._finalize_image(pil_img)
|
179
187
|
|
180
188
|
except (RuntimeError, RuntimeWarning) as e:
|
181
|
-
|
189
|
+
LOGGER.warning(
|
182
190
|
"%s: Runtime Error %s during image creation.",
|
183
191
|
self.file_name,
|
184
192
|
str(e),
|
@@ -192,6 +200,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
192
200
|
async def _setup_robot_and_image(
|
193
201
|
self, m_json, size_x, size_y, colors, destinations
|
194
202
|
):
|
203
|
+
"""Set up the elements of the map and the image."""
|
195
204
|
(
|
196
205
|
_,
|
197
206
|
robot_position,
|
@@ -214,13 +223,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
214
223
|
colors["background"],
|
215
224
|
DEFAULT_PIXEL_SIZE,
|
216
225
|
)
|
217
|
-
|
218
|
-
|
219
|
-
# Update element map for rooms
|
220
|
-
if 0 < room_id <= 15:
|
221
|
-
# This is a simplification - in a real implementation we would
|
222
|
-
# need to identify the exact pixels that belong to each room
|
223
|
-
pass
|
226
|
+
LOGGER.info("%s: Completed base Layers", self.file_name)
|
224
227
|
|
225
228
|
if room_id > 0 and not self.room_propriety:
|
226
229
|
self.room_propriety = await self.get_rooms_attributes(destinations)
|
@@ -229,14 +232,23 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
229
232
|
if not self.rooms_pos and not self.room_propriety:
|
230
233
|
self.room_propriety = await self.get_rooms_attributes(destinations)
|
231
234
|
|
232
|
-
# Always check robot position for zooming (
|
233
|
-
if
|
235
|
+
# Always check robot position for zooming (update if room info is missing)
|
236
|
+
if (
|
237
|
+
self.rooms_pos
|
238
|
+
and robot_position
|
239
|
+
and (self.robot_pos is None or "in_room" not in self.robot_pos)
|
240
|
+
):
|
234
241
|
self.robot_pos = await self.async_get_robot_in_room(
|
235
242
|
(robot_position[0] * 10),
|
236
243
|
(robot_position[1] * 10),
|
237
244
|
robot_position_angle,
|
238
245
|
)
|
246
|
+
# Delete old base layer before creating new one to free memory
|
247
|
+
if self.img_base_layer is not None:
|
248
|
+
del self.img_base_layer
|
239
249
|
self.img_base_layer = await self.async_copy_array(img_np_array)
|
250
|
+
# Delete source array after copying to free memory
|
251
|
+
del img_np_array
|
240
252
|
else:
|
241
253
|
# If floor is disabled, create an empty image
|
242
254
|
background_color = self.drawing_config.get_property(
|
@@ -245,7 +257,12 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
245
257
|
img_np_array = await self.draw.create_empty_image(
|
246
258
|
size_x, size_y, background_color
|
247
259
|
)
|
260
|
+
# Delete old base layer before creating new one to free memory
|
261
|
+
if self.img_base_layer is not None:
|
262
|
+
del self.img_base_layer
|
248
263
|
self.img_base_layer = await self.async_copy_array(img_np_array)
|
264
|
+
# Delete source array after copying to free memory
|
265
|
+
del img_np_array
|
249
266
|
|
250
267
|
# Check active zones BEFORE auto-crop to enable proper zoom functionality
|
251
268
|
# This needs to run on every frame, not just frame 0
|
@@ -277,7 +294,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
277
294
|
# Restore original rooms_pos
|
278
295
|
self.rooms_pos = original_rooms_pos
|
279
296
|
|
280
|
-
except
|
297
|
+
except (ValueError, KeyError, TypeError):
|
281
298
|
# Fallback to robot-position-based zoom if room extraction fails
|
282
299
|
if (
|
283
300
|
self.shared.image_auto_zoom
|
@@ -291,6 +308,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
291
308
|
async def _draw_map_elements(
|
292
309
|
self, img_np_array, m_json, colors, robot_position, robot_position_angle
|
293
310
|
):
|
311
|
+
"""Draw map elements on the image."""
|
294
312
|
# Draw charger if enabled
|
295
313
|
if self.drawing_config.is_enabled(DrawableElement.CHARGER):
|
296
314
|
img_np_array, self.charger_pos = await self.imd.async_draw_charger(
|
@@ -350,7 +368,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
350
368
|
else:
|
351
369
|
self.zooming = False
|
352
370
|
|
353
|
-
img_np_array = self.
|
371
|
+
img_np_array = self.auto_trim_and_zoom_image(
|
354
372
|
img_np_array,
|
355
373
|
detect_colour=colors["background"],
|
356
374
|
margin_size=int(self.shared.margins),
|
@@ -361,22 +379,24 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
361
379
|
return img_np_array
|
362
380
|
|
363
381
|
async def _finalize_image(self, pil_img):
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
return pil_img
|
382
|
+
"""Finalize the image by resizing if needed."""
|
383
|
+
if pil_img is None:
|
384
|
+
LOGGER.warning("%s: Image is None. Returning None.", self.file_name)
|
385
|
+
return None
|
369
386
|
if self.check_zoom_and_aspect_ratio():
|
370
387
|
resize_params = self.prepare_resize_params(pil_img, True)
|
371
388
|
pil_img = await self.async_resize_images(resize_params)
|
389
|
+
else:
|
390
|
+
LOGGER.warning(
|
391
|
+
"%s: Invalid image dimensions. Returning original image.",
|
392
|
+
self.file_name,
|
393
|
+
)
|
372
394
|
return pil_img
|
373
395
|
|
374
396
|
async def get_rooms_attributes(
|
375
397
|
self, destinations: JsonType = None
|
376
398
|
) -> tuple[RoomsProperties, Any, Any]:
|
377
399
|
"""Return the rooms attributes."""
|
378
|
-
if self.room_propriety:
|
379
|
-
return self.room_propriety
|
380
400
|
if self.json_data and destinations:
|
381
401
|
self.room_propriety = await self.extract_room_properties(
|
382
402
|
self.json_data, destinations
|
@@ -401,6 +421,12 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
401
421
|
}
|
402
422
|
# Handle active zones
|
403
423
|
self.active_zones = self.shared.rand256_active_zone
|
424
|
+
LOGGER.debug(
|
425
|
+
"%s: Robot is in %s room (polygon detection). %s",
|
426
|
+
self.file_name,
|
427
|
+
self.robot_in_room["room"],
|
428
|
+
self.active_zones,
|
429
|
+
)
|
404
430
|
self.zooming = False
|
405
431
|
if self.active_zones and (
|
406
432
|
self.robot_in_room["id"] in range(len(self.active_zones))
|
@@ -515,7 +541,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
515
541
|
"""Return the map calibration data."""
|
516
542
|
if not self.calibration_data and self.crop_img_size:
|
517
543
|
self.calibration_data = []
|
518
|
-
|
544
|
+
LOGGER.info(
|
519
545
|
"%s: Getting Calibrations points %s",
|
520
546
|
self.file_name,
|
521
547
|
str(self.crop_area),
|
@@ -6,17 +6,12 @@ Version: 0.1.9.b42
|
|
6
6
|
|
7
7
|
from __future__ import annotations
|
8
8
|
|
9
|
-
import logging
|
10
|
-
|
11
9
|
from .config.drawable import Drawable
|
12
10
|
from .config.drawable_elements import DrawableElement
|
13
|
-
from .config.types import Color, JsonType, NumpyArray
|
11
|
+
from .config.types import LOGGER, Color, JsonType, NumpyArray
|
14
12
|
from .map_data import ImageData, RandImageData
|
15
13
|
|
16
14
|
|
17
|
-
_LOGGER = logging.getLogger(__name__)
|
18
|
-
|
19
|
-
|
20
15
|
class ImageDraw:
|
21
16
|
"""Class to handle the image creation."""
|
22
17
|
|
@@ -48,7 +43,7 @@ class ImageDraw:
|
|
48
43
|
)
|
49
44
|
return np_array
|
50
45
|
except KeyError as e:
|
51
|
-
|
46
|
+
LOGGER.warning(
|
52
47
|
"%s: Error in extraction of go-to target: %s",
|
53
48
|
self.file_name,
|
54
49
|
e,
|
@@ -70,7 +65,7 @@ class ImageDraw:
|
|
70
65
|
)
|
71
66
|
except ValueError as e:
|
72
67
|
self.img_h.segment_data = None
|
73
|
-
|
68
|
+
LOGGER.info("%s: No segments data found: %s", self.file_name, e)
|
74
69
|
|
75
70
|
async def async_draw_base_layer(
|
76
71
|
self,
|
@@ -87,13 +82,13 @@ class ImageDraw:
|
|
87
82
|
walls_data = self.data.get_rrm_walls(m_json)
|
88
83
|
floor_data = self.data.get_rrm_floor(m_json)
|
89
84
|
|
90
|
-
|
85
|
+
LOGGER.info("%s: Empty image with background color", self.file_name)
|
91
86
|
img_np_array = await self.draw.create_empty_image(
|
92
87
|
self.img_h.img_size["x"], self.img_h.img_size["y"], color_background
|
93
88
|
)
|
94
89
|
room_id = 0
|
95
90
|
if self.img_h.frame_number == 0:
|
96
|
-
|
91
|
+
LOGGER.info("%s: Overlapping Layers", self.file_name)
|
97
92
|
|
98
93
|
# checking if there are segments too (sorted pixels in the raw data).
|
99
94
|
await self.async_segment_data(m_json, size_x, size_y, pos_top, pos_left)
|
@@ -148,10 +143,10 @@ class ImageDraw:
|
|
148
143
|
room_id = 0
|
149
144
|
rooms_list = [color_wall]
|
150
145
|
if not segment_data:
|
151
|
-
|
146
|
+
LOGGER.info("%s: No segments data found.", self.file_name)
|
152
147
|
return room_id, img_np_array
|
153
148
|
|
154
|
-
|
149
|
+
LOGGER.info("%s: Drawing segments.", self.file_name)
|
155
150
|
for pixels in segment_data:
|
156
151
|
room_color = self.img_h.shared.rooms_colors[room_id]
|
157
152
|
rooms_list.append(room_color)
|
@@ -211,7 +206,7 @@ class ImageDraw:
|
|
211
206
|
self.data.get_rrm_charger_position(m_json)
|
212
207
|
)
|
213
208
|
except KeyError as e:
|
214
|
-
|
209
|
+
LOGGER.warning("%s: No charger position found: %s", self.file_name, e)
|
215
210
|
else:
|
216
211
|
if charger_pos:
|
217
212
|
charger_pos_dictionary = {
|
@@ -238,7 +233,7 @@ class ImageDraw:
|
|
238
233
|
zone_clean = None
|
239
234
|
|
240
235
|
if zone_clean:
|
241
|
-
|
236
|
+
LOGGER.info("%s: Got zones.", self.file_name)
|
242
237
|
return await self.draw.zones(np_array, zone_clean, color_zone_clean)
|
243
238
|
return np_array
|
244
239
|
|
@@ -252,7 +247,7 @@ class ImageDraw:
|
|
252
247
|
virtual_walls = None
|
253
248
|
|
254
249
|
if virtual_walls:
|
255
|
-
|
250
|
+
LOGGER.info("%s: Got virtual walls.", self.file_name)
|
256
251
|
np_array = await self.draw.draw_virtual_walls(
|
257
252
|
np_array, virtual_walls, color_no_go
|
258
253
|
)
|
@@ -280,7 +275,7 @@ class ImageDraw:
|
|
280
275
|
self.data.rrm_valetudo_path_array(path_pixel["points"]), 2
|
281
276
|
)
|
282
277
|
except KeyError as e:
|
283
|
-
|
278
|
+
LOGGER.warning(
|
284
279
|
"%s: Error extracting paths data: %s", self.file_name, str(e)
|
285
280
|
)
|
286
281
|
finally:
|
@@ -297,7 +292,7 @@ class ImageDraw:
|
|
297
292
|
except (ValueError, KeyError):
|
298
293
|
entity_dict = None
|
299
294
|
else:
|
300
|
-
|
295
|
+
LOGGER.info("%s: Got the points in the json.", self.file_name)
|
301
296
|
return entity_dict
|
302
297
|
|
303
298
|
async def async_get_robot_position(self, m_json: JsonType) -> tuple | None:
|
@@ -310,7 +305,7 @@ class ImageDraw:
|
|
310
305
|
robot_pos = self.data.rrm_coordinates_to_valetudo(robot_pos_data)
|
311
306
|
angle = self.data.get_rrm_robot_angle(m_json)
|
312
307
|
except (ValueError, KeyError):
|
313
|
-
|
308
|
+
LOGGER.warning("%s No robot position found.", self.file_name)
|
314
309
|
return None, None, None
|
315
310
|
finally:
|
316
311
|
robot_position_angle = round(angle[0], 0)
|