valetudo-map-parser 0.1.9b56__py3-none-any.whl → 0.1.9b58__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 +6 -2
- valetudo_map_parser/config/auto_crop.py +150 -20
- valetudo_map_parser/config/rand256_parser.py +395 -0
- valetudo_map_parser/config/shared.py +47 -1
- valetudo_map_parser/config/types.py +2 -1
- valetudo_map_parser/config/utils.py +91 -2
- valetudo_map_parser/hypfer_draw.py +104 -49
- valetudo_map_parser/hypfer_handler.py +69 -19
- valetudo_map_parser/map_data.py +26 -2
- valetudo_map_parser/{rand25_handler.py → rand256_handler.py} +152 -33
- valetudo_map_parser/rooms_handler.py +6 -2
- {valetudo_map_parser-0.1.9b56.dist-info → valetudo_map_parser-0.1.9b58.dist-info}/METADATA +1 -1
- valetudo_map_parser-0.1.9b58.dist-info/RECORD +27 -0
- valetudo_map_parser/config/room_outline.py +0 -148
- valetudo_map_parser-0.1.9b56.dist-info/RECORD +0 -27
- {valetudo_map_parser-0.1.9b56.dist-info → valetudo_map_parser-0.1.9b58.dist-info}/LICENSE +0 -0
- {valetudo_map_parser-0.1.9b56.dist-info → valetudo_map_parser-0.1.9b58.dist-info}/NOTICE.txt +0 -0
- {valetudo_map_parser-0.1.9b56.dist-info → valetudo_map_parser-0.1.9b58.dist-info}/WHEEL +0 -0
@@ -2,7 +2,7 @@
|
|
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.9.
|
5
|
+
Version: 0.1.9.a6
|
6
6
|
"""
|
7
7
|
|
8
8
|
from __future__ import annotations
|
@@ -26,11 +26,13 @@ from .config.types import (
|
|
26
26
|
RobotPosition,
|
27
27
|
RoomsProperties,
|
28
28
|
RoomStore,
|
29
|
+
WebPBytes,
|
29
30
|
)
|
30
31
|
from .config.utils import (
|
31
32
|
BaseHandler,
|
32
33
|
initialize_drawing_config,
|
33
34
|
manage_drawable_elements,
|
35
|
+
numpy_to_webp_bytes,
|
34
36
|
prepare_resize_params,
|
35
37
|
)
|
36
38
|
from .map_data import RandImageData
|
@@ -68,7 +70,9 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
68
70
|
self.active_zones = None # Active zones
|
69
71
|
self.file_name = self.shared.file_name # File name
|
70
72
|
self.imd = ImageDraw(self) # Image Draw
|
71
|
-
self.rooms_handler = RandRoomsHandler(
|
73
|
+
self.rooms_handler = RandRoomsHandler(
|
74
|
+
self.file_name, self.drawing_config
|
75
|
+
) # Room data handler
|
72
76
|
|
73
77
|
async def extract_room_properties(
|
74
78
|
self, json_data: JsonType, destinations: JsonType
|
@@ -97,10 +101,17 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
97
101
|
|
98
102
|
# Update self.rooms_pos from room_properties for compatibility with other methods
|
99
103
|
self.rooms_pos = []
|
104
|
+
room_ids = [] # Collect room IDs for shared.map_rooms
|
100
105
|
for room_id, room_data in room_properties.items():
|
101
106
|
self.rooms_pos.append(
|
102
107
|
{"name": room_data["name"], "outline": room_data["outline"]}
|
103
108
|
)
|
109
|
+
# Store the room number (segment ID) for MQTT active zone mapping
|
110
|
+
room_ids.append(room_data["number"])
|
111
|
+
|
112
|
+
# Update shared.map_rooms with the room IDs for MQTT active zone mapping
|
113
|
+
self.shared.map_rooms = room_ids
|
114
|
+
_LOGGER.debug("Updated shared.map_rooms with room IDs: %s", room_ids)
|
104
115
|
|
105
116
|
# get the zones and points data
|
106
117
|
zone_properties = await self.async_zone_propriety(zones_data)
|
@@ -116,9 +127,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
116
127
|
_LOGGER.debug("Extracted data: %s", extracted_data)
|
117
128
|
else:
|
118
129
|
self.rooms_pos = None
|
119
|
-
_LOGGER.debug(
|
120
|
-
"%s: Rooms and Zones data not available!", self.file_name
|
121
|
-
)
|
130
|
+
_LOGGER.debug("%s: Rooms and Zones data not available!", self.file_name)
|
122
131
|
|
123
132
|
rooms = RoomStore(self.file_name, room_properties)
|
124
133
|
_LOGGER.debug("Rooms Data: %s", rooms.get_rooms())
|
@@ -135,8 +144,14 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
135
144
|
self,
|
136
145
|
m_json: JsonType, # json data
|
137
146
|
destinations: None = None, # MQTT destinations for labels
|
138
|
-
|
139
|
-
|
147
|
+
return_webp: bool = False,
|
148
|
+
) -> WebPBytes | Image.Image | None:
|
149
|
+
"""Generate Images from the json data.
|
150
|
+
@param m_json: The JSON data to use to draw the image.
|
151
|
+
@param destinations: MQTT destinations for labels (unused).
|
152
|
+
@param return_webp: If True, return WebP bytes; if False, return PIL Image (default).
|
153
|
+
@return WebPBytes | Image.Image: WebP bytes or PIL Image depending on return_webp parameter.
|
154
|
+
"""
|
140
155
|
colors: Colors = {
|
141
156
|
name: self.shared.user_colors[idx] for idx, name in enumerate(COLORS)
|
142
157
|
}
|
@@ -173,11 +188,21 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
173
188
|
img_np_array, m_json, colors, robot_position, robot_position_angle
|
174
189
|
)
|
175
190
|
|
176
|
-
#
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
191
|
+
# Return WebP bytes or PIL Image based on parameter
|
192
|
+
if return_webp:
|
193
|
+
# Convert directly to WebP bytes for better performance
|
194
|
+
webp_bytes = await numpy_to_webp_bytes(
|
195
|
+
img_np_array,
|
196
|
+
quality=90, # High quality for vacuum maps
|
197
|
+
lossless=False, # Use lossy compression for smaller size
|
198
|
+
)
|
199
|
+
del img_np_array # free memory
|
200
|
+
return webp_bytes
|
201
|
+
else:
|
202
|
+
# Convert to PIL Image (original behavior)
|
203
|
+
pil_img = Image.fromarray(img_np_array, mode="RGBA")
|
204
|
+
del img_np_array # free memory
|
205
|
+
return await self._finalize_image(pil_img)
|
181
206
|
|
182
207
|
except (RuntimeError, RuntimeWarning) as e:
|
183
208
|
_LOGGER.warning(
|
@@ -226,12 +251,18 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
226
251
|
|
227
252
|
if room_id > 0 and not self.room_propriety:
|
228
253
|
self.room_propriety = await self.get_rooms_attributes(destinations)
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
254
|
+
|
255
|
+
# Ensure room data is available for robot room detection (even if not extracted above)
|
256
|
+
if not self.rooms_pos and not self.room_propriety:
|
257
|
+
self.room_propriety = await self.get_rooms_attributes(destinations)
|
258
|
+
|
259
|
+
# Always check robot position for zooming (fallback)
|
260
|
+
if self.rooms_pos and robot_position and not hasattr(self, "robot_pos"):
|
261
|
+
self.robot_pos = await self.async_get_robot_in_room(
|
262
|
+
(robot_position[0] * 10),
|
263
|
+
(robot_position[1] * 10),
|
264
|
+
robot_position_angle,
|
265
|
+
)
|
235
266
|
self.img_base_layer = await self.async_copy_array(img_np_array)
|
236
267
|
else:
|
237
268
|
# If floor is disabled, create an empty image
|
@@ -242,6 +273,60 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
242
273
|
size_x, size_y, background_color
|
243
274
|
)
|
244
275
|
self.img_base_layer = await self.async_copy_array(img_np_array)
|
276
|
+
|
277
|
+
# Check active zones BEFORE auto-crop to enable proper zoom functionality
|
278
|
+
# This needs to run on every frame, not just frame 0
|
279
|
+
if (
|
280
|
+
self.shared.image_auto_zoom
|
281
|
+
and self.shared.vacuum_state == "cleaning"
|
282
|
+
and robot_position
|
283
|
+
and destinations # Check if we have destinations data for room extraction
|
284
|
+
):
|
285
|
+
# Extract room data early if we have destinations
|
286
|
+
try:
|
287
|
+
temp_room_properties = (
|
288
|
+
await self.rooms_handler.async_extract_room_properties(
|
289
|
+
m_json, destinations
|
290
|
+
)
|
291
|
+
)
|
292
|
+
if temp_room_properties:
|
293
|
+
# Create temporary rooms_pos for robot room detection
|
294
|
+
temp_rooms_pos = []
|
295
|
+
for room_id, room_data in temp_room_properties.items():
|
296
|
+
temp_rooms_pos.append(
|
297
|
+
{"name": room_data["name"], "outline": room_data["outline"]}
|
298
|
+
)
|
299
|
+
|
300
|
+
# Store original rooms_pos and temporarily use the new one
|
301
|
+
original_rooms_pos = self.rooms_pos
|
302
|
+
self.rooms_pos = temp_rooms_pos
|
303
|
+
|
304
|
+
# Perform robot room detection to check active zones
|
305
|
+
robot_room_result = await self.async_get_robot_in_room(
|
306
|
+
robot_position[0], robot_position[1], robot_position_angle
|
307
|
+
)
|
308
|
+
|
309
|
+
# Restore original rooms_pos
|
310
|
+
self.rooms_pos = original_rooms_pos
|
311
|
+
|
312
|
+
except Exception as e:
|
313
|
+
_LOGGER.debug(
|
314
|
+
"%s: Early room extraction failed: %s, falling back to robot-position zoom",
|
315
|
+
self.file_name,
|
316
|
+
e,
|
317
|
+
)
|
318
|
+
# Fallback to robot-position-based zoom if room extraction fails
|
319
|
+
if (
|
320
|
+
self.shared.image_auto_zoom
|
321
|
+
and self.shared.vacuum_state == "cleaning"
|
322
|
+
and robot_position
|
323
|
+
):
|
324
|
+
self.zooming = True
|
325
|
+
_LOGGER.debug(
|
326
|
+
"%s: Enabling fallback robot-position-based zoom",
|
327
|
+
self.file_name,
|
328
|
+
)
|
329
|
+
|
245
330
|
return self.img_base_layer, robot_position, robot_position_angle
|
246
331
|
|
247
332
|
async def _draw_map_elements(
|
@@ -288,6 +373,33 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
288
373
|
img_np_array, robot_position, robot_position_angle, robot_color
|
289
374
|
)
|
290
375
|
|
376
|
+
# Store robot position for potential zoom function use
|
377
|
+
if robot_position:
|
378
|
+
self.robot_position = robot_position
|
379
|
+
|
380
|
+
# Check if Zoom should be enabled based on active zones
|
381
|
+
if (
|
382
|
+
self.shared.image_auto_zoom
|
383
|
+
and self.shared.vacuum_state == "cleaning"
|
384
|
+
and robot_position
|
385
|
+
):
|
386
|
+
# For Rand256, we need to check active zones differently since room data is not available yet
|
387
|
+
# Use a simplified approach: enable zoom if any active zones are set
|
388
|
+
active_zones = self.shared.rand256_active_zone
|
389
|
+
if active_zones and any(zone for zone in active_zones):
|
390
|
+
self.zooming = True
|
391
|
+
_LOGGER.debug(
|
392
|
+
"%s: Enabling zoom for Rand256 - active zones detected: %s",
|
393
|
+
self.file_name,
|
394
|
+
active_zones,
|
395
|
+
)
|
396
|
+
else:
|
397
|
+
self.zooming = False
|
398
|
+
_LOGGER.debug(
|
399
|
+
"%s: Zoom disabled for Rand256 - no active zones set",
|
400
|
+
self.file_name,
|
401
|
+
)
|
402
|
+
|
291
403
|
img_np_array = await self.async_auto_trim_and_zoom_image(
|
292
404
|
img_np_array,
|
293
405
|
detect_colour=colors["background"],
|
@@ -390,22 +502,22 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
390
502
|
self.active_zones = self.shared.rand256_active_zone
|
391
503
|
self.zooming = False
|
392
504
|
if self.active_zones and (
|
393
|
-
self.robot_in_room["id"]
|
394
|
-
in range(len(self.active_zones))
|
505
|
+
self.robot_in_room["id"] in range(len(self.active_zones))
|
395
506
|
):
|
396
|
-
self.zooming = bool(
|
397
|
-
self.active_zones[self.robot_in_room["id"]]
|
398
|
-
)
|
507
|
+
self.zooming = bool(self.active_zones[self.robot_in_room["id"]])
|
399
508
|
else:
|
400
509
|
self.zooming = False
|
401
510
|
return temp
|
402
511
|
# Fallback to bounding box check if no outline data
|
403
|
-
elif all(
|
404
|
-
k in self.robot_in_room for k in ["left", "right", "up", "down"]
|
405
|
-
):
|
512
|
+
elif all(k in self.robot_in_room for k in ["left", "right", "up", "down"]):
|
406
513
|
if (
|
407
|
-
|
408
|
-
|
514
|
+
self.robot_in_room["right"]
|
515
|
+
<= int(robot_x)
|
516
|
+
<= self.robot_in_room["left"]
|
517
|
+
) and (
|
518
|
+
self.robot_in_room["up"]
|
519
|
+
<= int(robot_y)
|
520
|
+
<= self.robot_in_room["down"]
|
409
521
|
):
|
410
522
|
temp = {
|
411
523
|
"x": robot_x,
|
@@ -417,12 +529,9 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
417
529
|
self.active_zones = self.shared.rand256_active_zone
|
418
530
|
self.zooming = False
|
419
531
|
if self.active_zones and (
|
420
|
-
self.robot_in_room["id"]
|
421
|
-
in range(len(self.active_zones))
|
532
|
+
self.robot_in_room["id"] in range(len(self.active_zones))
|
422
533
|
):
|
423
|
-
self.zooming = bool(
|
424
|
-
self.active_zones[self.robot_in_room["id"]]
|
425
|
-
)
|
534
|
+
self.zooming = bool(self.active_zones[self.robot_in_room["id"]])
|
426
535
|
else:
|
427
536
|
self.zooming = False
|
428
537
|
return temp
|
@@ -488,6 +597,16 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
488
597
|
"angle": angle,
|
489
598
|
"in_room": self.robot_in_room["room"],
|
490
599
|
}
|
600
|
+
|
601
|
+
# Handle active zones - Set zooming based on active zones
|
602
|
+
self.active_zones = self.shared.rand256_active_zone
|
603
|
+
if self.active_zones and (
|
604
|
+
self.robot_in_room["id"] in range(len(self.active_zones))
|
605
|
+
):
|
606
|
+
self.zooming = bool(self.active_zones[self.robot_in_room["id"]])
|
607
|
+
else:
|
608
|
+
self.zooming = False
|
609
|
+
|
491
610
|
_LOGGER.debug(
|
492
611
|
"%s is in %s room (polygon detection).",
|
493
612
|
self.file_name,
|
@@ -19,6 +19,7 @@ from .config.types import LOGGER, RoomsProperties
|
|
19
19
|
|
20
20
|
from .map_data import RandImageData, ImageData
|
21
21
|
|
22
|
+
|
22
23
|
class RoomsHandler:
|
23
24
|
"""
|
24
25
|
Handler for extracting and managing room data from Hipfer vacuum maps.
|
@@ -225,6 +226,7 @@ class RoomsHandler:
|
|
225
226
|
LOGGER.debug("Room extraction Total time: %.3fs", total_time)
|
226
227
|
return room_properties
|
227
228
|
|
229
|
+
|
228
230
|
class RandRoomsHandler:
|
229
231
|
"""
|
230
232
|
Handler for extracting and managing room data from Rand25 vacuum maps.
|
@@ -247,7 +249,9 @@ class RandRoomsHandler:
|
|
247
249
|
"""
|
248
250
|
self.vacuum_id = vacuum_id
|
249
251
|
self.drawing_config = drawing_config
|
250
|
-
self.current_json_data =
|
252
|
+
self.current_json_data = (
|
253
|
+
None # Will store the current JSON data being processed
|
254
|
+
)
|
251
255
|
self.segment_data = None # Segment data
|
252
256
|
self.outlines = None # Outlines data
|
253
257
|
|
@@ -467,4 +471,4 @@ class RandRoomsHandler:
|
|
467
471
|
total_time = time.time() - start_total
|
468
472
|
LOGGER.debug("Room extraction Total time: %.3fs", total_time)
|
469
473
|
|
470
|
-
return room_properties
|
474
|
+
return room_properties
|
@@ -0,0 +1,27 @@
|
|
1
|
+
valetudo_map_parser/__init__.py,sha256=XO_eJwFDyU7hXJ4tAa2zY-n-SM2_kmIGMWDKY3GcauY,1163
|
2
|
+
valetudo_map_parser/config/__init__.py,sha256=DQ9plV3ZF_K25Dp5ZQHPDoG-40dQoJNdNi-dfNeR3Zc,48
|
3
|
+
valetudo_map_parser/config/auto_crop.py,sha256=1TGjUSbVHV15_sx1iFsEBWwD5BnWHPjgGIuPAJB6aiY,19142
|
4
|
+
valetudo_map_parser/config/color_utils.py,sha256=nXD6WeNmdFdoMxPDW-JFpjnxJSaZR1jX-ouNfrx6zvE,4502
|
5
|
+
valetudo_map_parser/config/colors.py,sha256=DG-oPQoN5gsnwDbEsuFr8a0hRCxmbFHObWa4_5pr-70,29910
|
6
|
+
valetudo_map_parser/config/drawable.py,sha256=2MeVHXqZuVuJk3eerMJYGwo25rVetHx3xB_vxecEFOQ,34168
|
7
|
+
valetudo_map_parser/config/drawable_elements.py,sha256=o-5oiXmfqPwNQLzKIhkEcZD_A47rIU9E0CqKgWipxgc,11516
|
8
|
+
valetudo_map_parser/config/enhanced_drawable.py,sha256=QlGxlUMVgECUXPtFwIslyjubWxQuhIixsRymWV3lEvk,12586
|
9
|
+
valetudo_map_parser/config/optimized_element_map.py,sha256=52BCnkvVv9bre52LeVIfT8nhnEIpc0TuWTv1xcNu0Rk,15744
|
10
|
+
valetudo_map_parser/config/rand256_parser.py,sha256=LU3y7XvRRQxVen9iwom0dOaDnJJvhZdg97NqOYRZFas,16279
|
11
|
+
valetudo_map_parser/config/rand25_parser.py,sha256=kIayyqVZBfQfAMkiArzqrrj9vqZB3pkgT0Y5ufrQmGA,16448
|
12
|
+
valetudo_map_parser/config/shared.py,sha256=ogXR5qC2HuLJ3KgYc9KDozXzDQptd8nhvNc-CepN6S0,12301
|
13
|
+
valetudo_map_parser/config/types.py,sha256=HO5fowAjbGmS2rEOFbFe7eFljvpfBr0fsvhcVRTByhI,17540
|
14
|
+
valetudo_map_parser/config/utils.py,sha256=w8r53KcCgYNq7CoIziQ3ktJu8ESFit5TYza-7g8ndhE,31203
|
15
|
+
valetudo_map_parser/hypfer_draw.py,sha256=Bi03FiYdxw2Kp8BwkggAkdKABNoNf-j7v7J-rER8tnQ,28953
|
16
|
+
valetudo_map_parser/hypfer_handler.py,sha256=-tMYd1lMH6_l8ufQH1A95uAgbt2P-1YYTfNkl9NXAc8,22269
|
17
|
+
valetudo_map_parser/hypfer_rooms_handler.py,sha256=NkpOA6Gdq-2D3lLAxvtNuuWMvPXHxeMY2TO5RZLSHlU,22652
|
18
|
+
valetudo_map_parser/map_data.py,sha256=5DDT5ABJCMSaWd8YrMY9SG4uhD3Rga_F0DEBNNNPipc,18618
|
19
|
+
valetudo_map_parser/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
20
|
+
valetudo_map_parser/rand256_handler.py,sha256=AY_x8iA4Lzl5m4LCJztahzwHU-fUPcyoJN9C_BeWST8,27894
|
21
|
+
valetudo_map_parser/reimg_draw.py,sha256=1q8LkNTPHEA9Tsapc_JnVw51kpPYNhaBU-KmHkefCQY,12507
|
22
|
+
valetudo_map_parser/rooms_handler.py,sha256=ovqQtAjauAqwUNPR0aX27P2zhheQmqfaFhDE3_AwYWk,17821
|
23
|
+
valetudo_map_parser-0.1.9b58.dist-info/LICENSE,sha256=Lh-qBbuRV0-jiCIBhfV7NgdwFxQFOXH3BKOzK865hRs,10480
|
24
|
+
valetudo_map_parser-0.1.9b58.dist-info/METADATA,sha256=XyH4mGr38kd2wMDzmm7kfKXTOgRr_MvVM8rIIxKKpDo,3321
|
25
|
+
valetudo_map_parser-0.1.9b58.dist-info/NOTICE.txt,sha256=5lTOuWiU9aiEnJ2go8sc7lTJ7ntMBx0g0GFnNrswCY4,2533
|
26
|
+
valetudo_map_parser-0.1.9b58.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
27
|
+
valetudo_map_parser-0.1.9b58.dist-info/RECORD,,
|
@@ -1,148 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Room Outline Extraction Utilities.
|
3
|
-
Uses scipy for efficient room outline extraction.
|
4
|
-
Version: 0.1.9
|
5
|
-
"""
|
6
|
-
|
7
|
-
from __future__ import annotations
|
8
|
-
|
9
|
-
import numpy as np
|
10
|
-
from scipy import ndimage
|
11
|
-
|
12
|
-
from .types import LOGGER
|
13
|
-
|
14
|
-
|
15
|
-
async def extract_room_outline_with_scipy(
|
16
|
-
room_mask, min_x, min_y, max_x, max_y, file_name=None, room_id=None
|
17
|
-
):
|
18
|
-
"""Extract a room outline using scipy for contour finding.
|
19
|
-
|
20
|
-
Args:
|
21
|
-
room_mask: Binary mask of the room (1 for room, 0 for non-room)
|
22
|
-
min_x, min_y, max_x, max_y: Bounding box coordinates
|
23
|
-
file_name: Optional file name for logging
|
24
|
-
room_id: Optional room ID for logging
|
25
|
-
|
26
|
-
Returns:
|
27
|
-
List of points forming the outline of the room
|
28
|
-
"""
|
29
|
-
# If the mask is empty, return a rectangular outline
|
30
|
-
if np.sum(room_mask) == 0:
|
31
|
-
LOGGER.warning(
|
32
|
-
"%s: Empty room mask for room %s, using rectangular outline",
|
33
|
-
file_name or "RoomOutline",
|
34
|
-
str(room_id) if room_id is not None else "unknown",
|
35
|
-
)
|
36
|
-
return [(min_x, min_y), (max_x, min_y), (max_x, max_y), (min_x, max_y)]
|
37
|
-
|
38
|
-
# Use scipy to clean up the mask (remove noise, fill holes)
|
39
|
-
# Fill small holes
|
40
|
-
room_mask = ndimage.binary_fill_holes(room_mask).astype(np.uint8)
|
41
|
-
|
42
|
-
# Remove small objects
|
43
|
-
labeled_array, num_features = ndimage.label(room_mask)
|
44
|
-
if num_features > 1:
|
45
|
-
# Find the largest connected component
|
46
|
-
component_sizes = np.bincount(labeled_array.ravel())[1:]
|
47
|
-
largest_component = np.argmax(component_sizes) + 1
|
48
|
-
room_mask = (labeled_array == largest_component).astype(np.uint8)
|
49
|
-
|
50
|
-
# Find the boundary points by tracing the perimeter
|
51
|
-
boundary_points = []
|
52
|
-
height, width = room_mask.shape
|
53
|
-
|
54
|
-
# Scan horizontally (top and bottom edges)
|
55
|
-
for x in range(width):
|
56
|
-
# Top edge
|
57
|
-
for y in range(height):
|
58
|
-
if room_mask[y, x] == 1:
|
59
|
-
boundary_points.append((x + min_x, y + min_y))
|
60
|
-
break
|
61
|
-
|
62
|
-
# Bottom edge
|
63
|
-
for y in range(height - 1, -1, -1):
|
64
|
-
if room_mask[y, x] == 1:
|
65
|
-
boundary_points.append((x + min_x, y + min_y))
|
66
|
-
break
|
67
|
-
|
68
|
-
# Scan vertically (left and right edges)
|
69
|
-
for y in range(height):
|
70
|
-
# Left edge
|
71
|
-
for x in range(width):
|
72
|
-
if room_mask[y, x] == 1:
|
73
|
-
boundary_points.append((x + min_x, y + min_y))
|
74
|
-
break
|
75
|
-
|
76
|
-
# Right edge
|
77
|
-
for x in range(width - 1, -1, -1):
|
78
|
-
if room_mask[y, x] == 1:
|
79
|
-
boundary_points.append((x + min_x, y + min_y))
|
80
|
-
break
|
81
|
-
|
82
|
-
# Remove duplicates while preserving order
|
83
|
-
unique_points = []
|
84
|
-
for point in boundary_points:
|
85
|
-
if point not in unique_points:
|
86
|
-
unique_points.append(point)
|
87
|
-
|
88
|
-
# If we have too few points, return a simple rectangle
|
89
|
-
if len(unique_points) < 4:
|
90
|
-
LOGGER.warning(
|
91
|
-
"%s: Too few boundary points for room %s, using rectangular outline",
|
92
|
-
file_name or "RoomOutline",
|
93
|
-
str(room_id) if room_id is not None else "unknown",
|
94
|
-
)
|
95
|
-
return [(min_x, min_y), (max_x, min_y), (max_x, max_y), (min_x, max_y)]
|
96
|
-
|
97
|
-
# Simplify the outline by keeping only significant points
|
98
|
-
simplified = simplify_outline(unique_points, tolerance=5)
|
99
|
-
|
100
|
-
LOGGER.debug(
|
101
|
-
"%s: Extracted outline for room %s with %d points",
|
102
|
-
file_name or "RoomOutline",
|
103
|
-
str(room_id) if room_id is not None else "unknown",
|
104
|
-
len(simplified),
|
105
|
-
)
|
106
|
-
|
107
|
-
return simplified
|
108
|
-
|
109
|
-
|
110
|
-
def simplify_outline(points, tolerance=5):
|
111
|
-
"""Simplify an outline by removing points that don't contribute much to the shape."""
|
112
|
-
if len(points) <= 4:
|
113
|
-
return points
|
114
|
-
|
115
|
-
# Start with the first point
|
116
|
-
simplified = [points[0]]
|
117
|
-
|
118
|
-
# Process remaining points
|
119
|
-
for i in range(1, len(points) - 1):
|
120
|
-
# Get previous and next points
|
121
|
-
prev = simplified[-1]
|
122
|
-
current = points[i]
|
123
|
-
next_point = points[i + 1]
|
124
|
-
|
125
|
-
# Calculate vectors
|
126
|
-
v1 = (current[0] - prev[0], current[1] - prev[1])
|
127
|
-
v2 = (next_point[0] - current[0], next_point[1] - current[1])
|
128
|
-
|
129
|
-
# Calculate change in direction
|
130
|
-
dot_product = v1[0] * v2[0] + v1[1] * v2[1]
|
131
|
-
len_v1 = (v1[0] ** 2 + v1[1] ** 2) ** 0.5
|
132
|
-
len_v2 = (v2[0] ** 2 + v2[1] ** 2) ** 0.5
|
133
|
-
|
134
|
-
# Avoid division by zero
|
135
|
-
if len_v1 == 0 or len_v2 == 0:
|
136
|
-
continue
|
137
|
-
|
138
|
-
# Calculate cosine of angle between vectors
|
139
|
-
cos_angle = dot_product / (len_v1 * len_v2)
|
140
|
-
|
141
|
-
# If angle is significant or distance is large, keep the point
|
142
|
-
if abs(cos_angle) < 0.95 or len_v1 > tolerance or len_v2 > tolerance:
|
143
|
-
simplified.append(current)
|
144
|
-
|
145
|
-
# Add the last point
|
146
|
-
simplified.append(points[-1])
|
147
|
-
|
148
|
-
return simplified
|
@@ -1,27 +0,0 @@
|
|
1
|
-
valetudo_map_parser/__init__.py,sha256=Fz-gtKf_OlZcDQqVfGlBwIWi5DJAiRucMbBMdQ2tX_U,1060
|
2
|
-
valetudo_map_parser/config/__init__.py,sha256=DQ9plV3ZF_K25Dp5ZQHPDoG-40dQoJNdNi-dfNeR3Zc,48
|
3
|
-
valetudo_map_parser/config/auto_crop.py,sha256=6xt_wJQqphddWhlrr7MNUkodCi8ZYdRk42qvAaxlYCM,13546
|
4
|
-
valetudo_map_parser/config/color_utils.py,sha256=nXD6WeNmdFdoMxPDW-JFpjnxJSaZR1jX-ouNfrx6zvE,4502
|
5
|
-
valetudo_map_parser/config/colors.py,sha256=DG-oPQoN5gsnwDbEsuFr8a0hRCxmbFHObWa4_5pr-70,29910
|
6
|
-
valetudo_map_parser/config/drawable.py,sha256=2MeVHXqZuVuJk3eerMJYGwo25rVetHx3xB_vxecEFOQ,34168
|
7
|
-
valetudo_map_parser/config/drawable_elements.py,sha256=o-5oiXmfqPwNQLzKIhkEcZD_A47rIU9E0CqKgWipxgc,11516
|
8
|
-
valetudo_map_parser/config/enhanced_drawable.py,sha256=QlGxlUMVgECUXPtFwIslyjubWxQuhIixsRymWV3lEvk,12586
|
9
|
-
valetudo_map_parser/config/optimized_element_map.py,sha256=52BCnkvVv9bre52LeVIfT8nhnEIpc0TuWTv1xcNu0Rk,15744
|
10
|
-
valetudo_map_parser/config/rand25_parser.py,sha256=kIayyqVZBfQfAMkiArzqrrj9vqZB3pkgT0Y5ufrQmGA,16448
|
11
|
-
valetudo_map_parser/config/room_outline.py,sha256=D20D-yeyKnlmVbW9lI7bsPtQGn2XkcWow6YNOEPnWVg,4800
|
12
|
-
valetudo_map_parser/config/shared.py,sha256=Vr4bicL7aJoRQbwbXyjEpiWhfzZ-cakLlfRqL3LBhpM,10475
|
13
|
-
valetudo_map_parser/config/types.py,sha256=TaRKoo7G7WIUw7ljOz2Vn5oYzKaLyQH-7Eb8ZYql8Ls,17464
|
14
|
-
valetudo_map_parser/config/utils.py,sha256=CFuuiS5IufEu9aeaZwi7xa1jEF1z6yDZB0mcyVX79Xo,29261
|
15
|
-
valetudo_map_parser/hypfer_draw.py,sha256=bwNTYopTJFY0nElrHquQrSfGHgN_-6t5E-8xBxDsRXA,26660
|
16
|
-
valetudo_map_parser/hypfer_handler.py,sha256=wvkZt6MsUF0gkHZDAwiUgOOawkdvakRzYBV3NtjxuJQ,19938
|
17
|
-
valetudo_map_parser/hypfer_rooms_handler.py,sha256=NkpOA6Gdq-2D3lLAxvtNuuWMvPXHxeMY2TO5RZLSHlU,22652
|
18
|
-
valetudo_map_parser/map_data.py,sha256=zQKE8EzWxR0r0qyfD1QQq51T1wFrpcIeXtnpm92-LXQ,17743
|
19
|
-
valetudo_map_parser/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
20
|
-
valetudo_map_parser/rand25_handler.py,sha256=GpY9R9EGWM06KYF4VQ1NxYw0idaJ8lZNkkA5wa0cr-c,22292
|
21
|
-
valetudo_map_parser/reimg_draw.py,sha256=1q8LkNTPHEA9Tsapc_JnVw51kpPYNhaBU-KmHkefCQY,12507
|
22
|
-
valetudo_map_parser/rooms_handler.py,sha256=YP8OLotBH-RXluv398l7TTT2zIBHJp91b8THWxl3NdI,17794
|
23
|
-
valetudo_map_parser-0.1.9b56.dist-info/LICENSE,sha256=Lh-qBbuRV0-jiCIBhfV7NgdwFxQFOXH3BKOzK865hRs,10480
|
24
|
-
valetudo_map_parser-0.1.9b56.dist-info/METADATA,sha256=LJXXb676sSJ9W7NGafpcJSF4CRGGhbg_lCyrzcxkge0,3321
|
25
|
-
valetudo_map_parser-0.1.9b56.dist-info/NOTICE.txt,sha256=5lTOuWiU9aiEnJ2go8sc7lTJ7ntMBx0g0GFnNrswCY4,2533
|
26
|
-
valetudo_map_parser-0.1.9b56.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
27
|
-
valetudo_map_parser-0.1.9b56.dist-info/RECORD,,
|
File without changes
|
{valetudo_map_parser-0.1.9b56.dist-info → valetudo_map_parser-0.1.9b58.dist-info}/NOTICE.txt
RENAMED
File without changes
|
File without changes
|