Neither Image nor Kernel implemented the plain NumPy __array__
protocol. Image already implements __array_ufunc__/__array_function__,
which cover ufuncs and the specific np.* functions it opts into, but
anything outside that -- SciPy functions, Matplotlib, or any code that
calls np.asarray() internally rather than going through those
protocols -- falls through to NumPy's default handling. Without
__array__, that default silently boxes the whole object into a
useless 0-d object-dtype array instead of exposing the real pixel/
kernel data:
np.asarray(kernel) # -> 0-d object array
np.linalg.matrix_rank(kernel) # -> silently wrong
scipy.signal.convolve2d(image, kernel) # -> ValueError
Root-caused via RVC3-python's chap11.ipynb, which hands a Kernel
directly to scipy.signal.convolve2d(), np.linalg.svd(), and
matplotlib's plot_surface() -- all broke the same way. idisp()/
plot_surface() additionally access .ndim directly (duck-typing, no
array coercion involved), so Kernel also gets a plain ndim property
(kernels are always 2D by the class's own constructor invariant).
Image's __array__ mirrors the existing .array property (read-only
view by default; dtype conversion or an explicit copy=True produce a
new, independent writeable array). This is purely additive -- ufunc
and array-function dispatch (already implemented) are tried first and
are unaffected; __array__ only kicks in as NumPy's fallback.
Added regression tests for both classes covering np.asarray(),
SciPy interop (convolve2d), dtype conversion, and the read-only-view
default -- all verified to fail against the pre-fix code with the
original error shapes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Summary
ImagenorKernelimplemented the plain NumPy__array__protocol.Imagealready implements__array_ufunc__/__array_function__(covers ufuncs and the specificnp.*functions it opts into), but anything outside that -- SciPy functions, Matplotlib, or any code callingnp.asarray()internally rather than going through those protocols -- falls through to NumPy's default handling, which silently boxes the whole object into a useless 0-d object-dtype array instead of exposing the real pixel/kernel data:Kerneldirectly toscipy.signal.convolve2d(),np.linalg.svd(), and Matplotlib'splot_surface()-- all broke the same way.idisp()/plot_surface()additionally access.ndimdirectly (duck-typing, no array coercion involved), soKernelalso gets a plainndimproperty (kernels are always 2D per the class's own constructor invariant).Image.__array__mirrors the existing.arrayproperty: read-only view by default; a dtype conversion or explicitcopy=Trueproduces a new, independent writeable array.Image) are tried first by NumPy and are unaffected;__array__only kicks in as NumPy's fallback for everything else.Test plan
np.asarray(), SciPy interop (convolve2d), dtype conversion, and the read-only-view defaultImagetests and bothKerneltests fail with the original error shapes (ValueError: convolve2d inputs must both be 2-D arrays,AttributeError: 'Kernel' object has no attribute 'ndim', etc.); pass with the fixtest_image_core.py,test_image_spatial.py,test_image_processing_kernel.py) pass: 119 passed