Skip to content

Commit

Permalink
feat(mesh-io): add read_mesh, read_mesh_async, write_mesh, write_mesh…
Browse files Browse the repository at this point in the history
…_async
  • Loading branch information
thewtex committed Nov 29, 2023
1 parent 75f8e13 commit acc4271
Show file tree
Hide file tree
Showing 19 changed files with 367 additions and 14 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ packages/image-io/typescript/demo-app-rollup/
packages/image-io/python/itkwasm-image-io-wasi/itkwasm_image_io_wasi/wasm_modules/

packages/mesh-io/test/
packages/mesh-io/python/itkwasm-mesh-io-wasi/itkwasm_mesh_io_wasi/wasm_modules/

cypress/screenshots/
cypress/videos/
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,16 @@
"build:bindgen:typescript:image-io": "./src/itk-wasm-cli.js -s packages/image-io -b emscripten-build bindgen --package-version 0.5.0 --package-name @itk-wasm/image-io --package-description \"Input and output for scientific and medical image file formats.\" --repository 'https://github.com/InsightSoftwareConsortium/itk-wasm'",
"build:bindgen:python:image-io": "./src/itk-wasm-cli.js -s packages/image-io -b wasi-build bindgen --interface python --package-name itkwasm-image-io --package-description \"Input and output for scientific and medical image file formats.\" --package-version 0.5.0 --repository 'https://github.com/InsightSoftwareConsortium/itk-wasm'",
"build:emscripten:mesh-io": "node ./src/itk-wasm-cli.js -s packages/mesh-io -b emscripten-build build",
"build:bindgen:typescript:mesh-io": "./src/itk-wasm-cli.js -s packages/mesh-io -b emscripten-build bindgen --package-version 0.1.0 --package-name @itk-wasm/mesh-io --package-description \"Input and output for scientific and medical image file formats.\" --repository 'https://github.com/InsightSoftwareConsortium/itk-wasm'",
"build:bindgen:python:mesh-io": "./src/itk-wasm-cli.js -s packages/mesh-io -b wasi-build bindgen --interface python --package-name itkwasm-mesh-io --package-description \"Input and output for scientific and medical image file formats.\" --package-version 0.1.0 --repository 'https://github.com/InsightSoftwareConsortium/itk-wasm'",
"build:bindgen:typescript:mesh-io": "./src/itk-wasm-cli.js -s packages/mesh-io -b emscripten-build bindgen --package-version 0.2.0 --package-name @itk-wasm/mesh-io --package-description \"Input and output for scientific and medical image file formats.\" --repository 'https://github.com/InsightSoftwareConsortium/itk-wasm'",
"build:bindgen:python:mesh-io": "./src/itk-wasm-cli.js -s packages/mesh-io -b wasi-build bindgen --interface python --package-name itkwasm-mesh-io --package-description \"Input and output for scientific and medical image file formats.\" --package-version 0.2.0 --repository 'https://github.com/InsightSoftwareConsortium/itk-wasm'",
"build:emscripten:packages": "npm run build:emscripten:compress-stringify && npm run build:bindgen:typescript:compress-stringify && npm run build:emscripten:dicom && npm run build:bindgen:typescript:dicom && npm run build:emscripten:compare-images && npm run build:bindgen:typescript:compare-images && npm run build:emscripten:image-io && npm run build:bindgen:typescript:image-io && npm run build:emscripten:mesh-io && npm run build:bindgen:typescript:mesh-io",
"build:wasi": "node ./src/build-wasi.js && npm run build:wasi:packages",
"build:wasi:compress-stringify": "node ./src/itk-wasm-cli.js -i itkwasm/wasi:latest -s packages/compress-stringify -b wasi-build build",
"build:wasi:dicom": "node ./src/itk-wasm-cli.js -i itkwasm/wasi:latest -s packages/dicom -b wasi-build build",
"build:wasi:compare-images": "node ./src/itk-wasm-cli.js -i itkwasm/wasi:latest -s packages/compare-images -b wasi-build build",
"build:wasi:image-io": "node ./src/itk-wasm-cli.js -i itkwasm/wasi:latest -s packages/image-io -b wasi-build build",
"build:wasi:mesh-io": "node ./src/itk-wasm-cli.js -i itkwasm/wasi:latest -s packages/mesh-io -b wasi-build build",
"build:wasi:packages": "npm run build:wasi:compress-stringify && npm run build:bindgen:python:compress-stringify && npm run build:wasi:compare-images && npm run build:bindgen:python:compare-images && npm run build:wasi:dicom && npm run build:bindgen:python:dicom && npm run build:wasi:image-io && npm run build:bindgen:python:image-io",
"build:wasi:packages": "npm run build:wasi:compress-stringify && npm run build:bindgen:python:compress-stringify && npm run build:wasi:compare-images && npm run build:bindgen:python:compare-images && npm run build:wasi:dicom && npm run build:bindgen:python:dicom && npm run build:wasi:image-io && npm run build:bindgen:python:image-io && npm run build:wasi:mesh-io && npm run build:bindgen:python:mesh-io",
"cypress:open": "npx cypress open",
"cypress:run": "npx cypress run --config defaultCommandTimeout=8000",
"cypress:install": "npx cypress install",
Expand Down
1 change: 1 addition & 0 deletions packages/image-io/python/itkwasm-image-io/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ classifiers = [
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
keywords = [
"itkwasm",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# Generated file. To retain edits, remove this comment.

"""itkwasm-mesh-io-emscripten: Input and output for scientific and medical image file formats. Emscripten implementation."""

from .read_mesh_async import read_mesh_async
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.1.0"
__version__ = "0.2.0"
1 change: 0 additions & 1 deletion packages/mesh-io/python/itkwasm-mesh-io-wasi/.gitignore

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.1.0"
__version__ = "0.2.0"
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# Generated file. To retain edits, remove this comment.

"""itkwasm-mesh-io: Input and output for scientific and medical image file formats."""

from .read_mesh_async import read_mesh_async, meshread_async
from .read_mesh import read_mesh, meshread
from .write_mesh_async import write_mesh_async, meshwrite_async
from .write_mesh import write_mesh, meshwrite

from .byu_read_mesh_async import byu_read_mesh_async
from .byu_read_mesh import byu_read_mesh
from .byu_write_mesh_async import byu_write_mesh_async
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.1.0"
__version__ = "0.2.0"
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import os
from typing import Optional, Union

from itkwasm import (
environment_dispatch,
Mesh,
)

def read_mesh(
serialized_mesh: os.PathLike,
information_only: bool = False,
) -> Mesh:
"""Read an mesh file format and convert it to the itk-wasm file format
:param serialized_mesh: Input mesh serialized in the file format
:type serialized_mesh: os.PathLike
:param information_only: Only read mesh metadata -- do not read pixel data.
:type information_only: bool
:return: Output mesh
:rtype: Mesh
"""
func = environment_dispatch("itkwasm_mesh_io", "read_mesh")
output = func(serialized_mesh, information_only=information_only)
return output

def meshread(
serialized_mesh: os.PathLike,
information_only: bool = False,
) -> Mesh:
return read_mesh(serialized_mesh, information_only=information_only)

meshread.__doc__ = f"""{read_mesh.__doc__}
Alias for read_mesh.
"""
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import os
from typing import Optional, Union

from itkwasm import (
environment_dispatch,
Mesh,
)

async def read_mesh_async(
serialized_mesh: os.PathLike,
information_only: bool = False,
) -> Mesh:
"""Read an mesh file format and convert it to the itk-wasm file format
:param serialized_mesh: Input mesh serialized in the file format
:type serialized_mesh: os.PathLike
:param information_only: Only read mesh metadata -- do not read pixel data.
:type information_only: bool
:return: Output mesh
:rtype: Mesh
"""
func = environment_dispatch("itkwasm_mesh_io", "read_mesh_async")
output = await func(serialized_mesh, information_only=information_only)
return output

async def meshread_async(
serialized_mesh: os.PathLike,
information_only: bool = False,
) -> Mesh:
return await read_mesh_async(serialized_mesh, information_only=information_only)

meshread_async.__doc__ = f"""{read_mesh_async.__doc__}
Alias for read_mesh.
"""
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import os
from typing import Dict, Tuple, Optional, List, Any

from itkwasm import (
environment_dispatch,
Mesh,
)

def write_mesh(
mesh: Mesh,
serialized_mesh: str,
information_only: bool = False,
use_compression: bool = False,
) -> None:
"""Write an itk-wasm file format converted to an mesh file format
:param mesh: Input mesh
:type mesh: Mesh
:param serialized_mesh: Output mesh serialized in the file format.
:type serialized_mesh: str
:param information_only: Only write mesh metadata -- do not write pixel data.
:type information_only: bool
:param use_compression: Use compression in the written file
:type use_compression: bool
"""
func = environment_dispatch("itkwasm_mesh_io", "write_mesh")
func(mesh, serialized_mesh, information_only=information_only, use_compression=use_compression)
return

def meshwrite(
mesh: Mesh,
serialized_mesh: os.PathLike,
information_only: bool = False,
use_compression: bool = False,
) -> None:
return write_mesh(mesh, serialized_mesh, information_only=information_only, use_compression=use_compression)

meshwrite.__doc__ = f"""{write_mesh.__doc__}
Alias for write_mesh.
"""
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import os
from typing import Dict, Tuple, Optional, List, Any

from itkwasm import (
environment_dispatch,
Mesh,
)

async def write_mesh_async(
mesh: Mesh,
serialized_mesh: str,
information_only: bool = False,
use_compression: bool = False,
) -> None:
"""Write an itk-wasm file format converted to an mesh file format
:param mesh: Input mesh
:type mesh: Mesh
:param serialized_mesh: Output mesh serialized in the file format.
:type serialized_mesh: str
:param information_only: Only write mesh metadata -- do not write pixel data.
:type information_only: bool
:param use_compression: Use compression in the written file
:type use_compression: bool
"""
func = environment_dispatch("itkwasm_mesh_io", "write_mesh_async")
await func(mesh, serialized_mesh, information_only=information_only, use_compression=use_compression)
return

async def meshwrite_async(
mesh: Mesh,
serialized_mesh: os.PathLike,
information_only: bool = False,
use_compression: bool = False,
) -> None:
return await write_mesh_async(mesh, serialized_mesh, information_only=information_only, use_compression=use_compression)

meshwrite_async.__doc__ = f"""{write_mesh_async.__doc__}
Alias for write_mesh.
"""
18 changes: 16 additions & 2 deletions packages/mesh-io/python/itkwasm-mesh-io/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,29 @@ path = "itkwasm_mesh_io/_version.py"
[tool.hatch.envs.default]
dependencies = [
"pytest",
"pytest-pyodide",
]

[project.urls]
Home = "https://github.com/InsightSoftwareConsortium/itk-wasm"
Source = "https://github.com/InsightSoftwareConsortium/itk-wasm"

[tool.hatch.envs.default.scripts]
test = "pytest"

test = [
"hatch build -t wheel",
"pytest --dist-dir=./dist --rt=chrome",
]
download-pyodide = [
"curl -L https://github.com/pyodide/pyodide/releases/download/0.24.1/pyodide-0.24.1.tar.bz2 -o pyodide.tar.bz2",
"tar xjf pyodide.tar.bz2",
"rm -rf dist pyodide.tar.bz2",
"mv pyodide dist",
]
serve = [
"hatch build -t wheel",
'echo "\nVisit http://localhost:8877/console.html\n"',
"python -m http.server --directory=./dist 8877",
]

[tool.hatch.build]
exclude = [
Expand Down
26 changes: 26 additions & 0 deletions packages/mesh-io/python/itkwasm-mesh-io/tests/fixtures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import pytest
import sys

if sys.version_info < (3,10):
pytest.skip("Skipping pyodide tests on older Python", allow_module_level=True)

from pytest_pyodide import run_in_pyodide

from itkwasm_mesh_io import __version__ as test_package_version

@pytest.fixture
def package_wheel():
return f"itkwasm_mesh_io-{test_package_version}-py3-none-any.whl"

@pytest.fixture
def input_data():
from pathlib import Path
input_base_path = Path('..', '..', 'test', 'data')
test_files = [
Path('input') / 'cow.vtk',
]
data = {}
for f in test_files:
with open(input_base_path / f, 'rb') as fp:
data[str(f.name)] = fp.read()
return data
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from itkwasm import IntTypes, FloatTypes
import numpy as np

from itkwasm_mesh_io import read_mesh, meshread, write_mesh, meshwrite

from .common import test_input_path, test_output_path

test_input_file_path = test_input_path / "cow.vtk"
test_output_file_path = test_output_path / "read-write-cow.vtk"

def verify_mesh(mesh):
assert mesh.meshType.dimension == 3
assert mesh.meshType.pointComponentType == FloatTypes.Float32
assert mesh.meshType.pointPixelComponentType == IntTypes.Int8
assert mesh.numberOfPoints == 2903
assert np.allclose(mesh.points[0],3.71636)
assert np.allclose(mesh.points[1],2.34339)
assert mesh.numberOfCells == 3263
assert mesh.cellBufferSize == 18856
assert mesh.cells[0] == 4
assert mesh.cells[1] == 4
assert mesh.cells[2] == 250

def test_read_mesh():
mesh = read_mesh(test_input_file_path)
verify_mesh(mesh)

def test_meshread():
mesh = meshread(test_input_file_path)
verify_mesh(mesh)

def test_write_mesh():
mesh = read_mesh(test_input_file_path)

use_compression = False
write_mesh(mesh, test_output_file_path, use_compression=use_compression)

mesh = read_mesh(test_output_file_path)
verify_mesh(mesh)

def test_meshwrite():
mesh = meshread(test_input_file_path)

use_compression = False
meshwrite(mesh, test_output_file_path, use_compression=use_compression)

mesh = meshread(test_output_file_path)
verify_mesh(mesh)
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import sys

if sys.version_info < (3,10):
pytest.skip("Skipping pyodide tests on older Python", allow_module_level=True)

from pytest_pyodide import run_in_pyodide
from .fixtures import package_wheel, input_data

@run_in_pyodide(packages=['micropip', 'numpy'])
async def test_read_write_mesh_async(selenium, package_wheel, input_data):
import micropip
await micropip.install(package_wheel)
def write_input_data_to_fs(input_data, filename):
with open(filename, 'wb') as fp:
fp.write(input_data[filename])

from pathlib import Path

from itkwasm import FloatTypes, IntTypes
import numpy as np

from itkwasm_mesh_io import read_mesh_async, write_mesh_async


def verify_mesh(mesh):
assert mesh.meshType.dimension == 3
assert mesh.meshType.pointComponentType == FloatTypes.Float32
assert mesh.meshType.pointPixelComponentType == IntTypes.Int8
assert mesh.numberOfPoints == 2903
assert np.allclose(mesh.points.ravel()[0], 3.71636)
assert np.allclose(mesh.points.ravel()[1], 2.34339)
assert mesh.numberOfCells == 3263
assert mesh.cellBufferSize == 18856
assert mesh.cells[0] == 4
assert mesh.cells[1] == 4
assert mesh.cells[2] == 250

test_input_file_path = 'cow.vtk'
test_output_file_path = "read-write-cow.vtk"
write_input_data_to_fs(input_data, test_input_file_path)

assert Path(test_input_file_path).exists()


mesh = await read_mesh_async(test_input_file_path)
verify_mesh(mesh)

use_compression = False
await write_mesh_async(mesh, test_output_file_path, use_compression=use_compression)

mesh = await read_mesh_async(test_output_file_path)
verify_mesh(mesh)
Loading

0 comments on commit acc4271

Please sign in to comment.