-
Notifications
You must be signed in to change notification settings - Fork 104
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
277 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/* | ||
* Copyright (C) 2006-2018 Istituto Italiano di Tecnologia (IIT) | ||
* Copyright (C) 2006-2010 RobotCub Consortium | ||
* All rights reserved. | ||
* | ||
* This software may be modified and distributed under the terms | ||
* of the BSD-3-Clause license. See the accompanying LICENSE file for | ||
* details. | ||
*/ | ||
|
||
/** | ||
* \defgroup clustering Clustering | ||
* | ||
* @ingroup ctrlLib | ||
* | ||
* Classes for clustering. | ||
* | ||
* \author Ugo Pattacini | ||
* | ||
*/ | ||
|
||
#ifndef __CLUSTERING_H__ | ||
#define __CLUSTERING_H__ | ||
|
||
#include <map> | ||
#include <set> | ||
|
||
#include <yarp/os/Property.h> | ||
#include <yarp/sig/Vector.h> | ||
|
||
namespace iCub | ||
{ | ||
|
||
namespace ctrl | ||
{ | ||
|
||
/** | ||
* \ingroup clustering | ||
* | ||
* Abstract class for clustering. | ||
*/ | ||
class Clustering | ||
{ | ||
public: | ||
/** | ||
* Cluster the provided data. | ||
* @param data contains points to be clustered. | ||
* @param options contains clustering options. | ||
* @return clusters as a mapping between classes and the sets of | ||
* elements indexes wrt the original data; | ||
*/ | ||
virtual std::map<size_t,std::set<size_t>> cluster(const std::vector<yarp::sig::Vector> &data, | ||
const yarp::os::Property &options) = 0; | ||
|
||
/** | ||
* Virtual destructor. | ||
*/ | ||
virtual ~Clustering() { } | ||
}; | ||
|
||
|
||
/** | ||
* \ingroup clustering | ||
* | ||
* Data clustering based on DBSCAN algorithm. | ||
* | ||
* @note This implementation is based on the code available at | ||
* https://github.com/gyaikhom/dbscan. | ||
*/ | ||
class DBSCAN : public Clustering | ||
{ | ||
public: | ||
/** | ||
* Cluster the provided data. | ||
* @param data contains points to be clustered. | ||
* @param options contains clustering options. The available | ||
* options are: "epsilon" representing the | ||
* proximity sensitivity; "minpts" representing | ||
* the minimum number of neighbours. | ||
* @return clusters as a mapping between classes and the sets of | ||
* elements indexes wrt the original data; | ||
*/ | ||
std::map<size_t,std::set<size_t>> cluster(const std::vector<yarp::sig::Vector> &data, | ||
const yarp::os::Property &options) override; | ||
|
||
/** | ||
* Virtual destructor. | ||
*/ | ||
virtual ~DBSCAN() { } | ||
}; | ||
|
||
} | ||
|
||
} | ||
|
||
#endif | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
/* | ||
* Copyright (C) 2006-2018 Istituto Italiano di Tecnologia (IIT) | ||
* Copyright (C) 2006-2010 RobotCub Consortium | ||
* All rights reserved. | ||
* | ||
* This software may be modified and distributed under the terms | ||
* of the BSD-3-Clause license. See the accompanying LICENSE file for | ||
* details. | ||
*/ | ||
|
||
#include <memory> | ||
#include <vector> | ||
#include <yarp/math/Math.h> | ||
#include <iCub/ctrl/clustering.h> | ||
|
||
using namespace std; | ||
using namespace yarp::os; | ||
using namespace yarp::sig; | ||
using namespace yarp::math; | ||
using namespace iCub::ctrl; | ||
|
||
namespace iCub { | ||
namespace ctrl { | ||
namespace dbscan { | ||
enum class PointType { | ||
unclassified=-1, | ||
noise=-2 | ||
}; | ||
|
||
struct Data_t { | ||
const vector<Vector> &points; | ||
map<size_t,set<size_t>> &table; | ||
vector<int> ids; | ||
Data_t(const vector<Vector> &points_, | ||
map<size_t,set<size_t>> &table_) : | ||
points(points_), table(table_) { | ||
ids.assign(points.size(),(int)PointType::unclassified); | ||
} | ||
}; | ||
|
||
struct Node_t { | ||
size_t index; | ||
shared_ptr<Node_t> next; | ||
}; | ||
|
||
struct Epsilon_neighbours_t { | ||
size_t num_members; | ||
shared_ptr<Node_t> head; | ||
weak_ptr<Node_t> tail; | ||
}; | ||
|
||
/**********************************************************************/ | ||
shared_ptr<Node_t> create_node(const size_t index) | ||
{ | ||
shared_ptr<Node_t> node(new Node_t()); | ||
node->index=index; | ||
node->next=nullptr; | ||
return node; | ||
} | ||
|
||
/**********************************************************************/ | ||
void append(const size_t index, shared_ptr<Epsilon_neighbours_t> &en) | ||
{ | ||
shared_ptr<Node_t> node=create_node(index); | ||
if (en->head==nullptr) | ||
{ | ||
en->head=node; | ||
en->tail=node; | ||
} | ||
else | ||
{ | ||
en->tail.lock()->next=node; | ||
en->tail=node; | ||
} | ||
en->num_members++; | ||
} | ||
|
||
/**********************************************************************/ | ||
shared_ptr<Epsilon_neighbours_t> get_epsilon_neighbours(const size_t index, | ||
Data_t &augData, | ||
const double epsilon) | ||
{ | ||
shared_ptr<Epsilon_neighbours_t> en(new Epsilon_neighbours_t()); | ||
for (size_t i=0; i<augData.points.size(); i++) | ||
{ | ||
if ((i!=index) && (norm(augData.points[index]-augData.points[i])<=epsilon)) | ||
{ | ||
append(i,en); | ||
} | ||
} | ||
return en; | ||
} | ||
|
||
/**********************************************************************/ | ||
void spread(const size_t index, shared_ptr<Epsilon_neighbours_t> &seeds, | ||
const size_t id, Data_t &augData, const double epsilon, | ||
const size_t minpts) | ||
{ | ||
shared_ptr<Epsilon_neighbours_t> spread=get_epsilon_neighbours(index,augData,epsilon); | ||
if (spread->num_members>=minpts) | ||
{ | ||
for (shared_ptr<Node_t> node=spread->head; node!=nullptr; node=node->next) | ||
{ | ||
if ((augData.ids[node->index]==(int)PointType::noise) || | ||
(augData.ids[node->index]==(int)PointType::unclassified)) | ||
{ | ||
if (augData.ids[node->index]==(int)PointType::unclassified) | ||
{ | ||
append(node->index,seeds); | ||
} | ||
augData.ids[node->index]=(int)id; | ||
augData.table[id].insert(node->index); | ||
} | ||
} | ||
} | ||
} | ||
|
||
/**********************************************************************/ | ||
bool expand(const size_t index, const size_t id, Data_t &augData, | ||
const double epsilon, const size_t minpts) | ||
{ | ||
shared_ptr<Epsilon_neighbours_t> seeds=get_epsilon_neighbours(index,augData,epsilon); | ||
if (seeds->num_members<minpts) | ||
{ | ||
augData.ids[index]=(int)PointType::noise; | ||
return false; | ||
} | ||
else | ||
{ | ||
augData.ids[index]=(int)id; | ||
for (shared_ptr<Node_t> h=seeds->head; h!=nullptr; h=h->next) | ||
{ | ||
augData.ids[h->index]=(int)id; | ||
} | ||
for (shared_ptr<Node_t> h=seeds->head; h!=nullptr; h=h->next) | ||
{ | ||
spread(h->index,seeds,id,augData,epsilon,minpts); | ||
} | ||
return true; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
||
/**********************************************************************/ | ||
map<size_t,set<size_t>> DBSCAN::cluster(const vector<Vector> &data, | ||
const Property &options) | ||
{ | ||
double epsilon=options.check("epsilon",Value(1.0)).asDouble(); | ||
size_t minpts=(size_t)options.check("minpts",Value(2)).asInt(); | ||
|
||
map<size_t,set<size_t>> table; | ||
dbscan::Data_t augData(data,table); | ||
|
||
size_t id=0; | ||
for (size_t i=0; i<augData.points.size(); i++) | ||
{ | ||
if (augData.ids[i]==(int)dbscan::PointType::unclassified) | ||
{ | ||
if (dbscan::expand(i,id,augData,epsilon,minpts)) | ||
{ | ||
id++; | ||
} | ||
} | ||
} | ||
return table; | ||
} | ||
|