From 4bfe81c8f028e392491639595abdecd33db63bce Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Wed, 21 Oct 2026 15:52:59 +1000 Subject: [PATCH 1/3] docs: fix broken runblock examples; showwindow defaults to current axes sphinx-pyrunblock prints "!! [RUNBLOCK-ERROR]" for an example that raises but the build still succeeds, so these tracebacks were being published. Fix the ones that are example or signature mistakes: - Blobs.vc: blobs.v -> blobs.vc - Image.array_as: array_int() -> array_as() - Image.A: build a new Image instead of assigning to the now read-only .A - Image.fiducial: tags.png is not shipped; use apriltag12.png with '36h11' - Image.showwindow: ax now defaults to None (the current axes), as the Window class and the documented examples already assume; fix the runblock, drop a duplicate .. plot:: block that used an undefined `window`, and correct Window.move's return annotation - ImageTensor.Tensor and tensor.rst: PyTorch is optional and not installed in the docs build, so make these non-executed code blocks Adds tests/test_image_showwindow.py (previously untested). Refs #159. Image.draw_point still fails on OpenCV 5 (#158), so the build still reports 2 RUNBLOCK-ERROR lines (was 19) and pycon_fail_on_error is not enabled here. Co-Authored-By: Claude Sonnet 5.5 --- docs/source/tensor.rst | 4 +-- src/machinevisiontoolbox/ImageBlobs.py | 2 +- src/machinevisiontoolbox/ImageCore.py | 6 ++-- src/machinevisiontoolbox/ImageFiducials.py | 4 +-- src/machinevisiontoolbox/ImageIO.py | 18 +++------- src/machinevisiontoolbox/ImageTensor.py | 2 +- tests/test_image_showwindow.py | 41 ++++++++++++++++++++++ 7 files changed, 55 insertions(+), 22 deletions(-) create mode 100644 tests/test_image_showwindow.py diff --git a/docs/source/tensor.rst b/docs/source/tensor.rst index 878040f3..31693022 100644 --- a/docs/source/tensor.rst +++ b/docs/source/tensor.rst @@ -24,7 +24,7 @@ Image → tensor Use the :meth:`tensor` method to convert an :class:`Image` to a tensor. For example: -.. runblock:: pycon +.. code-block:: pycon >>> from machinevisiontoolbox import Image >>> im = Image.Read("eiffel-1.png") @@ -41,7 +41,7 @@ tensor → Image Use the constructor :meth:`Tensor` method to create an :class:`Image` from a tensor. For example: -.. runblock:: pycon +.. code-block:: pycon >>> from machinevisiontoolbox import Image >>> from torch import rand diff --git a/src/machinevisiontoolbox/ImageBlobs.py b/src/machinevisiontoolbox/ImageBlobs.py index afcb90ae..29932b2a 100644 --- a/src/machinevisiontoolbox/ImageBlobs.py +++ b/src/machinevisiontoolbox/ImageBlobs.py @@ -819,7 +819,7 @@ def vc(self) -> Any: >>> im = Image.Read('shark2.png') >>> blobs = im.blobs() >>> blobs[0].vc - >>> blobs.v + >>> blobs.vc :seealso: :meth:`u` :meth:`centroid` """ diff --git a/src/machinevisiontoolbox/ImageCore.py b/src/machinevisiontoolbox/ImageCore.py index bb9fca25..febfaa6a 100644 --- a/src/machinevisiontoolbox/ImageCore.py +++ b/src/machinevisiontoolbox/ImageCore.py @@ -2138,7 +2138,7 @@ def A(self) -> np.ndarray: >>> img >>> type(img) >>> type(img.A) - >>> img.A = np.zeros((50,50)) + >>> img = Image(np.zeros((50,50))) # new image with a different array >>> img .. deprecated:: 2.0.0 @@ -2343,8 +2343,8 @@ def array_as(self, dtype: Dtype | None = None) -> np.ndarray: >>> img.array_as('uint8') >>> img = Image([[False, True], [True, False]]) >>> img - >>> img.array_int('uint8') - >>> img.array_int('float') + >>> img.array_as('uint8') + >>> img.array_as('float') .. note:: Works for greyscale or color (arbitrary number of planes) image diff --git a/src/machinevisiontoolbox/ImageFiducials.py b/src/machinevisiontoolbox/ImageFiducials.py index 0c94065b..26288302 100644 --- a/src/machinevisiontoolbox/ImageFiducials.py +++ b/src/machinevisiontoolbox/ImageFiducials.py @@ -112,8 +112,8 @@ def fiducial( .. runblock:: pycon >>> from machinevisiontoolbox import Image - >>> img = Image.Read('tags.png') - >>> fiducials = img.fiducial('5x5_50') + >>> img = Image.Read('apriltag12.png') + >>> fiducials = img.fiducial('36h11') >>> fiducials >>> fiducials[0].corners diff --git a/src/machinevisiontoolbox/ImageIO.py b/src/machinevisiontoolbox/ImageIO.py index f39b8f09..4b7aeff0 100644 --- a/src/machinevisiontoolbox/ImageIO.py +++ b/src/machinevisiontoolbox/ImageIO.py @@ -450,13 +450,14 @@ def showpixels( plt.draw() return ax - def showwindow(self, h, ax, **kwargs: Any) -> Any: + def showwindow(self, h, ax=None, **kwargs: Any) -> Any: """Superimpose a colored window on the image. :param h: The half-width of the window, so the total window size is (2h+1)x(2h+1) :type h: int - :param ax: The axes on which to display the window + :param ax: The axes on which to display the window, defaults to the + current axes :return: The window instance :rtype: Any @@ -495,19 +496,10 @@ def showwindow(self, h, ax, **kwargs: Any) -> Any: >>> from machinevisiontoolbox import Image >>> img = Image.Random(size=10) >>> img.showpixels() - >>> img.showwindow(h=1) # with 3x3 window + >>> window = img.showwindow(h=1) # with 3x3 window >>> W = window.move(2,3) # position window at (2,3) >>> print(W) # print the pixel values in the window - .. plot:: - - from machinevisiontoolbox import Image - img = Image.Random(size=10) - img.showpixels() - img.showwindow(h=1) # with 3x3 window - W = window.move(2,3) # position window at (2,3) - - .. plot:: from machinevisiontoolbox import Image @@ -545,7 +537,7 @@ def move( v: int | float, wincolor: Any = None, winalpha: float = 0.9, - ) -> None: + ) -> np.ndarray | None: self.patch.set_x(u - self.h - 0.5) self.patch.set_y(v - self.h - 0.5) diff --git a/src/machinevisiontoolbox/ImageTensor.py b/src/machinevisiontoolbox/ImageTensor.py index c50fcf67..683b4430 100644 --- a/src/machinevisiontoolbox/ImageTensor.py +++ b/src/machinevisiontoolbox/ImageTensor.py @@ -166,7 +166,7 @@ def Tensor( For batches where ``B > 1`` use :class:`TensorStack` which creates an image iterator for the batch. - .. runblock:: pycon + .. code-block:: pycon >>> from machinevisiontoolbox import Image >>> import torch diff --git a/tests/test_image_showwindow.py b/tests/test_image_showwindow.py new file mode 100644 index 00000000..505d5d08 --- /dev/null +++ b/tests/test_image_showwindow.py @@ -0,0 +1,41 @@ +"""Image.showwindow: the axes default to the current axes, as documented.""" + +import matplotlib.pyplot as plt +import numpy as np +import pytest + +from machinevisiontoolbox import Image + + +@pytest.fixture +def img() -> Image: + return Image(np.arange(100, dtype=np.uint8).reshape(10, 10)) + + +def test_showwindow_defaults_to_current_axes(img: Image) -> None: + fig, ax = plt.subplots() + try: + window = img.showwindow(h=1) + assert window.patch in ax.patches + finally: + plt.close(fig) + + +def test_showwindow_explicit_axes(img: Image) -> None: + fig, ax = plt.subplots() + try: + window = img.showwindow(h=1, ax=ax) + assert window.patch in ax.patches + finally: + plt.close(fig) + + +def test_window_move_returns_pixels(img: Image) -> None: + fig, _ = plt.subplots() + try: + window = img.showwindow(h=1) + pixels = window.move(2, 3) # centred on u=2, v=3 + assert pixels.shape == (3, 3) + np.testing.assert_array_equal(pixels, img.array[2:5, 1:4]) + finally: + plt.close(fig) From 4260759b0441b9cb3cabbed03148572149afa720 Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Wed, 21 Oct 2026 16:42:31 +1000 Subject: [PATCH 2/3] docs: fix Blobs.plot_labelbox plot example (statements were on one line) The .. plot:: block had 'im = Image.Read(...) im.disp() blobs = im.blobs()' on a single line, a SyntaxError that the plot directive reports only as a warning, so the example image was missing from the docs. Refs #159. Co-Authored-By: Claude Sonnet 5.5 --- src/machinevisiontoolbox/ImageBlobs.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/machinevisiontoolbox/ImageBlobs.py b/src/machinevisiontoolbox/ImageBlobs.py index 29932b2a..6c718701 100644 --- a/src/machinevisiontoolbox/ImageBlobs.py +++ b/src/machinevisiontoolbox/ImageBlobs.py @@ -1883,7 +1883,9 @@ def plot_labelbox(self, fmt=None, label: str = "{:d}", **kwargs: Any) -> None: from machinevisiontoolbox import Image - im = Image.Read("sharks.png") im.disp() blobs = im.blobs() + im = Image.Read("sharks.png") + im.disp() + blobs = im.blobs() blobs[:3].plot_labelbox(color="yellow") blobs[3].plot_labelbox(color="lightblue", linewidth=2, label="3") From cd5eba3b1b344ae0eedc54079b375a908e273809 Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Wed, 21 Oct 2026 17:06:12 +1000 Subject: [PATCH 3/3] fix: remove leftover debugging print from Blobs.plot_labelbox It printed the styling options dict to stdout on every call. Refs #159. Co-Authored-By: Claude Sonnet 5.5 --- src/machinevisiontoolbox/ImageBlobs.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/machinevisiontoolbox/ImageBlobs.py b/src/machinevisiontoolbox/ImageBlobs.py index 6c718701..93c32345 100644 --- a/src/machinevisiontoolbox/ImageBlobs.py +++ b/src/machinevisiontoolbox/ImageBlobs.py @@ -1893,7 +1893,6 @@ def plot_labelbox(self, fmt=None, label: str = "{:d}", **kwargs: Any) -> None: :func:`~machinevisiontoolbox.base.graphics.plot_labelbox` """ options = mpl_styling(fmt, dict(color="yellow", linewidth=2), kwargs) - print(options) for blob in self: plot_labelbox( text=label.format(blob.id), fmt=fmt, lrbt=blob.bbox, **options