CVB++ 14.1
Minos/QmlMinos

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#include <cvb/minos/classifier.hpp>
15#include <cvb/minos/search_result.hpp>
16
17#include "minos_search.hpp"
18#include "minos_result_model.hpp"
19
20int main(int argc, char* argv[])
21{
22 try
23 {
24 QGuiApplication app(argc, argv);
25 app.setWindowIcon(QIcon(":/qttutorial.png"));
26
27 // open device
28 auto device = Cvb::DeviceFactory::Open(Cvb::InstallPath() + Cvb::String(CVB_LIT("drivers/CVMock.vin")), Cvb::AcquisitionStack::Vin);
29
30 // setup QML interface objects
31 Cvb::UI::ImageController imageController;
32 MinosResultModel minosResultModel(imageController);
33
34 // main search object
35 MinosSearch minosSearch(device->Stream(), minosResultModel);
36
37
38 // register QML components for an image display
41
42
44 auto context = engine.rootContext();
45 // create a controller object to communicate with QML
46 context->setContextProperty("mainImage", &imageController);
47 // create a Minos result model to communicate with QML for overlays
48 context->setContextProperty("minosResultModel", &minosResultModel);
49 // create a Minos search object to communicate with QML (grab, snap)
50 context->setContextProperty("minosSearch", &minosSearch);
51
52 // load main QML file
53 engine.load(QUrl::fromLocalFile("../main.qml"));
54
55 // do a first snap at startup
56 minosSearch.Snap();
57
58 return app.exec();
59 }
60 catch (const std::exception& error)
61 {
62 std::cout << error.what() << std::endl;
63 }
64}
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:55
Controller object for the QML image view item.
Definition: image_view_item.hpp:252
static void Register()
Convenience method to register this type in QML.
Definition: image_view_item.hpp:90
static void Register()
Convenience method to register this type in QML.
Definition: image_view_item.hpp:667
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 search 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/minos/classifier.hpp>
7#include <cvb/ui/image_view_item.hpp>
8#include <cvb/utilities/stop_watch.hpp>
9
10#include "minos_result_model.hpp"
11
12
13class MinosSearch final:
14 public QObject,
16{
17 Q_OBJECT
18
19 public:
20
21 MinosSearch(const Cvb::StreamPtr & stream, MinosResultModel& model);
22
23 ~MinosSearch();
24
26
27 public Q_SLOTS:
28
29 void StartGrab();
30 void StopGrab();
31 void Snap();
32
33
34 private:
35
36 void SearchLine(const Cvb::ImagePlane & plane, Cvb::Area2D aoi, std::vector<MinosResultItem> & resultList);
37
38 void HandleAsyncStream(const Cvb::StreamPtr & stream) override;
39
40 Cvb::StreamPtr stream_;
41 Cvb::Minos::Classifier classifier_;
42 MinosResultModel& model_;
43
45
46};
Structure that represents an area of interest in the image.
Definition: area_2d.hpp:21
Handler object for a single stream.
Definition: decl_single_stream_handler.hpp:34
virtual void HandleAsyncStream(const Driver::StreamPtr &stream)
Asynchronously called for the registered stream.
Definition: detail_single_stream_handler.hpp:21
Image plane information container.
Definition: decl_image_plane.hpp:33
Minos classifier object.
Definition: classifier.hpp:251
Speed measurement object.
Definition: stop_watch.hpp:47
1#include "minos_search.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// Global params
12const double FIRST_AREA_RADIUS = 8.0;
13const double LINE_RADIUS = 4;
14const double WORD_HEIGHT = 4;
15const double WORD_WIDTH = 4;
16const double LINE_STEP_RADIUS = 8;
17
18const Cvb::Point2D<double> LINE_STEP = Cvb::Point2D<double>(0.0, 16.0);
19const Cvb::Area2D OCR_AOI = Cvb::Area2D(-3.0, -3.0, 3.0, -3.0, -3.0, 3.0);
20
21MinosSearch::MinosSearch(const Cvb::StreamPtr & stream, MinosResultModel& model)
22 : QObject()
23 , SingleStreamHandler(stream)
24 , stream_(stream)
25 , classifier_(Cvb::InstallPath() + Cvb::String(CVB_LIT("tutorial/Minos/Images/OCR/Training Set/Numbers.clf")))
26 , model_(model)
27 , stopWatch_(Cvb::StopWatchMode::MultiCPU)
28{
29}
30
31MinosSearch::~MinosSearch()
32{
33 TryFinish();
34}
35
36
37void MinosSearch::StartGrab()
38{
39 Run();
40}
41
42void MinosSearch::StopGrab()
43{
44 Finish();
45}
46
47void MinosSearch::Snap()
48{
49 auto waitResult = stream_->GetTimedSnapshot(std::chrono::seconds(1));
50 if (waitResult.Status != Cvb::WaitStatus::Ok)
51 return;
52
53 // Minos search in snap
54 MinosResultData result;
55
56 stopWatch_.Start();
57 result.List = Search(waitResult.Image->Plane(0));
58 model_.SetProcessingTime(stopWatch_.TimeSpan().count());
59
60 result.Image = waitResult.Image;
61 model_.PushData(result);
62 model_.Refresh();
63}
64
65// The Minos search
66std::vector<MinosResultItem> MinosSearch::Search(const Cvb::ImagePlane & plane)
67{
68
69 auto width = static_cast<double>(plane.Parent().Width());
70 auto height = static_cast<double>(plane.Parent().Height());
71
72 // STEP 1 finding the top most line
73 // create an area centered in horizontal direction
74 // size of the area is FIRST_AREA_WIDTH = ImageHeight
75 // the direction of the area is set to the bottom
76 // create an area from these values
77 Cvb::Area2D searchAoi(Cvb::Point2D<double>(width / 2 - FIRST_AREA_RADIUS, 0.0),
78 Cvb::Point2D<double>(width / 2 + FIRST_AREA_RADIUS, 0.0),
79 Cvb::Point2D<double>(width / 2 - FIRST_AREA_RADIUS, height - 1.0));
80
81 // search for the first object
82 auto searchResult = classifier_.Search(plane, Cvb::Minos::SearchMode::FindFirst, searchAoi);
83
85 if (searchResult.Quality() < 0.1)
86 return resultList;
87
88 // now we found the first line of the text
89
90 // STEP 2 finding the beginning of a line
91 // try to find the first char in the line
92 // create an area centered around the first line
93 // beginning at the left side of the image
94 // size of the area is LINE_HEIGHT * dXPos
95 // the direction of the area is set to the right
96 // create an area from these values
97 auto position = searchResult.Position();
98 Cvb::Area2D lineAoi(Cvb::Point2D<double>(0.0, position.Y() - LINE_RADIUS),
99 Cvb::Point2D<double>(0.0, position.Y() + LINE_RADIUS),
100 Cvb::Point2D<double>(position.X(), position.Y() - LINE_RADIUS));
101
102
103
104 // do a line by line search
105 SearchLine(plane, lineAoi, resultList);
106
107 return resultList;
108}
109
110// Minos search per line
111void MinosSearch::SearchLine(const Cvb::ImagePlane & plane, Cvb::Area2D lineAoi, std::vector<MinosResultItem> & resultList)
112{
113 // search for the first object
114 auto searchResult = classifier_.Search(plane, Cvb::Minos::SearchMode::FindFirst, lineAoi);
115
116 if (searchResult.Quality() == 0.0)
117 return;
118
119 // now we found the first char in the first line
120
121 // STEP 3 read the string
122 // save the x and y position of the line
123 // try to read the first word
124 // create an area left aligned the first char
125 // size of WORD_WIDTH * WORD_HEIGHT
126 // create an area from these values
127 auto lineStart = searchResult.Position();
128 Cvb::Area2D readAoi(Cvb::Point2D<double>(lineStart.X(), lineStart.Y() - WORD_HEIGHT / 2),
129 Cvb::Point2D<double>(lineStart.X() + WORD_WIDTH / 2,lineStart.Y() - WORD_HEIGHT / 2),
130 Cvb::Point2D<double>(lineStart.X(), lineStart.Y() + WORD_HEIGHT / 2));
131
132 // read the word
133 auto readResult = classifier_.Read(plane, readAoi, OCR_AOI);
134
135 // save the result
136 Cvb::StringStream stream;
137 for (const auto c : readResult)
138 stream << c.Name();
139 MinosResultItem resultItem = { lineStart, stream.str() };
140 resultList.push_back(resultItem);
141
142
143 lineStart += LINE_STEP;
144
145
146 // STEP 4 jump to the next line
147 // try to read the first word
148 // create an area left aligned the first char
149 // size of WORD_WIDTH * WORD_HEIGHT
150 // create an area from these values
151 Cvb::Area2D nextLineAoi(Cvb::Point2D<double>(lineStart.X() - LINE_STEP_RADIUS, lineStart.Y() - LINE_STEP_RADIUS),
152 Cvb::Point2D<double>(lineStart.X() + LINE_STEP_RADIUS, lineStart.Y() - LINE_STEP_RADIUS),
153 Cvb::Point2D<double>(lineStart.X() - LINE_STEP_RADIUS, lineStart.Y() + LINE_STEP_RADIUS));
154
155 // next line (recursion should - no to deep)
156 SearchLine(plane, nextLineAoi, resultList);
157}
158
159void MinosSearch::HandleAsyncStream(const Cvb::StreamPtr & stream)
160{
161 auto waitResult = stream->WaitFor<Cvb::RingBufferImage>(std::chrono::seconds(1));
162 if (waitResult.Status == Cvb::WaitStatus::Ok)
163 {
164
165
166 // Minos search in grab
167 MinosResultData result;
168
169 stopWatch_.Start();
170 result.List = Search(waitResult.Image->Plane(0));
171 model_.SetProcessingTime(stopWatch_.TimeSpan().count());
172
173 result.Image = waitResult.Image;
174 model_.PushData(result);
175
176 // Tell the UI thread to redraw and present the results
177 QMetaObject::invokeMethod(&model_, "Refresh", Qt::QueuedConnection);
178 }
179}
180
181
182
183
Stream image that is returned, when the ring buffer interface is available on a device.
Definition: decl_ring_buffer_image.hpp:29
@ FindFirst
Stop after the first result has been found.
Root namespace for the Image Manager interface.
Definition: c_barcode.h:24
@ Ok
Everything is fine, a new image arrived.
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)

The minos search result:

1#pragma once
2
3#include <deque>
4#include <mutex>
5
6#include <QAbstractListModel>
7
8#include <cvb/minos/search_result.hpp>
9#include <cvb/ui/image_view_item.hpp>
10
11
12struct MinosResultItem
13{
14 Cvb::Point2D<double> Position;
15 Cvb::String Text;
16};
17
18struct MinosResultData
19{
21 Cvb::ImagePtr Image;
22};
23
24
25
26class MinosResultModel
27 : public QAbstractListModel
28{
29 Q_OBJECT
30
31 Q_PROPERTY(double processingTime READ ProcessingTime NOTIFY NotifyProcessingTime);
32 Q_PROPERTY(QString searchResultText READ SearchResultText NOTIFY NotifySearchResultText);
33
34 friend class MinosSearch;
35
36 enum MinosResultRoles
37 {
38 LineText = Qt::UserRole,
39 StartPosition = Qt::UserRole + 1
40 };
41
42 public:
43
44 explicit MinosResultModel(Cvb::UI::ImageController& imageController);
45
46
47
48 private Q_SLOTS:
49
50 void Refresh();
51
52 private:
53
54 QString SearchResultText() const;
55
56 double ProcessingTime() const noexcept;
57
58 void SetProcessingTime(double processingTime);
59
60 void PushData(const MinosResultData & data);
61
62 QHash<int, QByteArray> roleNames() const override;
63
64 int rowCount(const QModelIndex & = QModelIndex()) const override;
65
66 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
67
68 void SetSearchResultText(const QString searchResultText);
69
70 private:
71
72 Cvb::UI::ImageController& imageController_;
73
74 std::mutex resultMutex_;
75 std::deque<MinosResultData> resultQue_;
76 MinosResultData currentResult_;
77
78 QString searchResultText_;
79
80 double processingTime_ = std::numeric_limits<double>::quiet_NaN();
81
82 Q_SIGNALS:
83
84 void NotifyProcessingTime();
85 void NotifySearchResultText();
86};
1#include "minos_result_model.hpp"
2
3#include <QRect>
4
5#include <cvb/ui/ui.hpp>
6
7MinosResultModel::MinosResultModel(Cvb::UI::ImageController& imageController)
9 , imageController_(imageController)
10{
11}
12
13
14
15
16QHash<int, QByteArray> MinosResultModel::roleNames() const
17{
19
20 names[LineText] = "lineText";
21 names[StartPosition] = "startPosition";
22
23 return names;
24}
25
26int MinosResultModel::rowCount(const QModelIndex &) const
27{
28 return static_cast<int>(currentResult_.List.size());
29}
30
31
32QVariant MinosResultModel::data(const QModelIndex &index, int role) const
33{
34 if (!index.isValid())
35 return QVariant();
36
37 const auto & result = currentResult_.List[index.row()];
38 switch (role)
39 {
40
41 default:
42 return QVariant();
43
44 case StartPosition:
45 return QVariant(Cvb::UI::CvbToQt(result.Position));
46
47 case LineText:
48 return QVariant(Cvb::UI::CvbToQt(result.Text));
49 }
50}
51
52QString MinosResultModel::SearchResultText() const
53{
54 return searchResultText_;
55}
56
57double MinosResultModel::ProcessingTime() const noexcept
58{
59 return processingTime_;
60}
61
62void MinosResultModel::SetProcessingTime(double processingTime)
63{
64 if (processingTime_ == processingTime)
65 return;
66 processingTime_ = processingTime;
67 NotifyProcessingTime();
68}
69
70void MinosResultModel::PushData(const MinosResultData & data)
71{
72 std::unique_lock<std::mutex> guard(resultMutex_);
73 resultQue_.push_back(data);
74}
75
76void MinosResultModel::Refresh()
77{
78 {
79 std::unique_lock<std::mutex> guard(resultMutex_);
80 if (!resultQue_.size())
81 return;
82 currentResult_ = resultQue_.back();
83 resultQue_.clear();
84 }
85 layoutChanged();
86 Cvb::StringStream stream;
87 for (const auto & result : currentResult_.List)
88 stream << result.Text << CVB_LIT("\n");
89 SetSearchResultText(Cvb::UI::CvbToQt(stream.str()));
90 imageController_.Refresh(currentResult_.Image);
91}
92
93void MinosResultModel::SetSearchResultText(const QString searchResultText)
94{
95 if (searchResultText_ == searchResultText)
96 return;
97 searchResultText_ = searchResultText;
98 NotifySearchResultText();
99}
100
101
102
103
QString CvbToQt(const Cvb::String &text) noexcept
Convenience converter for strings.
Definition: ui.hpp:253

The main QML:

1import QtQuick 2.3
2import CvbQuick 1.0 as CvbQuick
3import QtQuick.Controls 1.2
4import QtQuick.Layouts 1.3
5
6ApplicationWindow
7{
8 id: rootWin
9 visible: true
10 property int margin: 11
11 width: 1080
12 height: 720
13
14 ColumnLayout
15 {
16 id: mainLayout
17 anchors.fill: parent
18 anchors.margins: margin
19
20 CvbQuick.ImageView
21 {
22 id: view
23 image: mainImage
24 Layout.fillWidth: true
25 Layout.fillHeight: true
26
27 // Text result per line
28 Repeater
29 {
30 model : minosResultModel
31 delegate: TextLabel
32 {
33 imageView : view
34 imageX : startPosition.x
35 imageY : startPosition.y
36 text : lineText
37
38 }
39 }
40 }
41
42 RowLayout
43 {
44
45 TextArea
46 {
47 id: txtArea
48 text: minosResultModel.searchResultText
49 readOnly: true
50 wrapMode: TextEdit.WrapAnywhere
51 Layout.fillWidth: true
52 }
53
54 ColumnLayout
55 {
56 Layout.preferredWidth: 200
57
58 RowLayout
59 {
60
61 Layout.alignment: Qt.AlignCenter
62
63 Button
64 {
65 text: "Snap"
66 onClicked: minosSearch.Snap()
67 enabled: !cbGrab.checked
68 }
69
70 CheckBox
71 {
72 id: cbGrab
73 text: "Grab"
74 checked: false
75 onClicked:
76 {
77 if (checkedState === Qt.Checked)
78 return minosSearch.StartGrab()
79 else
80 return minosSearch.StopGrab()
81 }
82 }
83 }
84
85 Label
86 {
87 id: txtProcessingTime
88 text: "Processing time: " + String(minosResultModel.processingTime) + " ms"
89 font.bold: true
90 Layout.alignment: Qt.AlignCenter
91 }
92
93 }
94
95 }
96 }
97}

The label overlay:

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 labelScale : CvbQuick.LabelScale.On
10
11 Text
12 {
13 text: label.text
14 color: "red"
15 font.pointSize : 4
16 }
17
18}