Commit 4c595c32fc754cad409a292dd0eb97095cf57483
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 | */ |