An edit plugin to select parts of a point cloud in a reasonable way.

It is based on a knn-graph, built on the mesh, where we search for near points.
It takes two parameters, a maximum distance of hop (between two adjacent points in the graph) and a maximum distance from the selected point. Both of them are chosen via mouse (actually via keyboard).
This commit is contained in:
Manuele Sabbadin msabbadin 2012-01-24 17:01:38 +00:00
parent 64e1e7020a
commit fd90fd27db
7 changed files with 671 additions and 0 deletions

View File

@ -0,0 +1,202 @@
#ifndef CONNECTEDCOMPONENT_H
#define CONNECTEDCOMPONENT_H
#include "knnGraph.h"
#include <vector>
#include <stack>
#include <vcg/complex/complex.h>
#include <vcg/complex/allocate.h>
#include <vcg/complex/algorithms/update/flag.h>
using namespace std;
using namespace vcg;
template <typename _MyMeshType, typename _MyVertexType>
class ComponentFinder {
public:
static std::vector<_MyVertexType*> &FindComponent(_MyMeshType& m, _MyVertexType& v, int numOfNeighbours, int numOfHop);
static std::vector<_MyVertexType*> &FindComponent(_MyMeshType& m, _MyVertexType& v, int numOfNeighbours, float dim, float maxHopDist, vector<_MyVertexType*> &borderVect, vector<_MyVertexType*> &notReachableVect);
static void DeletePerVertexAttribute(_MyMeshType& m);
static void Dijkstra(_MyMeshType& m, _MyVertexType& v, int numOfNeighbours, float maxHopDist, vector<_MyVertexType*> &notReachableVect);
};
/** This function returns a vector which stores pointers to the vertex of the connected component with
* center v and max distance (in number of edges) 'numOfHop' from v.
* It uses the knnGraph with k = numOfNeighbours to find the component.
* In order to compute the connected component it is used a Depth-Limited search
**/
template <typename _MyMeshType, typename _MyVertexType>
std::vector<_MyVertexType*> &ComponentFinder<_MyMeshType, _MyVertexType>::FindComponent(_MyMeshType& m, _MyVertexType& v, int numOfNeighbours, int numOfHop) {
vector<_MyVertexType*> *resultVect = new vector<_MyVertexType*>();
stack<_MyVertexType*> DFSStack;
stack<int> levelStack;
tri::UpdateFlags<_MyMeshType>::VertexClearV(m);
KNNTree<_MyMeshType, _MyVertexType>::MakeKNNTree(m, numOfNeighbours);
typename _MyMeshType::template PerVertexAttributeHandle<vector<_MyVertexType*>* > neighboursVect = vcg::tri::Allocator<_MyMeshType>::template GetPerVertexAttribute<vector<_MyVertexType*>* >(m,"KNNGraph");
typename vector<_MyVertexType*>::iterator it;
int actualLevel;
DFSStack.push(&v);
levelStack.push(0);
v.SetV();
resultVect->push_back(&v);
// The Depth-Limited Search
_MyVertexType* element;
while (!DFSStack.empty()) {
element = DFSStack.top();
actualLevel = levelStack.top();
DFSStack.pop();
levelStack.pop();
if (actualLevel < numOfHop) {
for (it = neighboursVect[element]->begin(); it != neighboursVect[element]->end(); it++) {
if (!((*it)->IsV())) {
DFSStack.push(*it);
levelStack.push(actualLevel + 1);
(*it)->SetV();
resultVect->push_back(*it);
}
}
}
}
KNNTree<_MyMeshType, _MyVertexType>::DeleteKNNTree(m);
return *resultVect;
}
template <typename _MyMeshType, typename _MyVertexType>
class Compare {
private:
typename _MyMeshType::template PerVertexAttributeHandle<float> *distFromCenter;
public:
Compare(typename _MyMeshType::template PerVertexAttributeHandle<float> *distFromCenter) {
this->distFromCenter = distFromCenter;
}
bool operator() (const _MyVertexType* lhs, const _MyVertexType* rhs) const {
return (*distFromCenter)[*lhs] > (*distFromCenter)[*rhs];
}
};
/** This function returns a vector which stores pointers to the vertex of the connected component with
* center v and max distance 'dim' from v.
* It uses the knnGraph with k = numOfNeighbours to find the component.
* In order to compute the connected component it is used a Shortest Path algorithm where maxHopDist is the
* maximum distance we want between two vertex (with the Shortest Path we also compute the geodesic distance)
**/
template <typename _MyMeshType, typename _MyVertexType>
std::vector<_MyVertexType*> &ComponentFinder<_MyMeshType, _MyVertexType>::FindComponent(_MyMeshType& m, _MyVertexType& v, int numOfNeighbours, float dim, float maxHopDist, vector<_MyVertexType*> &borderVect, vector<_MyVertexType*> &notReachableVect) {
vector<_MyVertexType*> *resultVect = new vector<_MyVertexType*>();
tri::UpdateFlags<_MyMeshType>::VertexClearV(m);
bool hasDistParam = vcg::tri::HasPerVertexAttribute(m, "DistParam");
typename _MyMeshType::template PerVertexAttributeHandle<float> distFromCenter;
if (hasDistParam) distFromCenter = vcg::tri::Allocator<_MyMeshType>::template GetPerVertexAttribute<float>(m, std::string("DistParam"));
else return *resultVect;
for (typename _MyMeshType::VertexIterator vi = m.vert.begin(); vi != m.vert.end(); vi++) {
if (distFromCenter[vi] < dim) resultVect->push_back(&*vi);
}
typename vector<_MyVertexType*>::iterator it;
for (it = notReachableVect.begin(); it != notReachableVect.end(); it++) {
if (distFromCenter[*it] < dim) borderVect.push_back(*it);
}
return *resultVect;
}
template <typename _MyMeshType, typename _MyVertexType>
void ComponentFinder<_MyMeshType, _MyVertexType>::Dijkstra(_MyMeshType& m, _MyVertexType& v, int numOfNeighbours, float maxHopDist, vector<_MyVertexType*> &notReachableVect) {
bool hasKNNGraph = vcg::tri::HasPerVertexAttribute(m, "KNNGraph");
bool hasDistParam = vcg::tri::HasPerVertexAttribute(m, "DistParam");
notReachableVect.clear();
typename _MyMeshType::template PerVertexAttributeHandle<float> distFromCenter;
if (!hasDistParam) {
distFromCenter = vcg::tri::Allocator<_MyMeshType>::template AddPerVertexAttribute<float>(m, std::string("DistParam"));
}
else distFromCenter = vcg::tri::Allocator<_MyMeshType>::template GetPerVertexAttribute<float>(m, std::string("DistParam"));
if (!hasKNNGraph) {
KNNTree<_MyMeshType, _MyVertexType>::MakeKNNTree(m, numOfNeighbours);
}
typename _MyMeshType::template PerVertexAttributeHandle<vector<_MyVertexType*>* > neighboursVect = vcg::tri::Allocator<_MyMeshType>::template GetPerVertexAttribute<vector<_MyVertexType*>* >(m,"KNNGraph");
typename vector<_MyVertexType*>::iterator it;
typedef priority_queue<_MyVertexType*, vector<_MyVertexType*>, Compare<_MyMeshType, _MyVertexType> > VertPriorityQueue;
Compare<_MyMeshType, _MyVertexType> Comparator(&distFromCenter);
VertPriorityQueue prQueue (Comparator);
for (typename _MyMeshType::VertexIterator vi = m.vert.begin(); vi != m.vert.end(); vi++)
distFromCenter[vi] = numeric_limits<float>::max();
distFromCenter[v] = 0.f;
tri::UpdateFlags<_MyMeshType>::VertexClearV(m);
prQueue.push(&v);
v.SetV();
float distance;
_MyVertexType* element;
while (!prQueue.empty()) {
element = prQueue.top();
prQueue.pop();
for (it = neighboursVect[element]->begin(); it != neighboursVect[element]->end(); it++) {
distance = vcg::Distance((*it)->P(), element->P());
if (distFromCenter[*element] + distance < distFromCenter[*it]) distFromCenter[*it] = distFromCenter[*element] + distance;
if (!((*it)->IsV()) && distance <= maxHopDist) {
prQueue.push(*it);
(*it)->SetV();
}
else if (distance > maxHopDist) notReachableVect.push_back(*it);
}
}
}
template <typename _MyMeshType, typename _MyVertexType>
void ComponentFinder<_MyMeshType, _MyVertexType>::DeletePerVertexAttribute(_MyMeshType& m) {
KNNTree<_MyMeshType, _MyVertexType>::DeleteKNNTree(m);
bool hasDistParam = vcg::tri::HasPerVertexAttribute(m, "DistParam");
if (hasDistParam) {
vcg::tri::Allocator<_MyMeshType>::DeletePerVertexAttribute(m, "DistParam");
}
return;
}
#endif // CONNECTEDCOMPONENT_H

View File

@ -0,0 +1,179 @@
/****************************************************************************
* MeshLab o o *
* A versatile mesh processing toolbox o o *
* _ O _ *
* Copyright(C) 2005 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#include <QtGui>
#include <math.h>
#include <stdlib.h>
#include <meshlab/glarea.h>
#include "edit_point.h"
#include <wrap/gl/pick.h>
#include <wrap/qt/gl_label.h>
#include "connectedComponent.h"
using namespace std;
using namespace vcg;
EditPointPlugin::EditPointPlugin() {}
const QString EditPointPlugin::Info() {
return tr("Select a region of the mesh");
}
void EditPointPlugin::Decorate(MeshModel &m, GLArea * gla, QPainter *p)
{
if(haveToPick)
{
glPushMatrix();
glMultMatrix(m.cm.Tr);
vector<CMeshO::VertexPointer> NewSel;
GLPickTri<CMeshO>::PickVert(cur.x(), gla->height() - cur.y(), m.cm, NewSel);
if(NewSel.size() > 0) {
startingVertex = NewSel.front();
ComponentFinder<CMeshO, CVertexO>::Dijkstra(m.cm, *startingVertex, 6, this->maxHop, this->NotReachableVector);
ComponentVector.push_back(startingVertex);
}
haveToPick = false;
glPopMatrix();
}
if (startingVertex != NULL) {
glPushMatrix();
glMultMatrix(m.cm.Tr);
glPushAttrib(GL_ENABLE_BIT );
glDisable(GL_LIGHTING);
glDisable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glDepthRange (0.0, 0.9999);
glDepthFunc(GL_LEQUAL);
glPointSize(6.f);
glBegin(GL_POINTS);
glColor4f(1,0,0,.6f);
for(vector<CMeshO::VertexPointer>::iterator vi = ComponentVector.begin(); vi != ComponentVector.end(); ++vi)
{
glVertex((*vi)->cP());
}
glEnd();
glBegin(GL_POINTS);
glColor4f(1,1,0,.6f);
for(vector<CMeshO::VertexPointer>::iterator vi = BorderVector.begin(); vi != BorderVector.end(); ++vi)
{
glVertex((*vi)->cP());
}
glEnd();
glPopAttrib();
glPopMatrix();
}
}
bool EditPointPlugin::StartEdit(MeshModel &m, GLArea *gla ) {
BorderVector = std::vector<CVertexO*>();
NotReachableVector = std::vector<CVertexO*>();
this->maxHop = m.cm.bbox.Diag() / 100.0;
return true;
}
void EditPointPlugin::EndEdit(MeshModel &m, GLArea *gla) {
ComponentFinder<CMeshO, CVertexO>::DeletePerVertexAttribute(m.cm);
}
void EditPointPlugin::mousePressEvent(QMouseEvent *ev, MeshModel &m, GLArea *gla ) {
startingVertex = NULL;
cur = ev->pos();
haveToPick = true;
this->isMousePressed = true;
this->dist = 0.0;
this->startingClick = vcg::Point2f(ev->x(), ev->y());
BorderVector.clear();
ComponentVector.clear();
}
void EditPointPlugin::mouseMoveEvent(QMouseEvent *ev, MeshModel &m, GLArea *gla ) {
if (this->isMousePressed && startingVertex != NULL) {
float distFromCenter = math::Sqrt((startingClick.X() - ev->x())*(startingClick.X() - ev->x()) + (startingClick.Y() - ev->y()) * (startingClick.Y() - ev->y()));
float perim = 2 * m.cm.bbox.DimX() + 2 * m.cm.bbox.DimY();
perim = (perim < 2 * m.cm.bbox.DimY() + 2 * m.cm.bbox.DimZ()) ? 2 * m.cm.bbox.DimY() + 2 * m.cm.bbox.DimZ() : perim;
perim = (perim < 2 * m.cm.bbox.DimX() + 2 * m.cm.bbox.DimZ()) ? 2 * m.cm.bbox.DimX() + 2 * m.cm.bbox.DimZ() : perim;
this->dist = (perim * distFromCenter) / gla->width();
BorderVector.clear();
ComponentVector = ComponentFinder<CMeshO, CVertexO>::FindComponent(m.cm, *startingVertex, 6, this->dist, this->maxHop, BorderVector, NotReachableVector);
}
}
void EditPointPlugin::mouseReleaseEvent(QMouseEvent *ev, MeshModel &m, GLArea *gla) {
gla->update();
}
void EditPointPlugin::keyPressEvent(QKeyEvent *ev, MeshModel &m, GLArea *gla) {
bool hopDistModified = false;
int keyPressed = ev->key();
if (startingVertex != NULL) {
switch (keyPressed) {
case Qt::Key_A:
this->maxHop *= 1.2;
hopDistModified = true;
break;
case Qt::Key_Z:
this->maxHop /= 1.2;
hopDistModified = true;
break;
default: break;
}
}
if (hopDistModified) {
ComponentFinder<CMeshO, CVertexO>::Dijkstra(m.cm, *startingVertex, 6, this->maxHop, this->NotReachableVector);
BorderVector.clear();
ComponentVector = ComponentFinder<CMeshO, CVertexO>::FindComponent(m.cm, *startingVertex, 6, this->dist, this->maxHop, BorderVector, NotReachableVector);
}
gla->update();
return;
}

View File

@ -0,0 +1,67 @@
/****************************************************************************
* MeshLab o o *
* A versatile mesh processing toolbox o o *
* _ O _ *
* Copyright(C) 2005 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#ifndef EDITPOINTPLUGIN_H
#define EDITPOINTPLUGIN_H
#include <QObject>
#include <common/interfaces.h>
class EditPointPlugin : public QObject, public MeshEditInterface
{
Q_OBJECT
Q_INTERFACES(MeshEditInterface)
public:
EditPointPlugin();
virtual ~EditPointPlugin() {}
static const QString Info();
bool StartEdit(MeshModel &/*m*/, GLArea * /*parent*/);
void EndEdit(MeshModel &/*m*/, GLArea * /*parent*/);
void Decorate(MeshModel &/*m*/, GLArea * /*parent*/, QPainter *p);
void mousePressEvent(QMouseEvent *, MeshModel &, GLArea * );
void mouseMoveEvent(QMouseEvent *, MeshModel &, GLArea * );
void mouseReleaseEvent(QMouseEvent *, MeshModel &/*m*/, GLArea * );
void keyPressEvent(QKeyEvent *, MeshModel &/*m*/, GLArea *);
private:
bool isMousePressed;
bool haveToPick;
CMeshO::VertexPointer startingVertex;
vcg::Point2f startingClick;
float dist;
float maxHop;
std::vector<CMeshO::VertexPointer> ComponentVector;
std::vector<CMeshO::VertexPointer> BorderVector;
std::vector<CMeshO::VertexPointer> NotReachableVector;
QPoint cur;
};
#endif

View File

@ -0,0 +1,23 @@
include (../../shared.pri)
HEADERS = \
edit_point.h \
edit_point_factory.h \
connectedComponent.h \
knnGraph.h
SOURCES = \
edit_point.cpp \
edit_point_factory.cpp \
TARGET = edit_point
QT += opengl
#RESOURCES = sampleedit.qrc

View File

@ -0,0 +1,57 @@
/****************************************************************************
* MeshLab o o *
* A versatile mesh processing toolbox o o *
* _ O _ *
* Copyright(C) 2005-2008 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#include "edit_point_factory.h"
#include "edit_point.h"
PointEditFactory::PointEditFactory()
{
editPoint = new QAction(QIcon(":/images/icon_info.png"),"Point", this);
actionList << editPoint;
foreach(QAction *editAction, actionList)
editAction->setCheckable(true);
}
//gets a list of actions available from this plugin
QList<QAction *> PointEditFactory::actions() const
{
return actionList;
}
//get the edit tool for the given action
MeshEditInterface* PointEditFactory::getMeshEditInterface(QAction *action)
{
if(action == editPoint)
{
return new EditPointPlugin();
} else assert(0); //should never be asked for an action that isn't here
}
QString PointEditFactory::getEditToolDescription(QAction *)
{
return EditPointPlugin::Info();
}
Q_EXPORT_PLUGIN(PointEditFactory)

View File

@ -0,0 +1,55 @@
/****************************************************************************
* MeshLab o o *
* A versatile mesh processing toolbox o o *
* _ O _ *
* Copyright(C) 2005-2008 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#ifndef EditPointFactoryPLUGIN_H
#define EditPointFactoryPLUGIN_H
#include <QObject>
#include <common/interfaces.h>
class PointEditFactory : public QObject, public MeshEditInterfaceFactory
{
Q_OBJECT
Q_INTERFACES(MeshEditInterfaceFactory)
public:
PointEditFactory();
virtual ~PointEditFactory() { delete editPoint; }
//gets a list of actions available from this plugin
virtual QList<QAction *> actions() const;
//get the edit tool for the given action
virtual MeshEditInterface* getMeshEditInterface(QAction *);
//get the description for the given action
virtual QString getEditToolDescription(QAction *);
private:
QList <QAction *> actionList;
QAction *editPoint;
};
#endif

View File

@ -0,0 +1,88 @@
#ifndef KNNGRAPH_H
#define KNNGRAPH_H
#include <Vector>
#include <vcg/complex/complex.h>
#include <vcg/complex/allocate.h>
#include <vcg/space/index/kdtree/kdtree.h>
using namespace std;
using namespace vcg;
template <typename _MyMeshType, typename _MyVertexType>
class KNNTree {
public:
static void MakeKNNTree(_MyMeshType& m, int numOfNeighbours);
static void DeleteKNNTree(_MyMeshType& m);
};
/**
* Static function to create and fill the PerVertexAttribute KNNGraph, which stores the k-nearest
* neighbours via vertex pointers
*/
template <typename _MyMeshType, typename _MyVertexType>
void KNNTree<_MyMeshType, _MyVertexType>::MakeKNNTree(_MyMeshType& m, int numOfNeighbours) {
//we search k+1 neighbours in order to exclude the queryPoint from the returned heap
int neighboursVectSize = numOfNeighbours + 1;
int neighbours; //number of neighbours found (no more than neighboursVectSize)
//we have to use the indices of the vertices, and they MUST be continuous
tri::Allocator<_MyMeshType>::CompactVertexVector(m);
//the PerVertexAttribute handles is create and each of the vector capacity set to the maximum possible
typename _MyMeshType::template PerVertexAttributeHandle<vector<_MyVertexType*>* > kNeighboursVect;
kNeighboursVect = vcg::tri::Allocator<_MyMeshType>::template AddPerVertexAttribute<vector<_MyVertexType*>* >(m, std::string("KNNGraph"));
for (typename _MyMeshType::VertexIterator vi = m.vert.begin(); vi != m.vert.end(); vi++) {
kNeighboursVect[vi] = new vector<_MyVertexType*>();
kNeighboursVect[vi]->reserve(neighboursVectSize);
}
//we create and fill the DataWrapper we need to pass the points to the KdTree
vector<typename _MyMeshType::CoordType> input(m.vn);
int i = 0;
for (typename _MyMeshType::VertexIterator vi = m.vert.begin(); vi != m.vert.end(); vi++, ++i) {
input[i] = vi->cP();
}
ConstDataWrapper<typename _MyMeshType::CoordType> DW(&(input[0]), input.size());
KdTree<float> tree(DW);
tree.setMaxNofNeighbors(neighboursVectSize);
//For each vertex we insert the k-nearest neighbours in the associated vector.
//If the number of the found vertices is less than the required we exclude the point with
//the highest value (inserted by the doQueryK function of the vcg library). Moreover
//we have to exclude the queryPoint!
int neightId = -1;
for (int j = 0; j < m.vn; j++) {
tree.doQueryK(m.vert[j].cP());
neighbours = tree.getNofFoundNeighbors();
for (int i = 0; i < neighbours; i++) {
neightId = tree.getNeighborId(i);
if (neightId < m.vn && (neightId != j))
kNeighboursVect[m.vert[j]]->push_back(&(m.vert[neightId]));
}
}
return;
}
/**
* Static function which removes the KNNGraph PerVertex attribute
*/
template <typename _MyMeshType, typename _MyVertexType>
void KNNTree<_MyMeshType, _MyVertexType>::DeleteKNNTree(_MyMeshType& m) {
bool hasKNNGraph = tri::HasPerVertexAttribute(m, "KNNGraph");
if (hasKNNGraph) {
vcg::tri::Allocator<_MyMeshType>::DeletePerVertexAttribute(m, "KNNGraph");
}
return;
}
#endif // KNNGRAPH_H