Decimating a Mesh with Associated Cell Data

All–

I’m getting some non-intuitive behavior (I don’t feel confident calling it a bug) when decimating a mesh which has point and cell data assigned. Say that I assign point data and cell data to a coarse mesh, and then decimate it. The point data behaves as I would expect–as points are removed from the mesh during decimation, the associated point data is also removed. The cell data, however, is not removed, and the data becomes disassociated with the cells of the decimated mesh.

I realize that this may not be a trivial problem to solve depending on how the decimation algorithm works (i.e., if the decimation algorithm involves more than simply deleting and repositioning existing faces); however, the problem seems analogous to decimating a mesh with materials applied to the faces, which behaves more-or-less as expected in software such as Blender.

What would be the best way of achieving this? I’ve had a look through the source code to see if there is an obvious place where I could, for example, delete the cell data as cells are deleted, but the Quad Edge Mesh code is a bit dense and I’m having trouble isolating the spot.

Here’s a minimally compilable example showing the behavior.

Best, and thanks as always,

–Davis

#include <itkQuadEdgeMesh.h>
#include <itkRegularSphereMeshSource.h>
#include <itkQuadEdgeMeshDecimationCriteria.h>
#include <itkSquaredEdgeLengthDecimationQuadEdgeMeshFilter.h>

const unsigned int Dimension = 3;
using TCoordinate = float;

using TMesh = itk::QuadEdgeMesh<TCoordinate, Dimension>;
using TCriterion  = itk::NumberOfFacesCriterion< TMesh >;
using TDecimation = itk::SquaredEdgeLengthDecimationQuadEdgeMeshFilter< TMesh,
                                                              TMesh,
                                                              TCriterion >;
using TSphereSource = itk::RegularSphereMeshSource<TMesh>;

int main( int argc, char ** argv )
{

  const auto source = TSphereSource::New();
  source->Update();

  const auto mesh = TMesh::New();
  mesh->Graft( source->GetOutput() );
  mesh->DisconnectPipeline();

  // Assign Labels to Points

  for (auto it = mesh->GetPoints()->Begin();
       it != mesh->GetPoints()->End();
       ++it)
    {
    mesh->SetPointData( it.Index(), it.Index() % 2 );
    }

  // Assign Labels to Faces

  for (auto it = mesh->GetCells()->Begin();
       it != mesh->GetCells()->End();
       ++it)
    {
    mesh->SetCellData( it.Index(), it.Index() % 2);
    }

  std::cout << "Original Mesh:" << '\n';

  std::cout << "Points: " << mesh->GetNumberOfPoints() << '\n';
  std::cout << "Point Data: " << mesh->GetPointData()->Size() << '\n';

  std::cout << "Cells: " << mesh->GetNumberOfCells() << '\n';
  std::cout << "Cell Data: " << mesh->GetCellData()->Size() << "\n\n";

  // Original Mesh:
  // Points: 66
  // Point Data: 66
  // Cells: 128
  // Cell Data: 128

  const auto criterion = TCriterion::New();
  criterion->SetTopologicalChange( true );
  criterion->SetNumberOfElements(  50 );

  const auto decimate = TDecimation::New();
  decimate->SetInput( mesh );
  decimate->SetCriterion( criterion );
  decimate->Update();

  const auto decimated_mesh = TMesh::New();
  decimated_mesh->Graft( decimate->GetOutput() );
  decimated_mesh->DisconnectPipeline();

  std::cout << "Decimated Mesh:" << '\n';

  std::cout << "Points: " << decimated_mesh->GetNumberOfPoints() << '\n';
  std::cout << "Point Data: " << decimated_mesh->GetPointData()->Size() << '\n';

  std::cout << "Cells: " << decimated_mesh->GetNumberOfCells() << '\n';
  std::cout << "Cell Data: " << decimated_mesh->GetCellData()->Size() << '\n';

  // Decimated Mesh:
  // Points: 27
  // Point Data: 27
  // Cells: 50
  // Cell Data: 128 <== Intuitively, I expect this to be 50

  return EXIT_SUCCESS;

}

Hi Davis,

This does seem like a bug. It does not appear that the required passing of cell data is implemented correctly.

Cell data is likely passed along here:

Point data is corrected here:

Cell data is a little more complicated since multiple input cells can map to an output cell.

An approach worth investigation is generation of the output cell data after the mesh has been processed, making use of the associated data structures used during processing:

Does this code decimate triangular meshes from vtkpolydata?

Thanks so much, @matt.mccormick – I’ll try to take a deeper dive this weekend.

@srbn.ghosh89 – no, this code is for ITK meshes. I did try using vtkObjectImporter to import a mesh with materials assigned to faces, and to decimate the result. VTK sort of does the right thing, in terms of retaining the materials, but it seems to accomplish it by dividing the mesh up into pieces based on the materials. This results in a fissure in the resulting mesh along the line of the material between borders.

1 Like