Skip to content

Conversation

@sca075
Copy link
Owner

@sca075 sca075 commented Oct 8, 2025

Remove possible leak to keep the images in a baseline of around 250mb per frame.

Summary by CodeRabbit

  • New Features

    • More accurate image sizing that respects rotation.
    • Broader compatibility for destination timestamps (accepts integer or float).
  • Refactor

    • Optimized memory usage across image processing for smoother rendering and lower peak RAM.
    • Streamlined drawing pipeline; removed unused optimized/batch paths without changing visuals.
  • Bug Fixes

    • Improved handling of out-of-bounds pixels and alpha blending for consistent rendering.
  • Documentation

    • Added comprehensive docstrings to parsing helpers for better maintainability.
  • Chores

    • Pinned mvcrender dependency to 0.0.6 for stability.

sca075 and others added 15 commits October 2, 2025 18:18
…ean tested for rand.

Signed-off-by: Sandro Cantarella <sandro@Sandros-Mac-mini.fritz.box>
Signed-off-by: Sandro Cantarella <sandro@Sandros-Mac-mini.fritz.box>
Signed-off-by: Sandro Cantarella <sandro@Sandros-Mac-mini.fritz.box>
…unction with C based from mcvrender.

Signed-off-by: Sandro Cantarella <sandro@509dc541-f930-4e46-87da-4482e9d9a7bb.fritz.box>
Signed-off-by: Sandro Cantarella <sandro@13de464c-e6f5-4665-b8d7-d6acf4207b24.fritz.box>
Signed-off-by: Sandro Cantarella <sandro@13de464c-e6f5-4665-b8d7-d6acf4207b24.fritz.box>
Signed-off-by: Sandro Cantarella <sandro@Sandros-Mac-mini.fritz.box>
Signed-off-by: Sandro Cantarella <sandro@Sandros-Mac-mini.fritz.box>
Signed-off-by: Sandro Cantarella <sandro@03829bc9-740a-4ae1-9848-e7d8baa7c8ba.fritz.box>
Signed-off-by: Sandro Cantarella <sandro@e12ecfb7-0503-4719-87b7-0b014abdffa8.fritz.box>
@sca075 sca075 self-assigned this Oct 8, 2025
@sca075 sca075 added the bug Something isn't working label Oct 8, 2025
@coderabbitai
Copy link

coderabbitai bot commented Oct 8, 2025

Walkthrough

Removes optimized/batch drawing helpers and simplifies alpha handling in drawable. Adds docstrings in the rand256 parser. Introduces pil_size_rotation and adjusts size computation, plus memory management improvements and persistent working buffers in handlers. Expands a type union, reduces logging, pins mvcrender to 0.0.6, and applies minor test refactors.

Changes

Cohort / File(s) Summary
Drawable API simplification
SCR/valetudo_map_parser/config/drawable.py, tests/refactored.py
Removed _filled_circle_optimized, draw_filled_circle, and batch_draw_elements from Drawable; simplified alpha handling in from_json_to_image. Tests: signature/binding changes (removed @staticmethod on draw_filled_circle and async_draw_obstacles) and formatting.
Parser docstrings
SCR/valetudo_map_parser/config/rand256_parser.py
Added docstrings to multiple private parse helpers and module/class docstrings; no logic changes.
Image size/rotation utils
SCR/valetudo_map_parser/config/utils.py, SCR/valetudo_map_parser/config/shared.py
Added pil_size_rotation(image_rotate, pil_img) and used it in CameraShared.to_dict; centralized size calc respecting rotation; pruned debug logs; added defensive image closing before replacement.
Handlers memory management & flow
SCR/valetudo_map_parser/rand256_handler.py, SCR/valetudo_map_parser/hypfer_handler.py, SCR/valetudo_map_parser/rooms_handler.py
Introduced/reused persistent img_work_layer; ensured buffer shape/dtype; explicit deletions of old buffers/temps; adjusted destinations typing and propagation; freed intermediates in room outline computation. No external API changes except typed params.
Types alignment
SCR/valetudo_map_parser/config/types.py
Broadened Destinations.updated to `Optional[float
Minor logging cleanup
SCR/valetudo_map_parser/hypfer_draw.py
Removed a debug log in async_draw_zones.
Dependency pin
pyproject.toml
Pinned mvcrender to ==0.0.6 (was >=0.0.5).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Caller
  participant RH as Rand256Handler
  participant U as Utils
  participant D as Drawable
  participant BL as img_base_layer (np)
  participant WL as img_work_layer (np)
  participant PIL as PIL.Image

  C->>RH: get_image_from_rrm(m_json, destinations?)
  RH->>RH: if BL exists and outdated -> delete BL
  RH->>RH: ensure BL from JSON (allocate/copy)
  Note right of RH: Old intermediates deleted to free memory
  RH->>RH: if WL absent/mismatch -> allocate WL (shape/dtype of BL)
  RH->>WL: copy BL into WL
  RH->>D: draw elements onto WL (async/iterative)
  D-->>RH: WL with drawings
  RH->>U: convert WL to PIL (as needed)
  U-->>RH: PIL image
  RH-->>C: PIL
  Note over WL: WL persists across frames (reused)
Loading
sequenceDiagram
  autonumber
  participant CS as CameraShared
  participant U as Utils
  participant P as PIL.Image

  CS->>CS: to_dict()
  alt new_image present
    CS->>U: pil_size_rotation(image_rotate, new_image)
    U-->>CS: (width, height)
    CS-->>CS: size = returned tuple
  else
    CS-->>CS: size = (10, 10)
  end
  CS-->>CS: build dict with size respecting rotation
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

A rabbit taps keys with a soft little grin,
Buffers persist, and the pixels begin.
Circles once sprinted; now calmly they draw,
Rotation-wise sizes obey every law.
Memory freed like clouds on the run—
Pinned deps, tidy logs—rendering’s fun! 🥕✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “Memory optimize.” directly reflects the pull request’s main focus on improving memory management and preventing leaks during image processing, aligning with the stated objective of keeping frame memory usage around 250 MB.
Docstring Coverage ✅ Passed Docstring coverage is 96.88% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch memory_leaks

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
SCR/valetudo_map_parser/config/rand256_parser.py (1)

277-279: Use block_header_length (int16) for block advancement, not 8-bit header field

Increment currently adds _get_int8(header, 2). Since block_header_length was already parsed as int16, advance by block_header_length for consistency and to avoid mis-parsing.

Apply this diff:

-                block_start_position = (
-                    block_start_position + block_data_length + self._get_int8(header, 2)
-                )
+                block_start_position = (
+                    block_start_position + block_header_length + block_data_length
+                )
SCR/valetudo_map_parser/config/utils.py (1)

188-198: Fix return shape on exception path (must return tuple)

async_get_image is annotated to return Tuple[PilPNG | bytes, dict], but the exception path returns a single value. Return (image_or_bytes, {}) to avoid runtime/type issues.

Apply this diff:

-            return (
-                self.shared.last_image if hasattr(self.shared, "last_image") else None
-            )
+            img = self.shared.last_image if hasattr(self.shared, "last_image") else None
+            return img, {}
SCR/valetudo_map_parser/rand256_handler.py (1)

396-405: Correct return annotation/signature for get_rooms_attributes

The method returns a single RoomsProperties (or None), not a tuple. Fixing the annotation avoids confusion.

Apply this diff:

-    async def get_rooms_attributes(
-        self, destinations: JsonType = None
-    ) -> tuple[RoomsProperties, Any, Any]:
+    async def get_rooms_attributes(
+        self, destinations: Destinations | None = None
+    ) -> RoomsProperties | None:
         """Return the rooms attributes."""
🧹 Nitpick comments (6)
SCR/valetudo_map_parser/rooms_handler.py (1)

164-168: Good memory cleanup; consider boolean masks for further reduction

Explicitly deleting intermediates is helpful. You can cut mask memory further by:

  • Using dtype=bool for local_mask/struct_elem and keeping morph results as bool until hull extraction.
  • Cast to uint8 only if a downstream consumer truly needs it.

Example:

- local_mask = np.zeros((local_height, local_width), dtype=np.uint8)
+ local_mask = np.zeros((local_height, local_width), dtype=bool)
...
- struct_elem = np.ones((3, 3), dtype=np.uint8)
+ struct_elem = np.ones((3, 3), dtype=bool)
- eroded = binary_erosion(local_mask, structure=struct_elem, iterations=1)
- mask = binary_dilation(eroded, structure=struct_elem, iterations=1).astype(np.uint8)
+ eroded = binary_erosion(local_mask, structure=struct_elem, iterations=1)
+ mask = binary_dilation(eroded, structure=struct_elem, iterations=1)

convex_hull_outline works with a boolean mask (via np.where(mask)). Convert to uint8 only if needed later.

Also applies to: 172-174

SCR/valetudo_map_parser/config/drawable.py (1)

56-56: Simplified alpha path looks good; minor quality tweak optional

The center-sample-and-fill approach is efficient. If you want slightly better edge fidelity for large pixel_size, consider sampling multiple points (e.g., average of 4 corners) before filling; keeps complexity low while improving gradients.

Otherwise LGTM.

Also applies to: 68-80

tests/refactored.py (2)

575-581: Incorrect type hint for centers

centers is iterated as a sequence of points and docs say (N, 2) array. Update hint to reflect multiple centers.

- def draw_filled_circle(image: np.ndarray, centers: Tuple[int, int], ...
+ from typing import Iterable, Sequence, Tuple
+ def draw_filled_circle(image: np.ndarray, centers: Sequence[Tuple[int, int]] | np.ndarray, ...

610-645: Avoid executor overhead for small, CPU-light preprocessing

Building the centers array is trivial; calling run_in_executor adds unnecessary overhead. Inline the comprehension:

- centers = await asyncio.get_running_loop().run_in_executor(
-     None, extract_centers, obstacle_info_list
- )
- Drawable.draw_filled_circle(image, centers, 6, color)
+ centers = np.array([[obs["points"]["x"], obs["points"]["y"]] for obs in obstacle_info_list], dtype=np.int32)
+ Drawable.draw_filled_circle(image, centers, 6, color)
SCR/valetudo_map_parser/config/utils.py (1)

698-707: Add type hints to pil_size_rotation for clarity

Small polish: annotate parameters and return type.

Apply this diff:

-def pil_size_rotation(image_rotate, pil_img):
-    """Return the size of the image."""
+def pil_size_rotation(image_rotate: int, pil_img: PilPNG | None) -> tuple[int, int]:
+    """Return (width, height) adjusted for 90/270 rotation; (0, 0) if pil_img is None."""
     if not pil_img:
         return 0, 0
     if image_rotate in [0, 180]:
         width, height = pil_img.size
     else:
         height, width = pil_img.size
     return width, height
SCR/valetudo_map_parser/rand256_handler.py (1)

389-394: Clarify log message when no resize is needed

This branch executes when resizing isn’t required; “Invalid image dimensions” is misleading. Consider logging a low-level debug or nothing.

-            LOGGER.warning(
-                "%s: Invalid image dimensions. Returning original image.",
-                self.file_name,
-            )
+            # No aspect ratio change requested; return original image
+            # LOGGER.debug("%s: No resizing applied.", self.file_name)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6d4cea3 and c88110d.

📒 Files selected for processing (11)
  • SCR/valetudo_map_parser/config/drawable.py (1 hunks)
  • SCR/valetudo_map_parser/config/rand256_parser.py (6 hunks)
  • SCR/valetudo_map_parser/config/shared.py (2 hunks)
  • SCR/valetudo_map_parser/config/types.py (1 hunks)
  • SCR/valetudo_map_parser/config/utils.py (3 hunks)
  • SCR/valetudo_map_parser/hypfer_draw.py (0 hunks)
  • SCR/valetudo_map_parser/hypfer_handler.py (2 hunks)
  • SCR/valetudo_map_parser/rand256_handler.py (8 hunks)
  • SCR/valetudo_map_parser/rooms_handler.py (1 hunks)
  • pyproject.toml (1 hunks)
  • tests/refactored.py (5 hunks)
💤 Files with no reviewable changes (1)
  • SCR/valetudo_map_parser/hypfer_draw.py
🧰 Additional context used
🧬 Code graph analysis (5)
SCR/valetudo_map_parser/hypfer_handler.py (1)
SCR/valetudo_map_parser/config/utils.py (1)
  • async_copy_array (477-479)
SCR/valetudo_map_parser/rand256_handler.py (4)
SCR/valetudo_map_parser/config/types.py (1)
  • Destinations (37-41)
SCR/valetudo_map_parser/config/async_utils.py (2)
  • AsyncPIL (38-66)
  • async_fromarray (42-44)
SCR/valetudo_map_parser/hypfer_draw.py (1)
  • async_get_robot_in_room (379-542)
SCR/valetudo_map_parser/config/utils.py (1)
  • async_copy_array (477-479)
SCR/valetudo_map_parser/config/shared.py (1)
SCR/valetudo_map_parser/config/utils.py (1)
  • pil_size_rotation (698-706)
SCR/valetudo_map_parser/config/drawable.py (1)
SCR/valetudo_map_parser/config/colors.py (1)
  • sample_and_blend_color (457-519)
tests/refactored.py (2)
SCR/valetudo_map_parser/hypfer_draw.py (1)
  • ImageDraw (19-573)
SCR/valetudo_map_parser/config/drawable.py (1)
  • async_draw_obstacles (548-604)
🔇 Additional comments (11)
SCR/valetudo_map_parser/config/types.py (1)

41-41: LGTM: broader Destinations.updated type

Allowing int in addition to float is compatible and practical.

pyproject.toml (1)

21-21: Pinning to non-existent mvcrender==0.0.6 will break installs and demands Python ≥3.13

PyPI’s latest release is 0.0.5 (which itself requires Python ≥3.13); 0.0.6 isn’t published. Either pin to mvcrender ==0.0.5 (if you target ≥3.13) or use a range like >=0.0.5,<0.1.0. Manually confirm that v0.0.5 actually exports the needed symbols in mvcrender.blend (get_blended_color, sample_and_blend_color) and mvcrender.draw (circle_u8, line_u8).

SCR/valetudo_map_parser/config/shared.py (1)

219-220: Rotated size computation looks good; verify zero-size semantics

Using pil_size_rotation is correct and rotation-safe. It returns (0, 0) when pil_image is None; confirm consumers tolerate 0,0 (if they previously assumed non-zero fallback).

SCR/valetudo_map_parser/hypfer_handler.py (2)

257-263: Proactive base-layer cleanup reduces peak memory

Deleting the previous base layer before assigning the new copy is a good move to keep memory bounded.


271-280: Persistent working buffer reuse is correct

Shape/dtype-guarded reallocation + np.copyto avoids per-frame allocations. This should lower memory churn.

SCR/valetudo_map_parser/config/utils.py (2)

115-121: Good: releasing previous last_image before rollover

Closing the old last_image before reassigning helps cap memory. Safe and aligned with the fallback logic.


240-250: prepare_resize_params: rotation-aware sizing is correct

Using pil_size_rotation here makes downstream resizing consistent with rotation.

SCR/valetudo_map_parser/rand256_handler.py (4)

62-62: Persistent working buffer field is a good addition

Keeping img_work_layer across frames avoids repeated large allocations.


163-177: Buffer reuse block looks correct

Shape/dtype checks + np.copyto into np.empty_like(self.img_base_layer) are appropriate to reduce allocs.


246-265: Base/source array cleanup before/after copy is appropriate

Deleting the previous base and the transient source array will reduce peak memory. Order is safe.


22-25: Verify mvcrender version pin
The project pins mvcrender==0.0.6 in pyproject.toml but only 0.0.5 is published on PyPI. Confirm that 0.0.6 exists before release—or adjust the pin to a published version.

@sca075 sca075 merged commit cae586f into main Oct 14, 2025
5 checks passed
@sca075 sca075 deleted the memory_leaks branch October 18, 2025 07:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants