Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 13 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ print(f"PhysioMotion4D version: {physiomotion4d.__version__}")
- `ContourTools`: Mesh extraction and contour manipulation
- **USD Conversion**: VTK to USD conversion for Omniverse visualization
- `ConvertVTKToUSD`: High-level converter for PyVista/VTK objects with colormap support
- `vtk_to_usd` module: File-based conversion library
- `VTKToUSDConverter`: Core converter with time-series support
- `vtk_to_usd` module: Advanced low-level file conversion library
- `convert_vtk_file()`: Single-file VTK/VTP/VTU to USD facade
- `read_vtk_file()`: Read VTK/VTP/VTU files into MeshData
- `ConversionSettings`: Configurable conversion parameters
- `MaterialData`: USD material definitions
Expand Down Expand Up @@ -307,7 +307,7 @@ forward_transform = results["forward_transform"] # Moving to fixed

### VTK to USD Conversion

PhysioMotion4D provides two APIs for converting VTK data to USD for NVIDIA Omniverse visualization:
PhysioMotion4D provides two APIs for converting VTK data to USD for NVIDIA Omniverse visualization. Repository workflows, experiments, and CLIs use `ConvertVTKToUSD`; `vtk_to_usd` is a public advanced layer for users who need low-level file conversion primitives.

#### Option 1: High-Level ConvertVTKToUSD (for PyVista/VTK objects)

Expand Down Expand Up @@ -336,11 +336,10 @@ converter.set_colormap(
stage = converter.convert('cardiac_motion.usd')
```

#### Option 2: File-Based vtk_to_usd Library
#### Option 2: Advanced File-Based vtk_to_usd Facade

```python
from physiomotion4d.vtk_to_usd import (
VTKToUSDConverter,
ConversionSettings,
MaterialData,
convert_vtk_file,
Expand All @@ -349,11 +348,11 @@ from physiomotion4d.vtk_to_usd import (
# Simple single-file conversion
stage = convert_vtk_file('mesh.vtp', 'output.usd')

# Advanced: Custom settings and materials
# Advanced: custom settings and material
settings = ConversionSettings(
triangulate_meshes=True,
compute_normals=True,
meters_per_unit=0.001, # mm to meters
meters_per_unit=1.0, # USD stage units after built-in mm-to-m scaling
times_per_second=60.0,
Comment on lines +351 to 356
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clarify the unit-scaling example before users copy it.

ConversionSettings.meters_per_unit is the VTK→USD scale factor, and the library’s default path is the mm→m conversion validated by tests/test_vtk_to_usd_library.py. Setting this to 1.0 here without explicitly saying the source mesh is already in meters will make typical medical meshes 1000× too large. Either keep the default 0.001 in the example or call out that 1.0 is only for meter-authored inputs.

📝 Suggested doc tweak
 settings = ConversionSettings(
     triangulate_meshes=True,
     compute_normals=True,
-    meters_per_unit=1.0,  # coordinates are authored in meters
+    meters_per_unit=0.001,  # typical VTK medical meshes are authored in millimeters
     times_per_second=60.0,
 )

Or, if you want to keep 1.0, make the comment explicit:

-    meters_per_unit=1.0,  # coordinates are authored in meters
+    meters_per_unit=1.0,  # only when the source VTK coordinates are already in meters
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Advanced: custom settings and material
settings = ConversionSettings(
triangulate_meshes=True,
compute_normals=True,
meters_per_unit=0.001, # mm to meters
meters_per_unit=1.0, # coordinates are authored in meters
times_per_second=60.0,
settings = ConversionSettings(
triangulate_meshes=True,
compute_normals=True,
meters_per_unit=0.001, # typical VTK medical meshes are authored in millimeters
times_per_second=60.0,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 351 - 356, The example uses ConversionSettings with
meters_per_unit=1.0 but doesn't clarify units; update the snippet so it either
keeps the library default (meters_per_unit=0.001) to reflect the mm→m
conversion, or keep 1.0 but change the inline comment to explicitly state
"source mesh authored in meters (no scaling)" — adjust the example around
ConversionSettings and the meters_per_unit parameter accordingly so users won't
accidentally scale typical medical (mm) meshes 1000×.

)

Expand All @@ -363,20 +362,19 @@ material = MaterialData(
roughness=0.4,
)

converter = VTKToUSDConverter(settings)
stage = converter.convert_file('heart.vtp', 'heart.usd', material=material)

# Time-series conversion
files = ['frame_000.vtp', 'frame_001.vtp', 'frame_002.vtp']
time_codes = [0.0, 0.1, 0.2]
stage = converter.convert_sequence(files, 'animated.usd', time_codes=time_codes)
stage = convert_vtk_file(
'heart.vtp',
'heart.usd',
data_basename='Heart',
settings=settings,
material=material,
)
```

Features:
- Automatic coordinate system conversion (RAS to Y-up)
- Material system with UsdPreviewSurface
- Preserves all VTK data arrays as USD primvars
- Time-series animation support
- Supports VTP, VTK, and VTU file formats

### Logging and Debug Control
Expand Down
102 changes: 47 additions & 55 deletions docs/API_MAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ _Re-run `py utils/generate_api_map.py` whenever public APIs change._

## experiments/Convert_VTK_To_USD/convert_vtk_to_usd_using_class.py

- `def create_deformed_pv_mesh(base, time_step, num_steps=10)` (line 237): Return a sinusoidally scaled copy of base with a synthetic pressure field.
- `def verify_usd_file(usd_path)` (line 315): Verify USD file integrity.
- `def create_deformed_pv_mesh(base, time_step, num_steps=10)` (line 236): Return a sinusoidally scaled copy of base with a synthetic pressure field.
- `def verify_usd_file(usd_path)` (line 314): Verify USD file integrity.

## experiments/DisplacementField_To_USD/displacement_field_to_usd.py

Expand Down Expand Up @@ -115,13 +115,14 @@ _Re-run `py utils/generate_api_map.py` whenever public APIs change._

## src/physiomotion4d/convert_vtk_to_usd.py

- **class ConvertVTKToUSD** (line 38): Advanced VTK to USD converter with colormap and anatomical labeling support.
- `def __init__(self, data_basename, input_polydata, mask_ids=None, compute_normals=False, convert_to_surface=True, times_per_second=24.0, separate_by='none', solid_color=(0.8, 0.8, 0.8), log_level=logging.INFO)` (line 68): Initialize converter.
- `def from_files(cls, data_basename, vtk_files, *, extract_surface=True, separate_by='none', times_per_second=24.0, solid_color=(0.8, 0.8, 0.8), time_codes=None, static_merge=False, mask_ids=None, log_level=logging.INFO)` (line 139): Create a converter by loading VTK files from disk.
- `def supports_mesh_type(self, mesh)` (line 239): Check if mesh type is supported for conversion.
- `def list_available_arrays(self)` (line 267): List all point data arrays available across all time steps.
- `def set_colormap(self, color_by_array=None, colormap='plasma', intensity_range=None)` (line 313): Configure colormap for visualization.
- `def convert(self, output_usd_file, convert_to_surface=None, compute_normals=None)` (line 347): Convert VTK meshes to USD.
- **class ConvertVTKToUSD** (line 41): Advanced VTK to USD converter with colormap and anatomical labeling support.
- `def __init__(self, data_basename, input_polydata, mask_ids=None, compute_normals=False, convert_to_surface=True, times_per_second=24.0, separate_by='none', solid_color=(0.8, 0.8, 0.8), log_level=logging.INFO)` (line 71): Initialize converter.
- `def from_files(cls, data_basename, vtk_files, *, extract_surface=True, separate_by='none', times_per_second=24.0, solid_color=(0.8, 0.8, 0.8), time_codes=None, static_merge=False, mask_ids=None, log_level=logging.INFO)` (line 142): Create a converter by loading VTK files from disk.
- `def supports_mesh_type(self, mesh)` (line 240): Check if mesh type is supported for conversion.
- `def inspect_file(cls, vtk_file, *, extract_surface=True)` (line 269): Summarize a VTK file using the same low-level reader as conversion.
- `def list_available_arrays(self)` (line 341): List all point data arrays available across all time steps.
- `def set_colormap(self, color_by_array=None, colormap='plasma', intensity_range=None)` (line 387): Configure colormap for visualization.
- `def convert(self, output_usd_file, convert_to_surface=None, compute_normals=None)` (line 421): Convert VTK meshes to USD.

## src/physiomotion4d/image_tools.py

Expand Down Expand Up @@ -343,6 +344,10 @@ _Re-run `py utils/generate_api_map.py` whenever public APIs change._
- `def list_mesh_paths_under(self, stage_or_path, parent_path='/World/Meshes')` (line 1103): List paths of all mesh prims under a parent path.
- `def repair_mesh_primvar_element_sizes(self, stage_or_path, mesh_path, *, time_code=None, save=True)` (line 1130): Repair missing/incorrect primvar elementSize metadata for a mesh.

## src/physiomotion4d/vtk_to_usd/converter.py

- `def convert_vtk_file(vtk_file, output_usd_file, *, data_basename=None, mesh_name='Mesh', extract_surface=True, settings=None, material=None)` (line 14): Convert one VTK file to one USD stage.

## src/physiomotion4d/vtk_to_usd/data_structures.py

- **class DataType** (line 13): Data type enumeration for generic arrays.
Expand Down Expand Up @@ -390,14 +395,14 @@ _Re-run `py utils/generate_api_map.py` whenever public APIs change._
## src/physiomotion4d/vtk_to_usd/vtk_reader.py

- **class VTKReader** (line 20): Base class for VTK file readers.
- **class PolyDataReader** (line 222): Reader for VTK PolyData files (.vtp).
- `def read(filename)` (line 226): Read a VTP file and return MeshData.
- **class LegacyVTKReader** (line 282): Reader for legacy VTK files (.vtk).
- `def read(filename, extract_surface=True)` (line 294): Read a legacy VTK file and return MeshData.
- **class UnstructuredGridReader** (line 455): Reader for VTK UnstructuredGrid files (.vtu).
- `def read(filename, extract_surface=True)` (line 459): Read a VTU file and return MeshData.
- `def read_vtk_file(filename, extract_surface=True)` (line 568): Auto-detect VTK file format and read appropriately.
- `def validate_time_series_topology(mesh_data_sequence, filenames=None)` (line 596): Validate topology consistency across a time series of meshes.
- **class PolyDataReader** (line 234): Reader for VTK PolyData files (.vtp).
- `def read(filename)` (line 238): Read a VTP file and return MeshData.
- **class LegacyVTKReader** (line 294): Reader for legacy VTK files (.vtk).
- `def read(filename, extract_surface=True)` (line 306): Read a legacy VTK file and return MeshData.
- **class UnstructuredGridReader** (line 467): Reader for VTK UnstructuredGrid files (.vtu).
- `def read(filename, extract_surface=True)` (line 471): Read a VTU file and return MeshData.
- `def read_vtk_file(filename, extract_surface=True)` (line 580): Auto-detect VTK file format and read appropriately.
- `def validate_time_series_topology(mesh_data_sequence, filenames=None)` (line 608): Validate topology consistency across a time series of meshes.

## src/physiomotion4d/workflow_convert_ct_to_vtk.py

Expand Down Expand Up @@ -707,44 +712,31 @@ _Re-run `py utils/generate_api_map.py` whenever public APIs change._

## tests/test_vtk_to_usd_library.py

- `def get_data_dir()` (line 34): Get the data directory path.
- `def check_kcl_heart_data()` (line 41): Check if KCL Heart Model data is available.
- `def check_valve4d_data()` (line 48): Check if CHOP Valve4D data is available.
- `def get_or_create_average_surface(test_directories)` (line 55): Get or create average_surface.vtp from average_mesh.vtk.
- `def kcl_average_surface(test_directories)` (line 101): Fixture providing the KCL average heart surface.
- **class TestGenericArray** (line 117): Test GenericArray data structure validation and reshaping.
- `def test_scalar_1d_array(self)` (line 120): Test that 1D scalar arrays (num_components=1) are kept as-is.
- `def test_flat_multicomponent_array_reshape(self)` (line 133): Test that flat 1D arrays with num_components>1 are reshaped to 2D.
- `def test_2d_array_valid(self)` (line 149): Test that 2D arrays with correct shape are accepted.
- `def test_flat_array_not_divisible_raises_error(self)` (line 162): Test that flat arrays with length not divisible by num_components raise error.
- `def test_2d_array_wrong_shape_raises_error(self)` (line 173): Test that 2D arrays with wrong shape raise error.
- `def test_3d_array_raises_error(self)` (line 184): Test that 3D arrays are rejected.
- `def test_flat_array_large_components(self)` (line 195): Test reshaping with large num_components (e.g., 9 for 3x3 tensors).
- **class TestFromFilesValidation** (line 210): Synthetic tests for ConvertVTKToUSD.from_files() — no real data required.
- `def test_time_codes_length_mismatch_raises(self, tmp_path)` (line 222): from_files() must reject time_codes whose length != len(vtk_files).
- `def test_time_codes_non_monotone_raises(self, tmp_path)` (line 232): from_files() must reject time_codes that decrease between frames.
- `def test_time_codes_equal_consecutive_is_valid(self, tmp_path)` (line 242): Equal consecutive time codes are non-decreasing and must not raise.
- `def test_from_files_populates_cached_mesh_data(self, tmp_path)` (line 256): from_files() with >1 frame must populate _cached_mesh_data.
- `def test_from_files_cache_reused_in_convert(self, tmp_path)` (line 269): _convert_unified() must not call _vtk_to_mesh_data() when cache is populated.
- `def test_from_files_single_file_no_cache(self, tmp_path)` (line 287): A single-file converter must not populate _cached_mesh_data.
- `def test_from_files_static_merge_no_cache(self, tmp_path)` (line 295): static_merge=True must not populate _cached_mesh_data.
- **class TestVTKReader** (line 307): Test VTK file reading capabilities.
- `def test_read_vtp_file(self, kcl_average_surface)` (line 310): Test reading VTP (PolyData) files.
- `def test_read_legacy_vtk_file(self)` (line 331): Test reading legacy VTK files.
- `def test_generic_arrays_preserved(self, kcl_average_surface)` (line 358): Test that generic data arrays are preserved during reading.
- **class TestVTKToUSDConversion** (line 382): Test VTK to USD conversion capabilities.
- `def test_single_file_conversion(self, test_directories, kcl_average_surface)` (line 385): Test converting a single VTK file to USD.
- `def test_conversion_with_material(self, test_directories, kcl_average_surface)` (line 417): Test conversion with a custom solid color material.
- `def test_conversion_settings(self, test_directories, kcl_average_surface)` (line 455): Test that ConvertVTKToUSD applies correct default stage metadata.
- `def test_primvar_preservation(self, test_directories, kcl_average_surface)` (line 478): Test that VTK data arrays are preserved as USD primvars.
- **class TestTimeSeriesConversion** (line 514): Test time-series conversion capabilities.
- `def test_time_series_conversion(self, test_directories, kcl_average_surface)` (line 517): Test converting multiple VTK files as a time series.
- **class TestIntegration** (line 557): Integration tests combining multiple features.
- `def test_end_to_end_conversion(self, test_directories, kcl_average_surface)` (line 560): Test complete conversion workflow with all features.
- **class TestUnitScaling** (line 603): Verify that VTK mm coordinates are converted to USD meter coordinates.
- `def test_mm_to_m_point_scaling(self, tmp_path)` (line 606): Points written to USD must be 0.001× their original mm values.
- `def test_normals_remain_unit_length(self, tmp_path)` (line 638): Normal vectors must not be scaled — they should remain unit length.
- `def test_stage_meters_per_unit(self, tmp_path)` (line 664): Stage metersPerUnit metadata must be 1.0 (coordinates stored in meters).
- `def get_data_dir()` (line 24): Get the data directory path.
- `def check_kcl_heart_data()` (line 31): Check if KCL Heart Model data is available.
- `def get_or_create_average_surface(test_directories)` (line 38): Get or create average_surface.vtp from average_mesh.vtk.
- `def kcl_average_surface(test_directories)` (line 65): Fixture providing the KCL average heart surface.
- **class TestFromFilesValidation** (line 78): Synthetic tests for ConvertVTKToUSD.from_files().
- `def test_time_codes_length_mismatch_raises(self, tmp_path)` (line 81): from_files() must reject time_codes whose length != len(vtk_files).
- `def test_time_codes_non_monotone_raises(self, tmp_path)` (line 91): from_files() must reject time_codes that decrease between frames.
- `def test_time_codes_equal_consecutive_is_valid(self, tmp_path)` (line 101): Equal consecutive time codes are non-decreasing and must not raise.
- `def test_from_files_single_file_writes_static_mesh(self, tmp_path)` (line 113): A single-file converter writes a static mesh with no time range.
- `def test_from_files_static_merge_writes_separate_meshes(self, tmp_path)` (line 125): static_merge=True treats files as static objects, not time samples.
- **class TestSyntheticConversion** (line 144): Synthetic ConvertVTKToUSD tests that do not require downloaded data.
- `def test_inspect_file_reports_public_summary(self, tmp_path)` (line 147): inspect_file() reports geometry, bounds, arrays, and cell types.
- `def test_inspect_file_reports_empty_mesh(self, tmp_path)` (line 167): inspect_file() reports empty meshes without raising.
- `def test_file_primvar_preservation(self, tmp_path)` (line 183): Point arrays in a VTP file are preserved as USD primvars.
- `def test_time_series_conversion(self, tmp_path)` (line 202): Multiple VTP files write point time samples and stage time metadata.
- **class TestVTKToUSDConversion** (line 226): Test ConvertVTKToUSD on optional real VTK data.
- `def test_single_file_conversion(self, test_directories, kcl_average_surface)` (line 229): Test converting a single VTK file to USD.
- `def test_conversion_with_material(self, test_directories, kcl_average_surface)` (line 248): Test conversion with a custom solid color material.
- `def test_conversion_settings(self, test_directories, kcl_average_surface)` (line 276): Test that ConvertVTKToUSD applies correct default stage metadata.
- **class TestIntegration** (line 293): Integration tests combining multiple features.
- `def test_end_to_end_conversion(self, test_directories, kcl_average_surface)` (line 296): Test complete conversion workflow with all features.
- **class TestUnitScaling** (line 319): Verify that VTK mm coordinates are converted to USD meter coordinates.
- `def test_mm_to_m_point_scaling(self, tmp_path)` (line 322): Points written to USD must be 0.001x their original mm values.
- `def test_normals_remain_unit_length(self, tmp_path)` (line 342): Normal vectors must not be scaled.
- `def test_stage_meters_per_unit(self, tmp_path)` (line 362): Stage metersPerUnit metadata must be 1.0.

## utils/claude_github_reviews.py

Expand Down
2 changes: 0 additions & 2 deletions docs/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ This section provides detailed documentation for all PhysioMotion4D classes, fun
usd/tools
usd/anatomy_tools
usd/vtk_conversion
usd/polymesh
usd/tetmesh

.. toctree::
:maxdepth: 2
Expand Down
20 changes: 5 additions & 15 deletions docs/api/usd/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ USD generation tools for creating animated 3D models from medical images:

* **USD Tools**: Core USD file operations
* **Anatomy Tools**: Anatomical structure handling
* **VTK Conversion**: Convert VTK meshes to USD
* **PolyMesh**: Surface mesh representation
* **TetMesh**: Tetrahedral mesh representation
* **VTK Conversion**: Convert VTK meshes to USD with :class:`ConvertVTKToUSD`

Quick Links
===========
Expand All @@ -24,8 +22,6 @@ Quick Links
* :doc:`tools` - Core USD utilities
* :doc:`anatomy_tools` - Anatomical structure tools
* :doc:`vtk_conversion` - VTK to USD conversion
* :doc:`polymesh` - Surface mesh USD
* :doc:`tetmesh` - Tetrahedral mesh USD

Module Documentation
====================
Expand All @@ -36,8 +32,6 @@ Module Documentation
tools
anatomy_tools
vtk_conversion
polymesh
tetmesh

Quick Start
===========
Expand All @@ -49,16 +43,12 @@ Convert VTK to USD

from physiomotion4d import ConvertVTKToUSD

converter = ConvertVTKToUSD(
output_file="animated_heart.usd",
colormap="rainbow",
verbose=True
)

converter.convert(
converter = ConvertVTKToUSD.from_files(
data_basename="Heart",
vtk_files=["heart_phase_00.vtk", "heart_phase_01.vtk"],
time_points=[0.0, 0.1, 0.2]
time_codes=[0.0, 1.0],
)
stage = converter.convert("animated_heart.usd")

Create Anatomical Scene
-----------------------
Expand Down
20 changes: 0 additions & 20 deletions docs/api/usd/polymesh.rst

This file was deleted.

20 changes: 0 additions & 20 deletions docs/api/usd/tetmesh.rst

This file was deleted.

2 changes: 1 addition & 1 deletion docs/api/usd/vtk_conversion.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ Class Reference

.. rubric:: Navigation

:doc:`anatomy_tools` | :doc:`index` | :doc:`polymesh`
:doc:`anatomy_tools` | :doc:`index`
2 changes: 1 addition & 1 deletion docs/api/utilities/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,4 @@ See Also

.. rubric:: Navigation

:doc:`../usd/tetmesh` | :doc:`../index` | :doc:`image_tools`
:doc:`../usd/vtk_conversion` | :doc:`../index` | :doc:`image_tools`
4 changes: 1 addition & 3 deletions docs/developer/architecture.rst
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,7 @@ Most PhysioMotion4D classes inherit from :class:`PhysioMotion4DBase`:
│ │ └── RegisterImagesICON
│ └── (Model registration classes)
└── Conversion Classes
└── ConvertVTKToUSDBase
├── ConvertVTKToUSDPolyMesh
└── ConvertVTKToUSDTetMesh
└── ConvertVTKToUSD
The base class provides:

Expand Down
Loading
Loading