From f8d0fa38ffdbc82cf91db85641fd772128d844c8 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Wed, 19 Apr 2023 15:12:50 -0400 Subject: [PATCH] feat(Python): Add Pyodide PointSet support --- .../core/python/itkwasm/itkwasm/pyodide.py | 24 ++++++++++-- .../core/python/itkwasm/test/test_pointset.py | 4 +- .../core/python/itkwasm/test/test_pyodide.py | 37 ++++++++++++++++++- 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/packages/core/python/itkwasm/itkwasm/pyodide.py b/packages/core/python/itkwasm/itkwasm/pyodide.py index 05ffa8da2..fe8ec1c6e 100644 --- a/packages/core/python/itkwasm/itkwasm/pyodide.py +++ b/packages/core/python/itkwasm/itkwasm/pyodide.py @@ -64,12 +64,23 @@ def to_py(js_proxy): import pyodide if hasattr(js_proxy, "imageType"): image_dict = js_proxy.to_py() - image_type = image_dict['imageType'] - dimension = image_type['dimension'] - component_type = image_type['componentType'] + image_type = ImageType(**image_dict['imageType']) + image_dict['imageType'] = image_type + dimension = image_type.dimension + component_type = image_type.componentType image_dict['direction'] = _to_numpy_array(str(FloatTypes.Float64), image_dict['direction']).reshape((dimension, dimension)) image_dict['data'] = _to_numpy_array(component_type, image_dict['data']).reshape((dimension, dimension)) return Image(**image_dict) + elif hasattr(js_proxy, "pointSetType"): + point_set_dict = js_proxy.to_py() + point_set_type = PointSetType(**point_set_dict['pointSetType']) + point_set_dict['pointSetType'] = point_set_type + dimension = point_set_type.dimension + point_component_type = point_set_type.pointComponentType + point_pixel_component_type = point_set_type.pointPixelComponentType + point_set_dict['points'] = _to_numpy_array(point_component_type, point_set_dict['points']).reshape((-1, dimension)) + point_set_dict['pointData'] = _to_numpy_array(point_pixel_component_type, point_set_dict['pointData']) + return PointSet(**point_set_dict) return js_proxy.to_py() def to_js(py): @@ -77,7 +88,14 @@ def to_js(py): import js if isinstance(py, Image): image_dict = asdict(py) + print('to_js image dict', image_dict['imageType']) image_dict['direction'] = image_dict['direction'].ravel() image_dict['data'] = image_dict['data'].ravel() return pyodide.ffi.to_js(image_dict, dict_converter=js.Object.fromEntries) + elif isinstance(py, PointSet): + point_set_dict = asdict(py) + point_set_dict['points'] = point_set_dict['points'].ravel() + point_set_dict['pointData'] = point_set_dict['pointData'].ravel() + return pyodide.ffi.to_js(point_set_dict, dict_converter=js.Object.fromEntries) + return py diff --git a/packages/core/python/itkwasm/test/test_pointset.py b/packages/core/python/itkwasm/test/test_pointset.py index 39c5017ec..28fb691a2 100644 --- a/packages/core/python/itkwasm/test/test_pointset.py +++ b/packages/core/python/itkwasm/test/test_pointset.py @@ -16,7 +16,7 @@ def test_pointset(): point_data = np.random.random((n_points,)).astype(np.float32) pointset.SetPointData(itk.vector_container_from_array(point_data.ravel())) - + itk_pointset_dict = itk.dict_from_pointset(pointset) # Bug, to be fixed by 5.3.0 itk_pointset_dict.pop('dimension', None) @@ -24,7 +24,7 @@ def test_pointset(): itkwasm_pointset_dict = asdict(itkwasm_pointset) itk_pointset_roundtrip = itk.pointset_from_dict(itkwasm_pointset_dict) itk_pointset_roundtrip_dict = itk.dict_from_pointset(itk_pointset_roundtrip) - + pointSetType = itk_pointset_dict["pointSetType"] pointSetType_roundtrip = itk_pointset_roundtrip_dict["pointSetType"] assert pointSetType["dimension"] == pointSetType_roundtrip["dimension"] diff --git a/packages/core/python/itkwasm/test/test_pyodide.py b/packages/core/python/itkwasm/test/test_pyodide.py index a4a7ec793..72ebdf041 100644 --- a/packages/core/python/itkwasm/test/test_pyodide.py +++ b/packages/core/python/itkwasm/test/test_pyodide.py @@ -56,4 +56,39 @@ async def test_image_conversion(selenium, package_wheel): assert image_py.size[1] == 2 assert isinstance(image_py.metadata, dict) - assert np.array_equal(image_py.data, np.arange(4, dtype=np.uint8).reshape((2,2))) \ No newline at end of file + assert np.array_equal(image_py.data, np.arange(4, dtype=np.uint8).reshape((2,2))) + +@run_in_pyodide(packages=['micropip', 'numpy']) +async def test_point_set_conversion(selenium, package_wheel): + import micropip + await micropip.install(package_wheel) + + from itkwasm import PointSet, PointSetType, PixelTypes, FloatTypes + from itkwasm.pyodide import to_js, to_py + import numpy as np + + n_points = 5 + dimension = 3 + + points = np.random.random((n_points, dimension)).astype(np.float32) + point_data = np.random.random((n_points,)).astype(np.float32) + + point_set_type = PointSetType(dimension, FloatTypes.Float32, FloatTypes.Float32, PixelTypes.Scalar, FloatTypes.Float32) + + point_set = PointSet(point_set_type, 'point_set', n_points, points, n_points, point_data) + + point_set_js = to_js(point_set) + point_set_py = to_py(point_set_js) + + point_set_type_py = point_set_py.pointSetType + assert point_set_type.dimension == point_set_type_py.dimension + assert point_set_type.pointComponentType == point_set_type_py.pointComponentType + assert point_set_type.pointPixelComponentType == point_set_type_py.pointPixelComponentType + assert point_set_type.pointPixelType == point_set_type_py.pointPixelType + assert point_set_type.pointPixelComponents == point_set_type_py.pointPixelComponents + + assert point_set.name == point_set_py.name + assert point_set.numberOfPoints == point_set_py.numberOfPoints + assert np.array_equal(point_set.points, point_set_py.points) + assert point_set.numberOfPointPixels == point_set_py.numberOfPointPixels + assert np.array_equal(point_set.pointData, point_set_py.pointData) \ No newline at end of file