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 
20 int 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 
43  QQmlApplicationEngine engine;
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 }
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
STL class.
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.
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
void load(const QUrl &url)
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 
13 class 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 
25  std::vector<MinosResultItem> Search(const Cvb::ImagePlane & plane);
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 
44  Cvb::Utilities::StopWatch stopWatch_;
45 
46 };
Structure that represents an area of interest in the image.
Definition: area_2d.hpp:20
Image plane information container.
Definition: decl_image_plane.hpp:31
virtual void HandleAsyncStream(const Driver::StreamPtr &stream)
Asynchronously called for the registered stream.
Definition: detail_single_stream_handler.hpp:21
Minos classifier object.
Definition: classifier.hpp:247
Handler object for a single stream.
Definition: decl_single_stream_handler.hpp:32
STL class.
Speed measurement object.
Definition: stop_watch.hpp:46
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
12 const double FIRST_AREA_RADIUS = 8.0;
13 const double LINE_RADIUS = 4;
14 const double WORD_HEIGHT = 4;
15 const double WORD_WIDTH = 4;
16 const double LINE_STEP_RADIUS = 8;
17 
18 const Cvb::Point2D<double> LINE_STEP = Cvb::Point2D<double>(0.0, 16.0);
19 const Cvb::Area2D OCR_AOI = Cvb::Area2D(-3.0, -3.0, 3.0, -3.0, -3.0, 3.0);
20 
21 MinosSearch::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 
31 MinosSearch::~MinosSearch()
32 {
33  TryFinish();
34 }
35 
36 
37 void MinosSearch::StartGrab()
38 {
39  Run();
40 }
41 
42 void MinosSearch::StopGrab()
43 {
44  Finish();
45 }
46 
47 void 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
66 std::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
111 void 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 
159 void 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 
Stop after the first result has been found.
Structure that represents an area of interest in the image.
Definition: area_2d.hpp:20
Image plane information container.
Definition: decl_image_plane.hpp:31
std::string String
String for wide characters or unicode characters.
Definition: string.hpp:45
Everything is fine, a new image arrived.
const Image & Parent() const noexcept
Image to which this plane descriptor refers to.
Definition: detail_image_plane.hpp:87
Root namespace for the Image Manager interface.
Definition: version.hpp:11
Stream image that is returned, when the ring buffer interface is available on a device.
Definition: decl_ring_buffer_image.hpp:27
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)
STL class.
String InstallPath()
Directory Common Vision Blox has been installed to.
Definition: system_info.hpp:242
StopWatchMode
Mode at which the StopWatch should work.
Definition: utilities.hpp:30
int Width() const noexcept
Width of the image in pixels.
Definition: decl_image.hpp:285
int Height() const noexcept
Height of the image in pixels.
Definition: decl_image.hpp:278

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 
12 struct MinosResultItem
13 {
14  Cvb::Point2D<double> Position;
15  Cvb::String Text;
16 };
17 
18 struct MinosResultData
19 {
21  Cvb::ImagePtr Image;
22 };
23 
24 
25 
26 class 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 };
STL class.
Root namespace for the Image Manager interface.
Definition: version.hpp:11
Controller object for the QML image view item.
Definition: image_view_item.hpp:252
STL class.
1 #include "minos_result_model.hpp"
2 
3 #include <QRect>
4 
5 #include <cvb/ui/ui.hpp>
6 
7 MinosResultModel::MinosResultModel(Cvb::UI::ImageController& imageController)
9  , imageController_(imageController)
10 {
11 }
12 
13 
14 
15 
16 QHash<int, QByteArray> MinosResultModel::roleNames() const
17 {
19 
20  names[LineText] = "lineText";
21  names[StartPosition] = "startPosition";
22 
23  return names;
24 }
25 
26 int MinosResultModel::rowCount(const QModelIndex &) const
27 {
28  return static_cast<int>(currentResult_.List.size());
29 }
30 
31 
32 QVariant 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 
52 QString MinosResultModel::SearchResultText() const
53 {
54  return searchResultText_;
55 }
56 
57 double MinosResultModel::ProcessingTime() const noexcept
58 {
59  return processingTime_;
60 }
61 
62 void MinosResultModel::SetProcessingTime(double processingTime)
63 {
64  if (processingTime_ == processingTime)
65  return;
66  processingTime_ = processingTime;
67  NotifyProcessingTime();
68 }
69 
70 void MinosResultModel::PushData(const MinosResultData & data)
71 {
72  std::unique_lock<std::mutex> guard(resultMutex_);
73  resultQue_.push_back(data);
74 }
75 
76 void 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 
93 void MinosResultModel::SetSearchResultText(const QString searchResultText)
94 {
95  if (searchResultText_ == searchResultText)
96  return;
97  searchResultText_ = searchResultText;
98  NotifySearchResultText();
99 }
100 
101 
102 
103 
bool isValid() const const
Controller object for the QML image view item.
Definition: image_view_item.hpp:252
int row() const const
QString CvbToQt(const Cvb::String &text) noexcept
Convenience converter for strings.
Definition: ui.hpp:254

The main QML:

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

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