2015-09-24 4 views
1

Я пытаюсь вычислить объектно-ориентированную ограничительную рамку для набора точек. Я использую C++ и собственную библиотеку линейных алгебр.Объект, ориентированный на 3D-объект, с использованием PCA

Я использую две записи в блогах в качестве руководств, но все же мои ограничивающие прямоугольники являются неправильными (см. Изображения).

blog post 1 blog post 2

Я надеюсь, что мой комментарий код понятен моей попытка, но суть алгоритма состоит в том, чтобы использовать PCA, чтобы найти базисные векторы для объектно-ориентированных координат.

Чтобы затем проецировать все точки в новый кадр, найдите минимальные и максимальные точки, которые определяют поле, затем проецируйте эти точки в исходную координатную рамку и визуализируйте их.

Я могу успешно сделать поле, но не является ограничивающей рамкой и, по-видимому, выровнен по отношению к нормальной оси x, y, z. Это ясно видно на первом изображении для каждого из двух указанных объектов.

Любая помощь была бы действительно оценена. Заранее спасибо.

// iglVertices is a X by 3 Eigen::::MatrixXf 
// Covariance matrix and eigen decomposition 
Eigen::MatrixXf centered = iglVertices.rowwise() - iglVertices.colwise().mean(); 
Eigen::MatrixXf cov = centered.adjoint() * centered; 
Eigen::SelfAdjointEigenSolver<Eigen::MatrixXf> eig(cov); 

//Setup homogenous tranformation to act as new basis functions for new coordinate frame 
auto basis = Eigen::Matrix4f(eig.eigenvectors().colwise().homogeneous().rowwise().homogeneous()); 
basis.row(3) = Eigen::Vector4f::Zero(); 
basis.col(3) = Eigen::Vector4f::Zero(); 
basis(3,3) = 1.0f; 

std::cout << "eig.eigenvectors() " << eig.eigenvectors() << std::endl; 
std::cout << "Basis " << basis << std::endl; 

//invert matrix and and transform points into new coordinate frame 
auto invBasis = basis.inverse(); 
auto newVertices = invBasis * iglVertices.rowwise().homogeneous().transpose(); 

//Find max and min for all of the new axis 
auto maxP = newVertices.rowwise().maxCoeff(); 
auto minP = newVertices.rowwise().minCoeff(); 

std::cout << "max " << maxP << std::endl; 
std::cout << "min " << minP << std::endl; 

//Find center and half extent in new coordinate frame 
auto center = Eigen::Vector4f((maxP + minP)/2.0); 
auto half_extent = Eigen::Vector4f((maxP - minP)/2.0); 

auto t = Eigen::Vector4f((basis * center)); 
std::cout << "t " << t << std::endl; 
//Update basis function with the translation between two coordinate origins 
//I don't actually understand why I need this and have tried without it but still my bounding 
//box is wrong 
basis.col(3) = Eigen::Vector4f(t[0], t[1], t[2], t[3]); 


std::cout << "Basis complete " << basis << std::endl; 

std::cout << "center " << center << std::endl; 
std::cout << "half_extent " << half_extent << std::endl; 

//This is the same as the previous minP/maxP but thought i should try this as 
// box is paramaterised with center and half-extent 
auto max = center + half_extent; 
auto min = center - half_extent; 

//Transform back into the original coordinates 
auto minNormalBasis = (basis * min).hnormalized(); 
auto maxNormalBasis = (basis * max).hnormalized(); 

std::cout << "min new coord" << min << std::endl; 
std::cout << "max new coord"<< max << std::endl; 
std::cout << "min old coord" << minNormalBasis << std::endl; 
std::cout << "max old coord"<< maxNormalBasis << std::endl; 

//Extract min and max 
auto min_x = minNormalBasis[0]; 
auto min_y = minNormalBasis[1]; 
auto min_z = minNormalBasis[2]; 

auto max_x = maxNormalBasis[0]; 
auto max_y = maxNormalBasis[1]; 
auto max_z = maxNormalBasis[2]; 

bBox.clear(); 
//Build box for rendering 
//Ordering specific to the faces I have manually generated 
bBox.push_back(trimesh::point(min_x, min_y, min_z)); 
bBox.push_back(trimesh::point(min_x, max_y, min_z)); 

bBox.push_back(trimesh::point(min_x, min_y, max_z)); 
bBox.push_back(trimesh::point(min_x, max_y, max_z)); 

bBox.push_back(trimesh::point(max_x, min_y, max_z)); 
bBox.push_back(trimesh::point(max_x, max_y, max_z)); 

bBox.push_back(trimesh::point(max_x, min_y, min_z)); 
bBox.push_back(trimesh::point(max_x, max_y, min_z)); 

Выход печати для примера спрей бутылка

eig.eigenvectors()   0 -0.999992 -0.00411613 
    -0.707107 -0.00291054 0.707101 
    0.707107 -0.00291054 0.707101 
Basis   0 -0.999992 -0.00411613   0 
    -0.707107 -0.00291054 0.707101   0 
    0.707107 -0.00291054 0.707101   0 
      0   0   0   1 
max 2.98023e-08 
    0.216833 
    0.582629 
      1 
min -2.98023e-08 
     -0.215 
    -0.832446 
      1 
t -0.000402254 
    -0.0883253 
    -0.0883253 
      1 
Basis complete   0 -0.999992 -0.00411613 -0.000402254 
    -0.707107 -0.00291054  0.707101 -0.0883253 
    0.707107 -0.00291054  0.707101 -0.0883253 
      0   0   0   1 
center   0 
0.000916399 
    -0.124908 
      1 
half_extent 2.98023e-08 
    0.215916 
    0.707537 
      0 
min new coord-2.98023e-08 
     -0.215 
    -0.832446 
      1 
max new coord2.98023e-08 
    0.216833 
    0.582629 
      1 
min old coord 0.218022 
-0.676322 
-0.676322 
max old coord-0.219631 
0.323021 
0.323021 

enter image description here enter image description here

enter image description here enter image description here

ответ

1

Вы должны вычислить 8 углы оси выровнен коробки в пределах кадра PCA, а затем применить поворот к ним:

bBox.push_back(eig.eigenvectors() * Vector3f(minP.x(), minP.y(), minP.z())); 
bBox.push_back(eig.eigenvectors() * Vector3f(minP.x(), maxP.y(), minP.z())); 

bBox.push_back(eig.eigenvectors() * Vector3f(minP.x(), minP.y(), maxP.z())); 
bBox.push_back(eig.eigenvectors() * Vector3f(minP.x(), maxP.y(), maxP.z())); 

... 

и вы также можете непосредственно вычислить newVertices как:

Matrix<float,3,Dynamic> newVertices = eig.eigenvectors().transpose() * iglVertices.transpose(); 

После этих изменений, ваш код будет уменьшен вдвое;)

И что еще более важно, пожалуйста, избежать использование ключевого слова auto, если вы не знаете, что делаете. В вашем примере большая часть его использования - очень плохая практика, а не ошибаться. Пожалуйста, прочтите это page.

+0

Спасибо за помощь, очень просто и лаконично в конце :). Для будущих читателей newVertices вычисляется с помощью Matrix newVertices = eig.eigenvectors(). Inverse(). Transpose() * iglVertices.transpose(); – TrueWheel

Смежные вопросы