Common Vision Blox 15.0
Loading...
Searching...
No Matches
Minos/Cvb++/QmlMinos

This example program is located in your CVB installation under %CVB%Tutorial/Minos/Cvb++/QmlMinos.

main.cpp:

// The QmlMinos example shows OCR reading in QML environment.
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
#include <iostream>
#include <QGuiApplication>
#include <QIcon>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQuickView>
#include <cvb/minos/classifier.hpp>
#include <cvb/minos/search_result.hpp>
#include <cvb/ui/image_view_item.hpp>
#include "minos_result_model.hpp"
#include "minos_search.hpp"
int main(int argc, char *argv[]) {
try {
QGuiApplication app(argc, argv);
app.setWindowIcon(QIcon(":/qttutorial.png"));
// open device
auto device = Cvb::DeviceFactory::Open(
Cvb::InstallPath() + Cvb::String(CVB_LIT("drivers/CVMock.vin")),
Cvb::AcquisitionStack::Vin);
// setup QML interface objects
Cvb::UI::ImageController imageController;
MinosResultModel minosResultModel(imageController);
// main search object
MinosSearch minosSearch(device->Stream(), minosResultModel);
// register QML components for an image display
Cvb::UI::ImageViewItem::Register();
Cvb::UI::ImageLabelItem::Register();
QQmlApplicationEngine engine;
auto context = engine.rootContext();
// create a controller object to communicate with QML
context->setContextProperty("mainImage", &imageController);
// create a Minos result model to communicate with QML for overlays
context->setContextProperty("minosResultModel", &minosResultModel);
// create a Minos search object to communicate with QML (grab, snap)
context->setContextProperty("minosSearch", &minosSearch);
// load main QML file
engine.load(QUrl::fromLocalFile("../main.qml"));
// do a first snap at startup
minosSearch.Snap();
return app.exec();
} catch (const std::exception &error) {
std::cout << error.what() << std::endl;
}
}
static std::shared_ptr< T > Open(const String &provider, AcquisitionStack acquisitionStack=AcquisitionStack::PreferVin)
std::string String
String InstallPath()

minos_result_model.cpp:

#include "minos_result_model.hpp"
#include <QRect>
#include <cvb/ui/ui.hpp>
MinosResultModel::MinosResultModel(Cvb::UI::ImageController &imageController)
: QAbstractListModel(), imageController_(imageController) {}
QHash<int, QByteArray> MinosResultModel::roleNames() const {
QHash<int, QByteArray> names;
names[LineText] = "lineText";
names[StartPosition] = "startPosition";
return names;
}
int MinosResultModel::rowCount(const QModelIndex &) const {
return static_cast<int>(currentResult_.List.size());
}
QVariant MinosResultModel::data(const QModelIndex &index, int role) const {
if (!index.isValid())
return QVariant();
const auto &result = currentResult_.List[index.row()];
switch (role) {
default:
return QVariant();
case StartPosition:
return QVariant(Cvb::UI::CvbToQt(result.Position));
case LineText:
return QVariant(Cvb::UI::CvbToQt(result.Text));
}
}
QString MinosResultModel::SearchResultText() const { return searchResultText_; }
double MinosResultModel::ProcessingTime() const noexcept {
return processingTime_;
}
void MinosResultModel::SetProcessingTime(double processingTime) {
if (processingTime_ == processingTime)
return;
processingTime_ = processingTime;
NotifyProcessingTime();
}
void MinosResultModel::PushData(const MinosResultData &data) {
std::unique_lock<std::mutex> guard(resultMutex_);
resultQue_.push_back(data);
}
void MinosResultModel::Refresh() {
{
std::unique_lock<std::mutex> guard(resultMutex_);
if (!resultQue_.size())
return;
currentResult_ = resultQue_.back();
resultQue_.clear();
}
layoutChanged();
for (const auto &result : currentResult_.List)
stream << result.Text << CVB_LIT("\n");
SetSearchResultText(Cvb::UI::CvbToQt(stream.str()));
imageController_.Refresh(currentResult_.Image);
}
void MinosResultModel::SetSearchResultText(const QString searchResultText) {
if (searchResultText_ == searchResultText)
return;
searchResultText_ = searchResultText;
NotifySearchResultText();
}
QString CvbToQt(const Cvb::String &text) noexcept
std::stringstream StringStream

minos_result_model.hpp:

#pragma once
#include <deque>
#include <mutex>
#include <QAbstractListModel>
#include <cvb/minos/search_result.hpp>
#include <cvb/ui/image_view_item.hpp>
struct MinosResultItem {
};
struct MinosResultData {
std::vector<MinosResultItem> List;
};
class MinosResultModel : public QAbstractListModel {
Q_OBJECT
Q_PROPERTY(
double processingTime READ ProcessingTime NOTIFY NotifyProcessingTime);
Q_PROPERTY(QString searchResultText READ SearchResultText NOTIFY
NotifySearchResultText);
friend class MinosSearch;
enum MinosResultRoles {
LineText = Qt::UserRole,
StartPosition = Qt::UserRole + 1
};
public:
explicit MinosResultModel(Cvb::UI::ImageController &imageController);
private Q_SLOTS:
void Refresh();
private:
QString SearchResultText() const;
double ProcessingTime() const noexcept;
void SetProcessingTime(double processingTime);
void PushData(const MinosResultData &data);
QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex & = QModelIndex()) const override;
QVariant data(const QModelIndex &index,
int role = Qt::DisplayRole) const override;
void SetSearchResultText(const QString searchResultText);
private:
Cvb::UI::ImageController &imageController_;
std::mutex resultMutex_;
std::deque<MinosResultData> resultQue_;
MinosResultData currentResult_;
QString searchResultText_;
double processingTime_ = std::numeric_limits<double>::quiet_NaN();
Q_SIGNALS:
void NotifyProcessingTime();
void NotifySearchResultText();
};
void Refresh(void)
__int3264 Image
std::shared_ptr< Image > ImagePtr

minos_search.cpp:

#include "minos_search.hpp"
#include <QQmlComponent>
#include <QQmlEngine>
#include <cvb/area_2d.hpp>
#include <cvb/point_2d.hpp>
#include <cvb/ui/ui.hpp>
// Global params
const double FIRST_AREA_RADIUS = 8.0;
const double LINE_RADIUS = 4;
const double WORD_HEIGHT = 4;
const double WORD_WIDTH = 4;
const double LINE_STEP_RADIUS = 8;
const Cvb::Point2D<double> LINE_STEP = Cvb::Point2D<double>(0.0, 16.0);
const Cvb::Area2D OCR_AOI = Cvb::Area2D(-3.0, -3.0, 3.0, -3.0, -3.0, 3.0);
MinosSearch::MinosSearch(const Cvb::StreamPtr &stream, MinosResultModel &model)
: QObject(), SingleStreamHandler(stream), stream_(stream),
classifier_(Cvb::InstallPath() +
Cvb::String(CVB_LIT(
"tutorial/Minos/Images/OCR/Training Set/Numbers.clf"))),
model_(model), stopWatch_(Cvb::StopWatchMode::MultiCPU) {}
MinosSearch::~MinosSearch() { TryFinish(); }
void MinosSearch::StartGrab() { Run(); }
void MinosSearch::StopGrab() { Finish(); }
void MinosSearch::Snap() {
auto waitResult = stream_->GetTimedSnapshot(std::chrono::seconds(1));
if (waitResult.Status != Cvb::WaitStatus::Ok)
return;
// Minos search in snap
MinosResultData result;
stopWatch_.Start();
result.List = Search(waitResult.Image->Plane(0));
model_.SetProcessingTime(stopWatch_.TimeSpan().count());
result.Image = waitResult.Image;
model_.PushData(result);
model_.Refresh();
}
// The Minos search
std::vector<MinosResultItem> MinosSearch::Search(const Cvb::ImagePlane &plane) {
auto width = static_cast<double>(plane.Parent().Width());
auto height = static_cast<double>(plane.Parent().Height());
// STEP 1 finding the top most line
// create an area centered in horizontal direction
// size of the area is FIRST_AREA_WIDTH = ImageHeight
// the direction of the area is set to the bottom
// create an area from these values
Cvb::Area2D searchAoi(
Cvb::Point2D<double>(width / 2 - FIRST_AREA_RADIUS, 0.0),
Cvb::Point2D<double>(width / 2 + FIRST_AREA_RADIUS, 0.0),
Cvb::Point2D<double>(width / 2 - FIRST_AREA_RADIUS, height - 1.0));
// search for the first object
auto searchResult =
classifier_.Search(plane, Cvb::Minos::SearchMode::FindFirst, searchAoi);
std::vector<MinosResultItem> resultList;
if (searchResult.Quality() < 0.1)
return resultList;
// now we found the first line of the text
// STEP 2 finding the beginning of a line
// try to find the first char in the line
// create an area centered around the first line
// beginning at the left side of the image
// size of the area is LINE_HEIGHT * dXPos
// the direction of the area is set to the right
// create an area from these values
auto position = searchResult.Position();
Cvb::Area2D lineAoi(
Cvb::Point2D<double>(0.0, position.Y() - LINE_RADIUS),
Cvb::Point2D<double>(0.0, position.Y() + LINE_RADIUS),
Cvb::Point2D<double>(position.X(), position.Y() - LINE_RADIUS));
// do a line by line search
SearchLine(plane, lineAoi, resultList);
return resultList;
}
// Minos search per line
void MinosSearch::SearchLine(const Cvb::ImagePlane &plane, Cvb::Area2D lineAoi,
std::vector<MinosResultItem> &resultList) {
// search for the first object
auto searchResult =
classifier_.Search(plane, Cvb::Minos::SearchMode::FindFirst, lineAoi);
if (searchResult.Quality() == 0.0)
return;
// now we found the first char in the first line
// STEP 3 read the string
// save the x and y position of the line
// try to read the first word
// create an area left aligned the first char
// size of WORD_WIDTH * WORD_HEIGHT
// create an area from these values
auto lineStart = searchResult.Position();
Cvb::Area2D readAoi(
Cvb::Point2D<double>(lineStart.X(), lineStart.Y() - WORD_HEIGHT / 2),
Cvb::Point2D<double>(lineStart.X() + WORD_WIDTH / 2,
lineStart.Y() - WORD_HEIGHT / 2),
Cvb::Point2D<double>(lineStart.X(), lineStart.Y() + WORD_HEIGHT / 2));
// read the word
auto readResult = classifier_.Read(plane, readAoi, OCR_AOI);
// save the result
for (const auto c : readResult)
stream << c.Name();
MinosResultItem resultItem = {lineStart, stream.str()};
resultList.push_back(resultItem);
lineStart += LINE_STEP;
// STEP 4 jump to the next line
// try to read the first word
// create an area left aligned the first char
// size of WORD_WIDTH * WORD_HEIGHT
// create an area from these values
Cvb::Area2D nextLineAoi(
Cvb::Point2D<double>(lineStart.X() - LINE_STEP_RADIUS,
lineStart.Y() - LINE_STEP_RADIUS),
Cvb::Point2D<double>(lineStart.X() + LINE_STEP_RADIUS,
lineStart.Y() - LINE_STEP_RADIUS),
Cvb::Point2D<double>(lineStart.X() - LINE_STEP_RADIUS,
lineStart.Y() + LINE_STEP_RADIUS));
// next line (recursion should - no to deep)
SearchLine(plane, nextLineAoi, resultList);
}
void MinosSearch::HandleAsyncStream(const Cvb::StreamPtr &stream) {
auto waitResult =
stream->WaitFor<Cvb::RingBufferImage>(std::chrono::seconds(1));
if (waitResult.Status == Cvb::WaitStatus::Ok) {
// Minos search in grab
MinosResultData result;
stopWatch_.Start();
result.List = Search(waitResult.Image->Plane(0));
model_.SetProcessingTime(stopWatch_.TimeSpan().count());
result.Image = waitResult.Image;
model_.PushData(result);
// Tell the UI thread to redraw and present the results
QMetaObject::invokeMethod(&model_, "Refresh", Qt::QueuedConnection);
}
}
const Image & Parent() const noexcept
std::shared_ptr< Stream > StreamPtr

minos_search.hpp:

#include <QObject>
#include <QVariant>
#include <cvb/async/single_stream_handler.hpp>
#include <cvb/device_factory.hpp>
#include <cvb/minos/classifier.hpp>
#include <cvb/ui/image_view_item.hpp>
#include <cvb/utilities/stop_watch.hpp>
#include "minos_result_model.hpp"
class MinosSearch final : public QObject,
Q_OBJECT
public:
MinosSearch(const Cvb::StreamPtr &stream, MinosResultModel &model);
~MinosSearch();
std::vector<MinosResultItem> Search(const Cvb::ImagePlane &plane);
public Q_SLOTS:
void StartGrab();
void StopGrab();
void Snap();
private:
void SearchLine(const Cvb::ImagePlane &plane, Cvb::Area2D aoi,
std::vector<MinosResultItem> &resultList);
void HandleAsyncStream(const Cvb::StreamPtr &stream) override;
Cvb::StreamPtr stream_;
MinosResultModel &model_;
};
virtual void HandleAsyncStream(const Driver::StreamPtr &stream)
cvbres_t Snap(IMG Image)