Qhull(http://www.qhull.org/)是一个强大的计算几何库,可以用于计算高维的convex hull, Delaunay triangulation, Voronoi diagram等,在python中调用非常方便,最近由于需要将项目迁移到C++,因此折腾了一下。qhull的c++接口并不太完善,很多接口是基于Rbox(生成数据),因此我根据自身需要,修改了qhull的c++接口。
总体要求:输入:3D/4D点集,输出:对应的凸包。
主要修改libqhullcpp文件夹中的:Qhull.h 和 Qhull.cpp
1 Qhull.h 代码修改
public:
void runQhull3D(const std::vector<cv::Vec3d> &points, const char* args);
void runQhull4D(const std::vector<cv::Vec4d> &points, const char* args);
void runQhull(const PointCoordinates &points, const char *qhullCommand2);
2 Qhull.cpp 代码修改
void Qhull::runQhull3D(const std::vector<cv::Vec3d> &points, const char* args)
{
m_externalPoints = new PointCoordinates(3, ""); //3 = dimension
vector<double> allPoints;
for each (cv::Vec3d p in points)
{
allPoints.push_back(p[0]);
allPoints.push_back(p[1]);
allPoints.push_back(p[2]);
}
m_externalPoints->append(allPoints); //convert to vector<double>
runQhull(*m_externalPoints, args);
}
void Qhull::runQhull4D(const std::vector<cv::Vec4d> &points, const char* args)
{
m_externalPoints = new PointCoordinates(4, ""); //3 = dimension
vector<double> allPoints;
for each (cv::Vec4d p in points)
{
allPoints.push_back(p[0]);
allPoints.push_back(p[1]);
allPoints.push_back(p[2]);
allPoints.push_back(p[3]);
}
m_externalPoints->append(allPoints); //convert to vector<double>
runQhull(*m_externalPoints, args);
}
void Qhull::runQhull(const PointCoordinates &points, const char *qhullCommand2)
{
runQhull(points.comment().c_str(), points.dimension(), points.count(), &*points.coordinates(), qhullCommand2);
}
3.测试代码
求如下立方体的凸包:
#include "libqhullcpp/QhullFacetList.h"
#include "libqhullcpp/Qhull.h"
#include <opencv2/core.hpp>
using namespace orgQhull;
using namespace std;
//遍历凸包所有的顶点
void print_convexhull_vertices(Qhull& qhull) {
cout << "vertices are: " << endl;
QhullVertexList list = qhull.vertexList();
for (QhullVertexList::iterator it = list.begin(); it != list.end(); it++)
{
QhullVertex v = *it;
QhullPoint p = v.point();
cout << "id: " << p.id() << " coord:" << p << endl;;
}
}
//遍历凸包上的面片
void print_facets(Qhull& qhull) {
cout << "faces are: " << endl;
QhullFacetList facets = qhull.facetList();
for (QhullFacetList::iterator it = facets.begin(); it != facets.end(); ++it)
{
if (!(*it).isGood()) continue;
QhullFacet f = *it;
QhullVertexSet vSet = f.vertices();
for (QhullVertexSet::iterator vIt = vSet.begin(); vIt != vSet.end(); ++vIt)
{
QhullVertex v = *vIt;
QhullPoint p = v.point();
cout << p.id() << " ";
}
cout << endl;
}
cout << endl;
}
//遍历凸包上面片的法向
//qhull的法向能保证一致的朝向外部,但三角面片的顶点顺序并不一定对,可以通过法向来矫正
void print_face_normal(Qhull& qhull) {
cout << "normal are: " << endl;
std::vector<std::pair<cv::Vec3d, double> > facetsNormals;
for each (QhullFacet facet in qhull.facetList().toStdVector())
{
if (facet.hyperplane().isValid())
{
auto coord = facet.hyperplane().coordinates();
cv::Vec3d normal(coord[0], coord[1], coord[2]);
double offset = facet.hyperplane().offset();
facetsNormals.push_back(std::pair<cv::Vec3d, double>(normal, offset));
cout << normal << endl;
}
}
}
void test() {
Qhull qhull;
std::vector<cv::Vec3d> vertices1(9);
vertices1[0] = cv::Vec3d(0, 0, 0);
vertices1[1] = cv::Vec3d(2, 0, 0);
vertices1[2] = cv::Vec3d(2, 2, 0);
vertices1[3] = cv::Vec3d(0, 2, 0);
vertices1[4] = cv::Vec3d(0, 0, 2);
vertices1[5] = cv::Vec3d(2, 0, 2);
vertices1[6] = cv::Vec3d(2, 2, 2);
vertices1[7] = cv::Vec3d(0, 2, 2);
qhull.runQhull3D(vertices1, "Qt");
print_convexhull_vertices(qhull);
print_facets(qhull);
print_face_normal(qhull);
}
输出:
vertices are:
id: 3 coord: 0 2 0
id: 2 coord: 2 2 0
id: 1 coord: 2 0 0
id: 0 coord: 0 0 0
id: 5 coord: 2 0 2
id: 4 coord: 0 0 2
id: 7 coord: 0 2 2
id: 6 coord: 2 2 2
faces are:
3 2 1
3 1 0
5 1 0
5 4 0
7 3 0
7 4 0
6 2 1
6 5 1
6 3 2
6 7 3
6 5 4
6 7 4
normal are:
[0, 0, -1]
[0, 0, -1]
[0, -1, 0]
[0, -1, 0]
[-1, 0, 0]
[-1, 0, 0]
[1, 0, 0]
[1, 0, 0]
[0, 1, 0]
[0, 1, 0]
[0, 0, 1]
[0, 0, 1]
参考:https://stackoverflow.com/questions/19530731/qhull-library-c-interface