Polimago/QmlCookieClassification

This example requires Qt5 >= 5.9 setup for building.

You may build it with Ubuntu 18.04's default Qt5 after installing:

1 // ---------------------------------------------------------------------------
3 // ---------------------------------------------------------------------------
4 
5 #include <iostream>
6 
7 #include <QGuiApplication>
8 #include <QQmlApplicationEngine>
9 #include <QQmlContext>
10 #include <QQuickView>
11 #include <QIcon>
12 
13 #include <cvb/ui/image_view_item.hpp>
14 
15 
16 #include "classification.hpp"
17 #include "result_model.hpp"
18 
19 int main(int argc, char* argv[])
20 {
21  try
22  {
23  QGuiApplication app(argc, argv);
24  app.setWindowIcon(QIcon(":/qttutorial.png"));
25 
26 
27  // setup QML interface objects
28  Cvb::UI::ImageController imageController;
29  ResultModel resultModel(imageController);
30 
31  // main search object
32  Classification classification(resultModel);
33 
34 
35  // register QML components for an image display
38 
39 
40  QQmlApplicationEngine engine;
41  auto context = engine.rootContext();
42  // create a controller object to communicate with QML
43  context->setContextProperty("mainImage", &imageController);
44  // create a result model to communicate with QML for overlays
45  context->setContextProperty("resultModel", &resultModel);
46  // create a classification object to communicate with QML (snap, count)
47  context->setContextProperty("classification", &classification);
48 
49  // load main QML file
50  engine.load(QUrl::fromLocalFile("../main.qml"));
51 
52 
53  return app.exec();
54  }
55  catch (const std::exception& error)
56  {
57  std::cout << error.what() << std::endl;
58  }
59 }
void setWindowIcon(const QIcon &icon)
static void Register()
Convenience method to register this type in QML.
Definition: image_view_item.hpp:92
static void Register()
Convenience method to register this type in QML.
Definition: image_view_item.hpp:669
Controller object for the QML image view item.
Definition: image_view_item.hpp:252
void setContextProperty(const QString &name, QObject *value)
QQmlContext * rootContext() const const
STL class.
void load(const QUrl &url)
QUrl fromLocalFile(const QString &localFile)

The classification class:

1 #include <QObject>
2 #include <QVariant>
3 
4 #include <cvb/device_factory.hpp>
5 #include <cvb/async/single_stream_handler.hpp>
6 #include <cvb/polimago/search_predictor.hpp>
7 #include <cvb/polimago/classification_predictor.hpp>
8 #include <cvb/ui/image_view_item.hpp>
9 #include <cvb/utilities/stop_watch.hpp>
10 
11 #include "result_model.hpp"
12 
13 
14 class Classification final:
15  public QObject
16 
17 {
18  Q_OBJECT
19 
20  Q_PROPERTY(QUrl source READ Source WRITE SetSource NOTIFY NotifySource);
21  Q_PROPERTY(QUrl searchPredictor READ SearchPredictor WRITE SetSearchPredictor NOTIFY NotifySearchPredictor);
22  Q_PROPERTY(QUrl classificationPredictor READ ClassificationPredictor WRITE SetClassificationPredictor NOTIFY NotifyClassificationPredictor);
23 
24 
25  public:
26 
27  Classification(ResultModel& model);
28 
29 
30  public Q_SLOTS:
31 
32 
33  void Snap();
34  void Classify();
35 
36  private Q_SLOTS:
37 
38  void OnSourceChanged();
39  void OnSearchPredictorChanged();
40  void OnClassificationPredictorChnaged();
41 
42  private:
43 
44  QUrl Source() const
45  {
46  return source_;
47  }
48 
49  void SetSource(const QUrl & source)
50  {
51  if (source_ == source)
52  return;
53  source_ = source;
54  NotifySource();
55  }
56 
57  QUrl SearchPredictor() const
58  {
59  return searchPredictor_;
60  }
61 
62  void SetSearchPredictor(const QUrl & searchPredictor)
63  {
64  if (searchPredictor_ == searchPredictor)
65  return;
66  searchPredictor_ = searchPredictor;
67  NotifySearchPredictor();
68  }
69 
70  QUrl ClassificationPredictor() const
71  {
72  return classificationPredictor_;
73  }
74 
75  void SetClassificationPredictor(const QUrl & classificationPredictor)
76  {
77  if (classificationPredictor_ == classificationPredictor)
78  return;
79  classificationPredictor_ = classificationPredictor;
80  NotifyClassificationPredictor();
81  }
82 
83 
84  QUrl source_;
85  Cvb::DevicePtr device_;
86  Cvb::ImagePtr image_;
87 
88  QUrl searchPredictor_;
90 
91  QUrl classificationPredictor_;
93 
94 
95  ResultModel& controller_;
96 
97  Q_SIGNALS:
98 
99  void NotifySource();
100  void NotifySearchPredictor();
101  void NotifyClassificationPredictor();
102 
103 
104 
105 
106 };
STL class.
1 #include "classification.hpp"
2 
3 #include <QQmlEngine>
4 #include <QQmlComponent>
5 
6 #include <cvb/ui/ui.hpp>
7 #include <cvb/area_2d.hpp>
8 #include <cvb/point_2d.hpp>
9 
10 
11 
12 
13 Classification::Classification(ResultModel& model)
14  : QObject()
15  , controller_(model)
16 {
17  connect(this, &Classification::NotifySource, this, &Classification::OnSourceChanged);
18  connect(this, &Classification::NotifySearchPredictor, this, &Classification::OnSearchPredictorChanged);
19  connect(this, &Classification::NotifyClassificationPredictor, this, &Classification::OnClassificationPredictorChnaged);
20  // open default example
21  device_ = Cvb::DeviceFactory::Open(Cvb::InstallPath() + Cvb::String(CVB_LIT("drivers/CVMock.vin")), Cvb::AcquisitionStack::Vin); // CVMock.vin loads Cookies example emu
22  search_ = Cvb::Polimago::SearchPredictor::Load(Cvb::InstallPath() + Cvb::String(CVB_LIT("tutorial/Polimago/Images/Cookies/Cookies.psc")));
23  classification_ = Cvb::Polimago::ClassificationPredictor::Load(Cvb::InstallPath() + Cvb::String(CVB_LIT("tutorial/Polimago/Images/Cookies/CookieType.pcc")));
24  Snap();
25 }
26 
27 void Classification::Snap()
28 {
29  if (!device_)
30  return;
31 
32  controller_.Update({});
33  auto waitResult = device_->Stream()->GetSnapshot();
34  if (waitResult.Status != Cvb::WaitStatus::Ok)
35  return;
36 
37  image_ = waitResult.Image;
38  controller_.Refresh(image_);
39 }
40 
41 void Classification::Classify()
42 {
43  if(!image_ || !search_ || !classification_)
44  return;
45 
46  auto redImage = image_->Plane(0).Map();
47 
48  const auto gridStep = 0.6;
49  const auto threshold = 0.0;
50  const auto locality = 1.0;
51 
53 
54  // 1. find items
55  auto positions = search_->GridSearch(*redImage, redImage->Bounds(), gridStep, threshold, locality);
56  auto end = std::remove_if(positions.begin(), positions.end(), [](const Cvb::Polimago::SearchResult &position)
57  {
58  return position.Quality() < .3;
59  });
60 
61  // 2. classify the items
62  std::transform(positions.begin(), end, std::back_inserter(results), [this](const Cvb::Polimago::SearchResult &pos)
63  {
64  auto result = classification_->Classify(*image_, Cvb::Point2D<int>{static_cast<int>(pos.X()), static_cast<int>(pos.Y())});
65  return PolimagoResult(pos, result);
66  });
67 
68  // 3. update to UI
69  controller_.Update(results);
70 }
71 
72 void Classification::OnSourceChanged()
73 {
74  image_.reset();
75  device_.reset();
76 
77  search_.reset();
78  classification_.reset();
79 
80  auto fileName = source_.toLocalFile();
81  auto path = Cvb::UI::QtToCvb(fileName);
82  if (fileName.endsWith(".vin"))
83  {
84  device_ = Cvb::DeviceFactory::Open(path);
85  Snap();
86  }
87  else if (fileName.endsWith(".bmp"))
88  {
89  image_ = Cvb::Image::Load(path);
90  controller_.Update({});
91  controller_.Refresh(image_);
92  }
93 
94 }
95 
96 void Classification::OnSearchPredictorChanged()
97 {
98  search_ = Cvb::Polimago::SearchPredictor::Load(Cvb::UI::QtToCvb(searchPredictor_.toLocalFile()));
99 }
100 
101 void Classification::OnClassificationPredictorChnaged()
102 {
103  classification_ = Cvb::Polimago::ClassificationPredictor::Load(Cvb::UI::QtToCvb(classificationPredictor_.toLocalFile()));
104 }
105 
106 
107 
108 
109 
110 
111 
Cvb::String QtToCvb(const QString text) noexcept
Convenience converter for strings.
Definition: ui.hpp:239
Everything is fine, a new image arrived.
static std::unique_ptr< Image > Load(const String &fileName)
Loads an image with the given file name.
Definition: detail_image.hpp:32
T end(T... args)
STL class.
Search results as provided by a Search Classifier.
Definition: search_result.hpp:20
static std::unique_ptr< SearchPredictor > Load(const String &fileName)
Load a saved predictor from a file.
Definition: search_predictor.hpp:120
STL class.
static std::shared_ptr< T > Open(const String &provider, AcquisitionStack acquisitionStack=AcquisitionStack::PreferVin)
Opens a device with the given provider with its default board and port (if applicable).
Definition: decl_device_factory.hpp:50
static std::unique_ptr< ClassificationPredictor > Load(const String &fileName)
Load a saved predictor from a file.
Definition: classification_predictor.hpp:128

The polimago result:

1 #pragma once
2 
3 #include <iomanip>
4 #include <mutex>
5 #include <vector>
6 
7 #include <QAbstractListModel>
8 #include <QPoint>
9 
10 #include <cvb/point_2d.hpp>
11 #include <cvb/polimago/classification_result.hpp>
12 #include <cvb/polimago/search_result.hpp>
13 #include <cvb/ui/ui.hpp>
14 #include <cvb/ui/image_view_item.hpp>
15 
16 
17 class PolimagoResult
18 {
19 public:
20  PolimagoResult(const Cvb::Polimago::SearchResult &pos, const Cvb::Polimago::ClassificationResult &result)
21  : position_(static_cast<float>(pos.X()), static_cast<float>(pos.Y()))
22  , name_(Cvb::UI::CvbToQt(result.Name()))
23  , posQuality_(pos.Quality())
24  , colorQuality_(result.Quality())
25  {
26  }
27 
28  QPointF Position() const noexcept { return position_; }
29 
30  QString Name() const noexcept { return name_; }
31 
32  double PosQuality() const noexcept { return posQuality_; }
33 
34  double ColorQuality() const noexcept { return colorQuality_; }
35 
36 private:
37  QPointF position_;
38  QString name_;
39  double posQuality_;
40  double colorQuality_;
41 };
42 
43 
44 class ResultModel
45  : public QAbstractListModel
46 {
47  Q_OBJECT
48 
49 
50  friend class Classification;
51 
52  enum ResultRoles
53  {
54  LineText = Qt::UserRole,
55  StartPosition = Qt::UserRole + 1,
56  Quality
57  };
58 
59  public:
60 
61  explicit ResultModel(Cvb::UI::ImageController &controller);
62 
63  void Update(const std::vector<PolimagoResult> &results)
64  {
65  results_ = results;
66  layoutChanged();
67  }
68 
69  void Refresh(const Cvb::ImagePtr &image)
70  {
71  controller_.Refresh(image);
72  }
73 
74  private:
75 
76 
77 
78  QHash<int, QByteArray> roleNames() const override;
79 
80  int rowCount(const QModelIndex & = QModelIndex()) const override;
81 
82  QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
83 
84 
85 
86  private:
87  Cvb::UI::ImageController &controller_;
89 
90 
91  Q_SIGNALS:
92 
93 };
double X() const noexcept
X-position.
Definition: search_result.hpp:68
virtual int rowCount(const QModelIndex &parent) const const=0
double Y() const noexcept
Y-position.
Definition: search_result.hpp:78
void layoutChanged(const QList< QPersistentModelIndex > &parents, QAbstractItemModel::LayoutChangeHint hint)
virtual QHash< int, QByteArray > roleNames() const const
Search results as provided by a Search Classifier.
Definition: search_result.hpp:20
Controller object for the QML image view item.
Definition: image_view_item.hpp:252
virtual QVariant data(const QModelIndex &index, int role) const const=0
Polimago classification result container.
Definition: classification_result.hpp:19
QString CvbToQt(const Cvb::String &text) noexcept
Convenience converter for strings.
Definition: ui.hpp:254
double Quality() const noexcept
Quality / Confidence of the classification result.
Definition: classification_result.hpp:46
STL class.
double Quality() const noexcept
Search result quality or confidence.
Definition: search_result.hpp:48
String Name() const
Name of the pattern.
Definition: classification_result.hpp:36
1 #include "result_model.hpp"
2 
3 #include <QRect>
4 
5 
6 ResultModel::ResultModel(Cvb::UI::ImageController &controller)
8  , controller_(controller)
9 {
10 }
11 
12 
13 
14 
15 QHash<int, QByteArray> ResultModel::roleNames() const
16 {
18 
19  names[LineText] = "lineText";
20  names[StartPosition] = "startPosition";
21  names[Quality] = "quality";
22 
23  return names;
24 }
25 
26 int ResultModel::rowCount(const QModelIndex &) const
27 {
28  return static_cast<int>(results_.size());
29 }
30 
31 
32 QVariant ResultModel::data(const QModelIndex &index, int role) const
33 {
34  if (!index.isValid())
35  return QVariant();
36 
37  const auto & result = results_[index.row()];
38 
39  switch(role)
40  {
41  default:
42  return QVariant();
43 
44  case LineText:
45  return result.Name();
46  case StartPosition:
47  return result.Position();
48  case Quality:
49  return result.PosQuality();
50  }
51 }
52 
53 
54 
55 
56 
57 
bool isValid() const const
Controller object for the QML image view item.
Definition: image_view_item.hpp:252
int row() const const

The main QML:

1 import QtQuick 2.3
2 import CvbQuick 1.0 as CvbQuick
3 import QtQuick.Controls 1.3
4 import QtQuick.Layouts 1.2
5 import QtQuick.Dialogs 1.2
6 
7 ApplicationWindow
8 {
9  id: rootWin
10  visible: true
11  property int margin: 11
12  width: 1080
13  height: 720
14 
15  ColumnLayout
16  {
17  id: mainLayout
18  anchors.fill: parent
19  anchors.margins: margin
20 
21  CvbQuick.ImageView
22  {
23  id: view
24  image: mainImage
25  Layout.fillWidth: true
26  Layout.fillHeight: true
27 
28  // Text result per line
29  Repeater
30  {
31  model : resultModel
32  delegate: Marker
33  {
34  imageView : view
35  imageX : startPosition.x
36  imageY : startPosition.y
37  posQuality : quality
38  text : lineText
39 
40  }
41  }
42  }
43 
44  RowLayout
45  {
46  Button
47  {
48  text: "Load Image"
49  onClicked: openImageDialog.open()
50  }
51 
52  Button
53  {
54  text: "Load Search Predictor"
55  onClicked: loadSearchPredictorDialog.open()
56  }
57 
58  Button
59  {
60  text: "Load Classification Predictor"
61  onClicked: loadClassificationPredictorDialog.open()
62  }
63 
64  Button
65  {
66  id: btnSnap
67  text: "Snap"
68  onClicked: classification.Snap()
69  }
70 
71  Button
72  {
73  text: "Classify"
74  onClicked: classification.Classify()
75  }
76 
77 
78  }
79 
80  FileDialog
81  {
82  id: openImageDialog
83  title: "Open Image"
84  selectExisting: true
85  nameFilters: [ "Image source files (*.bmp)", "Common Vision Blox driver files (*.vin)" ]
86  onAccepted:
87  {
88  classification.source = openImageDialog.fileUrl;
89  var path = classification.source.toString();
90  if (path.endsWith(".bmp"))
91  btnSnap.enabled = false
92  else
93  btnSnap.enabled = true
94  }
95  }
96 
97  FileDialog
98  {
99  id: loadSearchPredictorDialog
100  title: "Load Search Predictor"
101  selectExisting: true
102  nameFilters: [ "Search classifier (*.psc)" ]
103  onAccepted: classification.searchPredictor = loadSearchPredictorDialog.fileUrl;
104  }
105 
106  FileDialog
107  {
108  id: loadClassificationPredictorDialog
109  title: "Load Classification Predictor"
110  selectExisting: true
111  nameFilters: [ "Classification predictor (*.pcc)" ]
112  onAccepted: classification.classificationPredictor = loadClassificationPredictorDialog.fileUrl;
113  }
114  }
115 }

The label:

1 import QtQuick 2.3
2 import CvbQuick 1.0 as CvbQuick
3 
4 // Output result as overlay
5 CvbQuick.ImageLabel
6 {
7  id: label
8  property var text : ""
9  property var posQuality : 0.0
10  labelScale : CvbQuick.LabelScale.Off
11 
12  Text
13  {
14  text: label.text + " (Q=" + String(label.posQuality.toFixed(2)) + ")"
15  font.pointSize : 10
16  font.bold: true
17  color : "white"
18  style: Text.Outline
19  styleColor: "black"
20  }
21 }