11 #include "ui/qt_utils.h"
12 #include "util/bitmap.h"
13 #include "util/misc.h"
14 #include "util/option_manager.h"
23 model_viewer_widget_(model_viewer_widget),
25 point3D_id_(kInvalidPoint3DId),
26 zoom_(250.0 / 1024.0) {
28 resize(parent->size().width() - 20, parent->size().height() - 20);
31 font.setPointSize(10);
34 QGridLayout* grid =
new QGridLayout(
this);
35 grid->setContentsMargins(5, 5, 5, 5);
37 info_table_ =
new QTableWidget(
this);
39 info_table_->setEditTriggers(QAbstractItemView::NoEditTriggers);
40 info_table_->setSelectionMode(QAbstractItemView::SingleSelection);
41 info_table_->setShowGrid(
true);
42 info_table_->horizontalHeader()->setStretchLastSection(
true);
43 info_table_->horizontalHeader()->setVisible(
false);
44 info_table_->verticalHeader()->setVisible(
false);
45 info_table_->verticalHeader()->setDefaultSectionSize(18);
47 info_table_->setColumnCount(2);
48 info_table_->setRowCount(3);
50 info_table_->setItem(0, 0,
new QTableWidgetItem(
"position"));
51 xyz_item_ =
new QTableWidgetItem();
52 info_table_->setItem(0, 1, xyz_item_);
54 info_table_->setItem(1, 0,
new QTableWidgetItem(
"color"));
55 rgb_item_ =
new QTableWidgetItem();
56 info_table_->setItem(1, 1, rgb_item_);
58 info_table_->setItem(2, 0,
new QTableWidgetItem(
"error"));
59 error_item_ =
new QTableWidgetItem();
60 info_table_->setItem(2, 1, error_item_);
62 grid->addWidget(info_table_, 0, 0);
64 location_table_ =
new QTableWidget(
this);
65 location_table_->setColumnCount(4);
66 QStringList table_header;
67 table_header <<
"image_id"
71 location_table_->setHorizontalHeaderLabels(table_header);
72 location_table_->resizeColumnsToContents();
73 location_table_->setShowGrid(
true);
74 location_table_->horizontalHeader()->setStretchLastSection(
true);
75 location_table_->verticalHeader()->setVisible(
true);
76 location_table_->setSelectionMode(QAbstractItemView::NoSelection);
77 location_table_->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
78 location_table_->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
79 location_table_->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
81 grid->addWidget(location_table_, 1, 0);
83 QHBoxLayout* button_layout =
new QHBoxLayout();
85 zoom_in_button_ =
new QPushButton(tr(
"+"),
this);
86 zoom_in_button_->setFont(font);
87 zoom_in_button_->setFixedWidth(50);
88 button_layout->addWidget(zoom_in_button_);
89 connect(zoom_in_button_, &QPushButton::released,
this,
90 &PointViewerWidget::ZoomIn);
92 zoom_out_button_ =
new QPushButton(tr(
"-"),
this);
93 zoom_out_button_->setFont(font);
94 zoom_out_button_->setFixedWidth(50);
95 button_layout->addWidget(zoom_out_button_);
96 connect(zoom_out_button_, &QPushButton::released,
this,
97 &PointViewerWidget::ZoomOut);
99 delete_button_ =
new QPushButton(tr(
"Delete"),
this);
100 button_layout->addWidget(delete_button_);
101 connect(delete_button_, &QPushButton::released,
this,
102 &PointViewerWidget::Delete);
104 grid->addLayout(button_layout, 2, 0, Qt::AlignRight);
108 location_pixmaps_.clear();
110 reproj_errors_.clear();
111 image_names_.clear();
113 if (model_viewer_widget_->
points3D.count(point3D_id) == 0) {
114 point3D_id_ = kInvalidPoint3DId;
122 point3D_id_ = point3D_id;
129 const auto& point3D = model_viewer_widget_->
points3D[point3D_id];
131 xyz_item_->setText(QString::number(point3D.X()) +
", " +
132 QString::number(point3D.Y()) +
", " +
133 QString::number(point3D.Z()));
134 rgb_item_->setText(QString::number(point3D.Color(0)) +
", " +
135 QString::number(point3D.Color(1)) +
", " +
136 QString::number(point3D.Color(2)));
137 error_item_->setText(QString::number(point3D.Error()));
143 std::vector<std::pair<TrackElement, std::string>>
144 track_idx_image_name_pairs;
145 track_idx_image_name_pairs.reserve(point3D.Track().Length());
146 for (
const auto& track_el : point3D.Track().Elements()) {
147 const Image&
image = model_viewer_widget_->
images[track_el.image_id];
148 track_idx_image_name_pairs.emplace_back(track_el,
image.Name());
151 std::sort(track_idx_image_name_pairs.begin(),
152 track_idx_image_name_pairs.end(),
153 [](
const std::pair<TrackElement, std::string>& track_el1,
154 const std::pair<TrackElement, std::string>& track_el2) {
155 return track_el1.second < track_el2.second;
160 for (
const auto& track_el : track_idx_image_name_pairs) {
162 model_viewer_widget_->
images[track_el.first.image_id];
163 const Camera& camera = model_viewer_widget_->
cameras[
image.CameraId()];
164 const Point2D& point2D =
image.Point2D(track_el.first.point2D_idx);
165 const Eigen::Vector2d proj_point2D = ProjectPointToImage(
166 point3D.XYZ(),
image.ProjectionMatrix(), camera);
167 const double reproj_error = (point2D.XY() - proj_point2D).norm();
169 colmap::Bitmap bitmap;
171 if (!bitmap.Read(
path,
true)) {
172 std::cerr <<
"ERROR: Cannot read image at path " <<
path
177 QPixmap pixmap = QPixmap::fromImage(BitmapToQImageRGB(bitmap));
180 QPainter painter(&pixmap);
181 painter.setRenderHint(QPainter::Antialiasing);
188 const int kCrossSize = 15;
189 const int x =
static_cast<int>(std::round(point2D.X()));
190 const int y =
static_cast<int>(std::round(point2D.Y()));
191 painter.drawLine(x - kCrossSize, y - kCrossSize, x + kCrossSize,
193 painter.drawLine(x - kCrossSize, y + kCrossSize, x + kCrossSize,
199 const int proj_x =
static_cast<int>(std::round(proj_point2D.x()));
200 const int proj_y =
static_cast<int>(std::round(proj_point2D.y()));
201 painter.drawEllipse(proj_x - 5, proj_y - 5, 10, 10);
202 painter.drawEllipse(proj_x - 15, proj_y - 15, 30, 30);
203 painter.drawEllipse(proj_x - 45, proj_y - 45, 90, 90);
205 location_pixmaps_.push_back(pixmap);
206 image_ids_.push_back(track_el.first.image_id);
207 reproj_errors_.push_back(reproj_error);
208 image_names_.push_back(
image.Name());
214 void PointViewerWidget::closeEvent(QCloseEvent*
event) {
216 location_pixmaps_.clear();
218 reproj_errors_.clear();
219 image_names_.clear();
223 void PointViewerWidget::ResizeInfoTable() {
225 info_table_->resizeColumnsToContents();
226 int height = info_table_->horizontalHeader()->height() +
227 2 * info_table_->frameWidth();
228 for (
int i = 0; i < info_table_->rowCount(); i++) {
229 height += info_table_->rowHeight(i);
231 info_table_->setFixedHeight(
height);
234 void PointViewerWidget::ClearLocations() {
235 while (location_table_->rowCount() > 0) {
236 location_table_->removeRow(0);
238 for (
auto location_label : location_labels_) {
239 delete location_label;
241 location_labels_.clear();
244 void PointViewerWidget::UpdateImages() {
247 location_table_->setRowCount(
static_cast<int>(location_pixmaps_.size()));
249 for (
size_t i = 0; i < location_pixmaps_.size(); ++i) {
250 QLabel* image_id_label =
251 new QLabel(QString::number(image_ids_[i]),
this);
252 image_id_label->setAlignment(Qt::AlignCenter);
253 location_table_->setCellWidget(i, 0, image_id_label);
254 location_labels_.push_back(image_id_label);
256 QLabel* error_label =
257 new QLabel(QString::number(reproj_errors_[i]),
this);
258 error_label->setAlignment(Qt::AlignCenter);
259 location_table_->setCellWidget(i, 1, error_label);
260 location_labels_.push_back(error_label);
262 const QPixmap& pixmap = location_pixmaps_[i];
263 QLabel* image_label =
new QLabel(
this);
264 image_label->setPixmap(pixmap.scaledToWidth(zoom_ * pixmap.width(),
265 Qt::FastTransformation));
266 location_table_->setCellWidget(i, 2, image_label);
267 location_table_->resizeRowToContents(i);
268 location_labels_.push_back(image_label);
270 QLabel* image_name_label =
new QLabel(image_names_[i].c_str(),
this);
271 image_name_label->setAlignment(Qt::AlignCenter);
272 location_table_->setCellWidget(i, 3, image_name_label);
273 location_labels_.push_back(image_name_label);
275 location_table_->resizeColumnToContents(2);
278 void PointViewerWidget::ZoomIn() {
283 void PointViewerWidget::ZoomOut() {
288 void PointViewerWidget::Delete() {
289 QMessageBox::StandardButton reply = QMessageBox::question(
290 this,
"", tr(
"Do you really want to delete this point?"),
291 QMessageBox::Yes | QMessageBox::No);
292 if (reply == QMessageBox::Yes) {
293 if (model_viewer_widget_->
reconstruction->ExistsPoint3D(point3D_id_)) {
std::shared_ptr< core::Tensor > image
QTextStream & endl(QTextStream &stream)
Tensor Minimum(const Tensor &input, const Tensor &other)
Computes the element-wise minimum of input and other. The tensors must have same data type and device...
static const std::string path
std::string JoinPaths(T const &...paths)
Generic file read and write utility for python interface.
colmap::OptionManager OptionManager
constexpr Rgb red(MAX, 0, 0)
constexpr Rgb green(0, MAX, 0)
std::string to_string(const T &n)