37 #include <QCoreApplication>
55 cmd.
print(
"[3DMASC]");
57 if (cmd.
clouds().empty()) {
58 return cmd.
error(
"No cloud loaded");
61 int minArgumentCount = 2;
62 if (cmd.
arguments().size() < minArgumentCount) {
64 QString(
"Missing parameter(s): options, classifier "
65 "filename (.txt) and cloud roles after \"-%1\"")
69 bool keepAttributes =
false;
70 bool onlyFeatures =
false;
71 bool skipFeatures =
false;
72 QString featureSourceFilename;
74 QString argument = cmd.
arguments().front();
77 keepAttributes =
true;
78 cmd.
print(
"Will keep attributes");
84 cmd.
print(
"Will compute only the features");
89 keepAttributes =
true;
93 cmd.
print(
"Will skip the computation of features");
97 featureSourceFilename = cmd.
arguments().front();
98 if (featureSourceFilename.isEmpty()) {
100 QString(
"Missing parameter(s): feature sources "
101 "filename after \"-%1\"")
114 if (onlyFeatures && skipFeatures) {
116 "Can't compute only the features and skip them at the same "
120 if (cmd.
arguments().size() < minArgumentCount) {
122 QString(
"Missing parameter(s): classifier filename (.txt) "
123 "and/or cloud roles after \"-%1\"")
127 QString classifierFilename = cmd.
arguments().front();
128 cmd.
print(
"Classifier filename: " + classifierFilename);
129 QCoreApplication::processEvents();
139 QString cloudRolesStr = cmd.
arguments().front();
140 cmd.
print(
"Cloud roles: " + cloudRolesStr);
141 QCoreApplication::processEvents();
145 QStringList tokens = cloudRolesStr.simplified().split(
149 QString mainCloudRole;
150 for (
const QString& token : tokens) {
151 QStringList subTokens = token.split(
"=");
152 int subTokenCount = subTokens.size();
153 if (subTokenCount != 2) {
155 "Malformed cloud roles description (expecting: "
156 "\"PC1=1 PC2=3 CTX=2\" for instance)");
158 QString role = subTokens[0].toUpper();
160 unsigned cloudIndex = subTokens[1].toUInt(&ok);
161 if (!ok || cloudIndex == 0) {
163 "Malformed cloud roles description (expecting the "
164 "cloud index corresponding to each role - starting "
167 if (cloudIndex > cmd.
clouds().size()) {
168 return cmd.
error(QString(
"Cloud index %1 exceeds the "
169 "number of loaded clouds (=%2)")
171 .arg(cmd.
clouds().size()));
173 cloudPerRole.insert(role, cmd.
clouds()[cloudIndex - 1].pc);
175 if (mainCloudRole.isEmpty()) {
176 mainCloudRole = role;
181 QList<QString> cloudLabels;
182 QString corePointsLabel;
183 bool filenamesSpecified =
false;
184 QMap<QString, QString> labelsAndNames;
186 classifierFilename, cloudLabels, corePointsLabel,
187 filenamesSpecified, labelsAndNames)) {
188 return cmd.
error(
"Failed to read classifier file");
191 if (!corePointsLabel.isEmpty()) {
193 mainCloudRole = corePointsLabel;
194 cmd.
print(
"Core points source: " + corePointsLabel +
195 " (will be used as the classified cloud)");
197 cmd.
print(
"The classified cloud role will be " + mainCloudRole);
205 for (QString label : cloudLabels) {
206 if (!cloudPerRole.contains(label.toUpper())) {
208 QString(
"Role %1 has not been defined").arg(label));
214 std::vector<double> scales;
216 &features, &scales,
nullptr,
nullptr,
218 return cmd.
error(
"Failed to load the classifier");
222 if (!cloudPerRole.contains(mainCloudRole)) {
223 return cmd.
error(
"Classified cloud not loaded/defined?!");
227 if (cloudPerRole.contains(
"TEST")) {
228 delete cloudPerRole[
"TEST"];
229 cloudPerRole.remove(
"TEST");
235 cloudPerRole[mainCloudRole];
239 QScopedPointer<ecvProgressDialog> pDlg;
242 pDlg->setAutoClose(
false);
246 QString errorMessage;
248 errorMessage, pDlg.data(),
249 &generatedScalarFields)) {
251 return cmd.
error(errorMessage);
255 pDlg->setAutoClose(
true);
258 QCoreApplication::processEvents();
265 QFileInfo fi(classifierFilename);
266 featureSourceFilename = fi.absolutePath() +
"/" +
267 fi.completeBaseName() +
268 "_feature_sources.txt";
270 featureSourceFilename)) {
271 cmd.
print(
"Feature sources file saved: " +
272 featureSourceFilename);
276 "Faild to write feature sources to file: " +
277 featureSourceFilename);
282 classifiedCloud = cmd.
clouds().front().pc;
286 featureSourceFilename)) {
287 return cmd.
error(
"Failed to load feature sources from: " +
288 featureSourceFilename);
296 nullptr,
nullptr,
nullptr, &classifier,
298 return cmd.
error(
"Failed to load the classifier");
301 QString errorMessage;
302 if (!classifier.
classify(featureSources, classifiedCloud,
305 return cmd.
error(errorMessage);
308 generatedScalarFields.
releaseSFs(keepAttributes);
313 if (desc.pc == classifiedCloud) {
316 onlyFeatures ?
"WITH_FEATURES" :
"CLASSIFIED");
317 if (!errorStr.isEmpty()) {
318 return cmd.
error(errorStr);
void releaseSFs(bool keepByDefault)
bool autoSaveMode() const
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.
bool silentMode() const
Returns the silent mode.
virtual QString exportEntity(CLEntityDesc &entityDesc, const QString &suffix=QString(), QString *outputFilename=nullptr, ccCommandLineInterface::ExportOptions options=ExportOption::NoOptions)=0
Exports a cloud or a mesh.
virtual QDialog * widgetParent()
Returns a (widget) parent (if any is available)
virtual std::vector< CLCloudDesc > & clouds()
Currently opened point clouds and their filename.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
Graphical progress indicator (thread-safe)
bool classify(const Feature::Source::Set &featureSources, ccPointCloud *cloud, QString &errorMessage, QWidget *parentWidget=nullptr, ecvMainAppInterface *app=nullptr)
Applies the classifier.
constexpr Qt::SplitBehavior SkipEmptyParts
static const char COMMAND_3DMASC_KEEP_ATTRIBS[]
static const char COMMAND_3DMASC_ONLY_FEATURES[]
static const char COMMAND_3DMASC_SKIP_FEATURES[]
static const char COMMAND_3DMASC_CLASSIFY[]
Loaded cloud description.
virtual bool process(ccCommandLineInterface &cmd) override
Main process.
Generic command interface.
Command(const QString &name, const QString &keyword)
Default constructor.
std::vector< Source > Set
static bool ExtractSources(const Set &features, Source::Set &sources)
Extracts the set of 'sources' from a set of features.
static bool SaveSources(const Source::Set &sources, QString filename)
Saves a set of 'sources' to a file.
std::vector< Shared > Set
Set of features.
static bool LoadSources(Source::Set &sources, QString filename)
Loads a set of 'sources' from a file.