CloudCompare / CCCoreLib

C++ library which provides data structures & algorithms for working with 3D point cloud data
Other
151 stars 50 forks source link

How to create a Mesh pointer and populate the vertices and triangles? #90

Closed wang89280 closed 1 year ago

wang89280 commented 1 year ago

I hope this message finds you well. I am currently working on a project that involves utilizing the registration tool within CCCoreLib to align two mesh files. However, I find myself in need of guidance regarding how to populate the vertices and triangles information within the GenericIndexedMesh pointer.

I have explored the documentation and resources available, but I am still unsure about the precise steps and code implementation required to achieve this. Your expertise in this matter would be immensely valuable to me, and I would greatly appreciate any assistance you can provide.

If you could kindly share some insights, code snippets, or step-by-step instructions on how to fill the vertices and triangles data into the GenericIndexedMesh pointer, it would be a significant help to my project.

Thank you in advance for your time and consideration. Your support means a lot to me, and I am sincerely looking forward to your response.

wang89280 commented 1 year ago

Please help me!Thanks you very much!

dgirardeau commented 1 year ago

Well, 'GenericIndexedMesh' is just an interface. And it's used throughout the code of CCCoreLib and CloudCompare, so you should find plenty of examples, but with a class that implements this interface. For your own needs, and since it seems you only use CCCoreLib, you can use the 'SimpleMesh' class: https://github.com/CloudCompare/CCCoreLib/blob/master/src/SimpleMesh.cpp

And here is an example of how to use it: https://github.com/CloudCompare/CCCoreLib/blob/master/src/Neighbourhood.cpp#L731

wang89280 commented 1 year ago

Special thanks for your help

wang89280 commented 1 year ago

I used the VTKSTLReader from VTK to read an STL file. I have already created a SimpleMesh pointer, and I used the addTriangle() function to populate the information for the triangular facets. However, I'm unsure how to populate the normals and vertices information inside it. Can you please provide me with some code assistance for this?

    vtkSmartPointer<vtkSTLReader> reader = vtkSmartPointer<vtkSTLReader>::New();
    reader->SetFileName(inputFilename.c_str());
    reader->Update();
    vtkSmartPointer<vtkPolyData> polyData = reader->GetOutput();
    vtkSmartPointer<vtkPoints> points = polyData->GetPoints();
        vtkSmartPointer<vtkCellArray> cells = polyData->GetPolys();
    CCCoreLib::SimpleMesh* inputModelMesh = new CCCoreLib::SimpleMesh(sc, true);
    vtkIdType cellCount = 0;
    for (vtkIdType i = 0; i < cells->GetNumberOfCells(); i++)
    {
        vtkIdType npts;
        vtkIdType* cellIds;
        cells->GetCell(i, npts, cellIds);
        inputModelMesh->addTriangle(cellIds[0], cellIds[1], cellIds[2]);
    }
    unsigned numberOfTriangles = inputModelMesh->size();
    std::cout << "numberOfTriangles: " << numberOfTriangles << std::endl;
    bool isnormal = inputModelMesh->normalsAvailable();
    std::cout << "isnormal: " << isnormal << std::endl;

The function Register need to modelMesh, because modelMesh's normals can improve register precision.

        static RESULT_TYPE Register(    GenericIndexedCloudPersist* modelCloud,
                GenericIndexedMesh* modelMesh,
                GenericIndexedCloudPersist* dataCloud,
                const Parameters& params,
                ScaledTransformation& totalTrans,
                double& finalRMS,
                unsigned& finalPointCount,
                GenericProgressCallback* progressCb = nullptr);
dgirardeau commented 1 year ago

For populating the vertices, that's part of the example I provided: https://github.com/CloudCompare/CCCoreLib/blob/master/src/Neighbourhood.cpp#L753C4-L753C12

And SimpleMesh (and the underlying point cloud structure used to store the vertices) only manage per-vertex normals. You can compute the per-vertex normals quite easily (see for instance https://github.com/CloudCompare/CloudCompare/blob/master/libs/qCC_db/src/ccMesh.cpp#L183). You'll have to use the 'reserveNormals' and 'addNormal' methods to populate the vertex normals.

wang89280 commented 1 year ago

Sorry for so much questions, but now I have successfully addTriangles and normals to mesh pointer. My actual requirement is to register mesh files with the "stl" suffix. When examining the registration function, I discovered that the Register function requires input parameters such as modelCloud, modelMesh, and dataCloud. Therefore, I called the following code.

When I call the CCCoreLib::ICPRegistrationTools::Register() function, and my input consists of three data sets, namely the reference cloud, reference mesh, and move cloud, I noticed that this operation takes an unusually long time.

I measured the execution time and found that this set of data registration takes approximately 18 minutes. However, if I input only the reference cloud and move cloud, the registration time is reduced to just 1 minute. Could you please provide some guidance? Below is a portion of my code. I look forward to your response.

// sc is reference cloud
CCCoreLib::PointCloud* sc = new CCCoreLib::PointCloud();
vtkSmartPointer<vtkSTLReader> reader = vtkSmartPointer<vtkSTLReader>::New();
reader->SetFileName(inputFilename.c_str());
reader->Update();

// add point to sc
vtkSmartPointer<vtkPolyData> polyData = reader->GetOutput();
vtkSmartPointer<vtkPoints> points = polyData->GetPoints();
for (vtkIdType i = 0; i < points->GetNumberOfPoints(); i++) 
{
      double point[3];
      points->GetPoint(i, point);
      CCVector3 vector(point[0], point[1], point[2]);
      sc->addPoint(vector);
}
sc->reserveNormals(points->GetNumberOfPoints());

// compute normals
vtkSmartPointer<vtkPolyDataNormals> normalsFilter = vtkSmartPointer<vtkPolyDataNormals>::New();
normalsFilter->SetInputConnection(reader->GetOutputPort());
normalsFilter->ComputePointNormalsOn();
normalsFilter->ComputeCellNormalsOff();
normalsFilter->Update();

// add normals to sc
vtkSmartPointer<vtkPolyData> polyDataWithNormals = normalsFilter->GetOutput();
vtkSmartPointer<vtkDataArray> normals = polyDataWithNormals->GetPointData()->GetNormals();
if (normals)
{
    for (vtkIdType i = 0; i < normals->GetNumberOfTuples(); i++)
    {
        double normal[3];
        normals->GetTuple(i, normal);
        CCVector3 N(normal[0], normal[1], normal[2]);
        sc->addNormal(N);
    }
}

// inputModelMesh  is reference --> won't move
vtkSmartPointer<vtkCellArray> cells = polyData->GetPolys();
// initiatal inputModelMesh and sc is input parameters
CCCoreLib::SimpleMesh* inputModelMesh = new CCCoreLib::SimpleMesh(sc, true);
// inputModelMesh  has vertices and normals(sourcing sc)
// add triangles to inputModelMesh
vtkIdType cellCount = 0;
for (vtkIdType i = 0; i < cells->GetNumberOfCells(); i++)
{
    vtkIdType npts;
    vtkIdType* cellIds;
    cells->GetCell(i, npts, cellIds);
    inputModelMesh->addTriangle(cellIds[0], cellIds[1], cellIds[2]);
}

// sc2 is dataCloud
CCCoreLib::PointCloud* sc2 = new CCCoreLib::PointCloud();
// .... Similar steps as above, including addPoint()

// register cloud
CCCoreLib::ICPRegistrationTools::ScaledTransformation* transofrmation = new CCCoreLib::ICPRegistrationTools::ScaledTransformation();
double RMS = 0.00;
unsigned int pointcount = 0;
CCCoreLib::ICPRegistrationTools::Parameters* params = new CCCoreLib::ICPRegistrationTools::Parameters();
params->filterOutFarthestPoints = false;
params->minRMSDecrease = 1.0e-6;
params->samplingLimit = 100000;
params->useC2MSignedDistances = false;
// input three data: sc、inputModelMesh(inlcude sc vertices triangles、verticrs normals)、sc2(data cloud)
CCCoreLib::ICPRegistrationTools::Register(sc, inputModelMesh, sc2, *params, *transofrmation, RMS, pointcount);

The function Register took 18 minutes when input parameters include sc and inputModelMesh and sc2. Can you tell me where am I wrong in this situation? Looking forward to your reply. Thank you.

dgirardeau commented 1 year ago

Are you maybe compiling in Debug mode? Because this mode is much slower than Release...

wang89280 commented 1 year ago

I compling in Release mode. Now I have given up registrating on mesh, just used two point clouds to registration.

Thank you for your previous help.