Commit 4c595c32fc754cad409a292dd0eb97095cf57483

Authored by Charles Otto
1 parent 40b8933d

Add a transform for manually marking rectangles

Showing 1 changed file with 225 additions and 0 deletions
openbr/plugins/gui.cpp
1 #include <QApplication> 1 #include <QApplication>
2 #include <QLabel> 2 #include <QLabel>
3 #include <QElapsedTimer> 3 #include <QElapsedTimer>
  4 +#include <QInputDialog>
4 #include <QWaitCondition> 5 #include <QWaitCondition>
5 #include <QMutex> 6 #include <QMutex>
6 #include <QMouseEvent> 7 #include <QMouseEvent>
@@ -182,6 +183,149 @@ public slots: @@ -182,6 +183,149 @@ public slots:
182 } 183 }
183 }; 184 };
184 185
  186 +class RectMarkingWindow : public DisplayWindow
  187 +{
  188 +public:
  189 + RectMarkingWindow() : DisplayWindow()
  190 + {
  191 + drawingRect = false;
  192 + }
  193 +
  194 + bool drawingRect;
  195 + QVector<QRectF> rects;
  196 + QList<QString> rectLabels;
  197 +
  198 + QPointF rectOrigin;
  199 + QPointF currentEnd;
  200 + QRectF currentRect;
  201 + bool disableAccept;
  202 +
  203 + bool eventFilter(QObject *obj, QEvent *event)
  204 + {
  205 + if (disableAccept)
  206 + return QObject::eventFilter(obj, event);
  207 +
  208 +
  209 + if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseMove)
  210 + {
  211 + event->accept();
  212 +
  213 + QMouseEvent *mouseEvent = (QMouseEvent*)event;
  214 +
  215 + if (event->type() == QEvent::MouseButtonPress)
  216 + {
  217 +
  218 + if (mouseEvent->button() == Qt::LeftButton) {
  219 + if (!drawingRect)
  220 + {
  221 + drawingRect = true;
  222 + rectOrigin = mouseEvent->pos();
  223 + return true;
  224 + }
  225 + else
  226 + {
  227 + drawingRect = false;
  228 +
  229 + rects.append(QRectF(rectOrigin, mouseEvent->pos()));
  230 + rects.last() = rects.last().normalized();
  231 + // If no labels were provided, we store everything as anonymous rectangles
  232 + if (promptKeys.empty())
  233 + rectLabels.append("rects");
  234 + // otherwise, prompt the user to select a label
  235 + else
  236 + {
  237 + // get a label from the user
  238 + bool ok = false;
  239 +
  240 + // Don't intercept events while the sub-dialog is up (if we take the events, then it will not work correctly)
  241 + disableAccept = true;
  242 + QString res = QInputDialog::getItem(this, "Select a label", "", promptKeys, next_idx, false, &ok);
  243 +
  244 + disableAccept = false;
  245 + if (ok) {
  246 + rectLabels.append(res);
  247 + for (int i=0; i < promptKeys.size(); i++)
  248 + {
  249 + if (res == promptKeys[i]) {
  250 + next_idx = (i + 1) % promptKeys.size();
  251 + break;
  252 + }
  253 + }
  254 + }
  255 + else {
  256 + rects.removeLast();
  257 + }
  258 + }
  259 + }
  260 + }
  261 + // rclick -- reset state if drawing, remove last rect if done
  262 + else if (mouseEvent->button() == Qt::RightButton && (!rects.isEmpty() || drawingRect))
  263 + {
  264 + if(drawingRect)
  265 + drawingRect = false;
  266 + else
  267 + {
  268 + rects.removeLast();
  269 + rectLabels.removeLast();
  270 + }
  271 + }
  272 + }
  273 + else
  274 + currentEnd = mouseEvent->pos();
  275 + QPixmap pixmapBuffer = pixmap;
  276 +
  277 + QPainter painter(&pixmapBuffer);
  278 + painter.setPen(Qt::red);
  279 +
  280 + painter.drawRects(rects);
  281 +
  282 + if (drawingRect)
  283 + {
  284 + currentRect = QRectF(rectOrigin, currentEnd);
  285 + painter.setPen(Qt::green);
  286 + painter.drawRect(currentRect);
  287 + }
  288 +
  289 + setPixmap(pixmapBuffer);
  290 +
  291 + return true;
  292 + } else {
  293 + if (event->type() == QEvent::KeyPress)
  294 + {
  295 + QKeyEvent * kevent = (QKeyEvent *) event;
  296 + if (kevent->key() == Qt::Key_Enter || kevent->key() == Qt::Key_Return) {
  297 + event->accept();
  298 + return true;
  299 + }
  300 + }
  301 + return DisplayWindow::eventFilter(obj, event);
  302 + }
  303 + }
  304 +
  305 +
  306 + QList<QPointF> waitForKey()
  307 + {
  308 +
  309 + rects.clear();
  310 + drawingRect = false;
  311 + disableAccept = false;
  312 + next_idx = 0;
  313 + DisplayWindow::waitForKey();
  314 +
  315 + return QList<QPointF>();
  316 + }
  317 +
  318 + void setKeys(const QStringList & keys)
  319 + {
  320 + promptKeys = keys;
  321 + }
  322 +
  323 + int next_idx;
  324 +private:
  325 + QStringList promptKeys;
  326 +
  327 +};
  328 +
185 class PointMarkingWindow : public DisplayWindow 329 class PointMarkingWindow : public DisplayWindow
186 { 330 {
187 bool eventFilter(QObject *obj, QEvent *event) 331 bool eventFilter(QObject *obj, QEvent *event)
@@ -556,6 +700,87 @@ BR_REGISTER(Transform, ManualTransform) @@ -556,6 +700,87 @@ BR_REGISTER(Transform, ManualTransform)
556 700
557 /*! 701 /*!
558 * \ingroup transforms 702 * \ingroup transforms
  703 + * \brief Manual select rectangular regions on an image.
  704 + * Stores marked rectangles as anonymous rectangles, or if a set of labels is provided, prompt the user
  705 + * to select one of those labels after drawing each rectangle.
  706 + * \author Charles Otto \cite caotto
  707 + */
  708 +class ManualRectsTransform : public ShowTransform
  709 +{
  710 + Q_OBJECT
  711 +
  712 +public:
  713 +
  714 + Q_PROPERTY(QStringList labels READ get_labels WRITE set_labels RESET reset_labels STORED false)
  715 + BR_PROPERTY(QStringList, labels, QStringList())
  716 +
  717 + void projectUpdate(const TemplateList &src, TemplateList &dst)
  718 + {
  719 +
  720 + dst = src;
  721 +
  722 + if (src.empty())
  723 + return;
  724 +
  725 + for (int i = 0; i < dst.size(); i++) {
  726 + foreach(const cv::Mat &m, dst[i]) {
  727 + qImageBuffer = toQImage(m);
  728 + displayBuffer->convertFromImage(qImageBuffer);
  729 +
  730 + emit updateImage(displayBuffer->copy(displayBuffer->rect()));
  731 +
  732 + // Blocking wait for a key-press
  733 + if (this->waitInput) {
  734 + window->waitForKey();
  735 + QVector<QRectF> rectSet = trueWindow->rects;
  736 + QList<QString> labelSet= trueWindow->rectLabels;
  737 +
  738 + for (int idx = 0; idx < rectSet.size(); idx++)
  739 + {
  740 + if (dst[i].file.contains(labelSet[idx]))
  741 + {
  742 + QVariant currentProp = dst[i].file.value(labelSet[idx]);
  743 + QList<QVariant> currentPropList;
  744 +
  745 + if (currentProp.canConvert<QList<QVariant> >() )
  746 + {
  747 + currentPropList = currentProp.toList();
  748 + }
  749 + else if (currentProp.canConvert<QRectF>())
  750 + {
  751 + currentPropList.append(currentProp);
  752 + }
  753 + else
  754 + {
  755 + qFatal("Unknown type of property");
  756 + }
  757 +
  758 + currentPropList.append(rectSet[idx]);
  759 + dst[i].file.set(labelSet[idx], QVariant::fromValue(currentPropList));
  760 + }
  761 + else
  762 + {
  763 + dst[i].file.set(labelSet[idx], rectSet[idx]);
  764 + }
  765 + }
  766 + }
  767 +
  768 + }
  769 + }
  770 + }
  771 + RectMarkingWindow * trueWindow;
  772 + void init()
  773 + {
  774 + initActual<RectMarkingWindow>();
  775 + trueWindow = dynamic_cast<RectMarkingWindow *> (this->window);
  776 + trueWindow->setKeys(this->keys);
  777 + }
  778 +};
  779 +
  780 +BR_REGISTER(Transform, ManualRectsTransform)
  781 +
  782 +/*!
  783 + * \ingroup transforms
559 * \brief Elicits metadata for templates in a pretty GUI 784 * \brief Elicits metadata for templates in a pretty GUI
560 * \author Scott Klum \cite sklum 785 * \author Scott Klum \cite sklum
561 */ 786 */