Skip to content

Support line2 meshes, NIfTI images and label map interpolation - #16

Merged
maxiludwig merged 4 commits into
mainfrom
vtu-line2-support
Sep 15, 2026
Merged

maxiludwig merged 4 commits into
mainfrom
vtu-line2-support

Conversation

@maxiludwig

Copy link
Copy Markdown
Owner

Summary

Enables inferring element properties for line2 meshes, e.g. lung trees generated by pneumogen, including terminal unit properties from their voxel regions.

Changes

  • VTU line2 discretizations: .vtu files are read via lnmmeshio, keeping all cell data as element fields.
  • Element filter: element_filter: {field, values} selects elements by any element field (e.g. block_id, MAT). material_ids is deprecated and translated with a warning.
  • Discretization export: export.type: discretization writes every field returned by the user function as cell data into a copy of the input .vtu. Unselected cells are NaN, so runs on different selections can be chained. output_parameter_name is now only allowed for json.
  • NIfTI images: .nii/.nii.gz files are read via nibabel (RAS→LPS), cropped to the mesh; modality via pixel_type (default CT).
  • Label map interpolation: method: labelmap averages all voxels of each element's region in a NIfTI label map (e.g. pneumogen terminal unit labels). Coarser label maps are supported.
  • Readers renamed: LnmmeshioReader and TrimeshReader.

Testing

234 unit tests pass. The label map interpolation was verified on a patient lung tree against a direct per-label mean, with 0 HU difference.

🤖 Generated with Claude Code.

maxiludwig and others added 4 commits September 15, 2026 10:06
Read .vtu via lnmmeshio, filter by any element field (material_ids deprecated), export into the input mesh.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Read .nii/.nii.gz images via nibabel (RAS to LPS, cropped to the mesh), pixel type from options.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Average all voxels within the region of each element in a NIfTI label map, e.g. terminal unit regions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
FourCYamlReader becomes LnmmeshioReader (reads .4C.yaml and .vtu), MeshReader becomes TrimeshReader.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 15, 2026 08:09
@maxiludwig
maxiludwig merged commit 274409f into main Sep 15, 2026
4 checks passed
@maxiludwig
maxiludwig deleted the vtu-line2-support branch September 15, 2026 08:15

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Four unresolved moderate review findings remain in mesh filtering, image-folder detection, and label-map memory handling.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds line2/VTU mesh support, NIfTI loading, label-map interpolation, element filtering, and discretization export.

Changes:

  • Adds unified mesh readers and configurable element filtering.
  • Adds NIfTI RAS→LPS conversion and label-map interpolation.
  • Adds VTU cell-data export, visualization updates, documentation, dependencies, and tests.
File summaries
File Reviewed change
tests/unittests/i2pp/core/visualizers/test_discretization_visualizer.py Tests line rendering.
tests/unittests/i2pp/core/test_run.py Updates export fixtures.
tests/unittests/i2pp/core/test_export_data.py Tests discretization export routing.
tests/unittests/i2pp/core/test_discretization_helpers.py Tests VTU and line cells.
tests/unittests/i2pp/core/interpolators/test_interpolator_types.py Tests label-map registration.
tests/unittests/i2pp/core/interpolators/test_interpolator_label_map.py Tests label-map interpolation.
tests/unittests/i2pp/core/image_readers/test_nifti_reader.py Tests NIfTI loading and cropping.
tests/unittests/i2pp/core/exporters/test_export_format.py Tests export format registration.
tests/unittests/i2pp/core/exporters/test_discretization_exporter.py Tests VTU cell-data export.
tests/unittests/i2pp/core/discretization_readers/test_trimesh_reader.py Tests the renamed mesh reader.
tests/unittests/i2pp/core/discretization_readers/test_lnmmeshio_reader.py Tests unified reading and filtering.
tests/unittests/i2pp/core/discretization_readers/test_fourc_yaml_reader.py Updates legacy reader tests.
tests/unittests/i2pp/core/discretization_readers/test_discretization_format.py Tests reader selection and VTU support.
tests/unittests/i2pp/core/configuration_validator/test_validator_labelmap.py Tests label-map validation.
tests/unittests/i2pp/core/configuration_validator/test_validator_export.py Tests export validation.
tests/unittests/i2pp/core/configuration_validator/test_validator_element_filter.py Tests element-filter validation.
templates/config/example_config.yaml Documents new configuration options.
src/i2pp/core/visualizers/visualizer.py Supports mesh rendering options.
src/i2pp/core/visualizers/discretization_visualizer.py Renders line cells as tubes.
src/i2pp/core/run.py Integrates label maps and exports.
src/i2pp/core/interpolators/interpolator.py Centralizes outlier filtering.
src/i2pp/core/interpolators/interpolator_types.py Registers label-map interpolation.
src/i2pp/core/interpolators/interpolator_label_map.py Implements label-map interpolation. Moderate (1 vote): Retaining all labels and pixel values defeats slice-by-slice memory savings; use accumulated statistics or bounded chunks.
src/i2pp/core/interpolators/interpolator_all_voxel.py Removes duplicated filtering logic.
src/i2pp/core/interpolate_element_data.py Passes label-map settings to interpolators.
src/i2pp/core/import_image.py Detects NIfTI inputs. Moderate (3 votes): Mixed NIfTI/DICOM/PNG folders silently ignore NIfTI files; reject these combinations.
src/i2pp/core/image_readers/nifti_reader.py Implements NIfTI loading, orientation conversion, and cropping.
src/i2pp/core/image_readers/label_map.py Implements label-map lookup and caching.
src/i2pp/core/image_readers/image_format.py Registers NIfTI support.
src/i2pp/core/exporters/export_format.py Registers discretization export.
src/i2pp/core/exporters/discretization_exporter.py Writes results into VTU cell data.
src/i2pp/core/export_data.py Routes discretization exports.
src/i2pp/core/discretization_readers/trimesh_reader.py Renames the trimesh reader. Moderate (3 votes): .mesh element filters reach an unimplemented runtime path and fail; implement filtering or reject the option during validation.
src/i2pp/core/discretization_readers/lnmmeshio_reader.py Adds unified reading and filtering. Moderate (1 vote): Filtering can leave surface node sets referencing removed nodes, causing KeyError; skip absent nodes and omit empty surfaces before scaling.
src/i2pp/core/discretization_readers/fourc_yaml_reader.py Removes the superseded reader implementation.
src/i2pp/core/discretization_readers/discretization_reader.py Adds element metadata and fields.
src/i2pp/core/discretization_readers/discretization_format.py Registers readers and VTU support.
src/i2pp/core/discretization_helpers.py Supports line and triangle cells.
src/i2pp/core/configuration_validator/validator.py Validates filters, label maps, and exports.
requirements.txt Locks the NIfTI dependency.
requirements.in Adds the NIfTI dependency.
README.md Documents the new features and workflows.
Review details

Suppressed comments (3)

src/i2pp/core/discretization_readers/lnmmeshio_reader.py:212

  • After _filter_discretization replaces raw_dis.nodes with only nodes referenced by selected elements, raw_dis.surfacenodesets still contains the original surface nodes. Indexing every surface node here therefore raises KeyError whenever a filtered-out node belongs to a surface, so filtered 4C meshes with surfaces cannot load. Skip nodes absent from node_id_to_idx and omit empty surfaces before applying the surface scaling.
            for node in surf.nodes:
                surf_node_ids.append(node.id)
                position = node_id_to_idx[node.id]
                nodes_scaling[position] = surface_node_scaling

src/i2pp/core/discretization_readers/trimesh_reader.py:69

  • The validator now normalizes material_ids and the documented element_filter for every discretization format, but this branch makes .mesh inputs call _filter_discretization, which is still an unconditional not implemented yet stub and cannot receive the filter. Any supported .mesh configuration using the documented filter consequently fails at runtime; implement filtering for the trimesh representation or reject this option for .mesh during validation.
    src/i2pp/core/interpolators/interpolator_label_map.py:123
  • Although the docstring says processing is slice-by-slice to limit memory, these lists retain every selected label and pixel value until the final concatenation, so peak memory still scales with the entire cropped volume and duplicates the image data. A patient-sized label-map run can therefore consume substantially more memory than expected; accumulate per-label statistics (and outlier state) or process bounded chunks instead of retaining all voxels.
        voxel_labels = []
        voxel_values = []
        for k, slice_coord in enumerate(grid.slice):
            slice_points[:, 0] = slice_coord
  • Files reviewed: 41/42 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/i2pp/core/import_image.py Resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants