Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Support for Mesh serialization #3154

Merged
merged 6 commits into from
Mar 16, 2022

Conversation

PranjalSahu
Copy link
Contributor

@PranjalSahu PranjalSahu commented Feb 3, 2022

This PR adds the functionality of mesh serialization.

  • SetPoints method is added for 1D array input argument in PointSet. Used for numpy interface.

  • SetCellsArray and GetCellsArray are added in Mesh. To provider a simpler API for creating mesh having only one type of cell, additional SetCellsArray(cell_vector_container, itk.CommonEnums.CellGeometry) is added.

  • CreateCell private method is added that creates a new cell given its type. The function can be reused for other purposes. Future additions of new kinds of cells should add an entry here to support the Python focussed APIs.

  • For serialization, we follow the approach similar to the MeshFileWriter where first the cell type is passed, followed by the count of points in that cell and finally the point ids. The count of points is needed here as the itkPolygonCell can take a variable number of points.

  • The functions (SetPoints, SetCellsArray) take as argument 1D VectorContainer which can be easily converted to a numpy and reverse.

  • For pickle-based serialization, getstate and setstate methods are added in python (both for mesh and pointset)(refer ENH: pickle serialization for itk images (issue 1091) #2829).

  • Support for key based updation of points, cells, point data, and cell data in the mesh is also added.

  • Test for consistency of points and cells using the newly added functions is added. The newly added API should perform seamlessly with older APIs and the results should be consistent.

  • Python Tests have also been added for mesh serialization, point set serialization, dict based methods in python, and for the conversion from VTK Mesh to ITK Mesh.

PR Checklist

  • No API changes were made (or the changes have been approved)
  • No major design changes were made (or the changes have been approved)
  • Added test (or behavior not changed)
  • Updated API documentation (or API not changed)
  • Added license to new files (if any)
  • Added Python wrapping to new files (if any) as described in ITK Software Guide Section 9.5
  • Added ITK examples for all new major features (if any)

Refer to the ITK Software Guide for
further development details if necessary.

@github-actions github-actions bot added area:Core Issues affecting the Core module area:Python wrapping Python bindings for a class type:Infrastructure Infrastructure/ecosystem related changes, such as CMake or buildbots type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct labels Feb 3, 2022
@thewtex
Copy link
Member

thewtex commented Feb 3, 2022

@PranjalSahu awesome! The data structure should follow:

https://insightsoftwareconsortium.github.io/itk-wasm/api/Mesh.html

@GenevieveBuckley
Copy link
Contributor

I'm slightly confused about why I've been requested as a reviewer, I don't do a whole lot with meshes (maybe will do more in the near future, but not a lot right now). Can you elaborate on what you were hoping for from me, @thewtex ?

@thewtex
Copy link
Member

thewtex commented Feb 4, 2022

@GenevieveBuckley , no expectations, but since you did such an amazing job on the Image serialization :-), your eyes may see pickling issues here. But, it is more just a heads-up that we are building on the work that you did for other data structures.

Copy link
Contributor

@GenevieveBuckley GenevieveBuckley left a comment

Choose a reason for hiding this comment

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

Ok, I've had a look over it now. It seems like nice work overall.
I know it says above this is still a work in progress. Before it is merged, my two biggest suggestions are that:

  1. The itkMeshSetGetCellTest.py test assertions need to be strengthened, and
  2. The dictionary returned on serialization currently includes a lot of empty string values, so it looks like there would be a fair bit of metadata loss upon serialization/de-serialization. This should also be addressed (and it will require a few new test assertions for these as well)

I don't feel like I can provide feedback on the header files, so I haven't left comments there.

Wrapping/Generators/Python/PyBase/pyBase.i Outdated Show resolved Hide resolved
Modules/Core/Mesh/wrapping/test/itkMeshSetGetCellTest.py Outdated Show resolved Hide resolved
elif key == 'direction':
self.SetDirection(np.flip(value, axis=None))
else:
self.GetMetaDataDictionary()[key] = value
Copy link
Contributor

Choose a reason for hiding this comment

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

There's no SetMetaDataDictionary option?
Assuming that's the case, this is a nice workaround.

Wrapping/Generators/Python/PyBase/pyBase.i Outdated Show resolved Hide resolved
Wrapping/Generators/Python/itk/support/extras.py Outdated Show resolved Hide resolved
@PranjalSahu PranjalSahu changed the title WIP: Support for Mesh serialization ENH: Support for Mesh serialization Feb 14, 2022
@github-actions github-actions bot added the type:Enhancement Improvement of existing methods or implementation label Feb 14, 2022
@PranjalSahu PranjalSahu force-pushed the pranjal21 branch 3 times, most recently from 50aeb39 to ded784d Compare February 14, 2022 00:55
@PranjalSahu
Copy link
Contributor Author

@thewtex I found that we have Object name functionality present for all objects. I am thinking of using that instead of using a new field for name in the PointSet. If you agree then I will remove the addition of new name field from this PR.

Also, I have not added the support for QuadEdge Mesh in the wasm data structure in this PR.

@PranjalSahu
Copy link
Contributor Author

Also I realized for code coverage I should instantiate all types of Cells while testing SetCellsAsArray.

Copy link
Member

@dzenanz dzenanz left a comment

Choose a reason for hiding this comment

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

Mostly looks good.

@PranjalSahu PranjalSahu force-pushed the pranjal21 branch 2 times, most recently from f8252ed to 29582eb Compare February 15, 2022 07:16
@PranjalSahu PranjalSahu force-pushed the pranjal21 branch 2 times, most recently from abfc813 to 2456a5b Compare February 23, 2022 00:40
Copy link
Member

@thewtex thewtex left a comment

Choose a reason for hiding this comment

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

Looking great!!!

A few comments inline.

Could we also please add a test for serialization of a PointSet?

Modules/Core/Common/include/itkPointSet.h Outdated Show resolved Hide resolved
Modules/Core/Common/include/itkPointSet.h Outdated Show resolved Hide resolved
Modules/Core/Common/include/itkPointSet.h Outdated Show resolved Hide resolved
Modules/Core/Common/include/itkPointSet.hxx Outdated Show resolved Hide resolved
Modules/Core/Common/include/itkPointSet.hxx Outdated Show resolved Hide resolved
Modules/Core/Common/include/itkPointSet.h Show resolved Hide resolved
Modules/Core/Mesh/include/itkMesh.h Outdated Show resolved Hide resolved
@PranjalSahu PranjalSahu force-pushed the pranjal21 branch 4 times, most recently from aa5e580 to 4548adc Compare March 8, 2022 16:54
@github-actions github-actions bot added area:IO Issues affecting the IO module area:ThirdParty Issues affecting the ThirdParty module type:Data Changes to testing data labels Mar 8, 2022
@github-actions github-actions bot removed area:Filtering Issues affecting the Filtering module area:IO Issues affecting the IO module area:ThirdParty Issues affecting the ThirdParty module type:Data Changes to testing data labels Mar 8, 2022
Copy link
Member

@dzenanz dzenanz left a comment

Choose a reason for hiding this comment

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

LGTM, assuming CI passes.

Modules/Core/Mesh/include/itkMesh.hxx Show resolved Hide resolved
Modules/Core/Mesh/include/itkMesh.hxx Show resolved Hide resolved
@thewtex
Copy link
Member

thewtex commented Mar 8, 2022

@PranjalSahu could you please rebase on master?

@PranjalSahu
Copy link
Contributor Author

yes working on it.

@thewtex
Copy link
Member

thewtex commented Mar 8, 2022

To investigate: Wrap PyVectorContainer for Point , direct copy, change Python test code to use SetPoints instead of SetPointsArray, etc.

Set Method needed for better Python support and Mesh serialization.
Already present GetPoints can be used in Python without any issues and
therefore no additional method is needed.
@PranjalSahu
Copy link
Contributor Author

@thewtex SetPointsArray is replaced with SetPoints. There are two versions for it now one
with argument type (itkVectorContainerULPF3 *) and other with (itkVectorContainerULF *).

The one with itkVectorContainerULF makes it easier to directly use NumPy arrays for setting
the points (no conversion to itkPoint needed). So the output from other libraries like scikit etc. (ex. Nx3 Numpy arrays) can be plugged directly.

I used the reinterpret_cast for this inside the newly added SetPoints:
auto * pointsPtr = reinterpret_cast<PointsContainer *>(points);

No need for assign operation is needed. Also, I found that for DynamicTraits it wasn't compiling as it
does not support direct assign operation (map).

Sample usage in Python:

points_vc = itk.vector_container_from_array(arr.flatten())
mesh.SetPoints(points_vc)

Copy link
Member

@thewtex thewtex left a comment

Choose a reason for hiding this comment

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

We have converged on an excellent outcome. @PranjalSahu thanks for your hard work. @GenevieveBuckley thanks for the review.

@PranjalSahu awesome to see the PointSet serialization here, too!



# Check the serialization of mesh
serialize_deserialize = pickle.loads(pickle.dumps(point_set))
Copy link
Member

Choose a reason for hiding this comment

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

💯

@thewtex thewtex merged commit 0e1c2c3 into InsightSoftwareConsortium:master Mar 16, 2022
@PranjalSahu PranjalSahu deleted the pranjal21 branch July 19, 2022 18:29
{
itkExceptionMacro("Number of entries in given 1d array incompatible with the point dimension");
}
auto * pointsPtr = reinterpret_cast<PointsContainer *>(points);
Copy link
Contributor

@N-Dekker N-Dekker Sep 11, 2024

Choose a reason for hiding this comment

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

I'm sorry but this reinterpret_cast<PointsContainer *>(points) very much looks like undefined behavior to me. (It tries to "reinterpret" the bits of an itk::VectorContainer of scalars as an itk::VectorContainer of itk::Point.) How did you find out that it seemed to work at all?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This can be tested by using a 1D array. I can write a small test case here if it is not present already.
I am checking if this is covered under test or not.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks @PranjalSahu I'm glad to see that you already tested this code:

// Insert the points using SetPointArray.
PointsVectorContainerPointer pointsArray = PointsVectorContainer::New();
// Test for in-compatible input array dimension
pointsArray->InsertElement(0, 1.0);
ITK_TRY_EXPECT_EXCEPTION(pset->SetPoints(pointsArray));
pointsArray->Reserve(numOfPoints * pointDimension);
int index = 0;
for (int i = 0; i < numOfPoints; ++i)
{
pointsArray->SetElement(index++, 1.0);
pointsArray->SetElement(index++, 2.0);
pointsArray->SetElement(index++, 3.0);
}
pset->SetPoints(pointsArray);
// Check the count of points after insertion using SetPoints.
pointsArrayAfter = pset->GetPoints();
if (static_cast<int>(pointsArrayAfter->Size()) != numOfPoints)
{
std::cerr << "Mismatch in count after inserting points." << std::endl;
return EXIT_FAILURE;
}

Nevertheless, it's officially undefined behavior. Sorry 🤷. The C++ Standard does not guarantee that it works. In theory, it might do anything. And in practice, undefined behavior does lead to trouble. I just asked about a similar example at the ACCU mailing list, and Jonathan Wakely (@jwakely) confirmed that it is indeed undefined behavior.

So I would prefer to see the reinterpret_cast<PointsContainer *> "trick" removed. But otherwise (at least), a note should be added that this function is unsafe.

for i in range(NumberOfCells):
cells_array[i, :] = [i * 1, (i + 1) * 2, (i + 2) * 3]

# Insert the cells in mesh by converting to 1D array
Copy link
Contributor Author

Choose a reason for hiding this comment

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

One similar test case is here which uses a 1D input array.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:Core Issues affecting the Core module area:Python wrapping Python bindings for a class type:Enhancement Improvement of existing methods or implementation type:Infrastructure Infrastructure/ecosystem related changes, such as CMake or buildbots type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants