EditDepthArea.cpp
Go to the documentation of this file.
1 /*
2 Copyright (c) 2010-2016, 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 <QWidget>
29 #include <QPainter>
30 #include <QMouseEvent>
31 #include <QMenu>
32 #include <QAction>
33 #include <QInputDialog>
34 
37 
38 namespace rtabmap {
39 
41  : QWidget(parent)
42 {
43  setAttribute(Qt::WA_StaticContents);
44  modified_ = false;
45  scribbling_ = false;
46  myPenWidth_ = 10;
47  clusterError_ = 0.02;
48 
49  menu_ = new QMenu(tr(""), this);
50  showRGB_ = menu_->addAction(tr("Show RGB Image"));
51  showRGB_->setCheckable(true);
52  showRGB_->setChecked(true);
53  removeCluster_ = menu_->addAction(tr("Remove Cluster"));
54  clusterErrorCluster_ = menu_->addAction(tr("Set Cluster Error"));
55  setPenWidth_ = menu_->addAction(tr("Set Pen Width..."));
56  QMenu * colorMap = menu_->addMenu("Depth color map");
57  colorMapWhiteToBlack_ = colorMap->addAction(tr("White to black"));
58  colorMapWhiteToBlack_->setCheckable(true);
59  colorMapWhiteToBlack_->setChecked(false);
60  colorMapBlackToWhite_ = colorMap->addAction(tr("Black to white"));
61  colorMapBlackToWhite_->setCheckable(true);
62  colorMapBlackToWhite_->setChecked(false);
63  colorMapRedToBlue_ = colorMap->addAction(tr("Red to blue"));
64  colorMapRedToBlue_->setCheckable(true);
65  colorMapRedToBlue_->setChecked(true);
66  colorMapBlueToRed_ = colorMap->addAction(tr("Blue to red"));
67  colorMapBlueToRed_->setCheckable(true);
68  colorMapBlueToRed_->setChecked(false);
69  QActionGroup * group = new QActionGroup(this);
70  group->addAction(colorMapWhiteToBlack_);
71  group->addAction(colorMapBlackToWhite_);
72  group->addAction(colorMapRedToBlue_);
73  group->addAction(colorMapBlueToRed_);
74  resetChanges_ = menu_->addAction(tr("Reset Changes"));
75 }
76 
77 void EditDepthArea::setImage(const cv::Mat &depth, const cv::Mat & rgb)
78 {
79  UASSERT(!depth.empty());
80  UASSERT(depth.type() == CV_32FC1 ||
81  depth.type() == CV_16UC1);
82  originalImage_ = depth;
83 
85  if(colorMapBlackToWhite_->isChecked())
86  {
87  colorMap = uCvQtDepthBlackToWhite;
88  }
89  else if(colorMapRedToBlue_->isChecked())
90  {
91  colorMap = uCvQtDepthRedToBlue;
92  }
93  else if(colorMapBlueToRed_->isChecked())
94  {
95  colorMap = uCvQtDepthBlueToRed;
96  }
97 
98  image_ = uCvMat2QImage(depth, true, colorMap).convertToFormat(QImage::Format_RGB32);
99 
100  imageRGB_ = QImage();
101  if(!rgb.empty())
102  {
103  imageRGB_ = uCvMat2QImage(rgb);
104  if( depth.cols != rgb.cols ||
105  depth.rows != rgb.rows)
106  {
107  // scale rgb to depth
108  imageRGB_ = imageRGB_.scaled(image_.size());
109  }
110  }
111  showRGB_->setEnabled(!imageRGB_.isNull());
112  modified_ = false;
113  update();
114 }
115 
117 {
118  cv::Mat modifiedImage = originalImage_.clone();
119  if(modified_)
120  {
121  UASSERT(image_.width() == modifiedImage.cols &&
122  image_.height() == modifiedImage.rows);
123  UASSERT(modifiedImage.type() == CV_32FC1 ||
124  modifiedImage.type() == CV_16UC1);
125  for(int j=0; j<image_.height(); ++j)
126  {
127  for(int i=0; i<image_.width(); ++i)
128  {
129  if(qRed(image_.pixel(i, j)) == 0 &&
130  qGreen(image_.pixel(i, j)) == 0 &&
131  qBlue(image_.pixel(i, j)) == 0)
132  {
133  if(modifiedImage.type() == CV_32FC1)
134  {
135  modifiedImage.at<float>(j,i) = 0.0f;
136  }
137  else // CV_16UC1
138  {
139  modifiedImage.at<unsigned short>(j,i) = 0;
140  }
141  }
142  }
143  }
144  }
145  return modifiedImage;
146 }
147 
148 void EditDepthArea::setPenWidth(int newWidth)
149 {
150  myPenWidth_ = newWidth;
151 }
152 
154 {
155  image_ = uCvMat2QImage(originalImage_).convertToFormat(QImage::Format_RGB32);
156  modified_ = false;
157  update();
158 }
159 
161 {
162  if(type == uCvQtDepthBlackToWhite)
163  {
164  colorMapBlackToWhite_->setChecked(true);
165  }
166  else if(type == uCvQtDepthRedToBlue)
167  {
168  colorMapRedToBlue_->setChecked(true);
169  }
170  else if(type == uCvQtDepthBlueToRed)
171  {
172  colorMapBlueToRed_->setChecked(true);
173  }
174  else
175  {
176  colorMapWhiteToBlack_->setChecked(true);
177  }
178 
179  if(!originalImage_.empty())
180  {
181  image_ = uCvMat2QImage(originalImage_, true, type).convertToFormat(QImage::Format_RGB32);
182  update();
183  }
184 }
185 
186 void EditDepthArea::mousePressEvent(QMouseEvent *event)
187 {
188  if (event->button() == Qt::LeftButton) {
189  float scale, offsetX, offsetY;
190  computeScaleOffsets(rect(), scale, offsetX, offsetY);
191  lastPoint_.setX((event->pos().x()-offsetX)/scale);
192  lastPoint_.setY((event->pos().y()-offsetY)/scale);
193 
194  scribbling_ = true;
195  }
196 }
197 
198 void EditDepthArea::mouseMoveEvent(QMouseEvent *event)
199 {
200  if ((event->buttons() & Qt::LeftButton) && scribbling_)
201  {
202  float scale, offsetX, offsetY;
203  computeScaleOffsets(rect(), scale, offsetX, offsetY);
204  QPoint to;
205  to.setX((event->pos().x()-offsetX)/scale);
206  to.setY((event->pos().y()-offsetY)/scale);
207  drawLineTo(to);
208  }
209 }
210 
211 void EditDepthArea::mouseReleaseEvent(QMouseEvent *event)
212 {
213  if (event->button() == Qt::LeftButton && scribbling_) {
214  float scale, offsetX, offsetY;
215  computeScaleOffsets(rect(), scale, offsetX, offsetY);
216  QPoint to;
217  to.setX((event->pos().x()-offsetX)/scale);
218  to.setY((event->pos().y()-offsetY)/scale);
219  drawLineTo(to);
220  scribbling_ = false;
221  }
222 }
223 
224 void EditDepthArea::computeScaleOffsets(const QRect & targetRect, float & scale, float & offsetX, float & offsetY) const
225 {
226  scale = 1.0f;
227  offsetX = 0.0f;
228  offsetY = 0.0f;
229 
230  if(!image_.isNull())
231  {
232  float w = image_.width();
233  float h = image_.height();
234  float widthRatio = float(targetRect.width()) / w;
235  float heightRatio = float(targetRect.height()) / h;
236 
237  //printf("w=%f, h=%f, wR=%f, hR=%f, sW=%d, sH=%d\n", w, h, widthRatio, heightRatio, this->rect().width(), this->rect().height());
238  if(widthRatio < heightRatio)
239  {
240  scale = widthRatio;
241  }
242  else
243  {
244  scale = heightRatio;
245  }
246 
247  //printf("ratio=%f\n",ratio);
248 
249  w *= scale;
250  h *= scale;
251 
252  if(w < targetRect.width())
253  {
254  offsetX = (targetRect.width() - w)/2.0f;
255  }
256  if(h < targetRect.height())
257  {
258  offsetY = (targetRect.height() - h)/2.0f;
259  }
260  //printf("offsetX=%f, offsetY=%f\n",offsetX, offsetY);
261  }
262 }
263 
264 void EditDepthArea::paintEvent(QPaintEvent *event)
265 {
266  //Scale
267  float ratio, offsetX, offsetY;
268  this->computeScaleOffsets(event->rect(), ratio, offsetX, offsetY);
269  QPainter painter(this);
270 
271  painter.translate(offsetX, offsetY);
272  painter.scale(ratio, ratio);
273 
274  if(showRGB_->isChecked() && !imageRGB_.isNull())
275  {
276  painter.setOpacity(0.5);
277  painter.drawImage(QPoint(0,0), imageRGB_);
278  }
279 
280  painter.drawImage(QPoint(0,0), image_);
281 }
282 
283 void EditDepthArea::resizeEvent(QResizeEvent *event)
284 {
285  QWidget::resizeEvent(event);
286 }
287 
288 void floodfill(QRgb * bits, const cv::Mat & depthImage, int x, int y, float lastDepthValue, float error, int &iterations)
289 {
290  ++iterations;
291  if(x>=0 && x<depthImage.cols &&
292  y>=0 && y<depthImage.rows)
293  {
294  float currentValue;
295  if(depthImage.type() == CV_32FC1)
296  {
297  currentValue = depthImage.at<float>(y, x);
298  }
299  else
300  {
301  currentValue = float(depthImage.at<unsigned short>(y, x))/1000.0f;
302  }
303  if(currentValue == 0.0f)
304  {
305  return;
306  }
307 
308  QRgb & rgb = bits[x+y*depthImage.cols];
309  if(qRed(rgb) == 0 && qGreen(rgb) == 0 && qBlue(rgb) == 0)
310  {
311  return;
312  }
313  if(lastDepthValue>=0.0f && fabs(lastDepthValue - currentValue) > error*lastDepthValue)
314  {
315  return;
316  }
317  rgb = 0;
318 
319  if(y+1<depthImage.rows)
320  {
321  QRgb & rgb = bits[x+(y+1)*depthImage.cols];
322  if(qRed(rgb) != 0 || qGreen(rgb) != 0 || qBlue(rgb) != 0)
323  {
324  floodfill(bits, depthImage, x, y+1, currentValue, error, iterations);
325  }
326  }
327  if(y-1>=0)
328  {
329  QRgb & rgb = bits[x+(y-1)*depthImage.cols];
330  if(qRed(rgb) != 0 || qGreen(rgb) != 0 || qBlue(rgb) != 0)
331  {
332  floodfill(bits, depthImage, x, y-1, currentValue, error, iterations);
333  }
334  }
335  if(x+1<depthImage.cols)
336  {
337  QRgb & rgb = bits[x+1+y*depthImage.cols];
338  if(qRed(rgb) != 0 || qGreen(rgb) != 0 || qBlue(rgb) != 0)
339  {
340  floodfill(bits, depthImage, x+1, y, currentValue, error, iterations);
341  }
342  }
343  if(x-1>=0)
344  {
345  QRgb & rgb = bits[x-1+y*depthImage.cols];
346  if(qRed(rgb) != 0 || qGreen(rgb) != 0 || qBlue(rgb) != 0)
347  {
348  floodfill(bits, depthImage, x-1, y, currentValue, error, iterations);
349  }
350  }
351  }
352 }
353 
354 void EditDepthArea::contextMenuEvent(QContextMenuEvent * e)
355 {
356  QAction * action = menu_->exec(e->globalPos());
357  if(action == showRGB_)
358  {
359  this->update();
360  }
361  else if(action == removeCluster_)
362  {
363  float scale, offsetX, offsetY;
364  computeScaleOffsets(rect(), scale, offsetX, offsetY);
365  QPoint pixel;
366  pixel.setX((e->pos().x()-offsetX)/scale);
367  pixel.setY((e->pos().y()-offsetY)/scale);
368  if(pixel.x()>=0 && pixel.x() < originalImage_.cols &&
369  pixel.y()>=0 && pixel.y() < originalImage_.rows)
370  {
371  int iterations=0;
372  floodfill((QRgb*)image_.bits(), originalImage_, pixel.x(), pixel.y(), -1.0f, clusterError_, iterations);
373  }
374  modified_=true;
375  this->update();
376  }
377  else if(action == clusterErrorCluster_)
378  {
379  bool ok;
380  double error = QInputDialog::getDouble(this, tr("Set Cluster Error"), tr("Error:"), clusterError(), 0.001, 1, 3, &ok);
381  if(ok)
382  {
383  clusterError_= error;
384  }
385  modified_=true;
386  }
387  else if(action == setPenWidth_)
388  {
389  bool ok;
390  int width = QInputDialog::getInt(this, tr("Set Pen Width"), tr("Width (pixels):"), penWidth(), 1, 999, 1, &ok);
391  if(ok)
392  {
393  myPenWidth_ = width;
394  }
395  }
396  else if(action == colorMapBlackToWhite_ ||
397  action == colorMapWhiteToBlack_ ||
398  action == colorMapRedToBlue_ ||
399  action == colorMapBlueToRed_)
400  {
402  if(colorMapBlackToWhite_->isChecked())
403  {
404  colorMap = uCvQtDepthBlackToWhite;
405  }
406  else if(colorMapRedToBlue_->isChecked())
407  {
408  colorMap = uCvQtDepthRedToBlue;
409  }
410  else if(colorMapBlueToRed_->isChecked())
411  {
412  colorMap = uCvQtDepthBlueToRed;
413  }
414  this->setColorMap(colorMap);
415  }
416  else if(action == resetChanges_)
417  {
418  this->resetChanges();
419  }
420 }
421 
422 void EditDepthArea::drawLineTo(const QPoint &endPoint)
423 {
424  QPainter painter(&image_);
425  painter.setPen(QPen(Qt::black, myPenWidth_, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
426  painter.drawLine(lastPoint_, endPoint);
427  modified_ = true;
428 
429  update();
430  lastPoint_ = endPoint;
431 }
432 
433 }
void setImage(const cv::Mat &depth, const cv::Mat &rgb=cv::Mat())
EditDepthArea(QWidget *parent=0)
QAction * colorMapRedToBlue_
Definition: EditDepthArea.h:94
void drawLineTo(const QPoint &endPoint)
void setPenWidth(int newWidth)
f
QAction * clusterErrorCluster_
Definition: EditDepthArea.h:89
QImage uCvMat2QImage(const cv::Mat &image, bool isBgr=true, uCvQtDepthColorMap colorMap=uCvQtDepthWhiteToBlack, float depthMin=0, float depthMax=0)
Definition: UCv2Qt.h:47
GLM_FUNC_DECL genType e()
void setColorMap(uCvQtDepthColorMap type)
virtual void resizeEvent(QResizeEvent *event)
GLM_FUNC_DECL detail::tmat4x4< T, P > scale(detail::tmat4x4< T, P > const &m, detail::tvec3< T, P > const &v)
virtual void contextMenuEvent(QContextMenuEvent *e)
virtual void mouseMoveEvent(QMouseEvent *event)
QAction * colorMapWhiteToBlack_
Definition: EditDepthArea.h:92
#define UASSERT(condition)
virtual void mouseReleaseEvent(QMouseEvent *event)
void floodfill(QRgb *bits, const cv::Mat &depthImage, int x, int y, float lastDepthValue, float error, int &iterations)
QAction * colorMapBlackToWhite_
Definition: EditDepthArea.h:93
virtual void mousePressEvent(QMouseEvent *event)
cv::Mat getModifiedImage() const
QAction * colorMapBlueToRed_
Definition: EditDepthArea.h:95
double clusterError() const
Definition: EditDepthArea.h:59
ULogger class and convenient macros.
uCvQtDepthColorMap
Definition: UCv2Qt.h:30
virtual void paintEvent(QPaintEvent *event)
void computeScaleOffsets(const QRect &targetRect, float &scale, float &offsetX, float &offsetY) const


rtabmap
Author(s): Mathieu Labbe
autogenerated on Mon Jan 23 2023 03:37:28