ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
qTreeIsoCommands.h
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 #pragma once
9 
10 // #######################################################################################
11 // # # # ACLOUDVIEWER PLUGIN: qTreeIso # # # # This
12 // program is free software; you can redistribute it and/or modify # #
13 // it under the terms of the GNU General Public License as published by # # the
14 // Free Software Foundation; version 2 or later of the License. # #
15 // # # This program is distributed in the hope that it will be useful, #
16 // # but WITHOUT ANY WARRANTY; without even the implied warranty of # #
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU
18 // General Public License for more details. # # # # Please cite the
19 // following paper if you find this tool helpful # # # # Xi, Z.;
20 // Hopkinson, C. 3D Graph-Based Individual-Tree Isolation (Treeiso) # # from
21 // Terrestrial Laser Scanning Point Clouds. Remote Sens. 2022, 14, 6116. # #
22 // https://doi.org/10.3390/rs14236116 # # # # Our work relies on
23 // the cut-pursuit algorithm, please also consider citing: # # Landrieu, L.;
24 // Obozinski, G. Cut Pursuit: Fast Algorithms to Learn Piecewise # # Constant
25 // Functions on General Weighted Graphs. SIAM J. Imaging Sci. # # 2017,
26 // 10, 1724–1766. # # # # Copyright © # #
27 // Artemis Lab, Department of Geography & Environment # #
28 // University of Lethbridge, Canada # # # # # # Zhouxin
29 // Xi and Chris Hopkinson; # #
30 // truebelief2010@gmail.com; c.hopkinson@uleth.ca # # #
31 // #######################################################################################
32 
33 // A Matlab version shared via:
34 // https://github.com/truebelief/artemis_treeiso
35 
36 // ACloudViewer
38 
39 // Local
40 #include "TreeIso.h"
41 #include "ccTreeIsoDlg.h"
42 #include "qTreeIso.h"
43 
44 static const char COMMAND_TREEISO[] = "TREEISO";
45 
46 static const char COMMAND_LAMBDA1[] = "LAMBDA1";
47 static const char COMMAND_K1[] = "K1";
48 static const char COMMAND_DECIMATE_RESOLUTION1[] = "DECIMATE_RESOLUTION1";
49 
50 static const char COMMAND_LAMBDA2[] = "LAMBDA2";
51 static const char COMMAND_K2[] = "K2";
52 static const char COMMAND_MAX_GAP[] = "MAX_GAP";
53 static const char COMMAND_DECIMATE_RESOLUTION2[] = "DECIMATE_RESOLUTION2";
54 
55 static const char COMMAND_RHO[] = "RHO";
56 static const char COMMAND_VERTICAL_OVERLAP_WEIGHT[] = "VERTICAL_OVERLAP_WEIGHT";
57 
62 
63  bool process(ccCommandLineInterface& cmd) override {
64  cmd.print("[TreeIso]");
65 
66  if (cmd.clouds().empty()) {
67  cmd.error("No cloud loaded");
68  return false;
69  }
70 
71  // initial parameters
72  qTreeIso::Parameters parameters;
73 
74  bool try_init_seg = false;
75  bool try_intermediate_seg = false;
76  bool try_final_seg = false;
77 
78  while (!cmd.arguments().empty()) {
79  const QString& ARGUMENT = cmd.arguments().front();
81  COMMAND_LAMBDA1))) {
82  try_init_seg = true;
83  cmd.arguments().pop_front();
84  bool convert = false;
85 
86  parameters.reg_strength1 =
87  cmd.arguments().takeFirst().toFloat(&convert);
88  if ((!convert) & (parameters.reg_strength1 <= 0.0)) {
89  return cmd.error(
90  QObject::tr(
91  "Invalid parameter: value after \"-%1\"")
92  .arg(COMMAND_LAMBDA1));
93  }
94  cmd.print(QString("lambda1 (Regularization strength for "
95  "initial segmentation) set: %1")
96  .arg(parameters.reg_strength1));
97  } else if (ccCommandLineInterface::IsCommand(ARGUMENT,
98  COMMAND_K1)) {
99  try_init_seg = true;
100  cmd.arguments().pop_front();
101  bool convert = false;
102 
103  parameters.min_nn1 =
104  cmd.arguments().takeFirst().toInt(&convert);
105  if ((!convert) & (parameters.min_nn1 < 3)) {
106  return cmd.error(
107  QObject::tr(
108  "Invalid parameter: value after \"-%1\"")
109  .arg(COMMAND_K1));
110  }
111  cmd.print(QString("K1 (Nearest neighbors to search for initial "
112  "segmentation) set: %1")
113  .arg(parameters.min_nn1));
115  ARGUMENT, COMMAND_DECIMATE_RESOLUTION1)) {
116  try_init_seg = true;
117  cmd.arguments().pop_front();
118  bool convert = false;
119 
120  parameters.decimate_res1 =
121  cmd.arguments().takeFirst().toFloat(&convert);
122  if ((!convert) & (parameters.decimate_res1 < 0.001)) {
123  return cmd.error(
124  QObject::tr(
125  "Invalid parameter: value after \"-%1\"")
127  }
128  cmd.print(QString("Decimated resolution (in m) for initial "
129  "segmentation set: %1")
130  .arg(parameters.decimate_res1));
131  } else if (ccCommandLineInterface::IsCommand(ARGUMENT,
132  COMMAND_LAMBDA2)) {
133  try_intermediate_seg = true;
134  cmd.arguments().pop_front();
135  bool convert = false;
136 
137  parameters.reg_strength2 =
138  cmd.arguments().takeFirst().toFloat(&convert);
139  if ((!convert) & (parameters.reg_strength2 <= 0.0)) {
140  return cmd.error(
141  QObject::tr(
142  "Invalid parameter: value after \"-%1\"")
143  .arg(COMMAND_LAMBDA2));
144  }
145  cmd.print(QString("lambda2 (Regularization strength for "
146  "intermediate segmentation) set: %1")
147  .arg(parameters.reg_strength2));
148  } else if (ccCommandLineInterface::IsCommand(ARGUMENT,
149  COMMAND_K2)) {
150  try_intermediate_seg = true;
151  cmd.arguments().pop_front();
152  bool convert = false;
153 
154  parameters.min_nn2 =
155  cmd.arguments().takeFirst().toInt(&convert);
156  if ((!convert) & (parameters.min_nn2 < 3)) {
157  return cmd.error(
158  QObject::tr(
159  "Invalid parameter: value after \"-%1\"")
160  .arg(COMMAND_K2));
161  }
162  cmd.print(QString("K2 (Nearest neighbors to search for "
163  "intermediate segmentation) set: %1")
164  .arg(parameters.min_nn2));
165  } else if (ccCommandLineInterface::IsCommand(ARGUMENT,
166  COMMAND_MAX_GAP)) {
167  try_intermediate_seg = true;
168  cmd.arguments().pop_front();
169  bool convert = false;
170 
171  parameters.max_gap =
172  cmd.arguments().takeFirst().toFloat(&convert);
173  if ((!convert) & (parameters.max_gap <= 0.0001)) {
174  return cmd.error(
175  QObject::tr(
176  "Invalid parameter: value after \"-%1\"")
177  .arg(COMMAND_MAX_GAP));
178  }
179  cmd.print(QString("Maximum point gap (in m) for intermediate "
180  "segmentation set: %1")
181  .arg(parameters.max_gap));
183  ARGUMENT, COMMAND_DECIMATE_RESOLUTION2)) {
184  try_intermediate_seg = true;
185  cmd.arguments().pop_front();
186  bool convert = false;
187 
188  parameters.decimate_res2 =
189  cmd.arguments().takeFirst().toFloat(&convert);
190  if ((!convert) & (parameters.decimate_res2 < 0.001)) {
191  return cmd.error(
192  QObject::tr(
193  "Invalid parameter: value after \"-%1\"")
195  }
196  cmd.print(QString("Decimated resolution (in m) for "
197  "intermediate segmentation set: %1")
198  .arg(parameters.decimate_res2));
199  } else if (ccCommandLineInterface::IsCommand(ARGUMENT,
200  COMMAND_RHO)) {
201  try_final_seg = true;
202  cmd.arguments().pop_front();
203  bool convert = false;
204 
205  parameters.rel_height_length_ratio =
206  cmd.arguments().takeFirst().toFloat(&convert);
207  if ((!convert) & (parameters.rel_height_length_ratio < 0.001)) {
208  return cmd.error(
209  QObject::tr(
210  "Invalid parameter: value after \"-%1\"")
211  .arg(COMMAND_RHO));
212  }
213  cmd.print(QString("Relative height to length ratio (used to "
214  "detect non-stems for final segmentation) "
215  "set: %1")
216  .arg(parameters.rel_height_length_ratio));
217 
219  ARGUMENT, COMMAND_VERTICAL_OVERLAP_WEIGHT)) {
220  try_final_seg = true;
221  cmd.arguments().pop_front();
222  bool convert = false;
223 
224  parameters.vertical_weight =
225  cmd.arguments().takeFirst().toFloat(&convert);
226  if ((!convert) & (parameters.vertical_weight < 0.001)) {
227  return cmd.error(
228  QObject::tr(
229  "Invalid parameter: value after \"-%1\"")
231  }
232  cmd.print(QString("Vertical overlapping ratio weight for final "
233  "segmentation set: %1")
234  .arg(parameters.vertical_weight));
235  } else {
236  cmd.print("Parameters All Set");
237  break;
238  }
239  }
240 
241  for (CLCloudDesc& desc : cmd.clouds()) {
242  // Convert CC point cloud to treeIso type
243  unsigned count = desc.pc->size();
244  if (count == 0) {
245  cmd.print(QString("Cloud %1 is empty").arg(desc.pc->getName()));
246  continue;
247  }
248 
249  if (try_init_seg) {
250  if (!TreeIso::Init_seg_pcd(desc.pc, parameters.min_nn1,
251  parameters.reg_strength1,
252  parameters.decimate_res1)) {
253  return cmd.error(
254  "Failed to finish initial segmentation due to "
255  "unknown reasons.");
256  }
257  }
258  if (try_intermediate_seg) {
259  if (!TreeIso::Intermediate_seg_pcd(desc.pc, parameters.min_nn2,
260  parameters.reg_strength2,
261  parameters.decimate_res2,
262  parameters.max_gap)) {
263  return cmd.error(
264  "Failed to finish intermediate segmentation due to "
265  "unknown reasons.");
266  }
267  }
268  if (try_final_seg) {
269  if (!TreeIso::Final_seg_pcd(desc.pc, parameters.min_nn2,
270  parameters.rel_height_length_ratio,
271  parameters.vertical_weight)) {
272  return cmd.error(
273  "Failed to finish final segmentation due to "
274  "unknown reasons.");
275  }
276  }
277  }
278 
279  return true;
280  }
281 };
void convert(int argc, char **argv, const std::string &file_in, const std::string &file_out)
int count
static bool Intermediate_seg_pcd(ccPointCloud *pc, const unsigned PR_MIN_NN2, const float PR_REG_STRENGTH2, const float PR_DECIMATE_RES2, const float PR_MAX_GAP, QProgressDialog *progressDlg=nullptr)
Definition: TreeIso.cpp:143
static bool Final_seg_pcd(ccPointCloud *pc, const unsigned PR_MIN_NN3, const float PR_REL_HEIGHT_LENGTH_RATIO, const float PR_VERTICAL_WEIGHT, QProgressDialog *progressDlg=nullptr)
Definition: TreeIso.cpp:413
static bool Init_seg_pcd(ccPointCloud *pc, const unsigned min_nn1, const float regStrength1, const float PR_DECIMATE_RES1, QProgressDialog *progressDlg=nullptr)
Definition: TreeIso.cpp:58
Command line interface.
virtual QStringList & arguments()=0
Returns the list of arguments.
virtual void print(const QString &message) const =0
virtual bool error(const QString &message) const =0
static bool IsCommand(const QString &token, const char *command)
Test whether a command line token is a valid command keyword or not.
virtual std::vector< CLCloudDesc > & clouds()
Currently opened point clouds and their filename.
static const char COMMAND_RHO[]
static const char COMMAND_LAMBDA1[]
static const char COMMAND_DECIMATE_RESOLUTION2[]
static const char COMMAND_LAMBDA2[]
static const char COMMAND_VERTICAL_OVERLAP_WEIGHT[]
static const char COMMAND_K2[]
static const char COMMAND_DECIMATE_RESOLUTION1[]
static const char COMMAND_K1[]
static const char COMMAND_TREEISO[]
static const char COMMAND_MAX_GAP[]
Loaded cloud description.
qTreeIso command line processor
bool process(ccCommandLineInterface &cmd) override
Main process.
Command(const QString &name, const QString &keyword)
Default constructor.