CVB++ 14.0
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
19int 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
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}
Controller object for the QML image view item.
Definition: image_view_item.hpp:254
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:670
void setWindowIcon(const QIcon &icon)
void load(const QUrl &url)
void setContextProperty(const QString &name, QObject *value)
QQmlContext * rootContext() const const
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
14class 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};
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
13Classification::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
27void 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
41void 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
72void 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
96void Classification::OnSearchPredictorChanged()
97{
98 search_ = Cvb::Polimago::SearchPredictor::Load(Cvb::UI::QtToCvb(searchPredictor_.toLocalFile()));
99}
100
101void Classification::OnClassificationPredictorChnaged()
102{
103 classification_ = Cvb::Polimago::ClassificationPredictor::Load(Cvb::UI::QtToCvb(classificationPredictor_.toLocalFile()));
104}
105
106
107
108
109
110
111
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< Image > Load(const String &fileName)
Loads an image with the given file name.
Definition: detail_image.hpp:32
static std::unique_ptr< ClassificationPredictor > Load(const String &fileName)
Load a saved predictor from a file.
Definition: classification_predictor.hpp:129
static std::unique_ptr< SearchPredictor > Load(const String &fileName)
Load a saved predictor from a file.
Definition: search_predictor.hpp:121
Search results as provided by a Search Classifier.
Definition: search_result.hpp:21
T end(T... args)
Cvb::String QtToCvb(const QString text) noexcept
Convenience converter for strings.
Definition: ui.hpp:239
@ Ok
Everything is fine, a new image arrived.

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
17class PolimagoResult
18{
19public:
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
36private:
37 QPointF position_;
38 QString name_;
39 double posQuality_;
40 double colorQuality_;
41};
42
43
44class 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;
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};
Polimago classification result container.
Definition: classification_result.hpp:20
String Name() const
Name of the pattern.
Definition: classification_result.hpp:36
double Quality() const noexcept
Quality / Confidence of the classification result.
Definition: classification_result.hpp:46
double Quality() const noexcept
Search result quality or confidence.
Definition: search_result.hpp:48
double Y() const noexcept
Y-position.
Definition: search_result.hpp:78
double X() const noexcept
X-position.
Definition: search_result.hpp:68
QString CvbToQt(const Cvb::String &text) noexcept
Convenience converter for strings.
Definition: ui.hpp:254
virtual QVariant data(const QModelIndex &index, int role) const const=0
void layoutChanged(const QList< QPersistentModelIndex > &parents, QAbstractItemModel::LayoutChangeHint hint)
virtual QHash< int, QByteArray > roleNames() const const
virtual int rowCount(const QModelIndex &parent) const const=0
1#include "result_model.hpp"
2
3#include <QRect>
4
5
6ResultModel::ResultModel(Cvb::UI::ImageController &controller)
8 , controller_(controller)
9{
10}
11
12
13
14
15QHash<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
26int ResultModel::rowCount(const QModelIndex &) const
27{
28 return static_cast<int>(results_.size());
29}
30
31
32QVariant 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

The main QML:

1import QtQuick 2.3
2import CvbQuick 1.0 as CvbQuick
3import QtQuick.Controls 1.3
4import QtQuick.Layouts 1.2
5import QtQuick.Dialogs 1.2
6
7ApplicationWindow
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:

1import QtQuick 2.3
2import CvbQuick 1.0 as CvbQuick
3
4// Output result as overlay
5CvbQuick.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}