Skip to content

Commit

Permalink
feat(Python): Add mesh pipeline support
Browse files Browse the repository at this point in the history
  • Loading branch information
thewtex committed Feb 7, 2023
1 parent 4adffc1 commit 832565f
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/python-wasm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
max-parallel: 3
matrix:
os: [ubuntu-22.04, windows-2022, macos-12]
python-version: ['3.7', '3.8', '3.9', '3.10']
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']

steps:
- uses: actions/checkout@v3
Expand Down
64 changes: 62 additions & 2 deletions src/python/itkwasm/itkwasm/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ def run(self, args: List[str], outputs: List[PipelineOutput]=[], inputs: List[Pi
elif input_.type == InterfaceTypes.Image:
image = input_.data
mv = bytes(image.data.data)
# self.memory.grow(15)
data_ptr = self._set_input_array(mv, index, 0)
dv = bytes(image.direction.data)
direction_ptr = self._set_input_array(dv, index, 1)
Expand All @@ -126,6 +125,34 @@ def run(self, args: List[str], outputs: List[PipelineOutput]=[], inputs: List[Pi
"data": f"data:application/vnd.itk.address,0:{data_ptr}"
}
self._set_input_json(image_json, index)
elif input_.type == InterfaceTypes.Mesh:
mesh = input_.data
pv = bytes(mesh.points)
points_ptr = self._set_input_array(pv, index, 0)
cv = bytes(mesh.cells)
cells_ptr = self._set_input_array(cv, index, 1)
pdv = bytes(mesh.pointData)
point_data_ptr = self._set_input_array(pdv, index, 2)
cdv = bytes(mesh.cellData)
cell_data_ptr = self._set_input_array(cdv, index, 3)
mesh_json = {
"meshType": asdict(mesh.meshType),
"name": mesh.name,

"numberOfPoints": mesh.numberOfPoints,
"points": f"data:application/vnd.itk.address,0:{points_ptr}",

"numberOfCells": mesh.numberOfCells,
"cells": f"data:application/vnd.itk.address,0:{cells_ptr}",
"cellBufferSize": mesh.cellBufferSize,

"numberOfPointPixels": mesh.numberOfPointPixels,
"pointData": f"data:application/vnd.itk.address,0:{point_data_ptr}",

"numberOfCellPixels": mesh.numberOfCellPixels,
"cellData": f"data:application/vnd.itk.address,0:{cell_data_ptr}",
}
self._set_input_json(mesh_json, index)
else:
raise ValueError(f'Unexpected/not yet supported input.type {input_.type}')

Expand Down Expand Up @@ -154,7 +181,6 @@ def run(self, args: List[str], outputs: List[PipelineOutput]=[], inputs: List[Pi
image_json = self._get_output_json(index)

image = Image(**image_json)
image.name = 'aoeu'

data_ptr = self.output_array_address(0, index, 0)
data_size = self.output_array_size(0, index, 0)
Expand All @@ -169,6 +195,40 @@ def run(self, args: List[str], outputs: List[PipelineOutput]=[], inputs: List[Pi
image.direction = direction_array

output_data = PipelineOutput(InterfaceTypes.Image, image)
elif output.type == InterfaceTypes.Mesh:
mesh_json = self._get_output_json(index)

mesh = Mesh(**mesh_json)

if mesh.numberOfPoints > 0:
data_ptr = self.output_array_address(0, index, 0)
data_size = self.output_array_size(0, index, 0)
mesh.points = _memoryview_to_numpy_array(mesh.meshType.pointComponentType, memoryview(self.memory.buffer)[data_ptr:data_ptr+data_size])
else:
mesh.points = _memoryview_to_numpy_array(mesh.meshType.pointComponentType, bytes([]))

if mesh.numberOfCells > 0:
data_ptr = self.output_array_address(0, index, 1)
data_size = self.output_array_size(0, index, 1)
mesh.cells = _memoryview_to_numpy_array(mesh.meshType.cellComponentType, memoryview(self.memory.buffer)[data_ptr:data_ptr+data_size])
else:
mesh.cells = _memoryview_to_numpy_array(mesh.meshType.cellComponentType, bytes([]))
if mesh.numberOfPointPixels > 0:
data_ptr = self.output_array_address(0, index, 2)
data_size = self.output_array_size(0, index, 2)
mesh.pointData = _memoryview_to_numpy_array(mesh.meshType.pointPixelComponentType, memoryview(self.memory.buffer)[data_ptr:data_ptr+data_size])
else:
mesh.pointData = _memoryview_to_numpy_array(mesh.meshType.pointPixelComponentType, bytes([]))

if mesh.numberOfCellPixels > 0:
data_ptr = self.output_array_address(0, index, 3)
data_size = self.output_array_size(0, index, 3)
mesh.cellData = _memoryview_to_numpy_array(mesh.meshType.cellPixelComponentType, memoryview(self.memory.buffer)[data_ptr:data_ptr+data_size])
else:
mesh.cellData = _memoryview_to_numpy_array(mesh.meshType.cellPixelComponentType, bytes([]))

output_data = PipelineOutput(InterfaceTypes.Mesh, mesh)

populated_outputs.append(output_data)

delayed_exit = instance.exports.itk_wasm_delayed_exit
Expand Down
3 changes: 2 additions & 1 deletion src/python/itkwasm/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ classifiers = [
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
]
keywords = [
"itk",
Expand All @@ -42,7 +43,7 @@ Issues = "https://github.com/InsightSoftwareConsortium/itk-wasm/issues"

[project.optional-dependencies]
test = [
"itk>=5.3rc4.post2",
"itk>=5.3.0",
"pytest >=2.7.3",
]
doc = ["sphinx"]
Expand Down
Binary file not shown.
2 changes: 0 additions & 2 deletions src/python/itkwasm/test/test_mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ def test_mesh():
itk_mesh = itk.meshread(data)

itk_mesh_dict = itk.dict_from_mesh(itk_mesh)
# Bug, to be fixed by 5.3.0
itk_mesh_dict.pop('dimension', None)
itkwasm_mesh = Mesh(**itk_mesh_dict)
itkwasm_mesh_dict = asdict(itkwasm_mesh)
itk_mesh_roundtrip = itk.mesh_from_dict(itkwasm_mesh_dict)
Expand Down
36 changes: 34 additions & 2 deletions src/python/itkwasm/test/test_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import itk
import numpy as np

from itkwasm import InterfaceTypes, TextStream, BinaryStream, PipelineInput, PipelineOutput, Pipeline, TextFile, BinaryFile, Image
from itkwasm import InterfaceTypes, TextStream, BinaryStream, PipelineInput, PipelineOutput, Pipeline, TextFile, BinaryFile, Image, Mesh

test_input_dir = Path(__file__).resolve().parent / 'input'
test_baseline_dir = Path(__file__).resolve().parent / 'baseline'
Expand Down Expand Up @@ -124,4 +124,36 @@ def test_pipeline_write_read_image():
baseline = itk.imread(test_baseline_dir / "test_pipeline_write_read_image.png")

difference = np.sum(itk.comparison_image_filter(out_image, baseline))
assert difference == 0.0
assert difference == 0.0

def test_pipeline_write_read_mesh():
pipeline = Pipeline(test_input_dir / 'mesh-read-write-test.wasi.wasm')

data = test_input_dir / "cow.vtk"
itk_mesh = itk.meshread(data)
itk_mesh_dict = itk.dict_from_mesh(itk_mesh)
itkwasm_mesh = Mesh(**itk_mesh_dict)

pipeline_inputs = [
PipelineInput(InterfaceTypes.Mesh, itkwasm_mesh),
]

pipeline_outputs = [
PipelineOutput(InterfaceTypes.Mesh),
]

args = [
'0',
'0',
'--memory-io',]

outputs = pipeline.run(args, pipeline_outputs, pipeline_inputs)

out_mesh = itk.mesh_from_dict(asdict(outputs[0].data))
expect(mesh.meshType.dimension, 'dimension').to.equal(3)
expect(mesh.meshType.pointComponentType, 'pointComponentType').to.equal(itk.FloatTypes.Float32)
expect(mesh.meshType.cellComponentType, 'cellComponentType').to.equal(itk.IntTypes.UInt64)
expect(mesh.meshType.pointPixelType, 'pointPixelType').to.equal(itk.PixelTypes.Scalar)
expect(mesh.meshType.cellPixelType, 'cellPixelType').to.equal(itk.PixelTypes.Scalar)
expect(mesh.numberOfPoints, 'numberOfPoints').to.equal(2903)
expect(mesh.numberOfCells, 'numberOfCells').to.equal(3263)

0 comments on commit 832565f

Please sign in to comment.