|
/****************************************************************************** |
|
* * |
|
* Copyright (C) 2018 Fondazione Istituto Italiano di Tecnologia (IIT) * |
|
* All Rights Reserved. * |
|
* * |
|
******************************************************************************/ |
|
|
|
/** |
|
* @file main.cpp |
|
* @authors: Ugo Pattacini <[email protected]> |
|
*/ |
|
|
|
#include <cstdlib> |
|
#include <memory> |
|
#include <cmath> |
|
#include <vector> |
|
#include <set> |
|
#include <algorithm> |
|
#include <string> |
|
#include <sstream> |
|
#include <fstream> |
|
|
|
#include <yarp/os/all.h> |
|
#include <yarp/sig/all.h> |
|
#include <yarp/math/Math.h> |
|
#include <yarp/math/Rand.h> |
|
|
|
#include <iCub/ctrl/clustering.h> |
|
|
|
#include <vtkSmartPointer.h> |
|
#include <vtkProperty.h> |
|
#include <vtkPolyDataMapper.h> |
|
#include <vtkPointData.h> |
|
#include <vtkSuperquadric.h> |
|
#include <vtkUnsignedCharArray.h> |
|
#include <vtkTransform.h> |
|
#include <vtkSampleFunction.h> |
|
#include <vtkContourFilter.h> |
|
#include <vtkRadiusOutlierRemoval.h> |
|
#include <vtkActor.h> |
|
#include <vtkOrientationMarkerWidget.h> |
|
#include <vtkAxesActor.h> |
|
#include <vtkRenderWindow.h> |
|
#include <vtkRenderWindowInteractor.h> |
|
#include <vtkRenderer.h> |
|
#include <vtkVertexGlyphFilter.h> |
|
#include <vtkCamera.h> |
|
#include <vtkInteractorStyleSwitch.h> |
|
|
|
#include "nlp.h" |
|
|
|
using namespace std; |
|
using namespace yarp::os; |
|
using namespace yarp::sig; |
|
using namespace yarp::math; |
|
using namespace iCub::ctrl; |
|
|
|
/****************************************************************/ |
|
class Object |
|
{ |
|
protected: |
|
vtkSmartPointer<vtkPolyDataMapper> vtk_mapper; |
|
vtkSmartPointer<vtkActor> vtk_actor; |
|
|
|
public: |
|
/****************************************************************/ |
|
vtkSmartPointer<vtkActor> &get_actor() |
|
{ |
|
return vtk_actor; |
|
} |
|
}; |
|
|
|
|
|
/****************************************************************/ |
|
class Points : public Object |
|
{ |
|
protected: |
|
vtkSmartPointer<vtkPoints> vtk_points; |
|
vtkSmartPointer<vtkUnsignedCharArray> vtk_colors; |
|
vtkSmartPointer<vtkPolyData> vtk_polydata; |
|
vtkSmartPointer<vtkVertexGlyphFilter> vtk_glyphFilter; |
|
|
|
public: |
|
/****************************************************************/ |
|
Points(const vector<Vector> &points, const int point_size) |
|
{ |
|
vtk_points=vtkSmartPointer<vtkPoints>::New(); |
|
for (size_t i=0; i<points.size(); i++) |
|
vtk_points->InsertNextPoint(points[i][0],points[i][1],points[i][2]); |
|
|
|
vtk_polydata=vtkSmartPointer<vtkPolyData>::New(); |
|
vtk_polydata->SetPoints(vtk_points); |
|
|
|
vtk_glyphFilter=vtkSmartPointer<vtkVertexGlyphFilter>::New(); |
|
vtk_glyphFilter->SetInputData(vtk_polydata); |
|
vtk_glyphFilter->Update(); |
|
|
|
vtk_mapper=vtkSmartPointer<vtkPolyDataMapper>::New(); |
|
vtk_mapper->SetInputConnection(vtk_glyphFilter->GetOutputPort()); |
|
|
|
vtk_actor=vtkSmartPointer<vtkActor>::New(); |
|
vtk_actor->SetMapper(vtk_mapper); |
|
vtk_actor->GetProperty()->SetPointSize(point_size); |
|
} |
|
|
|
/****************************************************************/ |
|
void set_points(const vector<Vector> &points) |
|
{ |
|
vtk_points=vtkSmartPointer<vtkPoints>::New(); |
|
for (size_t i=0; i<points.size(); i++) |
|
vtk_points->InsertNextPoint(points[i][0],points[i][1],points[i][2]); |
|
|
|
vtk_polydata->SetPoints(vtk_points); |
|
} |
|
|
|
/****************************************************************/ |
|
bool set_colors(const vector<vector<unsigned char>> &colors) |
|
{ |
|
if (colors.size()==vtk_points->GetNumberOfPoints()) |
|
{ |
|
vtk_colors=vtkSmartPointer<vtkUnsignedCharArray>::New(); |
|
vtk_colors->SetNumberOfComponents(3); |
|
for (size_t i=0; i<colors.size(); i++) |
|
vtk_colors->InsertNextTypedTuple(colors[i].data()); |
|
|
|
vtk_polydata->GetPointData()->SetScalars(vtk_colors); |
|
return true; |
|
} |
|
else |
|
return false; |
|
} |
|
|
|
/****************************************************************/ |
|
vtkSmartPointer<vtkPolyData> &get_polydata() |
|
{ |
|
return vtk_polydata; |
|
} |
|
}; |
|
|
|
|
|
/****************************************************************/ |
|
class Superquadric : public Object |
|
{ |
|
protected: |
|
vtkSmartPointer<vtkSuperquadric> vtk_superquadric; |
|
vtkSmartPointer<vtkSampleFunction> vtk_sample; |
|
vtkSmartPointer<vtkContourFilter> vtk_contours; |
|
vtkSmartPointer<vtkTransform> vtk_transform; |
|
|
|
public: |
|
/****************************************************************/ |
|
Superquadric(const Vector &r, const vector<double> &color) |
|
{ |
|
double bx=2.0*r[7]; |
|
double by=2.0*r[8]; |
|
double bz=2.0*r[9]; |
|
|
|
vtk_superquadric=vtkSmartPointer<vtkSuperquadric>::New(); |
|
vtk_superquadric->ToroidalOff(); |
|
vtk_superquadric->SetSize(1.0); |
|
vtk_superquadric->SetCenter(zeros(3).data()); |
|
|
|
vtk_superquadric->SetScale(r[7],r[8],r[9]); |
|
vtk_superquadric->SetPhiRoundness(r[10]); |
|
vtk_superquadric->SetThetaRoundness(r[11]); |
|
|
|
vtk_sample=vtkSmartPointer<vtkSampleFunction>::New(); |
|
vtk_sample->SetSampleDimensions(50,50,50); |
|
vtk_sample->SetImplicitFunction(vtk_superquadric); |
|
vtk_sample->SetModelBounds(-bx,bx,-by,by,-bz,bz); |
|
|
|
// The isosurface is defined at 0.0 as specified in |
|
// https://github.com/Kitware/VTK/blob/master/Common/DataModel/vtkSuperquadric.cxx |
|
vtk_contours=vtkSmartPointer<vtkContourFilter>::New(); |
|
vtk_contours->SetInputConnection(vtk_sample->GetOutputPort()); |
|
vtk_contours->GenerateValues(1,0.0,0.0); |
|
|
|
vtk_mapper=vtkSmartPointer<vtkPolyDataMapper>::New(); |
|
vtk_mapper->SetInputConnection(vtk_contours->GetOutputPort()); |
|
vtk_mapper->ScalarVisibilityOff(); |
|
|
|
vtk_actor=vtkSmartPointer<vtkActor>::New(); |
|
vtk_actor->SetMapper(vtk_mapper); |
|
vtk_actor->GetProperty()->SetColor(color[0],color[1],color[2]); |
|
vtk_actor->GetProperty()->SetOpacity(0.25); |
|
|
|
vtk_transform=vtkSmartPointer<vtkTransform>::New(); |
|
vtk_transform->Translate(r.subVector(0,2).data()); |
|
vtk_transform->RotateWXYZ((180.0/M_PI)*r[6],r.subVector(3,5).data()); |
|
vtk_actor->SetUserTransform(vtk_transform); |
|
} |
|
}; |
|
|
|
|
|
/****************************************************************/ |
|
class Finder : public RFModule |
|
{ |
|
Bottle outliersRemovalOptions; |
|
unsigned int uniform_sample; |
|
double random_sample; |
|
bool test_derivative; |
|
double inside_penalty; |
|
|
|
vector<Vector> all_points,in_points,out_points,dwn_points; |
|
vector<vector<unsigned char>> all_colors; |
|
|
|
unique_ptr<Points> vtk_all_points,vtk_out_points,vtk_dwn_points; |
|
|
|
RpcClient port; |
|
|
|
/****************************************************************/ |
|
void removeOutliers() |
|
{ |
|
double t0=Time::now(); |
|
if (outliersRemovalOptions.size()>=2) |
|
{ |
|
double radius=outliersRemovalOptions.get(0).asDouble(); |
|
int minpts=outliersRemovalOptions.get(1).asInt(); |
|
|
|
Property options; |
|
options.put("epsilon",radius); |
|
options.put("minpts",minpts); |
|
|
|
DBSCAN dbscan; |
|
map<size_t,set<size_t>> clusters=dbscan.cluster(all_points,options); |
|
|
|
size_t largest_class; size_t largest_size=0; |
|
for (auto it=begin(clusters); it!=end(clusters); it++) |
|
{ |
|
if (it->second.size()>largest_size) |
|
{ |
|
largest_size=it->second.size(); |
|
largest_class=it->first; |
|
} |
|
} |
|
|
|
auto &c=clusters[largest_class]; |
|
for (size_t i=0; i<all_points.size(); i++) |
|
{ |
|
if (c.find(i)==end(c)) |
|
out_points.push_back(all_points[i]); |
|
else |
|
in_points.push_back(all_points[i]); |
|
} |
|
} |
|
else |
|
in_points=all_points; |
|
|
|
double t1=Time::now(); |
|
yInfo()<<out_points.size()<<"outliers removed out of" |
|
<<all_points.size()<<"points in"<<t1-t0<<"[s]"; |
|
} |
|
|
|
/****************************************************************/ |
|
void sampleInliers() |
|
{ |
|
double t0=Time::now(); |
|
if (random_sample>=1.0) |
|
{ |
|
unsigned int cnt=0; |
|
for (auto &p:in_points) |
|
{ |
|
if ((cnt++%uniform_sample)==0) |
|
dwn_points.push_back(p); |
|
} |
|
} |
|
else |
|
{ |
|
set<unsigned int> idx; |
|
while (idx.size()<(size_t)(random_sample*in_points.size())) |
|
{ |
|
unsigned int i=(unsigned int)(Rand::scalar(0.0,1.0)*in_points.size()); |
|
if (idx.find(i)==idx.end()) |
|
{ |
|
dwn_points.push_back(in_points[i]); |
|
idx.insert(i); |
|
} |
|
} |
|
} |
|
|
|
double t1=Time::now(); |
|
yInfo()<<dwn_points.size()<<"samples out of" |
|
<<in_points.size()<<"inliers in"<<t1-t0<<"[s]"; |
|
} |
|
|
|
/****************************************************************/ |
|
Vector findSuperquadric() const |
|
{ |
|
Ipopt::SmartPtr<Ipopt::IpoptApplication> app=new Ipopt::IpoptApplication; |
|
app->Options()->SetNumericValue("tol",1e-6); |
|
app->Options()->SetNumericValue("constr_viol_tol",1e-3); |
|
app->Options()->SetIntegerValue("acceptable_iter",0); |
|
app->Options()->SetStringValue("mu_strategy","adaptive"); |
|
app->Options()->SetIntegerValue("max_iter",1000); |
|
app->Options()->SetStringValue("hessian_approximation","limited-memory"); |
|
app->Options()->SetStringValue("derivative_test",test_derivative?"first-order":"none"); |
|
app->Options()->SetIntegerValue("print_level",test_derivative?5:0); |
|
app->Initialize(); |
|
|
|
double t0=Time::now(); |
|
Ipopt::SmartPtr<SuperQuadricNLP> nlp=new SuperQuadricNLP(dwn_points,inside_penalty); |
|
Ipopt::ApplicationReturnStatus status=app->OptimizeTNLP(GetRawPtr(nlp)); |
|
double t1=Time::now(); |
|
|
|
Vector r=nlp->get_result(); |
|
yInfo()<<"center = ("<<r.subVector(0,2).toString(3,3)<<")"; |
|
yInfo()<<"angle ="<<r[3]<<"[deg]"; |
|
yInfo()<<"size = ("<<r.subVector(4,6).toString(3,3)<<")"; |
|
yInfo()<<"shape = ("<<r.subVector(7,8).toString(3,3)<<")"; |
|
yInfo()<<"found in ="<<t1-t0<<"[s]"; |
|
|
|
Vector rot(4,0.0); |
|
rot[2]=1.0; rot[3]=(M_PI/180.0)*r[3]; |
|
|
|
Vector r_=r.subVector(0,2); |
|
r_=cat(r_,rot); |
|
r_=cat(r_,r.subVector(4,6)); |
|
r_=cat(r_,r.subVector(7,8)); |
|
|
|
return r_; |
|
} |
|
|
|
/****************************************************************/ |
|
bool configure(ResourceFinder &rf) override |
|
{ |
|
Rand::init(); |
|
if (rf.check("file")) |
|
{ |
|
string file=rf.find("file").asString(); |
|
ifstream fin(file.c_str()); |
|
if (!fin.is_open()) |
|
{ |
|
yError()<<"Unable to open file \""<<file<<"\""; |
|
return false; |
|
} |
|
|
|
Vector p(3); |
|
vector<unsigned int> c_(3); |
|
vector<unsigned char> c(3); |
|
|
|
string line; |
|
while (getline(fin,line)) |
|
{ |
|
istringstream iss(line); |
|
if (!(iss>>p[0]>>p[1]>>p[2])) |
|
break; |
|
all_points.push_back(p); |
|
|
|
fill(c_.begin(),c_.end(),120); |
|
iss>>c_[0]>>c_[1]>>c_[2]; |
|
c[0]=(unsigned char)c_[0]; |
|
c[1]=(unsigned char)c_[1]; |
|
c[2]=(unsigned char)c_[2]; |
|
all_colors.push_back(c); |
|
} |
|
} |
|
else |
|
{ |
|
yError()<<"Unable to find command-line option \"--file\""; |
|
return false; |
|
} |
|
|
|
port.open("/test-superquadric:rpc"); |
|
if (!Network::connect(port.getName(),"/superquadric-model/rpc")) |
|
{ |
|
yError()<<"Unable to connect to superquadric-model"; |
|
close(); |
|
return false; |
|
} |
|
|
|
if (rf.check("remove-outliers")) |
|
if (const Bottle *ptr=rf.find("remove-outliers").asList()) |
|
outliersRemovalOptions=*ptr; |
|
|
|
uniform_sample=(unsigned int)rf.check("uniform-sample",Value(1)).asInt(); |
|
random_sample=rf.check("random-sample",Value(1.0)).asDouble(); |
|
inside_penalty=rf.check("inside-penalty",Value(1.0)).asDouble(); |
|
test_derivative=rf.check("test-derivative"); |
|
|
|
removeOutliers(); |
|
sampleInliers(); |
|
|
|
vtk_all_points=unique_ptr<Points>(new Points(all_points,2)); |
|
vtk_out_points=unique_ptr<Points>(new Points(out_points,4)); |
|
vtk_dwn_points=unique_ptr<Points>(new Points(dwn_points,1)); |
|
|
|
vtk_all_points->set_colors(all_colors); |
|
vtk_out_points->get_actor()->GetProperty()->SetColor(1.0,0.0,0.0); |
|
vtk_dwn_points->get_actor()->GetProperty()->SetColor(1.0,1.0,0.0); |
|
|
|
Bottle cmd,rep; |
|
cmd.addString("send_point_clouds"); |
|
Bottle &payLoad=cmd.addList(); |
|
for (auto &p:dwn_points) |
|
payLoad.addList().read(cat(p,zeros(3))); |
|
port.write(cmd,rep); |
|
|
|
cmd.clear(); |
|
cmd.addString("get_superq"); |
|
port.write(cmd,rep); |
|
|
|
Property prop; prop.fromString(rep.get(0).asList()->toString()); |
|
yInfo()<<"received options:"<<prop.toString(); |
|
|
|
Vector r_finitediff,v; |
|
prop.find("center").asList()->write(v); r_finitediff=cat(r_finitediff,v); |
|
prop.find("orientation").asList()->write(v); r_finitediff=cat(r_finitediff,v); |
|
prop.find("dimensions").asList()->write(v); r_finitediff=cat(r_finitediff,v); |
|
prop.find("exponents").asList()->write(v); r_finitediff=cat(r_finitediff,v); |
|
yInfo()<<"final superquadric_finitediff parameters:"<<r_finitediff.toString(3,3); |
|
unique_ptr<Superquadric> vtk_superquadric_finitediff=unique_ptr<Superquadric>(new Superquadric(r_finitediff,vector<double>{0.0,1.0,0.0})); |
|
|
|
Vector r_analytic=findSuperquadric(); |
|
unique_ptr<Superquadric> vtk_superquadric_analytic=unique_ptr<Superquadric>(new Superquadric(r_analytic,vector<double>{0.0,0.0,1.0})); |
|
|
|
vtkSmartPointer<vtkRenderer> vtk_renderer=vtkSmartPointer<vtkRenderer>::New(); |
|
vtkSmartPointer<vtkRenderWindow> vtk_renderWindow=vtkSmartPointer<vtkRenderWindow>::New(); |
|
vtk_renderWindow->SetSize(300,300); |
|
vtk_renderWindow->AddRenderer(vtk_renderer); |
|
vtkSmartPointer<vtkRenderWindowInteractor> vtk_renderWindowInteractor=vtkSmartPointer<vtkRenderWindowInteractor>::New(); |
|
vtk_renderWindowInteractor->SetRenderWindow(vtk_renderWindow); |
|
|
|
vtk_renderer->AddActor(vtk_all_points->get_actor()); |
|
vtk_renderer->AddActor(vtk_out_points->get_actor()); |
|
if (dwn_points.size()!=in_points.size()) |
|
vtk_renderer->AddActor(vtk_dwn_points->get_actor()); |
|
vtk_renderer->AddActor(vtk_superquadric_analytic->get_actor()); |
|
vtk_renderer->AddActor(vtk_superquadric_finitediff->get_actor()); |
|
vtk_renderer->SetBackground(0.1,0.2,0.2); |
|
|
|
vtkSmartPointer<vtkAxesActor> vtk_axes=vtkSmartPointer<vtkAxesActor>::New(); |
|
vtkSmartPointer<vtkOrientationMarkerWidget> vtk_widget=vtkSmartPointer<vtkOrientationMarkerWidget>::New(); |
|
vtk_widget->SetOutlineColor(0.9300,0.5700,0.1300); |
|
vtk_widget->SetOrientationMarker(vtk_axes); |
|
vtk_widget->SetInteractor(vtk_renderWindowInteractor); |
|
vtk_widget->SetViewport(0.0,0.0,0.2,0.2); |
|
vtk_widget->SetEnabled(1); |
|
vtk_widget->InteractiveOn(); |
|
|
|
vector<double> bounds(6),centroid(3); |
|
vtk_all_points->get_polydata()->GetBounds(bounds.data()); |
|
for (size_t i=0; i<centroid.size(); i++) |
|
centroid[i]=0.5*(bounds[i<<1]+bounds[(i<<1)+1]); |
|
|
|
vtkSmartPointer<vtkCamera> vtk_camera=vtkSmartPointer<vtkCamera>::New(); |
|
vtk_camera->SetPosition(centroid[0]+1.0,centroid[1],centroid[2]+0.5); |
|
vtk_camera->SetFocalPoint(centroid.data()); |
|
vtk_camera->SetViewUp(0.0,0.0,1.0); |
|
vtk_renderer->SetActiveCamera(vtk_camera); |
|
|
|
vtkSmartPointer<vtkInteractorStyleSwitch> vtk_style=vtkSmartPointer<vtkInteractorStyleSwitch>::New(); |
|
vtk_style->SetCurrentStyleToTrackballCamera(); |
|
vtk_renderWindowInteractor->SetInteractorStyle(vtk_style); |
|
vtk_renderWindowInteractor->Start(); |
|
|
|
return true; |
|
} |
|
|
|
/****************************************************************/ |
|
bool updateModule() override |
|
{ |
|
return false; |
|
} |
|
|
|
/****************************************************************/ |
|
bool close() override |
|
{ |
|
port.close(); |
|
return true; |
|
} |
|
}; |
|
|
|
|
|
/****************************************************************/ |
|
int main(int argc, char *argv[]) |
|
{ |
|
Network yarp; |
|
if (!yarp.checkNetwork()) |
|
{ |
|
yError()<<"Unable to find Yarp server!"; |
|
return EXIT_FAILURE; |
|
} |
|
|
|
ResourceFinder rf; |
|
rf.configure(argc,argv); |
|
|
|
Finder finder; |
|
return finder.runModule(rf); |
|
} |