app/main.cpp
Go to the documentation of this file.
1 /*
2 Copyright (c) 2011-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7  * Redistributions of source code must retain the above copyright
8  notice, this list of conditions and the following disclaimer.
9  * Redistributions in binary form must reproduce the above copyright
10  notice, this list of conditions and the following disclaimer in the
11  documentation and/or other materials provided with the distribution.
12  * Neither the name of the Universite de Sherbrooke nor the
13  names of its contributors may be used to endorse or promote products
14  derived from this software without specific prior written permission.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 
28 #include <QApplication>
29 #include <QtCore/QDir>
30 #include <QtCore/QFile>
31 #include <iostream>
32 #include <iomanip>
33 #include "find_object/MainWindow.h"
34 #include "find_object/Settings.h"
35 #include "find_object/FindObject.h"
36 #include "find_object/Camera.h"
37 #include "find_object/TcpServer.h"
38 #include "find_object/JsonWriter.h"
40 #include "TcpServerPool.h"
41 
42 bool running = true;
43 
44 #ifdef WIN32
45 #include <windows.h>
46 BOOL WINAPI my_handler(DWORD signal)
47 {
48  if (signal == CTRL_C_EVENT)
49  {
50  printf("\nCtrl-C caught! Quitting application...\n");
51  QCoreApplication::quit();
52  }
53  return TRUE;
54  running = false;
55 }
56 #else
57 #include <signal.h>
58 void my_handler(int s)
59 {
60  printf("\nCtrl-C caught! Quitting application...\n");
61  QCoreApplication::quit();
62  running = false;
63 }
64 inline void Sleep(unsigned int ms)
65 {
66  struct timespec req;
67  struct timespec rem;
68  req.tv_sec = ms / 1000;
69  req.tv_nsec = (ms - req.tv_sec * 1000) * 1000 * 1000;
70  nanosleep (&req, &rem);
71 }
72 #endif
73 
75 {
76 // Catch ctrl-c to close Qt
77 #ifdef WIN32
78  if (!SetConsoleCtrlHandler(my_handler, TRUE))
79  {
80  UERROR("Could not set control (ctrl-c) handler");
81  }
82 #else
83  struct sigaction sigIntHandler;
84  sigIntHandler.sa_handler = my_handler;
85  sigemptyset(&sigIntHandler.sa_mask);
86  sigIntHandler.sa_flags = 0;
87  sigaction(SIGINT, &sigIntHandler, NULL);
88 #endif
89 }
90 
91 void showUsage()
92 {
93  printf("\nUsage:\n"
94 #ifdef WIN32
95  " Find-Object.exe [options]\n"
96 #else
97  " find_object [options]\n"
98 #endif
99  "Options:\n"
100  " --console Don't use the GUI (by default the camera will be\n"
101  " started automatically). Option --objects must also be\n"
102  " used with valid objects.\n"
103  " --session \"path\" Path to a session to load (*.bin). Use \"--session_new\" to\n"
104  " create a session instead (will be saved to \"path\" on exit, only\n"
105  " on console mode).\n"
106  " --object \"path\" Path to an object to detect.\n"
107  " --objects \"path\" Directory of the objects to detect (--object is ignored).\n"
108  " --config \"path\" Path to configuration file (default: %s).\n"
109  " If set to \"\", default parameters are used\n"
110  " without saving modified parameters on closing.\n"
111  " --scene \"path\" Path to a scene image file.\n"
112  " --vocabulary \"path\" Path to a vocabulary file (*.yaml *.xml). Parameters \"General/invertedSearch\"\n"
113  " and \"General/vocabularyFixed\" will be also enabled. Ignored if \"--session\" is set.\n"
114  " --images_not_saved Don't keep images in RAM after the features are extracted (only\n"
115  " in console mode). Images won't be saved if an output session is set.\n"
116  " --tcp_threads # Number of TCP threads (default 1, only in --console mode). \"--General/port\" parameter should not be 0.\n"
117  " Port numbers start from \"General/port\" value. \"Detect\" TCP service can be\n"
118  " executed at the same time by multiple threads. \"Add/Remove\" TCP services\n"
119  " cannot be called by multiple threads, so calling these services on a port\n "
120  " will block all other threads on the other ports.\n"
121  " --debug Show debug log.\n"
122  " --log-time Show log with time.\n"
123  " --params Show all parameters.\n"
124  " --defaults Use default parameters (--config is ignored).\n"
125  " --My/Parameter \"value\" Set find-Object's parameter (look --params for parameters' name).\n"
126  " It will override the one in --config. Example to set 4 threads:\n"
127  " $ find_object --General/threads 4\n"
128  " --json \"path\" Path to an output JSON file (only in --console mode with --scene).\n"
129  " --help Show usage.\n"
130  , find_object::Settings::iniDefaultPath().toStdString().c_str());
131  exit(-1);
132 }
133 
134 int main(int argc, char* argv[])
135 {
138  ULogger::setPrintWhere(false);
139  ULogger::setPrintTime(false);
140 
142  // parse options BEGIN
144  bool guiMode = true;
145  QString sessionPath = "";
146  bool sessionNew = false;
147  QString objectsPath = "";
148  QString objectPath = "";
149  QString scenePath = "";
150  QString configPath = find_object::Settings::iniDefaultPath();
151  QString vocabularyPath = "";
152  QString jsonPath;
153  find_object::ParametersMap customParameters;
154  bool imagesSaved = true;
155  int tcpThreads = 1;
156 
157  for(int i=1; i<argc; ++i)
158  {
159 #ifdef __APPLE__
160  if(QString(argv[i]).startsWith("-psn"))
161  {
162  //safely ignore
163  continue;
164  }
165 #endif
166  if(strcmp(argv[i], "-objs") == 0 ||
167  strcmp(argv[i], "--objs") == 0 ||
168  strcmp(argv[i], "-objects") == 0 ||
169  strcmp(argv[i], "--objects") == 0)
170  {
171  ++i;
172  if(i < argc)
173  {
174  objectsPath = argv[i];
175  if(objectsPath.contains('~'))
176  {
177  objectsPath.replace('~', QDir::homePath());
178  }
179  if(!QDir(objectsPath).exists())
180  {
181  UERROR("Objects path not valid : %s", objectsPath.toStdString().c_str());
182  showUsage();
183  }
184  }
185  else
186  {
187  showUsage();
188  }
189  continue;
190  }
191  if(strcmp(argv[i], "-session") == 0 ||
192  strcmp(argv[i], "--session") == 0 ||
193  strcmp(argv[i], "-session_new") == 0 ||
194  strcmp(argv[i], "--session_new") == 0)
195  {
196  if(strcmp(argv[i], "-session_new") == 0 ||
197  strcmp(argv[i], "--session_new") == 0)
198  {
199  sessionNew = true;
200  }
201  ++i;
202  if(i < argc)
203  {
204  sessionPath = argv[i];
205  if(sessionPath.contains('~'))
206  {
207  sessionPath.replace('~', QDir::homePath());
208  }
209 
210  if(!sessionNew && !QFile(sessionPath).exists())
211  {
212  UERROR("Session path not valid : %s (if you want to create a new session, use \"--session_new\")", sessionPath.toStdString().c_str());
213  showUsage();
214  }
215  }
216  else
217  {
218  showUsage();
219  }
220  continue;
221  }
222  if(strcmp(argv[i], "-object") == 0 ||
223  strcmp(argv[i], "--object") == 0)
224  {
225  ++i;
226  if(i < argc)
227  {
228  objectPath = argv[i];
229  if(objectPath.contains('~'))
230  {
231  objectPath.replace('~', QDir::homePath());
232  }
233  if(!QFile(objectPath).exists())
234  {
235  UERROR("Object path not valid : %s", objectPath.toStdString().c_str());
236  showUsage();
237  }
238  }
239  else
240  {
241  showUsage();
242  }
243  continue;
244  }
245  if(strcmp(argv[i], "-scene") == 0 ||
246  strcmp(argv[i], "--scene") == 0)
247  {
248  ++i;
249  if(i < argc)
250  {
251  scenePath = argv[i];
252  if(scenePath.contains('~'))
253  {
254  scenePath.replace('~', QDir::homePath());
255  }
256  if(!QFile(scenePath).exists())
257  {
258  UERROR("Scene path not valid : %s", scenePath.toStdString().c_str());
259  showUsage();
260  }
261  }
262  else
263  {
264  showUsage();
265  }
266  continue;
267  }
268  if(strcmp(argv[i], "-vocabulary") == 0 ||
269  strcmp(argv[i], "--vocabulary") == 0)
270  {
271  ++i;
272  if(i < argc)
273  {
274  vocabularyPath = argv[i];
275  if(vocabularyPath.contains('~'))
276  {
277  vocabularyPath.replace('~', QDir::homePath());
278  }
279  if(!QFile(vocabularyPath).exists())
280  {
281  UERROR("Vocabulary path not valid : %s", vocabularyPath.toStdString().c_str());
282  showUsage();
283  }
284  }
285  else
286  {
287  showUsage();
288  }
289  continue;
290  }
291  if(strcmp(argv[i], "-config") == 0 ||
292  strcmp(argv[i], "--config") == 0)
293  {
294  ++i;
295  if(i < argc)
296  {
297  configPath = argv[i];
298  if(configPath.contains('~'))
299  {
300  configPath.replace('~', QDir::homePath());
301  }
302  if(!configPath.isEmpty() && !QFile::exists(configPath))
303  {
304  UWARN("Configuration file \"%s\" doesn't exist, it will be created with default values...", configPath.toStdString().c_str());
305  }
306  }
307  else
308  {
309  showUsage();
310  }
311  continue;
312  }
313  if(strcmp(argv[i], "-console") == 0 ||
314  strcmp(argv[i], "--console") == 0)
315  {
316  guiMode = false;
317  continue;
318  }
319  if(strcmp(argv[i], "-images_not_saved") == 0 ||
320  strcmp(argv[i], "--images_not_saved") == 0)
321  {
322  imagesSaved = false;
323  continue;
324  }
325  if(strcmp(argv[i], "-debug") == 0 ||
326  strcmp(argv[i], "--debug") == 0)
327  {
328  customParameters.insert(find_object::Settings::kGeneral_debug(), true);
329  continue;
330  }
331  if(strcmp(argv[i], "-log-time") == 0 ||
332  strcmp(argv[i], "--log-time") == 0)
333  {
335  ULogger::setPrintTime(true);
336  continue;
337  }
338  if(strcmp(argv[i], "-help") == 0 ||
339  strcmp(argv[i], "--help") == 0)
340  {
341  showUsage();
342  }
343  if(strcmp(argv[i], "-json") == 0 ||
344  strcmp(argv[i], "--json") == 0)
345  {
346  ++i;
347  if(i < argc)
348  {
349  jsonPath = argv[i];
350  if(jsonPath.contains('~'))
351  {
352  jsonPath.replace('~', QDir::homePath());
353  }
354  }
355  else
356  {
357  showUsage();
358  }
359  continue;
360  }
361  if(strcmp(argv[i], "-tcp_threads") == 0 ||
362  strcmp(argv[i], "--tcp_threads") == 0)
363  {
364  ++i;
365  if(i < argc)
366  {
367  tcpThreads = atoi(argv[i]);
368  if(tcpThreads < 1)
369  {
370  printf("tcp_threads should be >= 1!\n");
371  showUsage();
372  }
373  }
374  else
375  {
376  showUsage();
377  }
378  continue;
379  }
380  if(strcmp(argv[i], "--params") == 0)
381  {
383  for(find_object::ParametersMap::iterator iter=parameters.begin(); iter!=parameters.end(); ++iter)
384  {
385  std::string str = "Param: " + iter.key().toStdString() + " = \"" + iter.value().toString().toStdString() + "\"";
386  std::cout <<
387  str <<
388  std::setw(60 - str.size()) <<
389  " [" <<
390  find_object::Settings::getDescriptions().value(iter.key()).toStdString().c_str() <<
391  "]" <<
392  std::endl;
393  }
394  UINFO("Node will now exit after showing default Find-Object's parameters because "
395  "argument \"--params\" is detected!");
396  exit(0);
397  }
398 
399  // Check for custom parameters:
401  QString name = argv[i];
402  if(name.size() > 2)
403  {
404  //strip the "--"
405  name.remove(0, 2);
406  if(parameters.contains(name))
407  {
408  ++i;
409  if(i < argc)
410  {
411  customParameters.insert(name, argv[i]);
412  }
413  else
414  {
415  showUsage();
416  }
417  continue;
418  }
419  }
420 
421  UERROR("Unrecognized option : %s", argv[i]);
422  showUsage();
423  }
424 
425  UINFO("Options:");
426  UINFO(" GUI mode = %s", guiMode?"true":"false");
427  if(!sessionPath.isEmpty())
428  {
429  if(sessionNew)
430  {
431  UINFO(" Session path: \"%s\" [NEW]", sessionPath.toStdString().c_str());
432  }
433  else
434  {
435  UINFO(" Session path: \"%s\"", sessionPath.toStdString().c_str());
436  }
437  }
438  else if(!objectsPath.isEmpty())
439  {
440  UINFO(" Objects path: \"%s\"", objectsPath.toStdString().c_str());
441  }
442  else if(!objectPath.isEmpty())
443  {
444  UINFO(" Object path: \"%s\"", objectPath.toStdString().c_str());
445  }
446  UINFO(" Scene path: \"%s\"", scenePath.toStdString().c_str());
447  if(!guiMode)
448  {
449  UINFO(" JSON path: \"%s\"", jsonPath.toStdString().c_str());
450  }
451  UINFO(" Settings path: \"%s\"", configPath.toStdString().c_str());
452  UINFO(" Vocabulary path: \"%s\"", vocabularyPath.toStdString().c_str());
453 
454  if(!vocabularyPath.isEmpty())
455  {
456  if(customParameters.contains(find_object::Settings::kGeneral_vocabularyFixed()))
457  {
458  UWARN("\"General/vocabularyFixed\" custom parameter overwritten as a fixed vocabulary is used.");
459  }
460  if(customParameters.contains(find_object::Settings::kGeneral_invertedSearch()))
461  {
462  UWARN("\"General/invertedSearch\" custom parameter overwritten as a fixed vocabulary is used.");
463  }
464  customParameters[find_object::Settings::kGeneral_vocabularyFixed()] = true;
465  customParameters[find_object::Settings::kGeneral_invertedSearch()] = true;
466  }
467 
468  for(find_object::ParametersMap::iterator iter= customParameters.begin(); iter!=customParameters.end(); ++iter)
469  {
470  UINFO(" Param \"%s\"=\"%s\"", iter.key().toStdString().c_str(), iter.value().toString().toStdString().c_str());
471  }
472 
474  // parse options END
476 
477  // Load settings, should be loaded before creating other objects
478  find_object::Settings::init(configPath);
479 
480  // Override custom parameters:
481  for(find_object::ParametersMap::iterator iter= customParameters.begin(); iter!=customParameters.end(); ++iter)
482  {
483  find_object::Settings::setParameter(iter.key(), iter.value());
484  }
485 
486  // Create FindObject
487  find_object::FindObject * findObject = new find_object::FindObject(guiMode || imagesSaved);
488 
489  // Load objects if path is set
490  int objectsLoaded = 0;
491  if(!sessionPath.isEmpty() && !sessionNew)
492  {
493  if(!vocabularyPath.isEmpty() && !findObject->loadVocabulary(vocabularyPath))
494  {
495  UWARN("Vocabulary \"%s\" is not loaded as a session \"%s\" is already loaded",
496  vocabularyPath.toStdString().c_str(),
497  sessionPath.toStdString().c_str());
498  }
499  if(!findObject->loadSession(sessionPath))
500  {
501  UERROR("Could not load session \"%s\"", sessionPath.toStdString().c_str());
502  }
503  else
504  {
505  objectsLoaded = findObject->objects().size();
506  }
507  }
508  else if(!objectsPath.isEmpty())
509  {
510  if(!vocabularyPath.isEmpty() && !findObject->loadVocabulary(vocabularyPath))
511  {
512  UERROR("Failed to load vocabulary \"%s\"", vocabularyPath.toStdString().c_str());
513  }
514  objectsLoaded = findObject->loadObjects(objectsPath);
515  if(!objectsLoaded)
516  {
517  UWARN("No objects loaded from \"%s\"", objectsPath.toStdString().c_str());
518  }
519  }
520  else if(!objectPath.isEmpty())
521  {
522  if(!vocabularyPath.isEmpty() && !findObject->loadVocabulary(vocabularyPath))
523  {
524  UERROR("Failed to load vocabulary \"%s\"", vocabularyPath.toStdString().c_str());
525  }
526 
527  const find_object::ObjSignature * obj = findObject->addObject(objectPath);
528  if(obj)
529  {
530  ++objectsLoaded;
531  findObject->updateObjects();
532  findObject->updateVocabulary();
533  }
534  else
535  {
536  UWARN("No object loaded from \"%s\"", objectsPath.toStdString().c_str());
537  }
538  }
539  else if(!vocabularyPath.isEmpty() && !findObject->loadVocabulary(vocabularyPath))
540  {
541  UERROR("Failed to load vocabulary \"%s\"", vocabularyPath.toStdString().c_str());
542  }
543 
544  cv::Mat scene;
545  if(!scenePath.isEmpty())
546  {
547  scene = cv::imread(scenePath.toStdString());
548  if(scene.empty())
549  {
550  UERROR("Failed to load scene \"%s\"", scenePath.toStdString().c_str());
551  }
552  }
553 
554  if(guiMode)
555  {
556  QApplication app(argc, argv);
557  find_object::MainWindow mainWindow(findObject, 0); // ownership transfered
558 
559  app.connect( &app, SIGNAL( lastWindowClosed() ), &app, SLOT( quit() ) );
560  mainWindow.show();
561 
562  if(!scene.empty())
563  {
564  mainWindow.update(scene);
565  }
566 
567  app.exec();
568 
569  // Save settings
571  }
572  else
573  {
574  QCoreApplication app(argc, argv);
575 
576  if(!scene.empty())
577  {
578  // process the scene and exit
579  QTime time;
580  time.start();
582  findObject->detect(scene, info);
583 
584  if(info.objDetected_.size() > 1)
585  {
586  UINFO("%d objects detected! (%d ms)", (int)info.objDetected_.size(), time.elapsed());
587  }
588  else if(info.objDetected_.size() == 1)
589  {
590  UINFO("Object %d detected! (%d ms)", (int)info.objDetected_.begin().key(), time.elapsed());
591  }
592  else if(find_object::Settings::getGeneral_sendNoObjDetectedEvents())
593  {
594  UINFO("No objects detected. (%d ms)", time.elapsed());
595  }
596 
597  if(!jsonPath.isEmpty())
598  {
599  find_object::JsonWriter::write(info, jsonPath);
600  UINFO("JSON written to \"%s\"", jsonPath.toStdString().c_str());
601  }
602  }
603  else
604  {
605  TcpServerPool tcpServerPool(findObject, tcpThreads, find_object::Settings::getGeneral_port());
606 
607  setupQuitSignal();
608 
609  //If TCP camera is used
610  find_object::Camera * camera = 0;
611  if(find_object::Settings::getCamera_6useTcpCamera())
612  {
613  camera = new find_object::Camera();
614 
615  // [Camera] ---Image---> [FindObject]
616  QObject::connect(camera, SIGNAL(imageReceived(const cv::Mat &)), findObject, SLOT(detect(const cv::Mat &)));
617  QObject::connect(camera, SIGNAL(finished()), &app, SLOT(quit()));
618 
619  if(!camera->start())
620  {
621  UERROR("Camera initialization failed!");
622  running = false;
623  }
624  }
625 
626  // start processing!
627  if(running)
628  {
629  app.exec();
630 
631  if(!sessionPath.isEmpty())
632  {
633  if(findObject->isSessionModified())
634  {
635  UINFO("The session has been modified, updating the session file...");
636  if(findObject->saveSession(sessionPath))
637  {
638  UINFO("Session \"%s\" successfully saved (%d objects)!",
639  sessionPath.toStdString().c_str(), findObject->objects().size());
640  }
641  }
642  else if(sessionNew)
643  {
644  UINFO("The session has not been modified, session file not created...");
645  }
646  }
647  }
648 
649  // cleanup
650  if(camera)
651  {
652  camera->stop();
653  delete camera;
654  }
655  }
656 
657  delete findObject;
658  }
659  return 0;
660 }
app
#define NULL
static QString iniDefaultPath()
Definition: Settings.cpp:79
virtual bool start()
Definition: Camera.cpp:183
void setupQuitSignal()
Definition: app/main.cpp:74
static void setLevel(ULogger::Level level)
Definition: ULogger.h:301
static void setPrintTime(bool printTime)
Definition: ULogger.h:242
static void setType(Type type, const std::string &fileName=kDefaultLogFileName, bool append=true)
Definition: ULogger.cpp:175
void showUsage()
Definition: app/main.cpp:91
QMultiMap< int, QTransform > objDetected_
Definition: DetectionInfo.h:71
static void setParameter(const QString &key, const QVariant &value)
Definition: Settings.h:326
static void init(const QString &fileName)
Definition: Settings.cpp:97
static const DescriptionsMap & getDescriptions()
Definition: Settings.h:325
QMap< QString, QVariant > ParametersMap
Definition: Settings.h:41
void update(const cv::Mat &image)
static void setPrintWhere(bool printWhere)
Definition: ULogger.h:271
void Sleep(unsigned int ms)
Definition: app/main.cpp:64
void my_handler(int s)
Definition: app/main.cpp:58
virtual void stop()
Definition: Camera.cpp:55
bool running
Definition: app/main.cpp:42
#define UERROR(...)
ULogger class and convenient macros.
static void write(const DetectionInfo &info, const QString &path)
Definition: JsonWriter.cpp:39
#define UWARN(...)
static const ParametersMap & getDefaultParameters()
Definition: Settings.h:322
ROSCPP_DECL bool exists(const std::string &service_name, bool print_failure_reason)
int main(int argc, char *argv[])
Definition: app/main.cpp:134
static void saveSettings(const QString &fileName=QString())
Definition: Settings.cpp:226
#define UINFO(...)


find_object_2d
Author(s): Mathieu Labbe
autogenerated on Mon Jun 10 2019 13:21:31