Common Vision Blox 15.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Friends Modules Pages
Minos/CVBpy/QmlMinos

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

main.py:

# @brief Example for OCR reading with Minos in QML environment.
import os, sys
import cvb
import cvb.ui
from minos_result_model import MinosResultModel
from minos_search import MinosSearch
if sys.version_info >= (3, 11):
from PySide6.QtCore import QObject, QUrl, QAbstractListModel, Qt, QModelIndex
from PySide6.QtQml import QQmlApplicationEngine, qmlRegisterType
from PySide6.QtGui import QGuiApplication
from PySide6.QtGui import QIcon
else:
from PySide2.QtCore import QObject, QUrl, QAbstractListModel, Qt, QModelIndex
from PySide2.QtQml import QQmlApplicationEngine, qmlRegisterType
from PySide2.QtGui import QGuiApplication
from PySide2.QtGui import QIcon
if __name__ == "__main__":
app = QGuiApplication([])
app.setOrganizationName('STEMMER IMAGING')
app.setOrganizationDomain('https://www.stemmer-imaging.com/')
app.setApplicationName('Minos Python tutorial')
# tell Windows the correct AppUserModelID for this process (shows icon in the taskbar)
if sys.platform == 'win32':
import ctypes
myappid = u'stemmerimaging.commonvisionblox.pyminos.0'
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
app.setWindowIcon(QIcon('Tutorial-Python_32x32.png'))
# open device
os.path.join(cvb.install_path(), "drivers", "CVMock.vin"),
cvb.AcquisitionStack.Vin) as device:
# setup QML interface objects
image_controller = cvb.ui.ImageController()
minos_result_model = MinosResultModel(image_controller)
# main search object
minos_search = MinosSearch(device.stream(), minos_result_model)
# register QML components for an image display
engine = QQmlApplicationEngine()
context = engine.rootContext()
# create a controller object to communicate with QML
context.setContextProperty("mainImage", image_controller)
# create a Minos result model to communicate with QML for overlays
context.setContextProperty("minosResultModel", minos_result_model)
# create a Minos search object to communicate with QML (grab, snap)
context.setContextProperty("minosSearch", minos_search)
# load main QML file
engine.load(os.path.join(os.path.dirname(os.path.abspath(__file__)), "main.qml"))
# do a first snap at startup
minos_search.snap()
app.exec_()
Union[cvb.GenICamDevice, cvb.VinDevice, cvb.EmuDevice, cvb.VideoDevice, cvb.NonStreamingDevice] open(str provider, int acquisition_stack=cvb.AcquisitionStack.PreferVin)
None register(cls, str uri="CvbQuick", int version_major=1, int version_minor=0, str qml_name="ImageLabel")
None register(cls, str uri="CvbQuick", int version_major=1, int version_minor=0, str qml_name="ImageView")
str install_path()

minos_result_model.py:

import os, sys
import cvb
import cvb.ui
if sys.version_info >= (3, 11):
from PySide6.QtCore import QObject, QAbstractListModel, Qt, QModelIndex, Property, Signal, Slot
else:
from PySide2.QtCore import QObject, QAbstractListModel, Qt, QModelIndex, Property, Signal, Slot
class MinosResultData(object):
def __init__(self):
self.image = None
self.list = []
class MinosResultModel(QAbstractListModel):
LineText = Qt.UserRole
StartPosition = Qt.UserRole + 1
def __init__(self, image_controller , parent=None):
super(MinosResultModel, self).__init__(parent)
self._image_controller = image_controller
self._result_queue = [None] * 0
self._current_result = MinosResultData()
self._text = ""
self._processing_time = 0.0
self.notify_refresh.connect(self.refresh)
def roleNames(self):
roles = dict()
roles[MinosResultModel.LineText] = b"lineText"
roles[MinosResultModel.StartPosition] = b"startPosition"
return roles
def rowCount(self, parent = QModelIndex()):
return len(self._current_result.list)
def data(self, index, role = Qt.DisplayRole):
if not index.isValid():
return None
position, text = self._current_result.list[index.row()]
if role == MinosResultModel.LineText:
return text
elif role == MinosResultModel.StartPosition:
return cvb.ui.cvb_to_qt_point(position)
else:
return None
def push_data(self, data):
self._result_queue.append(data)
self.notify_refresh.emit()
@Slot()
def refresh(self):
if (len(self._result_queue) == 0):
return None
self._current_result = self._result_queue.pop()
self._result_queue.clear()
# refresh the model
self.layoutChanged.emit()
# refresh the text area
stream = ""
for i in range(len(self._current_result.list)):
stream += (self._current_result.list[i][1] + '\n')
self.set_search_result_text(stream)
# refresh the image
self._image_controller.refresh(self._current_result.image)
def set_search_result_text(self, text):
if (self._text != text):
self._text = text
self.notify_search_result_text.emit()
def set_processing_time(self, processing_time):
if (self._processing_time != processing_time):
self._processing_time = processing_time
self.notify_processing_time.emit()
def search_result_text(self):
return self._text
def processing_time(self):
return self._processing_time
notify_search_result_text = Signal()
notify_processing_time = Signal()
notify_refresh = Signal()
searchResultText = Property(str, search_result_text, notify=notify_search_result_text)
processingTime = Property(float, processing_time, notify=notify_processing_time)
Union[PySide2.QtCore.QPointF, PySide6.QtCore.QPointF] cvb_to_qt_point(cvb.Point2D point)

minos_search.py:

import os, sys
import cvb
import cvb.minos
if sys.version_info >= (3, 11):
from PySide6 import QtCore
from PySide6.QtCore import QObject, Qt, Slot
else:
from PySide2 import QtCore
from PySide2.QtCore import QObject, Qt, Slot
from minos_result_model import MinosResultModel, MinosResultData
# Global params
FIRST_AREA_RADIUS = 8.0
LINE_RADIUS = 4
WORD_HEIGHT = 4
WORD_WIDTH = 4
LINE_STEP_RADIUS = 8
LINE_STEP = cvb.Point2D(0.0, 16.0)
OCR_AOI = cvb.Area2D(cvb.Point2D(-3.0, -3.0), cvb.Point2D(3.0, -3.0), cvb.Point2D(-3.0, 3.0))
class MinosSearch(QtCore.QObject):
def __init__(self, stream, minos_result_model):
super().__init__()
self._stream = stream
self._model = minos_result_model
self._classifier = cvb.minos.Classifier(os.path.join(cvb.install_path(), "tutorial", "Minos", "Images", "OCR", "Training Set", "Numbers.clf"))
@Slot()
def snap(self):
image, wait_status = self._stream.get_timed_snapshot(1000)
if (wait_status != cvb.WaitStatus.Ok):
return None
# Minos search in snap
result = MinosResultData()
result.image = image
stop_watch = cvb.StopWatch()
stop_watch.start()
result.list = self.search(image.planes[0])
self._model.set_processing_time(stop_watch.time_span)
self._model.push_data(result)
# The Minos search
def search(self, plane):
width = plane.map().width
height = plane.map().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
search_aoi = cvb.Area2D(cvb.Point2D(width / 2 - FIRST_AREA_RADIUS, 0.0), cvb.Point2D(width / 2 + FIRST_AREA_RADIUS, 0.0), cvb.Point2D(width / 2 - FIRST_AREA_RADIUS, height - 1.0))
# search for the first object
search_result = self._classifier.search(plane, cvb.minos.SearchMode.FindFirst, search_aoi)
if (search_result.quality < 0.1):
return []
# 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
position = search_result.position
line_aoi = cvb.Area2D(cvb.Point2D(0.0, position.y - LINE_RADIUS), cvb.Point2D(0.0, position.y + LINE_RADIUS),cvb.Point2D(position.x, position.y - LINE_RADIUS))
# do a line by line search
result_list = [None] * 0
self.search_line(plane, line_aoi, result_list)
return result_list
# Minos search per line
def search_line(self, plane, line_aoi, result_list):
# search for the first object
search_result = self._classifier.search(plane, cvb.minos.SearchMode.FindFirst, line_aoi)
if (search_result.quality == 0.0):
return None
# 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
line_start = search_result.position
read_aoi = cvb.Area2D(cvb.Point2D(line_start.x, line_start.y - WORD_HEIGHT / 2), cvb.Point2D(line_start.x + WORD_WIDTH / 2,line_start.y - WORD_HEIGHT / 2), cvb.Point2D(line_start.x, line_start.y + WORD_HEIGHT / 2))
# read the word
search_result = self._classifier.read(plane, read_aoi, OCR_AOI)
# save the result
stream = ""
for i in range(len(search_result)):
stream += search_result[i].name
result = (line_start, stream)
result_list.append(result)
line_start += 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
next_line_aoi = cvb.Area2D(cvb.Point2D(line_start.x - LINE_STEP_RADIUS, line_start.y - LINE_STEP_RADIUS), cvb.Point2D(line_start.x + LINE_STEP_RADIUS, line_start.y - LINE_STEP_RADIUS), cvb.Point2D(line_start.x - LINE_STEP_RADIUS, line_start.y + LINE_STEP_RADIUS))
# next line (recursion should - no to deep)
self.search_line(plane, next_line_aoi, result_list)