27 #include <QApplication>
29 #include <QMainWindow>
30 #include <QProgressDialog>
31 #include <QtConcurrentMap>
65 ->getPointsInSphericalNeighbourhood(
66 *P, maxRadius, neighbours,
78 unsigned dimPerScale =
80 assert(desc.
params.size() == scaleCount * dimPerScale);
98 for (
int j = 0; j < n; ++j) {
105 for (
size_t i = 0; i < scaleCount; ++i) {
106 const double radius =
112 double squareRadius = radius * radius;
115 cloudViewer::DgmOctree::NeighboursSet::iterator up =
116 std::upper_bound(neighbours.begin(), neighbours.end(),
119 PointDescriptor::distComp);
120 if (up != neighbours.end()) {
121 size_t count = std::max<size_t>(1, up - neighbours.begin());
123 neighbours.resize(
count);
132 if (subset.
size() >= 3) {
137 unsigned lastIndex = subset.
size() - 1;
138 subset.
swap(0, lastIndex);
141 unsigned globalIndex =
152 ->getPoint(globalIndex);
155 computePoint2PlaneDistance(
156 centralPoint, lsPlane));
161 subset.
swap(0, lastIndex);
172 bool invalidScale =
false;
174 subset, radius, &(desc.
params[i * dimPerScale]),
186 for (
size_t j = i + 1; j < scaleCount; ++j) {
189 memcpy(&(desc.
params[j * dimPerScale]),
190 &(desc.
params[i * dimPerScale]),
191 sizeof(
float) * dimPerScale);
215 const std::vector<float>& sortedScales,
218 unsigned descriptorID ,
224 assert(!sortedScales.empty());
230 if (corePtsCount == 0) {
231 error =
"No core points?!";
234 size_t scaleCount = sortedScales.size();
235 if (scaleCount == 0) {
236 error =
"No scales?!";
244 error = QString(
"Unhandled descriptor ID (%1)!").arg(descriptorID);
249 error =
"Couldn't find a scalar field for core points!";
260 if (theOctree->
build(progressCb) == 0) {
261 error =
"Failed to build the octree (not enough memory?)";
271 qPrintable(QString(
"Core points: %1\nSource points: %2")
277 QApplication::processEvents();
283 corePointsDescriptors.resize(corePtsCount);
284 }
catch (
const std::bad_alloc&) {
289 success = corePointsDescriptors.
setScales(
293 error =
"Not enough memory!";
294 if (!inputOctree)
delete theOctree;
299 sortedScales.front() / 2;
316 bool useParallelStrategy =
true;
318 useParallelStrategy =
false;
321 std::vector<unsigned> corePointsIndexes;
322 if (useParallelStrategy) {
324 corePointsIndexes.resize(corePtsCount);
325 }
catch (
const std::bad_alloc&) {
327 useParallelStrategy =
false;
331 if (useParallelStrategy) {
332 for (
unsigned i = 0; i < corePtsCount; ++i) {
333 corePointsIndexes[i] = i;
336 if (maxThreadCount == 0) {
337 maxThreadCount = QThread::idealThreadCount();
339 assert(maxThreadCount <= QThread::idealThreadCount());
340 QThreadPool::globalInstance()->setMaxThreadCount(maxThreadCount);
341 QtConcurrent::blockingMap(corePointsIndexes,
345 for (
unsigned i = 0; i < corePtsCount; ++i) {
354 error =
"An error occurred during descriptors computation!";
355 else if (wasCanceled)
356 error =
"Process has been cancelled by the user";
375 if (!inputOctree)
delete theOctree;
381 const std::vector<float>& second) {
383 size_t firstCount = first.size();
384 if (firstCount != second.size())
return false;
386 for (
size_t i = 0; i < firstCount; ++i)
387 if (!Fpeq<float>(first[i], second[i]))
return false;
393 const std::vector<float>& second) {
394 size_t size1 = first.size();
395 size_t size2 = second.size();
396 size_t minCount =
std::min(size1, size2);
399 for (i = 0; i < minCount; ++i)
400 if (!Fpeq<float>(first[size1 - 1 - i], second[size2 - 1 - i]))
break;
412 if (
name.isEmpty())
name =
"unnamed";
420 assert(comboBox && dbRoot);
421 if (!comboBox || !dbRoot) {
427 int index = comboBox->currentIndex();
432 unsigned uniqueID = comboBox->itemData(index).toUInt();
444 const std::vector<float>& scales,
448 if (descriptors1.empty() || descriptors2.empty()) {
455 size_t nsamples1 = descriptors1.size();
458 for (
size_t i = 0; i < nsamples1; ++i) {
459 float d = classifier.
classify(descriptors1[i]);
465 sumd +=
static_cast<double>(d);
466 sumd2 +=
static_cast<double>(d * d);
468 params.mu1 = sumd /
static_cast<double>(nsamples1);
469 params.var1 = sumd2 /
static_cast<double>(nsamples1) -
475 size_t nsamples2 = descriptors2.size();
478 for (
size_t i = 0; i < nsamples2; ++i) {
479 float d = classifier.
classify(descriptors2[i]);
485 sumd +=
static_cast<double>(d);
486 sumd2 +=
static_cast<double>(d * d);
488 params.mu2 = sumd /
static_cast<double>(nsamples2);
489 params.var2 = sumd2 /
static_cast<double>(nsamples2) -
500 const std::vector<float>& scales,
505 size_t nsamples1 = descriptors1.size();
506 size_t nsamples2 = descriptors2.size();
508 if (nsamples1 == 0 || nsamples2 == 0) {
524 assert((descriptors1.front().params.size() % dimPerScale) == 0);
525 size_t paramsCount = descriptors1.front().params.size() / dimPerScale;
526 size_t scaleCount = scales.size();
527 assert(scaleCount <= paramsCount);
528 scaleCount =
std::min(scaleCount, paramsCount);
530 classifier.
scales = scales;
535 size_t nsamples = nsamples1 + nsamples2;
536 size_t fdim = scaleCount * dimPerScale;
538 std::vector<LDATrainer::sample_type> samples;
539 std::vector<float> labels;
542 nanSample.set_size(fdim, 1);
543 samples.resize(nsamples, nanSample);
547 }
catch (
const std::bad_alloc&) {
556 for (
size_t i = 0; i < nsamples1; ++i) {
561 (paramsCount - scaleCount) *
563 for (
size_t j = 0; j < fdim; ++j) {
564 sample(j) = desc.
params[shift + j];
572 for (
size_t i = 0; i < nsamples2; ++i) {
577 (paramsCount - scaleCount) *
579 for (
size_t j = 0; j < fdim; ++j) {
580 sample(j) = desc.
params[shift + j];
588 QMainWindow* parentWindow = (app ? app->
getMainWindow() :
nullptr);
591 QProgressDialog tempProgressDlg(
592 "LDA (step #1) in progress... please wait...", QString(), 0, 0,
594 tempProgressDlg.show();
595 QApplication::processEvents();
599 dlib::randomize_samples(samples, labels);
601 trainer.
train(10, samples, labels);
605 "Oups, it seems the LDA classifier just crashed!",
612 std::vector<float> proj1;
615 proj1.resize(nsamples);
616 }
catch (
const std::bad_alloc&) {
624 for (
size_t i = 0; i < nsamples; ++i) {
625 proj1[i] =
static_cast<float>(trainer.
predict(samples[i]));
630 "Oups, it seems the LDA classifier just crashed!",
642 dlib::matrix<LDATrainer::sample_type, 0, 1> basis;
644 basis.set_size(fdim);
645 for (
size_t i = 0; i < fdim; ++i) {
646 basis(i).set_size(fdim);
647 for (
size_t j = 0; j < fdim; ++j) basis(i)(j) = 0;
653 w_vect.set_size(fdim);
654 for (
size_t i = 0; i < fdim; ++i)
655 w_vect(i) =
static_cast<float>(trainer.
m_weights[i]);
661 std::vector<LDATrainer::sample_type> samples_reduced;
664 samples_reduced.resize(nsamples);
665 }
catch (
const std::bad_alloc&) {
672 for (
size_t i = 0; i < nsamples; ++i)
673 samples_reduced[i].set_size(fdim - 1);
676 for (
size_t si = 0; si < nsamples; ++si)
677 for (
size_t i = 1; i < fdim; ++i)
678 samples_reduced[si](i - 1) =
dlib::dot(samples[si], basis(i));
683 QProgressDialog tempProgressDlg(
684 "LDA (step #2) in progress... please wait...", QString(), 0, 0,
686 tempProgressDlg.show();
687 QApplication::processEvents();
689 orthoTrainer.
train(10, samples_reduced, labels);
693 "Oups, it seems the LDA classifier just crashed!",
702 }
catch (
const std::bad_alloc&) {
711 for (i = 0; i < fdim; ++i) w_vect(i) = 0;
712 for (i = 1; i < fdim; ++i)
713 w_vect += orthoTrainer.
m_weights[i - 1] * basis(i);
714 for (i = 0; i < fdim; ++i) orthoTrainer.
m_weights[i] = w_vect(i);
717 std::vector<float> proj2;
720 proj2.resize(nsamples);
721 }
catch (
const std::bad_alloc&) {
729 for (
size_t i = 0; i < nsamples; ++i) {
730 proj2[i] =
static_cast<float>(orthoTrainer.
predict(samples[i]));
735 "Oups, it seems the LDA classifier just crashed!",
752 proj1, proj2, labels);
761 trainer, orthoTrainer)) {
777 app->
dispToConsole(
"[Internal error] Invalid output MSC cloud!",
783 size_t cloudSize = nsamples;
784 if (evaluationDescriptors) cloudSize += evaluationDescriptors->size();
786 if (!mscCloud->
reserve(cloudSize)) {
789 "Not enough memory to determine the classifier "
795 bool hasColors =
true;
805 for (
size_t i = 0; i < cloudSize; ++i) {
807 sample.set_size(fdim);
813 desc = &descriptors1[i];
815 }
else if (i < nsamples) {
816 desc = &descriptors2[i - nsamples1];
818 }
else if (evaluationDescriptors) {
819 desc = &evaluationDescriptors->at(i - nsamples);
825 (paramsCount - scaleCount) *
827 for (
size_t j = 0; j < fdim; ++j) {
828 sample(j) = desc->
params[shift + j];
831 double x = trainer.
predict(sample);
832 double y = orthoTrainer.
predict(sample);
834 CCVector3(
static_cast<float>(x),
static_cast<float>(y), 0));
835 if (hasColors && col) {
842 if (evaluationDescriptors)
859 int minNeighborCount = 0;
863 const int c_searchTransSteps = 13;
864 const int c_searchDirCount = 90;
871 pDlg.setCancelButton(0);
872 pDlg.setWindowTitle(
"Determining boundary line");
873 pDlg.setRange(0,c_searchTransSteps*2);
874 pDlg.
setInfo(qPrintable(QString(
"Search steps: %1").arg(c_searchTransSteps*2)));
877 for (
int td=0; td<=c_searchTransSteps*2; ++td)
884 int sumds[c_searchDirCount];
888 for (
int sd=0; sd<c_searchDirCount; ++sd)
892 tableCosSin.
sines[sd] );
894 for (
int sp = -c_searchTransSteps; sp <= c_searchTransSteps; ++sp)
896 float s = sp * transStep;
910 if (sumds[sd] < sumds[minDirIndex])
914 if (td == 0 || sumds[minDirIndex] < minNeighborCount)
916 minNeighborCount = sumds[minDirIndex];
917 bestDir.
x = tableCosSin.
cosines[minDirIndex];
918 bestDir.
y = tableCosSin.
sines[minDirIndex];
919 bestStartingPoint = A;
929 boundaryCenter = bestStartingPoint;
930 boundaryDir = bestDir;
937 unsigned n1 = 0, n2 = 0;
945 const int c_searchSteps = 180;
950 pDlg.setWindowTitle(
"Determining boundary line");
951 pDlg.setRange(0, c_searchSteps);
953 qPrintable(QString(
"Search steps: %1").arg(c_searchSteps)));
955 for (
int sd = 1; sd < c_searchSteps; ++sd) {
957 tableCosSin.
sines[sd]);
959 dlib::matrix<double, 2, 2> basis;
962 basis(0, 0) = base_vec1.
x;
963 basis(0, 1) = base_vec2.
x;
964 basis(1, 0) = base_vec1.
y;
965 basis(1, 1) = base_vec2.
y;
968 double m1 = 0, m2 = 0;
969 std::vector<double> p1, p2;
972 for (
size_t i = 0; i < nsamples; ++i) {
973 dlib::matrix<double, 2, 1> P;
977 const double& d = P(0);
987 m1 /=
static_cast<double>(n1);
988 m2 /=
static_cast<double>(n2);
991 bool reversed =
false;
1000 for (
int i = 0; i <= 100; ++i) {
1001 double pos = m1 + i * (m2 - m1) / 100.0;
1003 size_t idx1 = std::lower_bound(p1.begin(), p1.end(), pos) -
1005 size_t idx2 = std::lower_bound(p2.begin(), p2.end(), pos) -
1008 double pr1 = idx1 /
static_cast<double>(n1);
1009 double pr2 = 1.0 - idx2 /
static_cast<double>(n2);
1010 double cba2 =
fabs(pr1 + pr2);
1011 if (cba2 > cba2_max) {
1013 double r = (pos - m1) / (m2 - m1);
1014 if (reversed) r = 1.0 - r;
1016 c1 + (c2 - c1) *
static_cast<float>(r);
1017 boundaryCenter = center;
1018 boundaryDir = base_vec2;
1031 classifier.
path.resize(2);
1032 classifier.
path[0] = boundaryCenter + boundaryDir * l;
1033 classifier.
path[1] = boundaryCenter - boundaryDir * l;
constexpr ScalarType NAN_VALUE
NaN as a ScalarType value.
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
float PointCoordinateType
Type of the coordinates of a (N-D) point.
cmdLineReadable * params[]
std::vector< float > weightsAxis2
unsigned dimPerScale
Dimension (per-scale)
unsigned descriptorID
Associated descriptor ID (see ccPointDescriptor.h)
std::vector< float > weightsAxis1
std::vector< Point2D > path
std::vector< float > scales
Associated scales.
float classify(const CorePointDesc &mscdata) const
Classification in MSC space.
Set of (core) point descriptors.
const unsigned descriptorID() const
Returns associated descriptor ID.
bool setScales(const std::vector< float > &scales)
Sets associated scales.
const unsigned dimPerScale() const
Returns the number of dimensions per scale.
void setDescriptorID(unsigned ID)
Sets associated descriptor ID.
void setDimPerScale(unsigned dim)
Sets associated descriptor ID.
double predict(const sample_type &data) const
std::vector< float > m_weights
Classifier weights.
trained_function_type train(const std::vector< sample_type > &samplesvec, const std::vector< float > &labels) const
dlib::matrix< float, 0, 1 > sample_type
Generic parameters 'computer' class (at a given scale)
static ScaleParamsComputer * GetByID(unsigned descID)
Vault: returns the computer corresponding to the given ID.
void normalize()
Sets vector norm to unity.
virtual void showColors(bool state)
Sets colors visibility.
A 3D cloud interface with associated features (color, normals, octree, etc.)
virtual ccOctree::Shared computeOctree(cloudViewer::GenericProgressCallback *progressCb=nullptr, bool autoAddChild=true)
Computes the cloud octree.
ccBBox getOwnBB(bool withGLFeatures=false) override
Returns the entity's own bounding-box.
virtual ccOctree::Shared getOctree() const
Returns the associated octree (if any)
Hierarchical CLOUDVIEWER Object.
ccHObject * find(unsigned uniqueID)
Finds an entity in this object hierarchy.
virtual QString getName() const
Returns object name.
virtual unsigned getUniqueID() const
Returns object unique ID.
bool isA(CV_CLASS_ENUM type) const
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
bool reserveTheRGBTable()
Reserves memory to store the RGB colors.
void clear() override
Clears the entity from all its points and features.
void addRGBColor(const ecvColor::Rgb &C)
Pushes an RGB color on stack.
A scalar field associated to display-related parameters.
Vector3Tpl< T > getDiagVec() const
Returns diagonal vector.
T getMaxBoxDim() const
Returns maximal box dimension.
The octree structure used throughout the library.
unsigned char findBestLevelForAGivenNeighbourhoodSizeExtraction(PointCoordinateType radius) const
int getPointsInSphericalNeighbourhood(const CCVector3 &sphereCenter, PointCoordinateType radius, NeighboursSet &neighbours, unsigned char level) const
Returns the points falling inside a sphere.
int build(GenericProgressCallback *progressCb=nullptr)
Builds the structure.
std::vector< PointDescriptor > NeighboursSet
A set of neighbours.
virtual bool enableScalarField()=0
Enables the scalar field associated to the cloud.
virtual unsigned size() const =0
Returns the number of points.
A generic 3D point cloud with index-based point access.
virtual void stop()=0
Notifies the fact that the process has ended.
virtual void setInfo(const char *infoStr)=0
Notifies some information about the ongoing process.
virtual void setMethodTitle(const char *methodTitle)=0
Notifies the algorithm title.
virtual bool textCanBeEdited() const
Returns whether the dialog title and info can be updated or not.
const PointCoordinateType * getLSPlane()
Returns best interpolating plane equation (Least-square)
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
A very simple point cloud (no point duplication)
virtual void swap(unsigned i, unsigned j)
Swaps two point references.
virtual bool addPointIndex(unsigned globalIndex)
Point global index insertion mechanism.
unsigned size() const override
Returns the number of points.
virtual unsigned getPointGlobalIndex(unsigned localIndex) const
virtual bool resize(unsigned n)
Presets the size of the vector used to store point references.
virtual bool reserve(unsigned n)
Reserves some memory for hosting the point references.
void setValue(std::size_t index, ScalarType value)
unsigned currentSize() const
Main application interface (for plugins)
virtual QMainWindow * getMainWindow()=0
Returns main window.
virtual void dispToConsole(QString message, ConsoleMessageLevel level=STD_CONSOLE_MESSAGE)=0
Graphical progress indicator (thread-safe)
virtual void setInfo(const char *infoStr) override
Notifies some information about the ongoing process.
__host__ __device__ float dot(float2 a, float2 b)
__host__ __device__ float2 fabs(float2 v)
static void error(char *msg)
constexpr Rgb red(MAX, 0, 0)
constexpr Rgb blue(0, 0, MAX)
constexpr Rgb lightGrey(static_cast< ColorCompType >(MAX *0.8), static_cast< ColorCompType >(MAX *0.8), static_cast< ColorCompType >(MAX *0.8))
void swap(cloudViewer::core::SmallVectorImpl< T > &LHS, cloudViewer::core::SmallVectorImpl< T > &RHS)
Implement std::swap in terms of SmallVector swap.
std::vector< float > params
Helper: pre-computed cos and sin values between 0 and Pi.
Structure used during nearest neighbour search.
static bool distComp(const PointDescriptor &a, const PointDescriptor &b)
Comparison operator.
static void GramSchmidt(dlib::matrix< LDATrainer::sample_type, 0, 1 > &basis, LDATrainer::sample_type &newX)
Gram-Schmidt process to re-orthonormalise the basis.
static bool DilateClassifier(Classifier &classifier, std::vector< float > &proj1, std::vector< float > &proj2, const std::vector< float > &labels, const std::vector< LDATrainer::sample_type > &samples, LDATrainer &trainer, LDATrainer &orthoTrainer)
static void ComputeReferencePoints(Classifier::Point2D &refpt_pos, Classifier::Point2D &refpt_neg, const std::vector< float > &proj1, const std::vector< float > &proj2, const std::vector< float > &labels, unsigned *_npos=0, unsigned *_nneg=0)
Compute pos. and neg. reference points.