diff --git a/CgMath/CgCovariance.h b/CgMath/CgCovariance.h deleted file mode 100644 index 8157154776cb36500cf438a2335bead675b4fcc8..0000000000000000000000000000000000000000 --- a/CgMath/CgCovariance.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef CGCOVARIANCE_H -#define CGCOVARIANCE_H - -#include <vector> -#include "glm/glm.hpp" -#include "Eigen/Dense" - -inline glm::vec3 getCentroid(const std::vector<glm::vec3> &points) { - glm::vec3 center(0.); - for(unsigned int i = 0; i < points.size(); i++) center += points[i]; - center /= double(points.size()); - return center; -} - -inline glm::mat3 getCovarianceMatrix(const std::vector<glm::vec3> &points) { - unsigned int size = points.size(); - Eigen::MatrixXd mat(size, 3); - glm::vec3 centroid = getCentroid(points); - for(unsigned int i = 0; i < size; i++) { - mat(i, 0) = points[i][0] - centroid[0]; - mat(i, 1) = points[i][1] - centroid[1]; - mat(i, 2) = points[i][2] - centroid[2]; - } - Eigen::Matrix3d res = (1 / double(size)) * (mat.transpose() * mat); - glm::mat3 ret( - res(0, 0), res(0, 1), res(0, 2), - res(1, 0), res(1, 1), res(1, 2), - res(2, 0), res(2, 1), res(2, 2) - ); - return ret; -} - -#endif // CGCOVARIANCE_H \ No newline at end of file diff --git a/CgMath/CgDecomposition.h b/CgMath/CgDecomposition.h new file mode 100644 index 0000000000000000000000000000000000000000..fd262a871d2671603333eb2d6e98b3ecbf74f870 --- /dev/null +++ b/CgMath/CgDecomposition.h @@ -0,0 +1,66 @@ +#ifndef CGCOVARIANCE_H +#define CGCOVARIANCE_H + +#include <vector> +#include <iostream> +#include "glm/glm.hpp" +#include "Eigen/Dense" +#include "CgMath/CgEigenDecomposition3x3.h" +#include <CgMath/Eigen/SVD> +#include <CgMath/Eigen/Core> +using namespace Eigen; + +inline glm::vec3 getCentroid(const std::vector<glm::vec3> &points) { + glm::vec3 center(0.); + for(unsigned int i = 0; i < points.size(); i++) center += points[i]; + center /= double(points.size()); + return center; +} + +inline void calculateEigenDecomposition3x3() { + +} + +inline glm::mat3 getCovarianceMatrix(const std::vector<glm::vec3> &points) { + unsigned int size = points.size(); + Eigen::MatrixXd mat(size, 3); + glm::vec3 centroid = getCentroid(points); + for(unsigned int i = 0; i < size; i++) { + mat(i, 0) = points[i][0] - centroid[0]; + mat(i, 1) = points[i][1] - centroid[1]; + mat(i, 2) = points[i][2] - centroid[2]; + } + Eigen::Matrix3d res = (1 / double(size)) * (mat.transpose() * mat); + + glm::mat3 ret( + res(0, 0), res(0, 1), res(0, 2), + res(1, 0), res(1, 1), res(1, 2), + res(2, 0), res(2, 1), res(2, 2) + ); + return ret; +} + +inline glm::vec3 estimateNormalFromPCA(const std::vector<glm::vec3> &points) { + // calculate covariance matrix + unsigned int size = points.size(); + Eigen::MatrixXd mat(size, 3); + glm::vec3 centroid = getCentroid(points); + for(unsigned int i = 0; i < size; i++) { + mat(i, 0) = points[i][0] - centroid[0]; + mat(i, 1) = points[i][1] - centroid[1]; + mat(i, 2) = points[i][2] - centroid[2]; + } + Eigen::Matrix3d res = (1 / double(size)) * (mat.transpose() * mat); + + // get eigendecomposition + // the covariance matrix is a symmetric real matrix and therefore + // self-adjoint + Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> es(res); + + // The eigenvalues of a selfadjoint matrix are always real. + // the eigenvalues are sorted in ascending order + Eigen::Vector3d normal = es.eigenvectors().col(0); + return glm::vec3(normal(0), normal(1), normal(2)); +} + +#endif // CGCOVARIANCE_H \ No newline at end of file diff --git a/CgSceneGraph/CgPointCloud.cpp b/CgSceneGraph/CgPointCloud.cpp index 0b774e119fb0384baa1338cea85de682928c2e93..4a64ba93eaff5377cfc7c0f77e07b32ae91d3df9 100644 --- a/CgSceneGraph/CgPointCloud.cpp +++ b/CgSceneGraph/CgPointCloud.cpp @@ -61,13 +61,14 @@ void CgPointCloud::init( std::string filename, bool cheat_normals) { ObjLoader loader; loader.load(filename); loader.getPositionData(m_vertices); + std::cout << "loaded " << m_vertices.size() << " points" << std::endl; + std::cout << "using " << std::thread::hardware_concurrency() << " threads" << std::endl; m_centroid = getCentroid(m_vertices); - buildKdTree(&m_vertices.front(), &(*(m_vertices.end() - 1)), 0); + run_timed("build kd", 1, [&](){buildKdTree(&m_vertices.front(), &(*(m_vertices.end() - 1)), 0);}); calculateAABB(); - // run_timed(1, [&]() {estimateNormals();}); - estimateNormals(); - std::cout << "got " << m_vertices.size() << " points" << std::endl; + run_timed("est. normals", 1, [&](){estimateNormals();}); + //estimateNormals(); // calculate local coordinate system for splats // (arbitrary orientation of ellipse in plane) @@ -80,12 +81,6 @@ void CgPointCloud::init( std::string filename, bool cheat_normals) { for(unsigned int i = 0;i < m_vertices.size(); i++) { m_vertex_colors.push_back(glm::vec3(0.0,1.0,0.0)); } - - unsigned int k = 50; - // "fast" if k is relatively small, if k is on the order - // of m_vertices.size(), this is slower than brute force. - std::vector<unsigned int> neighbors = getNearestNeighborsFast(0, k); - setColors(neighbors, glm::vec3(0.0, 0.0, 1.0)); } void CgPointCloud::buildKdTree(glm::vec3* begin, glm::vec3* end, int depth) { @@ -112,6 +107,7 @@ void CgPointCloud::buildKdTree(glm::vec3* begin, glm::vec3* end, int depth) { void CgPointCloud::estimateNormals() { unsigned int neighbourhood_size = 25; + unsigned int thread_count = std::thread::hardware_concurrency(); // find point with highest y-value //std::vector<unsigned int> spanning_tree; @@ -133,22 +129,26 @@ void CgPointCloud::estimateNormals() { // build minimal spanning tree (for each index, record the index we came from) // traverse the tree, search neighborhoods - for(unsigned int i = 0; i < m_vertices.size(); i++) { - //std::cout << "alive, now " << i << std::endl; + std::vector<std::thread> threads; + threads.reserve(thread_count); + auto worker = [&]( + const unsigned int start_index, + const unsigned int count + ) { + std::vector<glm::vec3> nh(neighbourhood_size); + for(unsigned int i = start_index; i < start_index + count; i++) { std::vector<unsigned int> nh_indices = getNearestNeighborsFast(i, neighbourhood_size); - std::vector<glm::vec3> nh; - nh.resize(neighbourhood_size); - for(unsigned int j = 0; j < neighbourhood_size; j++) { - nh[j] = m_vertices[nh_indices[j]]; - } - glm::mat3 cov_mat = getCovarianceMatrix(nh); - CgEigenDecomposition3x3 eigen(cov_mat); - // The eigenvalues/vectors are sorted in increasing order, and we want the smallest one. - glm::vec3 normal = eigen.getEigenvectors()[0]; - m_vertex_normals[i] = glm::dot(normal, m_vertices[i] - m_centroid) > 0.0 - ? normal - : -normal; - } + for(unsigned int j = 0; j < neighbourhood_size; j++) nh[j] = m_vertices[nh_indices[j]]; + glm::vec3 normal = estimateNormalFromPCA(nh); + m_vertex_normals[i] = glm::dot(normal, m_vertices[i] - m_centroid) > 0.0 ? normal : -normal; + } + }; + unsigned int slice_size = m_vertices.size() / thread_count; + unsigned int slice_rest = m_vertices.size() % thread_count; + unsigned int t = 0; + for(t = 0; t < thread_count - 1; t++) threads.push_back(std::thread(worker, t * slice_size, slice_size)); + threads.push_back(std::thread(worker, t * slice_size, slice_size + slice_rest)); + for(t = 0; t < thread_count; t++) threads[t].join(); // select normal pointing roughly in the same direction as parent's normal } diff --git a/CgSceneGraph/CgPointCloud.h b/CgSceneGraph/CgPointCloud.h index 0103dc03d17dfc28b1c311148892edb073f6692c..df391353c74345b9382b9d62b0f08f14fab329aa 100644 --- a/CgSceneGraph/CgPointCloud.h +++ b/CgSceneGraph/CgPointCloud.h @@ -11,13 +11,12 @@ #include <utility> #include <functional> #include <tuple> -#include <set> +#include <thread> #include "CgBase/CgBasePointCloud.h" #include "CgSceneGraph/CgAABB.h" #include "CgBase/CgEnums.h" #include "CgUtils/ObjLoader.h" -#include "CgMath/CgCovariance.h" -#include "CgMath/CgEigenDecomposition3x3.h" +#include "CgMath/CgDecomposition.h" #include <glm/glm.hpp> #include <glm/gtx/norm.hpp> #include <glm/gtc/matrix_transform.hpp> diff --git a/CgSceneGraph/CgSceneControl.cpp b/CgSceneGraph/CgSceneControl.cpp index 870c95c9b50bf832d574658a3b59f5669c09e41a..7c9a79d7bde71f0038a9c8d659b97e1367b897f8 100644 --- a/CgSceneGraph/CgSceneControl.cpp +++ b/CgSceneGraph/CgSceneControl.cpp @@ -21,11 +21,7 @@ #include <glm/gtc/matrix_transform.hpp> #include <glm/ext.hpp> #include "CgUtils/ObjLoader.h" -#include "CgMath/CgEigenDecomposition3x3.h" #include <string> -#include <CgMath/Eigen/SVD> -#include <CgMath/Eigen/Core> -using namespace Eigen; CgSceneControl::CgSceneControl() { @@ -119,34 +115,6 @@ void CgSceneControl::calculatePickRay(float x, float y) { m_renderer->init(m_select_ray); } - -void CgSceneControl::calculateEigenDecomposition3x3() { - - std::vector<glm::vec3> testvecs{ - glm::vec3(1.0, 1.0, 1.0), - glm::vec3(1.0, 1.0, -1.0), - glm::vec3(1.0, -1.0, 1.0), - glm::vec3(-1.0, 4.3, 1.0), - glm::vec3(1.0, -2.15, -1.0), - glm::vec3(-.45, 1.0, -1.0), - glm::vec3(-1.0, -1.0, 1.0), - glm::vec3(-1.0, -2.0, -1.0), - }; - - glm::mat3 testmat = getCovarianceMatrix(testvecs); - - std::cout << "CovMat is\n" << glm::to_string(testmat) << std::endl; - - CgEigenDecomposition3x3 eigen(testmat); - glm::mat3 eigenvectors= eigen.getEigenvectors(); - glm::vec3 eigenvalues = eigen.getEigenvalues(); - - std::cout << "evec0: " << glm::to_string(eigenvectors[0]) << std::endl; - std::cout << "evec1: " << glm::to_string(eigenvectors[1]) << std::endl; - std::cout << "evec2: " << glm::to_string(eigenvectors[2]) << std::endl; - std::cout << "eval:" << glm::to_string(eigenvalues) << std::endl; -} - void CgSceneControl::calculateSingularValueDecomposition() { //init some arbitrary test matrix MatrixXd C(2,3); diff --git a/CgSceneGraph/CgSceneControl.h b/CgSceneGraph/CgSceneControl.h index 741aefb55b858e3db902c78fa0f0d2bc64ad7c1a..4fd2c38cb1e329780f9356b6e6de433c7dda4c86 100644 --- a/CgSceneGraph/CgSceneControl.h +++ b/CgSceneGraph/CgSceneControl.h @@ -39,7 +39,6 @@ private: CgPolyLine* m_select_ray; std::vector<CgQuad*> m_kd_tree_mesh; - void calculateEigenDecomposition3x3(); void calculateSingularValueDecomposition(); void createKdTreeViz(int max_depth); void handlePickEvent(float x, float y); diff --git a/CgUtils/Timer.h b/CgUtils/Timer.h index 9f1a4cced61f02fda6c3156047f6cbedc2bdc0b3..1aee94726c44d5e12498ddeefeafe3eb2955c5c3 100644 --- a/CgUtils/Timer.h +++ b/CgUtils/Timer.h @@ -9,12 +9,12 @@ // benchmarking utility // takes a closure without params that returns void and runs it // amount times, printing the duration in milliseconds to stdout afterwards -void run_timed(unsigned int amount, std::function<void ()> fn) { +void run_timed(std::string tag, unsigned int amount, std::function<void ()> fn) { auto t1 = std::chrono::high_resolution_clock::now(); for(int i = 0; i < amount; i++) fn(); auto t2 = std::chrono::high_resolution_clock::now(); std::chrono::duration<double, std::milli> ms_double = t2 - t1; - std::cout << "average: " << ms_double.count() / double(amount) << " ms" << std::endl; + std::cout << "average for " << tag << ": "<< ms_double.count() / double(amount) << " ms" << std::endl; } #endif // TIMER_H \ No newline at end of file diff --git a/ExerciseVC.pro b/ExerciseVC.pro index c7f87359d4602a70143360916e94010ef623de4d..c623731de3ce1f8fb608023e43b299c1a359356a 100644 --- a/ExerciseVC.pro +++ b/ExerciseVC.pro @@ -1,7 +1,7 @@ QT += core gui opengl widgets TEMPLATE = app TARGET = PointViewer -QMAKE_CXXFLAGS += -g -std=c++20 +QMAKE_CXXFLAGS += -std=c++20 -pthread CONFIG += c++20 SOURCES += main.cpp \ @@ -37,7 +37,7 @@ HEADERS += \ CgEvents/CgKdTreeEvent.h \ CgEvents/CgSplatEvent.h \ CgMath/CgEigenDecomposition3x3.h \ - CgMath/CgCovariance.h \ + CgMath/CgDecomposition.h \ CgMath/Eigen/Core \ CgMath/Eigen/Eigen \ CgMath/Eigen/SVD \