ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
qAutoSeg.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #define CV_PCA_DATA_AS_ROW 150
9 
10 #include "qAutoSeg.h"
11 
12 #include <pcl/common/common.h>
13 #include <pcl/common/transforms.h>
14 #include <pcl/kdtree/kdtree_flann.h>
15 #include <pcl/point_cloud.h>
16 
17 #include <random>
18 
19 // Local
20 #include "profileImportDlg.h"
21 
22 // Qt
23 #include <QFile>
24 #include <QFileInfo>
25 #include <QMainWindow>
26 #include <QMessageBox>
27 #include <QSettings>
28 #include <QtGui>
29 #include <unordered_set>
30 
31 // CV_DB_LIB
33 #include <ecvCone.h>
34 #include <ecvFileUtils.h>
35 #include <ecvHObjectCaster.h>
36 #include <ecvMesh.h>
37 #include <ecvPointCloud.h>
38 #include <ecvPolyline.h>
39 #include <ecvProgressDialog.h>
40 #include <ecvScalarField.h>
41 
42 // CV_IO_LIB
43 #include <PlyFilter.h>
44 
45 // CWT
46 #define _USE_MATH_DEFINES
47 #include <algorithm>
48 #include <cmath>
49 #include <fstream>
50 #include <iostream>
51 #include <list>
52 #include <numeric>
53 #include <opencv2/core/core.hpp>
54 #include <opencv2/highgui/highgui.hpp>
55 #include <opencv2/imgcodecs.hpp>
56 #include <opencv2/imgproc/imgproc.hpp>
57 #include <opencv2/opencv.hpp>
58 #include <random>
59 #include <vector>
60 
61 // Subsampling pointclouds
62 #include <CVKdTree.h>
63 #include <CVPointCloud.h>
64 #include <CVTools.h>
65 #include <CloudSamplingTools.h>
69 #include <Neighbourhood.h>
70 #include <ReferenceCloud.h>
71 #include <ScalarField.h>
72 #include <ScalarFieldTools.h>
73 #include <SimpleMesh.h>
74 
75 #include <ctime>
76 
77 ccAutoSeg::ccAutoSeg(QObject* parent)
78  : QObject(parent),
79  ccStdPluginInterface(":/CC/plugin/qAutoSeg/info.json"),
80  m_action(nullptr) {}
81 
82 using namespace std;
83 using namespace cv;
84 
85 // Calculate std
86 double stddev(std::vector<double> const& func) {
87  double mean = std::accumulate(func.begin(), func.end(), 0.0) / func.size();
88  double sq_sum = std::inner_product(
89  func.begin(), func.end(), func.begin(), 0.0,
90  [](double const& x, double const& y) { return x + y; },
91  [mean](double const& x, double const& y) {
92  return (x - mean) * (y - mean);
93  });
94  return std::sqrt(sq_sum / (func.size() - 1));
95 }
96 
97 string dateStamp() {
98  time_t rawtime;
99  struct tm* timeinfo;
100  char buffer[80];
101 
102  time(&rawtime);
103  timeinfo = localtime(&rawtime);
104 
105  strftime(buffer, sizeof(buffer), "%H:%M:%S", timeinfo);
106  std::string str(buffer);
107 
108  string stamp("[");
109  stamp += str;
110  stamp += "]";
111 
112  return stamp;
113 }
114 
115 double optRotY(ccPointCloud*& cloud) {
116  pcl::PointCloud<pcl::PointXYZRGB>::Ptr pcl_cloud(
117  new pcl::PointCloud<pcl::PointXYZRGB>);
118  pcl_cloud->width = cloud->size();
119  pcl_cloud->height = 1;
120  pcl_cloud->points.resize(pcl_cloud->width * pcl_cloud->height);
121 
122  std::vector<int> v(cloud->size());
123  std::iota(v.begin(), v.end(), 0);
124  std::random_device rd; // non-deterministic generator
125  std::mt19937 gen(rd()); // to seed mersenne twister.
126  std::shuffle(v.begin(), v.end(), rd);
127  v.erase(v.begin(), v.begin() + (int)floor(cloud->size()));
128 
129  for (size_t i = 0; i < cloud->size(); i++) {
130  pcl_cloud->points[i].x = cloud->getPoint(i)->x;
131  pcl_cloud->points[i].y = cloud->getPoint(i)->y;
132  pcl_cloud->points[i].z = cloud->getPoint(i)->z;
133  }
134 
135  pcl::PointXYZRGB bbMin0, bbMax0;
136  pcl::getMinMax3D(*pcl_cloud, bbMin0, bbMax0);
137 
138  pcl::PointCloud<pcl::PointXYZRGB>::Ptr transformed_cloud(
139  new pcl::PointCloud<pcl::PointXYZRGB>);
140  transformed_cloud->width = cloud->size();
141  transformed_cloud->height = 1;
142  transformed_cloud->points.resize(transformed_cloud->width *
143  transformed_cloud->height);
144 
145  double th0 = 0;
146  double area0 = (bbMax0.x - bbMin0.x) * (bbMax0.z - bbMin0.z);
147  for (int th = -45; th < 46;
148  th++) { // Apply this to a sparser cloud to make the process lighter
149  double thD = (double)th * M_PI / 180.0;
150 
151  Eigen::Affine3f transform_2 = Eigen::Affine3f::Identity();
152 
153  // Define a translation
154  transform_2.translation() << 0, 0.0, 0.0;
155 
156  // The same rotation matrix as before; theta radians around Y axis
157  transform_2.rotate(Eigen::AngleAxisf(thD, Eigen::Vector3f::UnitY()));
158 
159  pcl::transformPointCloud(*pcl_cloud, *transformed_cloud, transform_2);
160 
161  pcl::PointXYZRGB bbMin, bbMax;
162  pcl::getMinMax3D(*transformed_cloud, bbMin, bbMax);
163  double area = (bbMax.x - bbMin.x) * (bbMax.z - bbMin.z);
164  if (area < area0) {
165  area0 = area;
166  th0 = thD;
167  }
168 
169  Eigen::Affine3f transform_inv = transform_2.inverse();
170 
171  pcl::transformPointCloud(*transformed_cloud, *pcl_cloud, transform_inv);
172  }
173 
174  return th0;
175 }
176 
177 void rotY(ccPointCloud*& cloud, double th0) {
178  pcl::PointCloud<pcl::PointXYZRGB>::Ptr pcl_cloud(
179  new pcl::PointCloud<pcl::PointXYZRGB>);
180  pcl_cloud->width = cloud->size();
181  pcl_cloud->height = 1;
182  pcl_cloud->points.resize(pcl_cloud->width * pcl_cloud->height);
183 
184  std::vector<int> v(cloud->size());
185  std::iota(v.begin(), v.end(), 0);
186  std::random_device rd; // non-deterministic generator
187  std::mt19937 gen(rd()); // to seed mersenne twister.
188  std::shuffle(v.begin(), v.end(), rd);
189  v.erase(v.begin(), v.begin() + (int)floor(cloud->size()));
190 
191  for (size_t i = 0; i < cloud->size(); i++) {
192  pcl_cloud->points[i].x = cloud->getPoint(i)->x;
193  pcl_cloud->points[i].y = cloud->getPoint(i)->y;
194  pcl_cloud->points[i].z = cloud->getPoint(i)->z;
195 
196  pcl_cloud->points[i].r = cloud->getPointColor(i).r;
197  pcl_cloud->points[i].g = cloud->getPointColor(i).g;
198  pcl_cloud->points[i].b = cloud->getPointColor(i).b;
199  }
200 
201  // Bring cloud to (0,0,0)
202  Eigen::Vector4d centroid;
203  pcl::compute3DCentroid(*pcl_cloud, centroid);
204 
205  Eigen::Affine3f t0 = Eigen::Affine3f::Identity();
206 
207  t0.translation() << -centroid[0], -centroid[1], -centroid[2];
208 
209  pcl::PointCloud<pcl::PointXYZRGB>::Ptr transformed0(
210  new pcl::PointCloud<pcl::PointXYZRGB>);
211  transformed0->width = pcl_cloud->size();
212  transformed0->height = 1;
213  transformed0->points.resize(transformed0->width * transformed0->height);
214 
215  pcl::transformPointCloud(*pcl_cloud, *transformed0, t0);
216 
217  // Rotate around Y axis
218  Eigen::Affine3f transform0 = Eigen::Affine3f::Identity();
219  transform0.translation() << 0.0, 0.0, 0.0;
220  transform0.rotate(Eigen::AngleAxisf(th0, Eigen::Vector3f::UnitY()));
221  pcl::transformPointCloud(*transformed0, *transformed0, transform0);
222 
223  // To original UCS
224  Eigen::Affine3f t1 = Eigen::Affine3f::Identity();
225  t1.translation() << centroid[0], centroid[1], centroid[2];
226  pcl::transformPointCloud(*transformed0, *transformed0, t1);
227 
228  // From PCL to cc object
229  ccPointCloud* cloudAligned = new ccPointCloud("Aligned");
230  for (int i = 0; i < transformed0->size(); i++) {
231  cloudAligned->reserveThePointsTable(1);
232  cloudAligned->reserveTheRGBTable();
233  CCVector3 p(transformed0->points[i].x, transformed0->points[i].y,
234  transformed0->points[i].z);
235  cloudAligned->addPoint(p);
236 
237  uchar rojo = transformed0->points[i].r;
238  uchar verde = transformed0->points[i].g;
239  uchar azul = transformed0->points[i].b;
240  const ecvColor::Rgb col{rojo, verde, azul};
241  cloudAligned->addRGBColor(col);
242  }
243 
244  cloud = cloudAligned;
245 }
246 
248  cv::Mat& corrMatS,
249  std::vector<cv::Point>& idxPxS,
250  std::vector<int>& idxPx1DS,
251  cv::Mat& imageBWS) {
252  // Correspondence 2D->1D. Each px has an index instead of two coords
253  int cntC = 0;
254  for (int y = 0; y < corrMatS.rows; y++) {
255  for (int x = 0; x < corrMatS.cols; x++) {
256  corrMatS.at<int>(y, x) = cntC;
257  cntC++;
258  }
259  }
260 
261  // Binary
262  int s = cloud->size();
263  const CCVector3* pointC0;
264  for (int i = 0; i < s; i++) {
265  // depth
266  pointC0 = cloud->getPoint(i);
267  int z = floor(pointC0->z);
268  int x = floor(pointC0->x);
269  imageBWS.at<uchar>(z, x) = 255;
270 
271  // idxPx per 3D point
272 
273  idxPxS.push_back(Point(z, x));
274  idxPx1DS.push_back(corrMatS.at<int>(z, x));
275  }
276 }
277 
278 // Extract Skeleton from Binary (CV_8U)
279 cv::Mat skeleton(cv::Mat I, bool flagInv) {
280  cv::Mat toSkel;
281  if (flagInv) { // If black-white inversion is required
282  toSkel = 255 * cv::Mat::ones(I.rows, I.cols, CV_8U) - I;
283  } else {
284  toSkel = I;
285  }
286 
287  cv::Mat skel(toSkel.size(), CV_8U, cv::Scalar(0));
288  cv::Mat temp;
289  cv::Mat eroded;
290 
291  cv::Mat element =
292  cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3, 3));
293  int iterations = 0;
294 
295  bool done;
296  do {
297  cv::erode(toSkel, eroded, element);
298  cv::dilate(eroded, temp, element);
299  cv::subtract(toSkel, temp, temp);
300  cv::bitwise_or(skel, temp, skel);
301  eroded.copyTo(toSkel);
302 
303  done = (cv::countNonZero(toSkel) == 0);
304  iterations++;
305  } while (!done && (iterations < 100));
306 
307  return skel;
308 }
309 
310 // Extract elements of a vector given the indices
311 std::vector<cv::Point> extractVectorFromIdx(std::vector<cv::Point> v,
312  vector<int> idx) {
313  std::vector<cv::Point> subset;
314 
315  for (int i = 0; i < idx.size(); i++) {
316  Point x(v.at(idx.at(i)).x, v.at(idx.at(i)).y);
317  subset.push_back(x);
318  }
319  return subset;
320 }
321 
322 // Populate a matrix with a given value
323 void populateMat(cv::Mat& populated,
324  std::vector<cv::Point> pixList,
325  int value) {
326  for (int i = 0; i < pixList.size(); i++) {
327  populated.at<uchar>((pixList.at(i).x), (pixList.at(i).y)) = value;
328  }
329 }
330 
331 // Calculate difference set in two pixel lists (Matlab setdiff equivalent).
332 // Returns pixels in listOld that are not in listNew
333 std::vector<cv::Point> setdiffPixs(std::vector<cv::Point> listNew,
334  std::vector<cv::Point> listOld) {
335  std::vector<cv::Point> setdiffList;
336  int cnt;
337  for (int i = 0; i < listOld.size(); i++) {
338  cnt = 0;
339  for (int j = 0; j < listNew.size(); j++) {
340  if (listOld.at(i).x == listNew.at(j).x &&
341  listOld.at(i).y == listNew.at(j).y) {
342  cnt++;
343  }
344  }
345  if (cnt == 0) {
346  Point x(listOld.at(i).x, listOld.at(i).y);
347  setdiffList.push_back(x);
348  }
349  }
350  return setdiffList;
351 }
352 
353 // Calculate intersection set in two pixel lists. Returns idx pixels in listNew
354 // that are in listOld
355 std::vector<int> setIntersectIdxPixs(std::vector<cv::Point> listNew,
356  std::vector<cv::Point> listOld) {
357  std::vector<int> commonList;
358 
359  // Common
360  for (int i = 0; i < listNew.size(); i++) {
361  for (int j = 0; j < listOld.size(); j++) {
362  if (listNew.at(i).x == listOld.at(j).x &&
363  listNew.at(i).y == listOld.at(j).y) {
364  commonList.push_back(i);
365  }
366  }
367  }
368 
369  return commonList;
370 }
371 
372 std::vector<int> setIntersectIdxPixs1D(std::vector<int> listNew,
373  std::vector<int> listOld) {
374  std::vector<int> commonList;
375 
376  // Common
377  int cnt = 0;
378  for (auto& e : listNew) {
379  for (auto& ee : listOld) {
380  if (e == ee) {
381  commonList.push_back(cnt);
382  break;
383  }
384  }
385  cnt++;
386  }
387 
388  return commonList;
389 }
390 
391 // Calculate exclusive OR in two pixel lists (Matlab setxor equivalent)
392 std::vector<cv::Point> setxorPixs(std::vector<cv::Point> listNew,
393  std::vector<cv::Point> listOld) {
394  vector<Point> setxorList;
395  vector<int> commonList;
396 
397  // Common
398  for (int i = 0; i < listNew.size(); i++) {
399  for (int j = 0; j < listOld.size(); j++) {
400  if (listNew.at(i).x == listOld.at(j).x &&
401  listNew.at(i).y == listOld.at(j).y) {
402  Point x(listNew.at(i).x, listNew.at(i).y);
403  /*setxorList.push_back(x);*/
404  commonList.push_back(i);
405  }
406  }
407  }
408 
409  // Setxor
410  int nLabels = listNew.size();
411  vector<int> v(nLabels);
412  iota(std::begin(v), std::end(v), 0);
413  vector<int> setxorIdx;
414  std::set_difference(std::begin(v), std::end(v), std::begin(commonList),
415  std::end(commonList), std::back_inserter(setxorIdx));
416 
417  for (int j = 0; j < setxorIdx.size(); j++) {
418  Point x(listNew.at(setxorIdx.at(j)).x, listNew.at(setxorIdx.at(j)).y);
419  setxorList.push_back(x);
420  }
421 
422  return setxorList;
423 }
424 
425 // Extract values of pixels per labeled segment in a binary image
426 std::vector<int> pixelValues(cv::Mat inputM,
427  std::vector<cv::Point>& pixelList) {
428  std::vector<int> pixelV;
429  for (int i = 0; i < pixelList.size(); i++) {
430  int val = inputM.at<uchar>(pixelList.at(i).x, pixelList.at(i).y);
431  pixelV.push_back(val);
432  }
433  return pixelV;
434 }
435 
436 // Extract pixels per labeled segment in a binary image
437 std::vector<std::vector<cv::Point>> pixelListMat(cv::Mat inputM, int nLabels) {
438  std::vector<std::vector<cv::Point>> pixelSegments(nLabels);
439  for (int i = 0; i < inputM.rows; i++) {
440  for (int j = 0; j < inputM.cols; j++) {
441  int pixelValue = inputM.at<int>(i, j);
442  if (pixelValue > 0) {
443  cv::Point x(i, j);
444  pixelSegments[pixelValue - 1].push_back(
445  x); //-1 to correct value 0 for background
446  }
447  }
448  }
449  return pixelSegments;
450 }
451 
452 // Find values over/under a value in an array. op = 1: lt; op = 2: leq; op = 3:
453 // gt; op = 4: geq; op = 5: eq
454 std::vector<int> findMatlab(std::vector<double> inputV, int op, double thres) {
455  std::vector<int> indices;
456 
457  if (op == 1) {
458  for (int i = 0; i < inputV.size(); i++) {
459  if (inputV.at(i) < thres) indices.push_back(i);
460  }
461  }
462  if (op == 2) {
463  for (int i = 0; i < inputV.size(); i++) {
464  if (inputV.at(i) <= thres) indices.push_back(i);
465  }
466  }
467  if (op == 3) {
468  for (int i = 0; i < inputV.size(); i++) {
469  if (inputV.at(i) > thres) indices.push_back(i);
470  }
471  }
472  if (op == 4) {
473  for (int i = 0; i < inputV.size(); i++) {
474  if (inputV.at(i) >= thres) indices.push_back(i);
475  }
476  }
477  if (op == 5) {
478  for (int i = 0; i < inputV.size(); i++) {
479  if (inputV.at(i) == thres) indices.push_back(i);
480  }
481  }
482  return indices;
483 }
484 
485 std::vector<int> findIdx(std::vector<int> inputV, int thres) {
486  std::vector<int> indices;
487 
488  for (int i = 0; i < inputV.size(); i++) {
489  if (inputV.at(i) == thres) indices.push_back(i);
490  }
491 
492  return indices;
493 }
494 
495 // Dilation
496 void dilationCC(int dilation_elem,
497  int dilation_size,
498  cv::Mat& src,
499  cv::Mat& dilation_dst) {
500  int dilation_type = 0;
501  if (dilation_elem == 0) {
502  dilation_type = MORPH_RECT;
503  } else if (dilation_elem == 1) {
504  dilation_type = MORPH_CROSS;
505  } else if (dilation_elem == 2) {
506  dilation_type = MORPH_ELLIPSE;
507  }
508  cv::Mat element = cv::getStructuringElement(
509  dilation_type, Size(2 * dilation_size + 1, 2 * dilation_size + 1),
510  cv::Point(dilation_size, dilation_size));
511  cv::dilate(src, dilation_dst, element);
512 }
513 
514 // Erosion
515 void erosionCC(int erosion_elem,
516  int erosion_size,
517  cv::Mat& src,
518  cv::Mat& erosion_dst) {
519  int erosion_type = 0;
520  if (erosion_elem == 0) {
521  erosion_type = MORPH_RECT;
522  } else if (erosion_elem == 1) {
523  erosion_type = MORPH_CROSS;
524  } else if (erosion_elem == 2) {
525  erosion_type = MORPH_ELLIPSE;
526  }
527  cv::Mat element = cv::getStructuringElement(
528  erosion_type, Size(2 * erosion_size + 1, 2 * erosion_size + 1),
529  cv::Point(erosion_size, erosion_size));
530  cv::erode(src, erosion_dst, element);
531 }
532 
533 // Stones division in smaller segments (when joint by narrow areas)
534 cv::Mat stonesDivision(std::vector<std::vector<cv::Point>> pixsLabel,
535  cv::Mat labels,
536  std::vector<int> bigStones) {
537  // smallIndices
538  int nLabels = pixsLabel.size();
539  std::vector<int> v(nLabels);
540  std::iota(std::begin(v), std::end(v), 0);
541 
542  std::vector<int> noBigStones;
543  std::set_difference(std::begin(v), std::end(v), std::begin(bigStones),
544  std::end(bigStones), std::back_inserter(noBigStones));
545 
546  // smallMatrix
547  cv::Mat smallStones = cv::Mat::zeros(labels.size(), CV_8U);
548  for (int i = 0; i < noBigStones.size(); i++) {
549  for (int j = 0; j < pixsLabel[noBigStones.at(i)].size(); j++) {
550  smallStones.at<uchar>(pixsLabel[noBigStones.at(i)].at(j).x,
551  pixsLabel[noBigStones.at(i)].at(j).y) = 255;
552  }
553  }
554 
555  // bigMatrix
556  cv::Mat input = cv::Mat::zeros(labels.size(), CV_8U);
557  for (int i = 0; i < bigStones.size(); i++) {
558  for (int j = 0; j < pixsLabel[bigStones.at(i)].size(); j++) {
559  input.at<uchar>(pixsLabel[bigStones.at(i)].at(j).x,
560  pixsLabel[bigStones.at(i)].at(j).y) = 255;
561  }
562  }
563 
564  // Erosion of big stones (in matlab was open: erosion + dilation, but here
565  // we dilated before)
566  int erosion_elem = 2;
567  int erosion_size = 1;
568  cv::Mat output;
569  erosionCC(erosion_elem, erosion_size, input, output);
570 
571  // Combine small + eroded big stones
572  cv::Mat combined = output + smallStones;
573 
574  return combined; // new segments
575 }
576 
577 // Gets only the biggest segments
578 cv::Mat threshSegments(cv::Mat& src, double threshSize) {
579  // FindContours:
580  std::vector<std::vector<cv::Point>> contours;
581  std::vector<cv::Vec4i> hierarchy;
582  cv::Mat srcBuffer, output;
583  src.copyTo(srcBuffer);
584  cv::findContours(srcBuffer, contours, hierarchy, cv::RETR_CCOMP,
585  cv::CHAIN_APPROX_TC89_KCOS);
586 
587  std::vector<std::vector<cv::Point>> allSegments;
588 
589  // For each segment:
590  for (size_t i = 0; i < contours.size(); ++i) {
591  cv::drawContours(srcBuffer, contours, i, cv::Scalar(200, 0, 0), 1, 8,
592  hierarchy, 0, Point());
593  cv::Rect brect = cv::boundingRect(contours[i]);
594  cv::rectangle(srcBuffer, brect, cv::Scalar(255, 0, 0));
595 
596  int result;
597  std::vector<cv::Point> segment;
598  for (unsigned int row = brect.y; row < brect.y + brect.height; ++row) {
599  for (unsigned int col = brect.x; col < brect.x + brect.width;
600  ++col) {
601  result = cv::pointPolygonTest(contours[i], Point(col, row),
602  false);
603  if (result == 1 || result == 0) {
604  segment.push_back(Point(col, row));
605  }
606  }
607  }
608  allSegments.push_back(segment);
609  }
610 
611  output = cv::Mat::zeros(src.size(), CV_8U);
612  int totalSize = output.rows * output.cols;
613  for (int segmentCount = 0; segmentCount < allSegments.size();
614  ++segmentCount) {
615  std::vector<cv::Point> segment = allSegments[segmentCount];
616  if (segment.size() > threshSize) {
617  for (int idx = 0; idx < segment.size(); ++idx) {
618  output.at<uchar>(segment[idx].y, segment[idx].x) = 255;
619  }
620  }
621  }
622 
623  return output;
624 }
625 
626 // Returns the type of a matrix as a string
627 string type2str(int type) {
628  string r;
629 
630  uchar depth = type & CV_MAT_DEPTH_MASK;
631  uchar chans = 1 + (type >> CV_CN_SHIFT);
632 
633  switch (depth) {
634  case CV_8U:
635  r = "8U";
636  break;
637  case CV_8S:
638  r = "8S";
639  break;
640  case CV_16U:
641  r = "16U";
642  break;
643  case CV_16S:
644  r = "16S";
645  break;
646  case CV_32S:
647  r = "32S";
648  break;
649  case CV_32F:
650  r = "32F";
651  break;
652  case CV_64F:
653  r = "64F";
654  break;
655  default:
656  r = "User";
657  break;
658  }
659 
660  r += "C";
661  r += (chans + '0');
662 
663  return r;
664 }
665 
666 // Get stats and additional info from a binary matrix
667 void extractInfo(cv::Mat& input,
668  std::vector<std::vector<cv::Point>>& pixsLabel,
669  cv::Mat& labels,
670  cv::Mat& stats,
671  std::vector<std::vector<Point>>& contours0) {
672  // Get connected components and stats
673  const int connectivity_8 = 8;
674  cv::Mat centroids;
675 
676  int nLabels = connectedComponentsWithStats(
677  input, labels, stats, centroids, connectivity_8,
678  CV_32S); // Note that component 0 is the background (i.e. nLabels =
679  // nsegments+1)
680 
681  // Extract the contours
682  std::vector<std::vector<cv::Point>> contours;
683  std::vector<cv::Vec4i> hierarchy;
684  cv::findContours(input, contours0, hierarchy, RETR_TREE,
685  CHAIN_APPROX_SIMPLE);
686 
687  string ty = type2str(labels.type());
688  pixsLabel = pixelListMat(labels, nLabels - 1);
689 }
690 
691 // Fill in the holes inside segments
692 void fillEdgeImage(cv::Mat edgesIn, cv::Mat& filledEdgesOut, int z) {
693  cv::Mat edgesNeg = edgesIn.clone();
694  string hola = type2str(edgesNeg.type());
695 
697  cv::Mat labels, stats;
698  const int connectivity_8 = 8;
699  cv::Mat centroids;
700  // invert 1s and 0s to label black areas from CWT instead of whites. In this
701  // way, we know that label 1 (the bigger segment) corresponds to mortar
702  // joints
703  cv::bitwise_not(edgesIn, edgesIn);
704 
705  int nLabels = cv::connectedComponentsWithStats(
706  edgesIn, labels, stats, centroids, connectivity_8,
707  CV_32S); // Note that component 0 is the background (i.e. nLabels =
708  // nsegments+1)
709 
710  cv::bitwise_not(edgesIn, edgesIn);
711 
712  int stSize = (int)stats.at<int>(
713  1, 4); // 0 is stone area, largest (not considering that one) is
714  // continuous mortar
715  int cntBiggestSize = 1;
716  for (int i = 2; i < stats.rows; i++) {
717  int loopSize = (int)stats.at<int>(i, 4);
718 
719  if (loopSize > stSize) {
720  stSize = loopSize;
721 
722  cntBiggestSize = i;
723  }
724  }
725 
726  int f, c;
727  for (int i = 0; i < edgesNeg.rows; i++) {
728  for (int j = 0; j < edgesNeg.cols; j++) {
729  int h = (int)edgesNeg.at<uchar>(i, j);
730  int l = (int)labels.at<int>(i, j);
731  if (h == 0 && l == cntBiggestSize) { // seed is a 0 in CWT and
732  // belongs to mortar joints
733  f = i;
734  c = j;
735  goto stop;
736  }
737  }
738  }
739 
740 stop:
741 
742  cv::floodFill(edgesNeg, cv::Point(c, f),
743  CV_RGB(255, 255, 255)); // The seed must be a black pixel
744  ;
745  cv::bitwise_not(edgesNeg, edgesNeg);
746 
747  filledEdgesOut = (edgesNeg | edgesIn);
748 
749  return;
750 }
751 
752 // Calculates the fft2
753 void fft2(const cv::Mat in, cv::Mat& complexI) {
754  cv::Mat padded;
755  int m = getOptimalDFTSize(in.rows);
756  int n = getOptimalDFTSize(in.cols);
757  copyMakeBorder(in, padded, 0, m - in.rows, 0, n - in.cols, BORDER_CONSTANT,
758  cv::Scalar::all(0));
759 
760  cv::Mat planes[] = {cv::Mat_<float>(padded),
761  cv::Mat::zeros(padded.size(), CV_32F)};
762  cv::merge(planes, 2, complexI);
763  cv::dft(complexI, complexI);
764 }
765 
766 // Calculates the conjugate of a complex matrix
767 cv::Mat conjugate(cv::Mat_<std::complex<double>> M1, bool* flag) {
768  using CP = std::complex<double>;
769  cv::Mat_<CP> M3(M1.rows, M1.cols);
770  // When there is no complex part, half of the values of the real part are
771  // considered as complex
772  try {
773  double a = M1.at<CP>(M1.rows - 1, M1.cols - 1).real();
774  for (int i = 0; i < M1.rows; ++i) {
775  for (int j = 0; j < M1.cols; ++j) {
776  M3.at<CP>(i, j) =
777  CP(M1.at<CP>(i, j).real(), -M1.at<CP>(i, j).imag());
778  }
779  }
780 
781  *flag = true;
782  return M3;
783  } // If there is no complex part
784  catch (const std::exception& e) {
785  M3 = M1;
786  *flag = false;
787  return M3;
788  }
789 }
790 
791 // Multiplies two complex matrices
792 cv::Mat multiplicationCC(cv::Mat_<std::complex<double>> M1,
793  cv::Mat_<std::complex<double>> M2) {
794  using CP = std::complex<double>;
795  cv::Mat_<CP> M3(M1.rows, M1.cols);
796  for (int i = 0; i < M3.rows; ++i) {
797  for (int j = 0; j < M3.cols; ++j) {
798  M3.at<CP>(i, j) =
799  CP(M1.at<CP>(i, j).real() * M2.at<CP>(i, j).real() -
800  M1.at<CP>(i, j).imag() * M2.at<CP>(i, j).imag(),
801  M1.at<CP>(i, j).real() * M2.at<CP>(i, j).imag() +
802  M1.at<CP>(i, j).imag() * M2.at<CP>(i, j).real());
803  }
804  }
805  return M3;
806 }
807 
808 // Calculates the Continuous Wavelet Transform
809 void cwt2d(double sca, cv::Mat image, cv::Mat& cwtcfs, cv::Mat& bincfs) {
810  cv::Mat I = imread("depthMap.bmp", cv::IMREAD_GRAYSCALE);
811  cv::Mat fImage = image; //(if there is 'image' matrix)
812 
813  // FFT
814  std::cout << "Direct transform...\n";
815  cv::Mat fourierTransform;
816  fft2(fImage, fourierTransform);
817 
818  // Create frequency plane
819  Size S = fourierTransform.size();
820  int H = S.height;
821  int W = S.width;
822 
823  int W2 = floor((W - 1) / 2);
824  int H2 = floor((H - 1) / 2);
825 
826  std::vector<int> w1(W2 + 1); // vector with W2 ints.
827  std::iota(std::begin(w1), std::end(w1), 0);
828  std::vector<int> w2(W2 + 1); // vector with W2 ints.
829  std::iota(std::begin(w2), std::end(w2), (W2 - W + 1));
830 
831  std::vector<double> v;
832  v.reserve(w1.size() + w2.size()); // preallocate memory
833  v.insert(v.end(), w1.begin(), w1.end());
834  v.insert(v.end(), w2.begin(), w2.end());
835 
836  std::vector<double> W_puls(v.size());
837  std::transform(v.begin(), v.end(), W_puls.begin(),
838  [W](int i) { return i * 2 * M_PI / W; });
839 
840  std::vector<int> h1(H2 + 1); // vector with H2 ints.
841  std::iota(std::begin(h1), std::end(h1), 0);
842  std::vector<int> h2(H2 + 1); // vector with H2 ints.
843  std::iota(std::begin(h2), std::end(h2), (H2 - H + 1));
844 
845  std::vector<double> v2;
846  v2.reserve(h1.size() + h2.size()); // preallocate memory
847  v2.insert(v2.end(), h1.begin(), h1.end());
848  v2.insert(v2.end(), h2.begin(), h2.end());
849 
850  std::vector<double> H_puls(v2.size());
851  std::transform(v2.begin(), v2.end(), H_puls.begin(),
852  [H](int i) { return i * 2 * M_PI / H; });
853 
854  cv::Mat matW = cv::Mat(W_puls);
855  cv::Mat matH = cv::Mat(H_puls);
856 
857  cv::Mat1d xx, yy;
858  cv::repeat(matW.reshape(1, 1), matH.total(), 1,
859  xx); // Matlab's Meshgrid equivalent
860  cv::repeat(matH.reshape(1, 1).t(), 1, matW.total(), yy);
861 
862  double dxxyy = abs(xx.at<double>(0, 1) - xx.at<double>(0, 0)) *
863  (yy.at<double>(1, 0) - yy.at<double>(0, 0));
864 
865  double wav_norm = 0;
866 
867  double ang = 0;
868 
869  cv::Mat nxx = sca * (cos(ang) * xx - sin(ang) * yy);
870  cv::Mat nyy = sca * (sin(ang) * xx + cos(ang) * yy);
871 
872  int sigmax = 1;
873  int sigmay = 1;
874  int order = 2;
875 
876  cv::Mat nxx2;
877  pow(nxx, 2, nxx2);
878  cv::Mat nyy2;
879  pow(nyy, 2, nyy2);
880 
881  cv::Mat sum = nxx2 + nyy2;
882  cv::Mat sumPow;
883  pow(sum, order / 2, sumPow);
884 
885  cv::Mat prod1 = sigmax * nxx;
886  cv::Mat prod12;
887  cv::pow(prod1, 2, prod12);
888  cv::Mat prod2 = sigmay * nyy;
889  cv::Mat prod22;
890  cv::pow(prod2, 2, prod22);
891 
892  cv::Mat ex = -(prod12 + prod22) / 2;
893  cv::Mat ex2;
894  cv::exp(ex, ex2);
895 
896  cv::Mat waveft2 = -(2 * M_PI) * sumPow.mul(ex2);
897 
898  // Complex mask
899  using CP = std::complex<double>;
900  cv::Mat_<CP> mask(Size(waveft2.rows, waveft2.cols));
901  mask = sca * waveft2;
902 
903  bool flag = false;
904  cv::Mat mask_conj = mask;
905 
906  mask.convertTo(mask_conj, CV_32F);
907 
908  cv::Mat g, mask2;
909  std::vector<cv::Mat> channels;
910  if (flag) { // Mask is complex. TBC if there is the case
911 
912  } else {
913  g = cv::Mat::zeros(Size(mask_conj.cols, mask_conj.rows), CV_32FC1);
914  mask2 = cv::Mat::zeros(Size(mask_conj.cols, mask_conj.rows), CV_32FC2);
915 
916  channels.push_back(mask_conj);
917  channels.push_back(g);
918 
919  cv::merge(channels, mask2);
920  }
921 
922  // Matrices product
923  cv::Mat M3;
924  M3 = multiplicationCC(fourierTransform, mask2);
925 
926  // IFFT
927  std::cout << "Inverse transform...\n";
928  cv::dft(M3, cwtcfs, cv::DFT_INVERSE | cv::DFT_REAL_OUTPUT);
929 
930  // Back to 8-bits. angle values in Matlab code
931  cwtcfs.convertTo(bincfs, CV_8U);
932 }
933 
934 // Returns the contour of a stone as a polyline
935 
937  std::vector<cloudViewer::PointProjectionTools::IndexedCCVector2> point;
938  std::list<cloudViewer::PointProjectionTools::IndexedCCVector2*> hullPoint2;
939 
940  for (unsigned i = 0; i < stone->size(); ++i) {
941  PointCoordinateType x = stone->getPoint(i)->x;
942  PointCoordinateType z = stone->getPoint(i)->z;
944 
945  point.push_back(P);
946  }
947 
949  0.0001);
950 
951  vector<unsigned> Vec;
952  for (int i = 0; i < (hullPoint2.size()); ++i) {
953  list<cloudViewer::PointProjectionTools::IndexedCCVector2*>::iterator
954  it = hullPoint2.begin();
955  std::advance(it, i);
957  int k = P2->index;
958  Vec.push_back(k);
959  }
960 
961  ccPointCloud* contour =
962  new ccPointCloud(stone->getName() + " - Contour Cloud");
963 
964  for (auto e : Vec) {
965  contour->reserveThePointsTable(1);
966  contour->reserveTheRGBTable();
967  CCVector3 p(stone->getPoint(e)->x, stone->getPoint(e)->y,
968  stone->getPoint(e)->z); // kike added "y" value (it was 0)
969  contour->addPoint(p);
970 
971  const ecvColor::Rgb col = stone->getPointColor(e);
972  contour->addRGBColor(col);
973  }
974 
975  int cnt = Vec.size();
976 
977  ccPolyline* pContour = new ccPolyline(contour);
978 
979  if (pContour->reserve(cnt)) {
980  pContour->addPointIndex(0, cnt);
981  pContour->setClosed(true);
982  pContour->setVisible(true);
983  pContour->setName(stone->getName() + " - Contour");
984  pContour->addChild(contour);
985  pContour->setColor(ecvColor::cyan);
986  pContour->showColors(true);
987  pContour->showVertices(true);
988  }
989 
990  return pContour;
991 }
992 
993 ccPolyline* contourPoly2(ccPointCloud* cloud0, vector<int> V, QString name) {
994  std::vector<cloudViewer::PointProjectionTools::IndexedCCVector2> point;
995  std::list<cloudViewer::PointProjectionTools::IndexedCCVector2*> hullPoint2;
996 
997  for (unsigned i = 0; i < V.size(); ++i)
998 
999  {
1000  PointCoordinateType x = cloud0->getPoint(V[i])->x;
1001  PointCoordinateType z = cloud0->getPoint(V[i])->z;
1003 
1004  point.push_back(P);
1005  }
1006 
1008  0.0001);
1009 
1010  vector<unsigned> Vec;
1011  for (int i = 0; i < (hullPoint2.size()); ++i) {
1012  list<cloudViewer::PointProjectionTools::IndexedCCVector2*>::iterator
1013  it = hullPoint2.begin();
1014  std::advance(it, i);
1016  int k = P2->index;
1017  Vec.push_back(k);
1018  }
1019 
1020  ccPointCloud* contour = new ccPointCloud(name + " - Contour Cloud");
1021 
1022  for (auto e : Vec) {
1023  contour->reserveThePointsTable(1);
1024  contour->reserveTheRGBTable();
1025  CCVector3 p(cloud0->getPoint(V[e])->x, cloud0->getPoint(V[e])->y,
1026  cloud0->getPoint(V[e])->z);
1027  contour->addPoint(p);
1028 
1029  const ecvColor::Rgb col = cloud0->getPointColor(V[e]);
1030  contour->addRGBColor(col);
1031  }
1032 
1033  int cnt = Vec.size();
1034 
1035  ccPolyline* pContour = new ccPolyline(contour);
1036 
1037  if (pContour->reserve(cnt)) {
1038  pContour->addPointIndex(0, cnt);
1039  pContour->setClosed(true);
1040  pContour->setVisible(true);
1041  pContour->setName(name + " - Contour");
1042  pContour->addChild(contour);
1043  pContour->setColor(ecvColor::cyan);
1044  pContour->showColors(true);
1045  pContour->showVertices(true);
1046  }
1047 
1048  return pContour;
1049 }
1050 
1051 // Returns mortar maps (Depth and width)
1053  ccPointCloud* f_cloudMortar) {
1054  // Binary Mortar
1055 
1056  CCVector3 minBox;
1057  minBox = CCVector3(0, 0, 0);
1058  CCVector3 maxBox;
1059  maxBox = CCVector3(0, 0, 0);
1060  f_cloudMortar->scale(100, 1000, 100); // To cm mm cm
1061  f_cloudMortar->getBoundingBox(minBox, maxBox);
1062  int rowsM = ceil(maxBox.z);
1063  int colsM = ceil(maxBox.x);
1064 
1065  cv::Mat corrMatM = Mat::zeros(rowsM, colsM, CV_32F);
1066  vector<Point> idxPxM;
1067  vector<int> idxPxM1D;
1068  cv::Mat imageBWS = Mat::zeros(rowsM, colsM, CV_8U);
1069 
1070  cloud2binary(f_cloudMortar, corrMatM, idxPxM, idxPxM1D, imageBWS);
1071 
1072  // Skeleton
1073  Mat skel = skeleton(imageBWS, false);
1074 
1075  f_cloudMortar->scale(0.01, 0.001, 0.01); // Scale back
1076 
1077  // Skeleton 3D
1078  vector<int> pixelsSkel;
1079  for (int i = 0; i < skel.rows; i++) {
1080  for (int j = 0; j < skel.cols; j++) {
1081  unsigned char a = skel.at<char>(i, j);
1082  if (a == 255) {
1083  int idx = corrMatM.at<int>(i, j);
1084  pixelsSkel.push_back(idx);
1085  }
1086  }
1087  }
1088 
1089  vector<int> idxCloudMortarSkel =
1090  setIntersectIdxPixs1D(idxPxM1D, pixelsSkel);
1091  ccPointCloud* skelM = new ccPointCloud("Skeleton Mortar");
1092  for (auto e : idxCloudMortarSkel) {
1093  skelM->reserveThePointsTable(1);
1094  skelM->reserveTheRGBTable();
1095  CCVector3 p(f_cloudMortar->getPoint(e)->x,
1096  f_cloudMortar->getPoint(e)->y,
1097  f_cloudMortar->getPoint(e)->z);
1098  skelM->addPoint(p);
1099  const ecvColor::Rgb col = f_cloudMortar->getPointColor(e);
1100  skelM->addRGBColor(col);
1101  }
1102 
1104  false); // Subsample result
1105  cloudViewer::ReferenceCloud* refCloud =
1107  skelM, 0.01, modParams, 0, 0); // 1 point per cm^2
1108 
1109  ccPointCloud* f_skelMortar = skelM->partialClone(refCloud); // save output
1110  delete refCloud;
1111  delete skelM;
1112  refCloud = 0;
1113  skelM = 0;
1114 
1115  // Maps using PCL
1116  // Mortar depth map
1117  pcl::PointCloud<pcl::PointXYZ>::Ptr pcl_cloudStones(
1118  new pcl::PointCloud<pcl::PointXYZ>);
1119  pcl_cloudStones->width = f_cloudStones->size();
1120  pcl_cloudStones->height = 1;
1121  pcl_cloudStones->points.resize(pcl_cloudStones->width *
1122  pcl_cloudStones->height);
1123 
1124  for (size_t i = 0; i < f_cloudStones->size(); ++i) {
1125  pcl_cloudStones->points[i].x = f_cloudStones->getPoint(i)->x;
1126  pcl_cloudStones->points[i].y = f_cloudStones->getPoint(i)->y;
1127  pcl_cloudStones->points[i].z = f_cloudStones->getPoint(i)->z;
1128  }
1129 
1130  pcl::KdTreeFLANN<pcl::PointXYZ> kdtree;
1131 
1132  kdtree.setInputCloud(pcl_cloudStones);
1133 
1134  unsigned int nnIdx;
1135  vector<double> distStD, dist3St, distStD2;
1136  vector<double> distStWidth;
1137  vector<double> distStW;
1138  for (int i = 0; i < f_skelMortar->size(); i++) {
1139  pcl::PointXYZ searchPoint;
1140 
1141  searchPoint.x = f_skelMortar->getPoint(i)->x;
1142  searchPoint.y = f_skelMortar->getPoint(i)->y;
1143  searchPoint.z = f_skelMortar->getPoint(i)->z;
1144 
1145  std::vector<int> pointIdxRadiusSearch;
1146  std::vector<float> pointRadiusSquaredDistance;
1147 
1148  float radius = 0.1; // 10cm
1149  pcl::PointXYZ nnPoint;
1150 
1151  if (kdtree.radiusSearch(searchPoint, radius, pointIdxRadiusSearch,
1152  pointRadiusSquaredDistance) > 0) {
1153  nnPoint.x = pcl_cloudStones->points[pointIdxRadiusSearch[0]].x;
1154  nnPoint.y = pcl_cloudStones->points[pointIdxRadiusSearch[0]].y;
1155  nnPoint.z = pcl_cloudStones->points[pointIdxRadiusSearch[0]].z;
1156 
1157  double disty = abs(searchPoint.y - nnPoint.y); // Depth
1158  double distx = abs(searchPoint.x - nnPoint.x);
1159  double distz = abs(searchPoint.z - nnPoint.z);
1160  double dist3 = sqrt(distx * distx +
1161  distz * distz); // 2D distance (XZ) will be
1162  // used as a reference for the
1163  // creation of mortar width map
1164 
1165  distStD.push_back(disty * 1000); // depth (y axis) in mm
1166  dist3St.push_back(dist3);
1167 
1168  double dist1 = 1000;
1169  double dist2 = 1000;
1170  double dist1y = 1000;
1171  double dist2y = 1000;
1172  int idx1 = -1;
1173  int idx2 = -1;
1174  for (size_t j = 0; j < pointIdxRadiusSearch.size(); ++j) {
1175  double distx =
1176  abs(searchPoint.x -
1177  pcl_cloudStones->points[pointIdxRadiusSearch[j]]
1178  .x); // 2D distance
1179  double distz =
1180  abs(searchPoint.z -
1181  pcl_cloudStones->points[pointIdxRadiusSearch[j]].z);
1182  double disty =
1183  abs(searchPoint.y -
1184  pcl_cloudStones->points[pointIdxRadiusSearch[j]].y);
1185  double dist3 =
1186  sqrt(abs(distx) * abs(distx) + abs(distz) * abs(distz));
1187 
1188  int idxSt = f_cloudStones->getPointScalarValue(
1189  pointIdxRadiusSearch[j]);
1190 
1191  if (j == 0) { // Initialise NN stone 1
1192  dist1 = dist3;
1193  dist1y = disty;
1194  idx1 = idxSt;
1195  } else {
1196  if (idxSt == idx1) { // If point belongs to stone 1
1197  if (dist3 < dist1) dist1 = dist3;
1198  dist1y = disty;
1199  }
1200  if (idxSt != idx1 & idx2 == -1) { // Initialise stone 2
1201  idx2 = idxSt;
1202  dist2 = dist3;
1203  dist2y = disty;
1204  }
1205  if (idxSt == idx2) { // If point belongs to stone 2
1206  if (dist3 < dist2) dist2 = dist3;
1207  dist2y = disty;
1208  }
1209  }
1210 
1211  } // Points are ordered by radius. Idx 0 is the nearest neighbour
1212 
1213  // For each point, there is dist1 or dist1 and dist2
1214  if (idx1 > -1 & idx2 == -1 &
1215  dist1 < 1000) { // Only one stone (boundaries of wall)
1216  distStW.push_back(dist1 * 1000);
1217  distStD2.push_back(dist1y * 1000);
1218  } else if (idx1 > -1 & idx2 > -1) {
1219  distStW.push_back((dist1 + dist2) * 1000);
1220  distStD2.push_back((dist1y + dist2y) / 2 * 1000);
1221  }
1222 
1223  } else {
1224  distStD.push_back(
1225  100); // Add high values for non-calculated values (no
1226  // neighbours available). These values can be
1227  // modified according to the needs
1228  distStD2.push_back(100);
1229  dist3St.push_back(200);
1230 
1231  distStW.push_back(200);
1232  }
1233  }
1234 
1235  cloudViewer::ScalarField* depthSF =
1236  nullptr; // Add depth as a scalarField to f_skelMortar
1237 
1238  int sfIdxD = f_skelMortar->getScalarFieldIndexByName(
1239  "Mortar relative depth (mm)");
1240  if (sfIdxD < 0) {
1241  sfIdxD = f_skelMortar->addScalarField("Mortar relative depth (mm)");
1242  }
1243  if (sfIdxD < 0) {
1244  return nullptr;
1245  }
1246 
1247  depthSF = f_skelMortar->getScalarField(sfIdxD);
1248 
1249  for (unsigned int i = 0; i < distStD2.size(); i++) {
1250  double index = static_cast<double>(distStD2.at(i));
1251  depthSF->setValue(i, index);
1252  }
1253 
1254  f_skelMortar->setCurrentDisplayedScalarField(sfIdxD);
1255 
1256  depthSF->computeMinAndMax();
1257 
1258  cloudViewer::ScalarField* widthSF =
1259  nullptr; // Add width as a scalarField to f_skelMortar
1260 
1261  int sfIdxW = f_skelMortar->getScalarFieldIndexByName(
1262  "Mortar relative width (mm)");
1263  if (sfIdxW < 0) {
1264  sfIdxW = f_skelMortar->addScalarField("Mortar relative width (mm)");
1265  }
1266  if (sfIdxW < 0) {
1267  return nullptr;
1268  }
1269 
1270  widthSF = f_skelMortar->getScalarField(sfIdxW);
1271 
1272  for (unsigned int i = 0; i < distStW.size(); i++) {
1273  double index = static_cast<double>(distStW.at(i));
1274  widthSF->setValue(i, index);
1275  }
1276 
1277  f_skelMortar->setCurrentDisplayedScalarField(sfIdxW);
1278 
1279  widthSF->computeMinAndMax();
1280  // f_skelMortar->setPointSize(10);
1281  f_skelMortar->showSF(true);
1282  f_skelMortar->setName("Mortar Maps");
1283  f_skelMortar->setPointSize(7);
1284  return f_skelMortar;
1285 }
1286 
1287 float getDensity(ccPointCloud* cloud, vector<int> idx) {
1288  ccPointCloud* stone = new ccPointCloud("stone");
1289  for (auto e : idx) {
1290  stone->reserveThePointsTable(1);
1291  CCVector3 p(cloud->getPoint(e)->x, cloud->getPoint(e)->y,
1292  cloud->getPoint(e)->z);
1293  stone->addPoint(p);
1294  }
1295 
1296  CCVector3 minBox;
1297  minBox = CCVector3(0, 0, 0);
1298  CCVector3 maxBox;
1299  maxBox = CCVector3(0, 0, 0);
1300 
1301  stone->getBoundingBox(minBox, maxBox);
1302  float area = (maxBox.x - minBox.x) * (maxBox.z - minBox.z);
1303  float density = idx.size() / area;
1304  return density;
1305 }
1306 // This method should enable or disable your plugin actions
1307 // depending on the currently selected entities ('selectedEntities').
1308 void ccAutoSeg::onNewSelection(const ccHObject::Container& selectedEntities) {
1309  if (m_action) {
1310  // always active
1311  }
1312 }
1313 
1314 // This method returns all the 'actions' your plugin can perform.
1315 // getActions() will be called only once, when plugin is loaded.
1316 QList<QAction*> ccAutoSeg::getActions() {
1317  if (!m_action) {
1318  m_action = new QAction(getName(), this);
1319  m_action->setToolTip(getDescription());
1320  m_action->setIcon(QIcon(
1321  QString::fromUtf8(":/CC/plugin/qAutoSeg/cyberbuildIcon.png")));
1322 
1323  // Connect appropriate signal
1324  connect(m_action, &QAction::triggered, this, &ccAutoSeg::doAction);
1325  }
1326 
1327  return QList<QAction*>{
1328  m_action,
1329  };
1330 }
1331 
1332 void ccAutoSeg::doAction() {
1333  if (m_app == nullptr) {
1334  // m_app should have already been initialized by CC when plugin is
1335  // loaded
1336  Q_ASSERT(false);
1337 
1338  return;
1339  }
1340 
1341  // Create log file
1342 
1343  time_t rawtime;
1344  struct tm* timeinfo;
1345  char buffer[80];
1346 
1347  time(&rawtime);
1348  timeinfo = localtime(&rawtime);
1349 
1350  strftime(buffer, sizeof(buffer), "%Y%m%d_%H%M%S", timeinfo);
1351  std::string str(buffer);
1352 
1353  string filename("APS_log_");
1354  filename += str;
1355  filename += ".txt";
1356 
1357  time_t my_time = time(NULL);
1358 
1359  // Create pop-up window
1360  ProfileImportDlg piDlg(m_app->getMainWindow());
1361 
1362  if (!piDlg.exec()) return;
1363 
1364  double joints = piDlg.jointsSpinBox->value(); // Mortar joint for CWT
1365  double segH = piDlg.segmentHSpinBox->value(); // Segmentation window
1366  double segV = piDlg.segmentVSpinBox->value();
1367 
1368  bool checkAlignment = piDlg.alignmentCheckBox->isChecked();
1369  bool checkSegment = piDlg.segmentCheckBox->isChecked();
1370  bool checkMortar = piDlg.mortarCheckBox->isChecked();
1371 
1372  // If mortar is selected but not segmentation, show an error message and ask
1373  // for checking both
1374  if (checkMortar == true && checkSegment == false) {
1375  m_app->dispToConsole(
1376  "Segmentation is required for the creation of mortar maps. "
1377  "Please, check both \"Automatic segmentation\" and \"Mortar "
1378  "maps\".",
1380  return;
1381  }
1382 
1383  // Select point cloud (manually, from CC UI)
1384  const ccHObject::Container& selectedEntities = m_app->getSelectedEntities();
1385 
1386  if (!m_app->haveOneSelection() ||
1387  !selectedEntities.front()->isA(CV_TYPES::POINT_CLOUD)) {
1388  m_app->dispToConsole("Select one cloud!",
1390  return;
1391  }
1392 
1393  QFileInfo fileInfo(selectedEntities.front()->getFullPath());
1394  QDir dir(fileInfo.absolutePath());
1395  QString autoSegLogFile = dir.absoluteFilePath(filename.c_str());
1396  filename = CVTools::FromQString(autoSegLogFile);
1397  ofstream auto_seg_log;
1398  auto_seg_log.open(filename);
1399 
1400  string stamp;
1401  stamp = dateStamp();
1402  auto_seg_log << stamp << " Importing point cloud" << endl;
1403  ccPointCloud* cloud0 = static_cast<ccPointCloud*>(selectedEntities.front());
1404 
1405  QString qName = cloud0->getName();
1406  string cloudName = qName.toLocal8Bit().constData();
1407 
1408  QString qFileName = cloud0->getParent()->getName();
1409  string name2 = qFileName.toLocal8Bit().constData();
1410 
1411  stamp = dateStamp();
1412  auto_seg_log << stamp << " File \"" << name2 << "\" loaded" << endl;
1413  auto_seg_log << stamp << " Cloud \"" << cloudName << "\" imported" << endl;
1414 
1415  // Add input parameters and tasks to be done to the log
1416  stamp = dateStamp();
1417  auto_seg_log << stamp << " Estimated mortar joints width: " << joints
1418  << "cm" << endl;
1419  auto_seg_log << stamp << " Segmentation window. X: " << segH
1420  << "m and Y: " << segV << "m" << endl;
1421 
1422  auto_seg_log << stamp << " Alignment: " << checkAlignment << endl;
1423  auto_seg_log << stamp << " Segmentation: " << checkSegment << endl;
1424  auto_seg_log << stamp << " Mortar maps: " << checkMortar << endl;
1425  auto_seg_log << " Starting process..." << endl;
1426 
1427  // Check plane orientation
1428  cv::Mat_<double> cldm(cloud0->size(), 3);
1429  for (unsigned int i = 0; i < cloud0->size(); i++) {
1430  cldm.row(i)(0) = cloud0->getPoint(i)->x;
1431  cldm.row(i)(1) = cloud0->getPoint(i)->y;
1432  cldm.row(i)(2) = cloud0->getPoint(i)->z;
1433  }
1434 
1435  cv::Mat_<double> mean;
1436  cv::PCA pca(cldm, mean, CV_PCA_DATA_AS_ROW);
1437 
1438  cv::Vec3d nrm = pca.eigenvectors.row(2);
1439  nrm = nrm / norm(nrm);
1440  cv::Vec3d x0 = pca.mean;
1441 
1442  // Built-in function
1443  CCVector3 nrm2(nrm(0), nrm(1), nrm(2));
1444  CCVector3 a2(0, 1, 0);
1445  ccGLMatrix R2 = ccGLMatrix::FromToRotation(nrm2, a2);
1446 
1447  // Another rotation to aligned boundaries of the wall to axes X and Z is
1448  // needed (rotate in Y axis and take angle linked to smallest bounding box)
1449  cloud0->applyRigidTransformation(R2);
1450 
1451  // Subsample cloud0 to obtain optimal rotY if size is over a threshold
1452  ccPointCloud* cloud2Rot = new ccPointCloud("cloud2Rot"); // Kike
1453 
1454  std::vector<int> v2(cloud0->size());
1455  std::iota(v2.begin(), v2.end(), 0);
1456 
1457  std::random_device rd; // non-deterministic generator
1458  std::mt19937 gen(rd()); // to seed mersenne twister.
1459  std::shuffle(v2.begin(), v2.end(), rd);
1460 
1461  std::vector<int> v;
1462  int nbSub;
1463  if (v2.size() > 1000000)
1464  nbSub = 1000000;
1465  else
1466  nbSub = v2.size();
1467 
1468  for (int i = 0; i < nbSub; i++) {
1469  v.push_back(v2.at(i));
1470  }
1471 
1472  for (int i = 0; i < v.size(); i++) {
1473  cloud2Rot->reserveThePointsTable(1);
1474  cloud2Rot->reserveTheRGBTable();
1475  CCVector3 p(cloud0->getPoint(v.at(i))->x, cloud0->getPoint(v.at(i))->y,
1476  cloud0->getPoint(v.at(i))->z);
1477  cloud2Rot->addPoint(p);
1478 
1479  if (cloud0->hasColors()) {
1480  const ecvColor::Rgb col = cloud0->getPointColor(v.at(i));
1481  cloud2Rot->addRGBColor(col);
1482  }
1483  }
1484 
1485  double th0 = optRotY(cloud2Rot);
1486 
1487  CCVector3 c1, c2, c3, c4;
1488  c1[0] = cos(th0);
1489  c1[1] = 0;
1490  c1[2] = -sin(th0);
1491  c1[3] = 0;
1492  c2[0] = 0;
1493  c2[1] = 1;
1494  c2[2] = 0;
1495  c2[3] = 0;
1496  c3[0] = sin(th0);
1497  c3[1] = 0;
1498  c3[2] = cos(th0);
1499  c3[3] = 0;
1500  c4[0] = 0;
1501  c4[1] = 0;
1502  c4[2] = 0;
1503  c4[3] = 1;
1504  ccGLMatrix R3(c1, c2, c3, c4);
1505 
1506  cloud0->applyRigidTransformation(R3);
1507 
1508  // If only alignment is required
1509  if (checkAlignment == true & checkSegment == false & checkMortar == false) {
1510  if (m_app) {
1511  m_app->setView(CC_FRONT_VIEW);
1512  }
1513  }
1514 
1515  stamp = dateStamp();
1516  auto_seg_log << stamp << "Cloud aligned to XZ." << endl;
1517 
1518  // Bounding box
1519  CCVector3 minBox;
1520  minBox = CCVector3(0, 0, 0);
1521  CCVector3 maxBox;
1522  maxBox = CCVector3(0, 0, 0);
1523 
1524  cloud0->getBoundingBox(minBox, maxBox);
1525 
1526  // Move point cloud to origin
1527  cloud0->Translate(-minBox);
1528 
1529  double axX = maxBox.x - minBox.x;
1530  double axY = maxBox.z - minBox.z;
1531 
1532  // targeted window dimensions
1533  float winX = segH;
1534  float winY = segV;
1535 
1536  // Number of windows
1537  unsigned int nwinX = ceil(axX / winX);
1538  unsigned int nwinY = ceil(axY / winY);
1539 
1540  // real window dimensions without overlaping
1541  double rwinX = axX / nwinX;
1542  double rwinY = axY / nwinY;
1543 
1544  // boundaries of windows [(xmin1,xmax1,ymin1,ymax1),(xmin2,xmax2....)...]
1545  vector<vector<double>> boundaries;
1546 
1547  double X = 0;
1548  vector<int> global_idx;
1549 
1550  for (unsigned int i = 0; i < nwinX; i++) {
1551  double Y = 0;
1552  for (unsigned int j = 0; j < nwinY; j++) {
1553  boundaries.push_back({X, X + rwinX + 0.3, Y, Y + rwinY + 0.3});
1554 
1555  Y += rwinY;
1556  }
1557 
1558  X += rwinX;
1559  }
1560 
1561  // spliting points by index
1562  vector<vector<int>> idxClouds(boundaries.size());
1563  vector<vector<int>> idxCloudsC;
1564 
1565  stamp = dateStamp();
1566  auto_seg_log << stamp << " Splitting cloud in patches (" << nwinX * nwinY
1567  << ")" << endl;
1568 
1569  for (unsigned int i = 0; i < cloud0->size(); i++) {
1570  for (unsigned int j = 0; j < boundaries.size(); j++) {
1571  CCVector3 p(cloud0->getPoint(i)->x, cloud0->getPoint(i)->y,
1572  cloud0->getPoint(i)->z);
1573  if (p.x >= boundaries[j][0] && p.x <= boundaries[j][1] &&
1574  p.z >= boundaries[j][2] &&
1575  p.z <= boundaries[j][3]) // Kike. Not y, but z
1576  {
1577  idxClouds[j].push_back(i);
1578  }
1579  }
1580  global_idx.push_back(i);
1581  }
1582 
1583  // removing empty ones
1584  for (auto e : idxClouds)
1585  if (e.size() != 0) idxCloudsC.push_back(e);
1586 
1587  idxClouds = idxCloudsC;
1588 
1589  stamp = dateStamp();
1590  auto_seg_log << stamp << " Splitting done" << endl;
1591 
1592  vector<ccPointCloud*> sub_clouds;
1593  stamp = dateStamp();
1594  auto_seg_log << stamp << " Creating subclouds" << endl;
1595 
1596  // adding points to the corresponding window
1597  for (unsigned int i = 0; i < idxClouds.size(); i++) {
1598  string name = "part_" + to_string(i);
1599  QString str = QString::fromUtf8(name.c_str());
1600  ccPointCloud* win_cloud = new ccPointCloud(str);
1601  if (idxClouds[i].size() > 0) {
1602  for (auto e : idxClouds[i]) {
1603  win_cloud->reserveThePointsTable(1);
1604  win_cloud->reserveTheRGBTable();
1605  CCVector3 p(cloud0->getPoint(e)->x, cloud0->getPoint(e)->y,
1606  cloud0->getPoint(e)->z);
1607  win_cloud->addPoint(p);
1608 
1609  if (cloud0->hasColors()) {
1610  const ecvColor::Rgb col = cloud0->getPointColor(e);
1611  win_cloud->addRGBColor(col);
1612  }
1613  }
1614 
1615  sub_clouds.push_back(win_cloud);
1616  }
1617  }
1618 
1619  // Going through the sub_clouds vector
1620  vector<vector<pair<int, int>>> stones;
1621  int stoneIdx = 0;
1622 
1623  for (unsigned int z = 0; z < sub_clouds.size(); ++z) {
1624  ccPointCloud* cloud = sub_clouds[z];
1625 
1626  stamp = dateStamp();
1627  auto_seg_log << stamp << " Cloud " << z << ": " << cloud->size()
1628  << " pts" << endl;
1629 
1630  if (cloud->size() > 0) {
1631  string cComment = "part_" + to_string(z) + " has " +
1632  to_string(cloud->size()) + " points";
1633  QString qComment = QString::fromUtf8(cComment.c_str());
1634  m_app->dispToConsole(qComment,
1636 
1637  // Bounding box
1638  CCVector3 minBox_0;
1639  minBox_0 = CCVector3(0, 0, 0);
1640  CCVector3 maxBox_0;
1641  maxBox_0 = CCVector3(0, 0, 0);
1642 
1643  cloud->getBoundingBox(minBox_0, maxBox_0);
1644 
1645  // Move point cloud to origin
1646  cloud->Translate(-minBox_0);
1647 
1648  // Create and populate maps
1649 
1650  // Bounding box
1651  CCVector3 minBox2;
1652  minBox2 = CCVector3(0, 0, 0);
1653  CCVector3 maxBox2;
1654  maxBox2 = CCVector3(0, 0, 0);
1655  const CCVector3* pointC;
1656  int b = cloud->size();
1657 
1658  cloud->scale(100, 1000, 100); // To cm mm cm
1659 
1660  cloud->getBoundingBox(minBox2, maxBox2);
1661 
1662  int rows = ceil(maxBox2.z);
1663  int cols = ceil(maxBox2.x);
1664 
1665  // Correspondence 2D->1D. Each px has an index instead of two coords
1666  cv::Mat corrMat = cv::Mat::zeros(rows, cols, CV_32F);
1667  int cntC = 0;
1668  for (int y = 0; y < corrMat.rows; y++) {
1669  for (int x = 0; x < corrMat.cols; x++) {
1670  corrMat.at<int>(y, x) = cntC;
1671  cntC++;
1672  }
1673  }
1674 
1675  vector<Point> idxPx;
1676  vector<int> idxPx1D;
1677 
1678  // Depth+colour
1679  cv::Mat image = cv::Mat::zeros(rows, cols, CV_32F);
1680  Mat imageR(rows, cols, CV_8U);
1681  Mat imageG(rows, cols, CV_8U);
1682  Mat imageB(rows, cols, CV_8U);
1683  for (int i = 0; i < b; i++) {
1684  // depth
1685  pointC = cloud->getPoint(i);
1686  int z = floor(pointC->z);
1687  int x = floor(pointC->x);
1688  image.at<float>(z, x) = pointC->y;
1689  // color
1690  const ecvColor::Rgb colorC = cloud->getPointColor(i);
1691  imageR.at<uchar>(z, x) = colorC.r;
1692  imageG.at<uchar>(z, x) = colorC.g;
1693  imageB.at<uchar>(z, x) = colorC.b;
1694 
1695  // idxPx per 3D point
1696  idxPx.push_back(Point(z, x));
1697 
1698  idxPx1D.push_back(corrMat.at<int>(z, x));
1699  }
1700 
1701  cv::Mat imageC;
1702  std::vector<cv::Mat> channels;
1703 
1704  channels.push_back(imageR);
1705  channels.push_back(imageG);
1706  channels.push_back(imageB);
1707 
1708  cv::merge(channels, imageC);
1709 
1710  // Median filter to fill holes
1711  cv::Mat imageFilt = cv::Mat::zeros(rows, cols, CV_32F);
1712  cv::medianBlur(image, imageFilt, 3);
1713 
1714  for (int y = 0; y < image.rows; y++) {
1715  for (int x = 0; x < image.cols; x++) {
1716  if (image.at<float>(y, x) == 0) {
1717  image.at<float>(y, x) = imageFilt.at<float>(y, x);
1718  }
1719  }
1720  }
1721 
1722  // Check for black areas after filling holes. These are structural
1723  // holes (windows, doors)
1724  std::vector<int> nullCols, nullRows;
1725 
1726  for (int y = 0; y < image.rows; y++) {
1727  for (int x = 0; x < image.cols; x++) {
1728  if (image.at<float>(y, x) == 0) {
1729  nullCols.push_back(x);
1730  nullRows.push_back(y);
1731  }
1732  }
1733  }
1734 
1735  cv::Mat imagePlot;
1736  image.convertTo(imagePlot, CV_8U);
1737 
1738  cloud->scale(0.01, 0.001, 0.01); // Scale back
1739  cloud->Translate(minBox_0); // Translate back
1740 
1741  stamp = dateStamp();
1742  auto_seg_log << stamp << " Depth and colour maps created" << endl;
1743 
1744  // CWT
1745  double scaleCWT = joints * 0.254; // 1.27 for 5cm
1746  cv::Mat cwtcfs;
1747  cv::Mat bincfs = cv::Mat::zeros(cwtcfs.size(), CV_8UC1);
1748 
1749  cwt2d(scaleCWT, image, cwtcfs, bincfs);
1750 
1751  // Cropping borders added in cwt
1752 
1753  cv::Rect myROI(0, 0, image.cols, image.rows);
1754  bincfs = bincfs(myROI);
1755 
1756  cv::Mat hola;
1757 
1758  stamp = dateStamp();
1759  auto_seg_log << stamp << " CWT calculated" << endl;
1760 
1761  // Clean areas from holes
1762  for (int i = 0; i < nullRows.size(); i++) {
1763  bincfs.at<uchar>(nullRows.at(i), nullCols.at(i)) = 0;
1764  }
1765 
1766  // Fill in the holes
1767  fillEdgeImage(bincfs, hola, z);
1768 
1769  // Remove small segments
1770  cv::Mat output = threshSegments(hola, 20);
1771 
1772  // Erosion + Dilation
1773  int erosion_elem = 2; // 0 = morph_rect, 1 = morph_cross, 2 =
1774  // morph_ellipse (I think Matlab uses 0)
1775  int erosion_size = 1;
1776  cv::Mat erodedM;
1777  erosionCC(erosion_elem, erosion_size, output, erodedM);
1778 
1780  int dilation_elem =
1781  2; // 0 = morph_rect, 1 = morph_cross, 2 = morph_ellipse
1782  int dilation_size = 1;
1783  cv::Mat dilatedM;
1784  dilationCC(dilation_elem, dilation_size, erodedM, dilatedM);
1785 
1786  stamp = dateStamp();
1787  auto_seg_log << stamp << " CWT post-processing done" << endl;
1788 
1789  // EXTRACT INFO FROM BINARY SEGMENTATION
1790  vector<vector<Point>> pixsLabel, contours;
1791  Mat labels, stats;
1792 
1793  extractInfo(dilatedM, pixsLabel, labels, stats, contours);
1794 
1795  // OPTIMISE CWT
1796  // areas
1797  vector<double> area(
1798  stats.rows - 1,
1799  0); // nLabels-1 as i = 0 corresponds to background
1800  for (int i = 1; i < stats.rows; i++) {
1801  area.at(i - 1) = stats.at<int>(i, CC_STAT_AREA);
1802  }
1803  // mean
1804  double meanArea = std::accumulate(area.begin(), area.end(), 0) /
1805  (stats.rows - 1);
1806 
1807  // std
1808  std::vector<double> diff(area.size());
1809  std::transform(area.begin(), area.end(), diff.begin(),
1810  [meanArea](double x) { return x - meanArea; });
1811  double sq_sum = std::inner_product(diff.begin(), diff.end(),
1812  diff.begin(), 0.0);
1813  double stdevArea = std::sqrt(sq_sum / area.size());
1814 
1815  int op = 3; // op = 1: lt; op = 2: leq; op = 3: gt; op = 4: geq;
1816  double thres = meanArea + stdevArea; // meanArea + 5*stdevArea
1817  std::vector<int> bigStones = findMatlab(area, op, thres);
1818 
1819  // Divide big stones. Erode big > new segments. New matrix (from
1820  // dilateM) without big ones but adding new segments
1821  cv::Mat firstSegmentation;
1822  firstSegmentation = stonesDivision(pixsLabel, labels, bigStones);
1823 
1824  std::vector<std::vector<Point>> pixsLabel2, contours2;
1825  cv::Mat labels2, stats2;
1826  extractInfo(firstSegmentation, pixsLabel2, labels2, stats2,
1827  contours2); // Extract info after big stone division
1828 
1829  stamp = dateStamp();
1830  auto_seg_log << stamp << " Big stones divided" << endl;
1831 
1832  cv::Mat skel = skeleton(firstSegmentation, 1);
1833 
1834  // CONVEX IMAGES
1835  std::vector<Mat> channelsC(3);
1836  cv::split(imageC, channelsC);
1837  cv::Mat mapB = channelsC[0]; // 3channel matrices are BGR
1838  cv::Mat mapG = channelsC[1];
1839  cv::Mat mapR = channelsC[2];
1840 
1841  // Find the convex hull object for each contour
1842  std::vector<std::vector<Point>> hull(contours2.size());
1843  for (int i = 0; i < contours2.size(); i++) {
1844  convexHull(cv::Mat(contours2[i]), hull[i], false);
1845  }
1846  std::vector<std::vector<Point>> hull0;
1847 
1848  // Draw contours + hull results
1849  RNG rng(12345);
1850  std::vector<std::vector<Point>> pixsHull, contoursHull;
1851  std::vector<std::vector<Point>> pixsHullSt(contours2.size());
1852  cv::Mat labelsHull, statsHull;
1853  cv::Mat drawingAll =
1854  cv::Mat::zeros(firstSegmentation.size(), CV_8UC3);
1855 
1856  // Segments growth
1857  std::vector<std::vector<Point>> segmentsColor;
1858  std::vector<cv::Mat> matConvexSt;
1859  std::vector<int> pixsLabels(
1860  firstSegmentation.cols * firstSegmentation.rows,
1861  -1); // Store idxs in a vector instead of vector of vectors
1862  // //FK2310
1863  std::vector<int> pixsLabels2(
1864  firstSegmentation.cols * firstSegmentation.rows,
1865  -1); // Store idxs in a vector instead of vector of
1866  // vectors. Consider overlapping //FK2310
1867  for (int i = 0; i < contours2.size(); i++) //
1868  {
1869  cv::Mat drawing =
1870  cv::Mat::zeros(firstSegmentation.size(), CV_8UC1);
1871 
1872  Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255),
1873  rng.uniform(0, 255));
1874  Scalar color2 = Scalar(255, 255, 255);
1875  drawContours(drawing, hull, i, color2, -1, 8,
1876  std::vector<Vec4i>(), 0,
1877  cv::Point()); // 5th input param to -1 to colour
1878  // segments
1879  drawContours(drawingAll, hull, i, color, -1, 8,
1880  std::vector<Vec4i>(), 0, cv::Point()); // To plot
1881 
1882  extractInfo(drawing, pixsHull, labelsHull, statsHull,
1883  contoursHull); // Extract info from individual
1884  // convex hulls
1885 
1886  pixsHullSt[i] = pixsHull[0];
1887 
1888  for (int j = 0; j < pixsHull[0].size(); j++) {
1889  int idx = corrMat.at<int>(pixsHull[0].at(j).x,
1890  pixsHull[0].at(j).y);
1891  if (pixsLabels.at(idx) == -1) {
1892  // Stones inside each other not considered as duplicated
1893  pixsLabels.at(idx) = i;
1894  } else { // Additional vector to consider pixels labeled as
1895  // part of two stones (due to convex hull
1896  // operation, for example)
1897  pixsLabels2.at(idx) = i;
1898  }
1899  }
1900 
1901  std::vector<Point> pixelsH = pixsHull[0]; // Segments base
1902  std::vector<int> coeffB = pixelValues(mapB, pixelsH);
1903  std::vector<int> coeffG = pixelValues(mapG, pixelsH);
1904  std::vector<int> coeffR = pixelValues(mapR, pixelsH);
1905 
1906  sort(coeffB.begin(), coeffB.end());
1907  double medB = coeffB[coeffB.size() / 2];
1908  sort(coeffG.begin(), coeffG.end());
1909  double medG = coeffG[coeffG.size() / 2];
1910  sort(coeffR.begin(), coeffR.end());
1911  double medR = coeffR[coeffR.size() / 2];
1912  std::vector<double> coeff0 = {medB, medG, medR};
1913 
1914  int j = 1;
1915  int noPix = 0;
1916  double growthRate = 1000;
1917  cv::Mat convexM, matNew;
1918  int dSize = 1;
1919  }
1920 
1921  stamp = dateStamp();
1922  auto_seg_log << stamp << " Convex hulls defined" << endl;
1923 
1924  // EXTRACT 3D POINTS PER SEGMENT
1925  for (int i = 0; i < contours2.size(); i++) //
1926  {
1927  std::vector<cv::Point> pixels = pixsHullSt[i];
1928 
1930  std::vector<int> pixels1d;
1931  for (auto e : pixels) {
1932  int idx = corrMat.at<int>(e.x, e.y);
1933  pixels1d.push_back(idx);
1934  }
1935 
1936  std::vector<int> idxCloud;
1937  for (int j = 0; j < idxPx.size(); j++) {
1938  if (pixsLabels.at(idxPx1D.at(j)) == i)
1939  idxCloud.push_back(j);
1940  if (pixsLabels2.at(idxPx1D.at(j)) ==
1941  i) // Check potential second label (this will be
1942  // cleaned afterwards)
1943  idxCloud.push_back(j);
1944  }
1945 
1946  std::vector<std::pair<int, int>> global_SglStoneIdx;
1947 
1948  int max_size =
1949  cloud->size() / 1; // maximum size per single stone
1950  float density = getDensity(cloud, idxCloud);
1951  float max_density = 40000;
1952  if (idxCloud.size() < max_size / 10) {
1953  for (auto e : idxCloud) {
1954  if (e < idxClouds[z].size())
1955  global_SglStoneIdx.push_back(
1956  make_pair(idxClouds[z][e], stoneIdx));
1957 
1958  else {
1959  auto_seg_log << "====> Error :" << z << " " << e
1960  << endl;
1961  }
1962  }
1963 
1964  stones.push_back(global_SglStoneIdx);
1965  stoneIdx++;
1966  }
1967 
1968  idxCloud.clear();
1969  global_SglStoneIdx.clear();
1970  pixels.clear();
1971  pixels1d.clear();
1972  }
1973 
1974  stamp = dateStamp();
1975  auto_seg_log << stamp << " 3D points per segment extracted" << endl;
1976 
1977  /*** HERE ENDS THE ACTION ***/
1978 
1979  // Deleting releasing memory ?
1980  stamp = dateStamp();
1981  auto_seg_log << stamp
1982  << " Point cloud processed. Releasing memory ... "
1983  << endl;
1984 
1985  cComment.erase();
1986  qComment.clear();
1987 
1988  corrMat.release();
1989  image.release();
1990  imageR.release();
1991  imageG.release();
1992  imageC.release();
1993  channels.clear();
1994  imageFilt.release();
1995  imagePlot.release();
1996  cwtcfs.release();
1997  bincfs.release();
1998 
1999  hola.release();
2000  output.release();
2001  erodedM.release();
2002  dilatedM.release();
2003  pixsLabel.clear();
2004  contours.clear();
2005  labels.release();
2006  stats.release();
2007  area.clear();
2008  diff.clear();
2009  bigStones.clear();
2010  firstSegmentation.release();
2011  pixsLabel2.clear();
2012  contours2.clear();
2013  labels2.release();
2014  skel.release();
2015  channelsC.clear();
2016  mapB.release();
2017  mapG.release();
2018  mapR.release();
2019  hull.clear();
2020  hull0.clear();
2021  pixsHull.clear();
2022  contoursHull.clear();
2023  pixsHullSt.clear();
2024  statsHull.release();
2025  drawingAll.release();
2026  segmentsColor.clear();
2027  matConvexSt.clear();
2028 
2029  pointC = nullptr;
2030  }
2031  stamp = dateStamp();
2032 
2033  cloud->removeMetaData("cloud");
2034 
2035  cloud->clear();
2036  }
2037 
2038  stamp = dateStamp();
2039  auto_seg_log << stamp << " Stitching stones" << endl;
2040 
2041  // concatenating all stone's cloudidx and stoneidx pairs into one vector
2042  // instead of a vector of vectors with separate stone in each vector
2043 
2044  vector<pair<int, int>> allStones;
2045 
2046  if (stones.size() > 0) allStones = stones[0];
2047 
2048  for (unsigned int i = 1; i < stones.size(); i++) {
2049  allStones.insert(allStones.end(), stones[i].begin(), stones[i].end());
2050  }
2051  stamp = dateStamp();
2052  auto_seg_log << stamp
2053  << " Total number of unstitched stones: " << stones.size()
2054  << endl;
2055 
2056  // Start of stiching
2057  // indentifying and merging indentical stones after the stitching
2058  // This can be defined as a function
2059 
2060  // sorting a vector of pairs
2061  sort(allStones.begin(), allStones.end(),
2062  [](auto& left, auto& right) { return left.first < right.first; });
2063  stamp = dateStamp();
2064  auto_seg_log << stamp << " Start looking for whole stones " << endl;
2065 
2066  unsigned int j = 0;
2067  unsigned int i = 0;
2068  while (i < allStones.size()) {
2069  j = i + 1;
2070 
2071  while (j < allStones.size() &&
2072  allStones[i].first == allStones[j].first &&
2073  allStones[i].second != allStones[j].second)
2074 
2075  {
2076  unsigned int oldIdx = allStones[j].second;
2077  unsigned int newIdx = allStones[i].second;
2078  for (unsigned int k = 0; k < allStones.size(); k++) {
2079  if (allStones[k].second == oldIdx) allStones[k].second = newIdx;
2080  }
2081  j++;
2082  }
2083  i = j;
2084  }
2085 
2086  // End of stitching
2087  my_time = time(NULL);
2088  stamp = dateStamp();
2089  auto_seg_log << stamp << " Indices corrected " << endl;
2090 
2091  // eliminating repeated pairs ( originaly common points in stones)
2092 
2093  allStones.erase(unique(allStones.begin(), allStones.end()),
2094  allStones.end());
2095 
2096  stamp = dateStamp();
2097  auto_seg_log << stamp << " Repeated pairs removed " << endl;
2098 
2099  int b = cloud0->size();
2100  vector<int> allStones_Idx; // storing only cloud index of points that's
2101  // within stones
2102  vector<int> stoneId; // saving all the existing and used indices for stones
2103  vector<int> allMortar;
2104 
2105  stamp = dateStamp();
2106  auto_seg_log << stamp << " Getting all stones idx " << endl;
2107 
2108  for (auto e : allStones) allStones_Idx.push_back(e.first);
2109 
2110  sort(allStones_Idx.begin(), allStones_Idx.end());
2111 
2112  // Mortar idx
2113  std::set_difference(global_idx.begin(), global_idx.end(),
2114  allStones_Idx.begin(), allStones_Idx.end(),
2115  std::inserter(allMortar, allMortar.end()));
2116 
2117  stamp = dateStamp();
2118  auto_seg_log << stamp << " Mortar idx identified " << endl;
2119 
2120  // correcting stone indices (make them 1,2,3 .... in order)
2121 
2122  for (auto e : allStones) stoneId.push_back(e.second);
2123 
2124  sort(stoneId.begin(), stoneId.end());
2125 
2126  stoneId.erase(unique(stoneId.begin(), stoneId.end()), stoneId.end());
2127 
2128  vector<vector<int>> vecStones(stoneId.size());
2129 
2130  sort(allStones.begin(), allStones.end(),
2131  [](auto& left, auto& right) { return left.second < right.second; });
2132 
2133  int old_idx = allStones[0].second;
2134  int new_idx = 0;
2135 
2136  for (unsigned i = 0; i < allStones.size(); i++) {
2137  if (allStones[i].second == old_idx) {
2138  allStones[i].second = new_idx;
2139  vecStones[new_idx].push_back(allStones[i].first);
2140 
2141  } else {
2142  new_idx++;
2143  old_idx = allStones[i].second;
2144  allStones[i].second = new_idx;
2145  vecStones[new_idx].push_back(allStones[i].first);
2146  }
2147  }
2148 
2149  stoneId.clear();
2150  allStones_Idx.clear(); // values were sorted
2151 
2152  for (auto e : allStones) {
2153  allStones_Idx.push_back(e.first);
2154  stoneId.push_back(e.second);
2155  }
2156 
2157  // suffling stone IDs for better visuals
2158  vector<int> stIdShuffle = stoneId;
2159 
2160  sort(stIdShuffle.begin(), stIdShuffle.end());
2161 
2162  stIdShuffle.erase(unique(stIdShuffle.begin(), stIdShuffle.end()),
2163  stIdShuffle.end());
2164 
2165  auto rng = std::default_random_engine{};
2166  std::shuffle(stIdShuffle.begin(), stIdShuffle.end(), rng);
2167 
2168  // generating final clouds
2169  stamp = dateStamp();
2170  auto_seg_log << stamp << " Writing final clouds " << endl;
2171 
2172  ccPointCloud* f_cloudStones = new ccPointCloud("f_cloudStones");
2173  ccPointCloud* f_cloudMortar = new ccPointCloud("f_cloudMortar");
2174 
2175  // stones
2176  if (allStones_Idx.size() > 0) {
2177  for (auto e : allStones_Idx) {
2178  f_cloudStones->reserveThePointsTable(1);
2179  f_cloudStones->reserveTheRGBTable();
2180  CCVector3 p(cloud0->getPoint(e)->x, cloud0->getPoint(e)->y,
2181  cloud0->getPoint(e)->z);
2182  f_cloudStones->addPoint(p);
2183 
2184  const ecvColor::Rgb col = cloud0->getPointColor(e);
2185  f_cloudStones->addRGBColor(col);
2186  }
2187  }
2188 
2189  stamp = dateStamp();
2190  auto_seg_log << stamp << " Stone cloud OK. " << allStones_Idx.size()
2191  << " pts. " << vecStones.size() << " stones." << endl;
2192 
2193  // adding scalar field of stoneId to stone point cloud
2194 
2195  cloudViewer::ScalarField* stIdSF = nullptr;
2196 
2197  int sfIdx = f_cloudStones->getScalarFieldIndexByName("Stone Index");
2198  if (sfIdx < 0) {
2199  sfIdx = f_cloudStones->addScalarField("Stone Index");
2200  }
2201  if (sfIdx < 0) {
2202  return;
2203  }
2204 
2205  stIdSF = f_cloudStones->getScalarField(sfIdx);
2206 
2207  if (allStones_Idx.size() > 0) {
2208  for (unsigned int i = 0; i < stoneId.size(); i++) {
2209  int index = static_cast<int>(stIdShuffle[stoneId[i]]);
2210  stIdSF->setValue(i, index);
2211  }
2212  }
2213 
2214  f_cloudStones->setCurrentDisplayedScalarField(sfIdx);
2215 
2216  stIdSF->computeMinAndMax();
2217 
2218  stamp = dateStamp();
2219  auto_seg_log << stamp << " Stone cloud SF OK " << endl;
2220 
2221  // Mortar
2222  if (allMortar.size() > 0) {
2223  for (auto e : allMortar) {
2224  f_cloudMortar->reserveThePointsTable(1);
2225  f_cloudMortar->reserveTheRGBTable();
2226  CCVector3 p(cloud0->getPoint(e)->x, cloud0->getPoint(e)->y,
2227  cloud0->getPoint(e)->z);
2228  f_cloudMortar->addPoint(p);
2229 
2230  const ecvColor::Rgb col = cloud0->getPointColor(e);
2231  f_cloudMortar->addRGBColor(col);
2232  }
2233  }
2234 
2235  stamp = dateStamp();
2236  auto_seg_log << stamp << " Mortar OK. " << allMortar.size() << " pts"
2237  << endl;
2238 
2239  // ADDING CLOUDS TO DB
2240  ccPointCloud* mortarMaps = nullptr;
2241  if (checkMortar == true) {
2242  stamp = dateStamp();
2243  auto_seg_log << " Starting mortar maps generation... " << endl;
2244 
2245  if (allMortar.size() > 0)
2246  mortarMaps = getMortarMaps(f_cloudStones, f_cloudMortar);
2247 
2248  stamp = dateStamp();
2249  auto_seg_log << stamp << " Mortar maps OK " << endl;
2250 
2251  mortarMaps->showSF(true);
2252  }
2253 
2254  // Move all the clouds back
2255  cloud0->Translate(minBox);
2256  cloud0->applyRigidTransformation(R3.inverse());
2257  cloud0->applyRigidTransformation(R2.inverse());
2258 
2259  if (allMortar.size() > 0) {
2260  f_cloudMortar->Translate(minBox);
2261  f_cloudMortar->applyRigidTransformation(R3.inverse());
2262  f_cloudMortar->applyRigidTransformation(R2.inverse());
2263  }
2264 
2265  if (allStones.size() > 0) {
2266  f_cloudStones->Translate(minBox);
2267  f_cloudStones->applyRigidTransformation(R3.inverse());
2268  f_cloudStones->applyRigidTransformation(R2.inverse());
2269  }
2270 
2271  if (checkMortar == true) {
2272  mortarMaps->Translate(minBox);
2273  mortarMaps->applyRigidTransformation(R3.inverse());
2274  mortarMaps->applyRigidTransformation(R2.inverse());
2275  }
2276 
2277  // Generating contour polylines
2278  stamp = dateStamp();
2279  auto_seg_log << stamp << " Start of contour generation " << endl;
2280 
2281  vector<ccPolyline*> contours;
2282 
2283  ccHObject* contoursContainer = new ccHObject("Stone contours");
2284 
2285  int cntSt = 0;
2286 
2287  // contours in an alternative way using stone indexes and original cloud
2288  vector<double> stonesSize;
2289  if (vecStones.size() > 0) {
2290  for (auto ve : vecStones) {
2291  string s_name = "stone " + to_string(cntSt);
2292  QString qs_name = QString::fromUtf8(s_name.c_str());
2293 
2294  ccPolyline* contour = nullptr;
2295 
2296  if (ve.size() > 0) {
2297  contour = contourPoly2(cloud0, ve, qs_name);
2298  contoursContainer->addChild(contour);
2299  cntSt++;
2300  // Get BB per stone from contour.
2301  CCVector3 bbMinS;
2302  bbMinS = CCVector3(0, 0, 0);
2303  CCVector3 bbMaxS;
2304  bbMaxS = CCVector3(0, 0, 0);
2305  contour->getBoundingBox(bbMinS, bbMaxS);
2306  double sizeS = (bbMaxS.x - bbMinS.x) * (bbMaxS.z - bbMinS.z);
2307  stonesSize.push_back(sizeS);
2308  }
2309  }
2310 
2311  // Mean and median size of stones
2312 
2313  double meanSizeStones =
2314  accumulate(stonesSize.begin(), stonesSize.end(), 0.0) /
2315  stonesSize.size();
2316  double standDev = stddev(stonesSize);
2317 
2318  stamp = dateStamp();
2319  auto_seg_log << stamp << " Average size of stones (BB) "
2320  << meanSizeStones << " m2 +-" << standDev << " ("
2321  << meanSizeStones * 10000 << " cm2)" << endl;
2322 
2323  sort(stonesSize.begin(), stonesSize.end());
2324  bool evenAmount = (stonesSize.size() % 2 == 0);
2325  int oddAmountIdx = ceil(stonesSize.size() / 2);
2326  double evenAmountMedian =
2327  (stonesSize[oddAmountIdx - 1] + stonesSize[oddAmountIdx]) / 2;
2328  double medianSizeStones =
2329  evenAmount ? evenAmountMedian : stonesSize[oddAmountIdx];
2330 
2331  stamp = dateStamp();
2332  auto_seg_log << stamp << " Median size of stones (BB) "
2333  << medianSizeStones << " m2 (" << medianSizeStones * 10000
2334  << " cm2)" << endl;
2335  }
2336 
2337  stamp = dateStamp();
2338  auto_seg_log << stamp << " Contour generation done " << endl;
2339 
2340  f_cloudStones->showColors(true);
2341 
2342  if (allMortar.size() > 0) {
2343  f_cloudMortar->showColors(true);
2344  }
2345 
2346  m_app->addToDB(f_cloudStones, true, true, false, true);
2347 
2348  if (allMortar.size() > 0)
2349  m_app->addToDB(f_cloudMortar, true, true, false, true);
2350 
2351  stamp = dateStamp();
2352  auto_seg_log << stamp << " Mortar cloud OK " << endl;
2353 
2354  m_app->addToDB(contoursContainer, true, true, false, true);
2355 
2356  if (checkMortar == true && allMortar.size() > 0)
2357  m_app->addToDB(mortarMaps, true, true, false, true);
2358 
2359  m_app->setView(CC_FRONT_VIEW);
2360 }
constexpr double M_PI
Pi.
Definition: CVConst.h:19
@ CC_FRONT_VIEW
Definition: CVConst.h:105
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
Definition: CVGeom.h:798
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
std::string filename
std::shared_ptr< core::Tensor > image
int size
std::string name
char type
math::float4 color
void * X
Definition: SmallVector.cpp:45
std::vector< float > Vec3d
Definition: TreeIso.cpp:41
#define NULL
core::Tensor result
Definition: VtkUtils.cpp:76
static std::string FromQString(const QString &qs)
Definition: CVTools.cpp:100
Dialog for importing a 2D revolution profile (qSRA plugin)
Type y
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
virtual void setVisible(bool state)
Sets entity visibility.
virtual void showColors(bool state)
Sets colors visibility.
virtual void showSF(bool state)
Sets active scalarfield visibility.
static ccGLMatrixTpl< float > FromToRotation(const Vector3Tpl< float > &from, const Vector3Tpl< float > &to)
Creates a transformation matrix that rotates a vector to another.
ccGLMatrixTpl< T > inverse() const
Returns inverse transformation.
Float version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:19
void setPointSize(unsigned size=0)
Sets point size.
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
ccHObject * getParent() const
Returns parent object.
Definition: ecvHObject.h:245
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
Definition: ecvHObject.h:337
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
virtual void setName(const QString &name)
Sets object name.
Definition: ecvObject.h:75
bool removeMetaData(const QString &key)
Removes a given associated meta-data.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void setCurrentDisplayedScalarField(int index)
Sets the currently displayed scalar field.
virtual void applyRigidTransformation(const ccGLMatrix &trans) override
Applies a rigid transformation (rotation + translation)
virtual void scale(PointCoordinateType fx, PointCoordinateType fy, PointCoordinateType fz, CCVector3 center=CCVector3(0, 0, 0)) override
Multiplies all coordinates by constant factors (one per dimension)
int addScalarField(const char *uniqueName) override
Creates a new scalar field and registers it.
bool reserveTheRGBTable()
Reserves memory to store the RGB colors.
void clear() override
Clears the entity from all its points and features.
virtual ccPointCloud & Translate(const Eigen::Vector3d &translation, bool relative=true) override
Apply translation to the geometry coordinates.
bool hasColors() const override
Returns whether colors are enabled or not.
const ecvColor::Rgb & getPointColor(unsigned pointIndex) const override
Returns color corresponding to a given point.
void addRGBColor(const ecvColor::Rgb &C)
Pushes an RGB color on stack.
ccPointCloud * partialClone(const cloudViewer::ReferenceCloud *selection, int *warnings=nullptr, bool withChildEntities=true) const
Creates a new point cloud object from a ReferenceCloud (selection)
bool reserveThePointsTable(unsigned _numberOfPoints)
Reserves memory to store the points coordinates.
Colored polyline.
Definition: ecvPolyline.h:24
void showVertices(bool state)
Sets whether to display or hide the polyline vertices.
Definition: ecvPolyline.h:129
void setColor(const ecvColor::Rgb &col)
Sets the polyline color.
Definition: ecvPolyline.h:81
Standard ECV plugin interface.
static ReferenceCloud * resampleCloudSpatially(GenericIndexedCloudPersist *cloud, PointCoordinateType minDistance, const SFModulationParams &modParams, DgmOctree *octree=nullptr, GenericProgressCallback *progressCb=nullptr)
Resamples a point cloud (process based on inter point distance)
int getScalarFieldIndexByName(const char *name) const
Returns the index of a scalar field represented by its name.
ScalarField * getScalarField(int index) const
Returns a pointer to a specific scalar field.
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
void getBoundingBox(CCVector3 &bbMin, CCVector3 &bbMax) override
Definition: PointCloudTpl.h:57
unsigned size() const override
Definition: PointCloudTpl.h:38
ScalarType getPointScalarValue(unsigned pointIndex) const override
const CCVector3 * getPoint(unsigned index) const override
static bool extractConcaveHull2D(std::vector< IndexedCCVector2 > &points, std::list< IndexedCCVector2 * > &hullPoints, PointCoordinateType maxSquareLength=0)
Determines the 'concave' hull of a set of points.
void setClosed(bool state)
Sets whether the polyline is closed or not.
Definition: Polyline.h:29
A very simple point cloud (no point duplication)
virtual bool addPointIndex(unsigned globalIndex)
Point global index insertion mechanism.
void getBoundingBox(CCVector3 &bbMin, CCVector3 &bbMax) override
Returns the cloud bounding box.
virtual bool reserve(unsigned n)
Reserves some memory for hosting the point references.
A simple scalar field (to be associated to a point cloud)
Definition: ScalarField.h:25
virtual void computeMinAndMax()
Determines the min and max values.
Definition: ScalarField.h:123
void setValue(std::size_t index, ScalarType value)
Definition: ScalarField.h:96
RGB color structure.
Definition: ecvColorTypes.h:49
__host__ __device__ int2 abs(int2 v)
Definition: cutil_math.h:1267
Definition: API.h:123
@ POINT_CLOUD
Definition: CVTypes.h:104
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
CLOUDVIEWER_HOST_DEVICE Pair< First, Second > make_pair(const First &_first, const Second &_second)
Definition: SlabTraits.h:49
MiniVec< float, N > floor(const MiniVec< float, N > &a)
Definition: MiniVec.h:75
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
Definition: MiniVec.h:89
constexpr Rgb cyan(0, MAX, MAX)
unsigned char uchar
Definition: matrix.h:41
std::string to_string(const T &n)
Definition: Common.h:20
Definition: Eigen.h:85
ccPolyline * contourPoly(ccPointCloud *stone)
Definition: qAutoSeg.cpp:936
float getDensity(ccPointCloud *cloud, vector< int > idx)
Definition: qAutoSeg.cpp:1287
void dilationCC(int dilation_elem, int dilation_size, cv::Mat &src, cv::Mat &dilation_dst)
Definition: qAutoSeg.cpp:496
void extractInfo(cv::Mat &input, std::vector< std::vector< cv::Point >> &pixsLabel, cv::Mat &labels, cv::Mat &stats, std::vector< std::vector< Point >> &contours0)
Definition: qAutoSeg.cpp:667
cv::Mat stonesDivision(std::vector< std::vector< cv::Point >> pixsLabel, cv::Mat labels, std::vector< int > bigStones)
Definition: qAutoSeg.cpp:534
ccPolyline * contourPoly2(ccPointCloud *cloud0, vector< int > V, QString name)
Definition: qAutoSeg.cpp:993
std::vector< int > findMatlab(std::vector< double > inputV, int op, double thres)
Definition: qAutoSeg.cpp:454
ccPointCloud * getMortarMaps(ccPointCloud *f_cloudStones, ccPointCloud *f_cloudMortar)
Definition: qAutoSeg.cpp:1052
void fft2(const cv::Mat in, cv::Mat &complexI)
Definition: qAutoSeg.cpp:753
cv::Mat multiplicationCC(cv::Mat_< std::complex< double >> M1, cv::Mat_< std::complex< double >> M2)
Definition: qAutoSeg.cpp:792
std::vector< int > findIdx(std::vector< int > inputV, int thres)
Definition: qAutoSeg.cpp:485
void erosionCC(int erosion_elem, int erosion_size, cv::Mat &src, cv::Mat &erosion_dst)
Definition: qAutoSeg.cpp:515
std::vector< int > setIntersectIdxPixs1D(std::vector< int > listNew, std::vector< int > listOld)
Definition: qAutoSeg.cpp:372
std::vector< cv::Point > setdiffPixs(std::vector< cv::Point > listNew, std::vector< cv::Point > listOld)
Definition: qAutoSeg.cpp:333
cv::Mat conjugate(cv::Mat_< std::complex< double >> M1, bool *flag)
Definition: qAutoSeg.cpp:767
std::vector< cv::Point > extractVectorFromIdx(std::vector< cv::Point > v, vector< int > idx)
Definition: qAutoSeg.cpp:311
double optRotY(ccPointCloud *&cloud)
Definition: qAutoSeg.cpp:115
void cwt2d(double sca, cv::Mat image, cv::Mat &cwtcfs, cv::Mat &bincfs)
Definition: qAutoSeg.cpp:809
void cloud2binary(ccPointCloud *cloud, cv::Mat &corrMatS, std::vector< cv::Point > &idxPxS, std::vector< int > &idxPx1DS, cv::Mat &imageBWS)
Definition: qAutoSeg.cpp:247
std::vector< std::vector< cv::Point > > pixelListMat(cv::Mat inputM, int nLabels)
Definition: qAutoSeg.cpp:437
void fillEdgeImage(cv::Mat edgesIn, cv::Mat &filledEdgesOut, int z)
Definition: qAutoSeg.cpp:692
cv::Mat skeleton(cv::Mat I, bool flagInv)
Definition: qAutoSeg.cpp:279
#define CV_PCA_DATA_AS_ROW
Definition: qAutoSeg.cpp:8
std::vector< int > setIntersectIdxPixs(std::vector< cv::Point > listNew, std::vector< cv::Point > listOld)
Definition: qAutoSeg.cpp:355
string dateStamp()
Definition: qAutoSeg.cpp:97
void rotY(ccPointCloud *&cloud, double th0)
Definition: qAutoSeg.cpp:177
cv::Mat threshSegments(cv::Mat &src, double threshSize)
Definition: qAutoSeg.cpp:578
double stddev(std::vector< double > const &func)
Definition: qAutoSeg.cpp:86
string type2str(int type)
Definition: qAutoSeg.cpp:627
std::vector< int > pixelValues(cv::Mat inputM, std::vector< cv::Point > &pixelList)
Definition: qAutoSeg.cpp:426
std::vector< cv::Point > setxorPixs(std::vector< cv::Point > listNew, std::vector< cv::Point > listOld)
Definition: qAutoSeg.cpp:392
void populateMat(cv::Mat &populated, std::vector< cv::Point > pixList, int value)
Definition: qAutoSeg.cpp:323
Parameters for the scalar-field based modulation of a parameter.
Definition: lsd.c:149