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
ccHObject * getParent() const
Returns parent object.
Definition: ecvHObject.h:245
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
Definition: ecvHObject.cpp:534
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.
Definition: ecvObject.cpp:212
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void setCurrentDisplayedScalarField(int index)
Sets the currently displayed scalar field.
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)
ccPointCloud * partialClone(const cloudViewer::ReferenceCloud *selection, int *warnings=nullptr, bool withChildEntities=true) const
Creates a new point cloud object from a ReferenceCloud (selection)
int addScalarField(const char *uniqueName) override
Creates a new scalar field and registers it.
virtual ccPointCloud & Translate(const Eigen::Vector3d &translation, bool relative=true) override
Apply translation to the geometry coordinates.
bool reserveTheRGBTable()
Reserves memory to store the RGB colors.
void clear() override
Clears the entity from all its points and features.
virtual void applyRigidTransformation(const ccGLMatrix &trans) override
Applies a rigid transformation (rotation + translation)
bool hasColors() const override
Returns whether colors are enabled or not.
void addRGBColor(const ecvColor::Rgb &C)
Pushes an RGB color on stack.
const ecvColor::Rgb & getPointColor(unsigned pointIndex) const override
Returns color corresponding to a given point.
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
a[190]
const double * e
normal_z y
normal_z x
normal_z z
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)
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
unsigned char uchar
Definition: shpopen.c:319
Parameters for the scalar-field based modulation of a parameter.