ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
FeatureExtractionWidget.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 
9 
10 #include "ThreadControlWidget.h"
11 #include "base/camera_models.h"
12 #include "feature/extraction.h"
13 #include "ui/options_widget.h"
14 #include "ui/qt_utils.h"
15 #include "util/misc.h"
16 #include "util/option_manager.h"
17 
18 namespace cloudViewer {
19 
20 using namespace colmap;
21 
22 class ExtractionWidget : public colmap::OptionsWidget {
23 public:
24  ExtractionWidget(QWidget* parent, OptionManager* options);
25 
26  virtual void Run() = 0;
27 
28 protected:
31 };
32 
34 public:
35  SIFTExtractionWidget(QWidget* parent, OptionManager* options);
36 
37  void Run() override;
38 };
39 
41 public:
42  ImportFeaturesWidget(QWidget* parent, OptionManager* options);
43 
44  void Run() override;
45 
46 private:
47  std::string import_path_;
48 };
49 
51  : OptionsWidget(parent),
52  options_(options),
53  thread_control_widget_(new ThreadControlWidget(this)) {}
54 
56  OptionManager* options)
57  : ExtractionWidget(parent, options) {
58  AddOptionDirPath(&options->image_reader->mask_path, "mask_path");
59  AddOptionFilePath(&options->image_reader->camera_mask_path,
60  "camera_mask_path");
61 
62  AddOptionInt(&options->sift_extraction->max_image_size, "max_image_size");
63  AddOptionInt(&options->sift_extraction->max_num_features,
64  "max_num_features");
65  AddOptionInt(&options->sift_extraction->first_octave, "first_octave", -5);
66  AddOptionInt(&options->sift_extraction->num_octaves, "num_octaves");
67  AddOptionInt(&options->sift_extraction->octave_resolution,
68  "octave_resolution");
69  AddOptionDouble(&options->sift_extraction->peak_threshold, "peak_threshold",
70  0.0, 1e7, 0.00001, 5);
71  AddOptionDouble(&options->sift_extraction->edge_threshold,
72  "edge_threshold");
73  AddOptionBool(&options->sift_extraction->estimate_affine_shape,
74  "estimate_affine_shape");
75  AddOptionInt(&options->sift_extraction->max_num_orientations,
76  "max_num_orientations");
77  AddOptionBool(&options->sift_extraction->upright, "upright");
78  AddOptionBool(&options->sift_extraction->domain_size_pooling,
79  "domain_size_pooling");
80  AddOptionDouble(&options->sift_extraction->dsp_min_scale, "dsp_min_scale",
81  0.0, 1e7, 0.00001, 5);
82  AddOptionDouble(&options->sift_extraction->dsp_max_scale, "dsp_max_scale",
83  0.0, 1e7, 0.00001, 5);
84  AddOptionInt(&options->sift_extraction->dsp_num_scales, "dsp_num_scales",
85  1);
86 
87  AddOptionInt(&options->sift_extraction->num_threads, "num_threads", -1);
88  AddOptionBool(&options->sift_extraction->use_gpu, "use_gpu");
89  AddOptionText(&options->sift_extraction->gpu_index, "gpu_index");
90 }
91 
93  WriteOptions();
94 
95  // Validate paths before extraction
96  if (options_->database_path->empty()) {
97  QMessageBox::critical(this, "",
98  tr("Database path is not set. Please set it in "
99  "Project settings."));
100  return;
101  }
102 
103  if (options_->image_path->empty()) {
104  QMessageBox::critical(this, "",
105  tr("Image path is not set. Please set it in "
106  "Project settings."));
107  return;
108  }
109 
110  if (!ExistsDir(*options_->image_path)) {
111  QMessageBox::critical(
112  this, "",
113  tr("Image path does not exist: %1")
114  .arg(QString::fromStdString(*options_->image_path)));
115  return;
116  }
117 
118  // Ensure database directory exists
119  const std::string database_dir = GetParentDir(*options_->database_path);
120  if (!ExistsDir(database_dir)) {
121  QMessageBox::critical(
122  this, "",
123  tr("Database directory does not exist: %1")
124  .arg(QString::fromStdString(database_dir)));
125  return;
126  }
127 
128  ImageReaderOptions reader_options = *options_->image_reader;
129  reader_options.database_path = *options_->database_path;
130  reader_options.image_path = *options_->image_path;
131 
132  // Validate reader options
133  if (!reader_options.Check()) {
134  QMessageBox::critical(this, "", tr("Invalid image reader options."));
135  return;
136  }
137 
138  // Validate SIFT extraction options
139  if (!options_->sift_extraction->Check()) {
140  QMessageBox::critical(this, "", tr("Invalid SIFT extraction options."));
141  return;
142  }
143 
144  Thread* extractor = new SiftFeatureExtractor(reader_options,
145  *options_->sift_extraction);
146  thread_control_widget_->StartThread("Extracting...", true, extractor);
147 }
148 
150  OptionManager* options)
151  : ExtractionWidget(parent, options) {
152  AddOptionDirPath(&import_path_, "import_path");
153 }
154 
156  WriteOptions();
157 
158  if (!ExistsDir(import_path_)) {
159  QMessageBox::critical(this, "", tr("Path is not a directory"));
160  return;
161  }
162 
163  ImageReaderOptions reader_options = *options_->image_reader;
164  reader_options.database_path = *options_->database_path;
165  reader_options.image_path = *options_->image_path;
166 
167  Thread* importer = new FeatureImporter(reader_options, import_path_);
168  thread_control_widget_->StartThread("Importing...", true, importer);
169 }
170 
172  OptionManager* options)
173  : parent_(parent), options_(options) {
174  // Do not change flag, to make sure feature database is not accessed from
175  // multiple threads
176  setWindowFlags(Qt::Window);
177  setWindowTitle("Feature extraction");
178 
179  QGridLayout* grid = new QGridLayout(this);
180 
181  grid->addWidget(CreateCameraModelBox(), 0, 0);
182 
183  tab_widget_ = new QTabWidget(this);
184 
185  QScrollArea* extraction_widget = new QScrollArea(this);
186  extraction_widget->setAlignment(Qt::AlignHCenter);
187  extraction_widget->setWidget(new SIFTExtractionWidget(this, options));
188  tab_widget_->addTab(extraction_widget, tr("Extract"));
189 
190  QScrollArea* import_widget = new QScrollArea(this);
191  import_widget->setAlignment(Qt::AlignHCenter);
192  import_widget->setWidget(new ImportFeaturesWidget(this, options));
193  tab_widget_->addTab(import_widget, tr("Import"));
194 
195  grid->addWidget(tab_widget_);
196 
197  QPushButton* extract_button = new QPushButton(tr("Extract"), this);
198  connect(extract_button, &QPushButton::released, this,
199  &FeatureExtractionWidget::Extract);
200  grid->addWidget(extract_button, grid->rowCount(), 0);
201 }
202 
203 QGroupBox* FeatureExtractionWidget::CreateCameraModelBox() {
204  camera_model_ids_.clear();
205 
206  camera_model_cb_ = new QComboBox(this);
207 
208 #define CAMERA_MODEL_CASE(CameraModel) \
209  camera_model_cb_->addItem(QString::fromStdString( \
210  CameraModelIdToName(CameraModel::model_id))); \
211  camera_model_ids_.push_back(static_cast<int>(CameraModel::model_id));
212 
213  CAMERA_MODEL_CASES
214 
215 #undef CAMERA_MODEL_CASE
216 
217  camera_params_exif_rb_ = new QRadioButton(tr("Parameters from EXIF"), this);
218  camera_params_exif_rb_->setChecked(true);
219 
220  camera_params_custom_rb_ = new QRadioButton(tr("Custom parameters"), this);
221 
222  camera_params_info_ = new QLabel(tr(""), this);
223  QPalette pal = QPalette(camera_params_info_->palette());
224  pal.setColor(QPalette::WindowText, QColor(130, 130, 130));
225  camera_params_info_->setPalette(pal);
226 
227  camera_params_text_ = new QLineEdit(this);
228  camera_params_text_->setEnabled(false);
229 
230  single_camera_cb_ = new QCheckBox("Shared for all images", this);
231  single_camera_cb_->setChecked(false);
232 
233  single_camera_per_folder_cb_ = new QCheckBox("Shared per sub-folder", this);
234  single_camera_per_folder_cb_->setChecked(false);
235 
236  QGroupBox* box = new QGroupBox(tr("Camera model"), this);
237 
238  QVBoxLayout* vbox = new QVBoxLayout(box);
239  vbox->addWidget(camera_model_cb_);
240  vbox->addWidget(camera_params_info_);
241  vbox->addWidget(single_camera_cb_);
242  vbox->addWidget(single_camera_per_folder_cb_);
243  vbox->addWidget(camera_params_exif_rb_);
244  vbox->addWidget(camera_params_custom_rb_);
245  vbox->addWidget(camera_params_text_);
246  vbox->addStretch(1);
247 
248  box->setLayout(vbox);
249 
250  SelectCameraModel(camera_model_cb_->currentIndex());
251 
252  connect(camera_model_cb_,
253  (void(QComboBox::*)(int)) & QComboBox::currentIndexChanged, this,
254  &FeatureExtractionWidget::SelectCameraModel);
255  connect(camera_params_exif_rb_, &QRadioButton::clicked, camera_params_text_,
256  &QLineEdit::setDisabled);
257  connect(camera_params_custom_rb_, &QRadioButton::clicked,
258  camera_params_text_, &QLineEdit::setEnabled);
259 
260  return box;
261 }
262 
263 void FeatureExtractionWidget::showEvent(QShowEvent* event) {
264  parent_->setDisabled(true);
265  ReadOptions();
266 }
267 
268 void FeatureExtractionWidget::hideEvent(QHideEvent* event) {
269  parent_->setEnabled(true);
270  WriteOptions();
271 }
272 
273 void FeatureExtractionWidget::ReadOptions() {
274  const auto camera_code =
275  CameraModelNameToId(options_->image_reader->camera_model);
276  for (size_t i = 0; i < camera_model_ids_.size(); ++i) {
277  if (camera_model_ids_[i] == camera_code) {
278  SelectCameraModel(i);
279  camera_model_cb_->setCurrentIndex(i);
280  break;
281  }
282  }
283  single_camera_cb_->setChecked(options_->image_reader->single_camera);
284  single_camera_per_folder_cb_->setChecked(
285  options_->image_reader->single_camera_per_folder);
286  camera_params_text_->setText(
287  QString::fromStdString(options_->image_reader->camera_params));
288 }
289 
290 void FeatureExtractionWidget::WriteOptions() {
291  options_->image_reader->camera_model = CameraModelIdToName(
292  camera_model_ids_[camera_model_cb_->currentIndex()]);
293  options_->image_reader->single_camera = single_camera_cb_->isChecked();
294  options_->image_reader->single_camera_per_folder =
295  single_camera_per_folder_cb_->isChecked();
296  options_->image_reader->camera_params =
297  camera_params_text_->text().toUtf8().constData();
298 }
299 
300 void FeatureExtractionWidget::SelectCameraModel(const int idx) {
301  const int code = camera_model_ids_[idx];
302  camera_params_info_->setText(QString::fromStdString(
303  StringPrintf("<small>Parameters: %s</small>",
304  CameraModelParamsInfo(code).c_str())));
305 }
306 
307 void FeatureExtractionWidget::Extract() {
308  // If the custom parameter radiobuttion is not checked, but the
309  // parameters textbox contains parameters.
310  const auto old_camera_params_text = camera_params_text_->text();
311  if (!camera_params_custom_rb_->isChecked()) {
312  camera_params_text_->setText("");
313  }
314 
315  WriteOptions();
316 
317  if (!ExistsCameraModelWithName(options_->image_reader->camera_model)) {
318  QMessageBox::critical(this, "", tr("Camera model does not exist"));
319  return;
320  }
321 
322  const std::vector<double> camera_params =
323  CSVToVector<double>(options_->image_reader->camera_params);
324  const auto camera_code =
325  CameraModelNameToId(options_->image_reader->camera_model);
326 
327  if (camera_params_custom_rb_->isChecked() &&
328  !CameraModelVerifyParams(camera_code, camera_params)) {
329  QMessageBox::critical(this, "", tr("Invalid camera parameters"));
330  return;
331  }
332 
333  QWidget* widget =
334  static_cast<QScrollArea*>(tab_widget_->currentWidget())->widget();
335  static_cast<ExtractionWidget*>(widget)->Run();
336 
337  camera_params_text_->setText(old_camera_params_text);
338 }
339 
340 } // namespace cloudViewer
MouseEvent event
int Run(int argc, const char *argv[])
ThreadControlWidget * thread_control_widget_
ExtractionWidget(QWidget *parent, OptionManager *options)
FeatureExtractionWidget(QWidget *parent, OptionManager *options)
ImportFeaturesWidget(QWidget *parent, OptionManager *options)
SIFTExtractionWidget(QWidget *parent, OptionManager *options)
void StartThread(const QString &progress_text, const bool stoppable, colmap::Thread *thread)
std::string StringPrintf(const char *format,...)
Definition: Helper.cpp:110
Generic file read and write utility for python interface.
colmap::OptionManager OptionManager
struct Window Window
Definition: sqlite3.c:14678