效果图如下:
原图像
识别后的图像(窗体左上角为识别的字符)
一、开发平台搭建:
本人用开发平台如下:
1> Visual Studio 2017
2> Qt5.12(包含MVSC编译器)
3> Halcon18.05-64Bit
二、Qt项目的创建
1>新建Qt Widget项目>>选择MainWindow类>>选择MSVC2017编译器进行源码编译。
2>打开Halcon的安装目录,找到并拷贝lib和include文件夹到所创建的Qt项目文件夹中。
3>在Qt项目.pro文件夹中添加如下代码,用于包含与Halcon相关的C++头文件和lib库。
4>打开Qt项目中的.ui文件,创建一个QWidget控件至主界面,在菜单栏添加采集和字符识别按钮。
三、 程式编写
1.Qt C++代码:
1> 创建Halcon窗体相关的类(该类源码参考Halcon自带的Qt开发例程):
QHalconWindow类:
头文件:
#ifndef QHALCONWINDOW_H
#define QHALCONWINDOW_H
#include <QWidget>
#include <QPainter>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QScopedPointer>
#include "HalconCpp.h"
using namespace HalconCpp;
class QHalconWindow : public QWidget
{
Q_OBJECT
public:
explicit QHalconWindow(QWidget *parent = nullptr, long width = 0, long height = 0);
HalconCpp::HWindow* GetHalconBuffer(void) { return halconBuffer.data(); }
protected:
void resizeEvent(QResizeEvent *event);
void paintEvent(QPaintEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void mouseDoubleClickEvent(QMouseEvent *event);
void wheelEvent(QWheelEvent *event);
private:
void GetPartFloat(double *row1, double *col1, double *row2, double *col2);
void SetPartFloat(double row1, double col1, double row2, double col2);
QScopedPointer<HalconCpp::HWindow> halconBuffer;
QPoint lastMousePos;
double lastRow1, lastCol1, lastRow2, lastCol2;
};
#endif // QHALCONWINDOW_H
源文件:
#include "qhalconwindow.h"
Herror __stdcall ContentUpdateCallback(void* context)
{
QHalconWindow* hwindow = (QHalconWindow*)context;
hwindow->update();
return H_MSG_OK;
}
QHalconWindow::QHalconWindow(QWidget *parent, long width, long height) : QWidget(parent)
{
show();
resize(width, height);
halconBuffer.reset(new HalconCpp::HWindow(0, 0, 200, 100, 0, "buffer", ""));
halconBuffer->SetWindowParam("graphics_stack", "true");
halconBuffer->SetWindowParam("flush", "false");
halconBuffer->SetContentUpdateCallback((void*)&ContentUpdateCallback, this);
}
void QHalconWindow::resizeEvent(QResizeEvent *event)
{
Q_UNUSED(event);
halconBuffer->SetWindowExtents(0, 0, width(), height());
halconBuffer->FlushBuffer();
}
void QHalconWindow::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
HString type;
Hlong width, height;
HImage image = halconBuffer->DumpWindowImage();
HImage imageInterleaved = image.InterleaveChannels("argb", "match", 0);
unsigned char* pointer = (unsigned char*)imageInterleaved.GetImagePointer1(&type, &width, &height);
QImage qimage(pointer, width/4, height, QImage::Format_RGB32);
QPainter painter(this);
painter.drawImage(QPoint(0, 0), qimage);
}
void QHalconWindow::mouseMoveEvent(QMouseEvent *event)
{
Q_UNUSED(event);
if ((event->buttons() == Qt::LeftButton) && lastMousePos.x() != -1)
{
QPoint delta = lastMousePos - event->globalPos();
double scalex = (lastCol2 - lastCol1 + 1) / (double)width();
double scaley = (lastRow2 - lastRow1 + 1) / (double)height();
try
{
SetPartFloat(lastRow1 + (delta.y() * scaley),
lastCol1 + (delta.x() * scalex),
lastRow2 + (delta.y() * scaley),
lastCol2 + (delta.x() * scalex));
halconBuffer->FlushBuffer();
}
catch (HalconCpp::HOperatorException)
{
}
}
}
void QHalconWindow::mousePressEvent(QMouseEvent *event)
{
Q_UNUSED(event);
GetPartFloat(&lastRow1, &lastCol1, &lastRow2, &lastCol2);
lastMousePos = event->globalPos();
}
void QHalconWindow::mouseReleaseEvent(QMouseEvent *event)
{
Q_UNUSED(event);
lastMousePos = QPoint(-1, -1);
}
void QHalconWindow::mouseDoubleClickEvent(QMouseEvent *event)
{
Q_UNUSED(event);
if (event->buttons() == Qt::LeftButton)
{
halconBuffer->SetPart(0, 0, -1, -1);
halconBuffer->FlushBuffer();
}
}
void QHalconWindow::wheelEvent(QWheelEvent *event)
{
Q_UNUSED(event);
int num_notch = std::abs(event->delta()) / 120;
double factor = (event->delta() > 0) ? std::sqrt(2.0) : 1.0 / std::sqrt(2.0);
while (num_notch > 1)
{
factor = factor * ((event->delta() > 0) ? std::sqrt(2.0) : 1.0 / std::sqrt(2.0));
num_notch--;
}
double centerRow, centerCol;
halconBuffer->ConvertCoordinatesWindowToImage(event->y(), event->x(), ¢erRow, ¢erCol);
double row1, col1, row2, col2;
GetPartFloat(&row1, &col1, &row2, &col2);
double left = centerRow - row1;
double right = row2 - centerRow;
double top = centerCol - col1;
double buttom = col2 - centerCol;
double newRow1 = centerRow - left * factor;
double newRow2 = centerRow + right * factor;
double newCol1 = centerCol - top * factor;
double newCol2 = centerCol + buttom * factor;
try
{
SetPartFloat(newRow1, newCol1, newRow2, newCol2);
halconBuffer->FlushBuffer();
}
catch (HalconCpp::HOperatorException)
{
}
}
void QHalconWindow::GetPartFloat(double *row1, double *col1, double *row2, double *col2)
{
HalconCpp::HTuple trow1, tcol1, trow2, tcol2;
halconBuffer->GetPart(&trow1, &tcol1, &trow2, &tcol2);
*row1 = trow1.D();
*col1 = tcol1.D();
*row2 = trow2.D();
*col2 = tcol2.D();
}
void QHalconWindow::SetPartFloat(double row1, double col1, double row2, double col2)
{
halconBuffer->SetPart(HalconCpp::HTuple(row1), HalconCpp::HTuple(col1),
HalconCpp::HTuple(row2), HalconCpp::HTuple(col2));
}
2>项目中的MainWindow类:
MainWindow类:
头文件:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QVBoxLayout>
#include <QDir>
#include <QFile>
#include <QFileDialog>
#include <QByteArray>
#include "qhalconwindow.h"
#include "HalconCpp.h"
using namespace HalconCpp;
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_actionGrabImage_triggered();
void on_actionFindChars_triggered();
private:
Ui::MainWindow *ui;
QHalconWindow* m_pDisp;
QVBoxLayout* m_pVBox;
HImage m_Image;
Hlong m_WinWidth, m_WinHeight, m_ImgWidth, m_ImgHeight;
};
#endif // MAINWINDOW_H
源文件:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowTitle("OCR");
//实例化QVbox、HalconWindow,添加HalconWindow到ui的QWidget控件中
m_pVBox = new QVBoxLayout(ui->mainDisplay);
m_pDisp = new QHalconWindow(ui->mainDisplay);
m_pVBox->addWidget(m_pDisp);
}
MainWindow::~MainWindow()
{
delete m_pDisp;
delete m_pVBox;
delete ui;
}
void MainWindow::on_actionGrabImage_triggered()
{
//选择图片,将图片转化为HTuple类型
QString currentDir = QDir::currentPath();
QString fileImage = QFileDialog::getOpenFileName(this, "Select Image...", currentDir);
QByteArray byteArray = fileImage.toUtf8();
const char* pImageData = byteArray.data();
HTuple imageData(pImageData);
//读取并显示图片
ReadImage(&m_Image, imageData);
m_Image.GetImageSize(&m_ImgWidth, &m_ImgHeight);
m_pDisp->GetHalconBuffer()->SetPart(0, 0, m_ImgHeight-1, m_ImgWidth-1);
m_pDisp->GetHalconBuffer()->SetLineWidth(3);
m_pDisp->GetHalconBuffer()->DispObj(m_Image);
m_pDisp->GetHalconBuffer()->FlushBuffer();
}
void MainWindow::on_actionFindChars_triggered()
{
//执行Halcon的C++相关操作语句,读取图片中的字符
HTuple Row, Column, Phi, Length1, Length2, Area, Row1, Column1, HomMat2D, Sum;
HTuple TextModel, TextResultID, Row2, Column2, Phi1, Length11, Length21, ResultValue;
HObject Regions, RegionClosing, ConnectedRegions, SelectedRegions, RegionUnion;
HObject Rectangle, ImageAffineTrans, RegionAffineTrans, ImageReduced, Characters;
Threshold(m_Image, &Regions, 3, 100);
ClosingRectangle1(Regions, &RegionClosing, 3, 3);
Connection(RegionClosing, &ConnectedRegions);
SelectShape(ConnectedRegions, &SelectedRegions, (HTuple("height").Append("width")),
"and", (HTuple(42.431).Append(25.367)), (HTuple(47.661).Append(31.514)));
Union1(SelectedRegions, &RegionUnion);
SmallestRectangle2(RegionUnion, &Row, &Column, &Phi, &Length1, &Length2);
GenRectangle2(&Rectangle, Row, Column, Phi, Length1, Length2);
AreaCenter(Rectangle, &Area, &Row1, &Column1);
VectorAngleToRigid(Row1, Column1, Phi, m_ImgHeight/2, m_ImgWidth/2, 0, &HomMat2D);
AffineTransImage(m_Image, &ImageAffineTrans, HomMat2D, "constant", "false");
AffineTransRegion(Rectangle, &RegionAffineTrans, HomMat2D, "nearest_neighbor");
ReduceDomain(ImageAffineTrans, RegionAffineTrans, &ImageReduced);
m_pDisp->GetHalconBuffer()->ClearWindow();
m_pDisp->GetHalconBuffer()->FlushBuffer();
m_pDisp->GetHalconBuffer()->DispObj(ImageReduced);
m_pDisp->GetHalconBuffer()->FlushBuffer();
CreateTextModelReader("auto", "Universal_Rej.occ", &TextModel);
FindText(ImageReduced, TextModel, &TextResultID);
GetTextObject(&Characters, TextResultID, "all_lines");
SmallestRectangle2(RegionAffineTrans, &Row2, &Column2, &Phi1, &Length11,
&Length21);
GetTextResult(TextResultID, "class", &ResultValue);
TupleSum(ResultValue, &Sum);
m_pDisp->GetHalconBuffer()->DispText(Sum, "window", "top", "left", "black", "box", "true");
m_pDisp->GetHalconBuffer()->FlushBuffer();
}
2、Halcon代码:
//*打开窗体、读取图像
dev_close_window ()
read_image (Image, 'C:/OCR/Image 08.bmp')
get_image_size (Image, Width, Height)
dev_open_window (0, 0, Width, Height, 'black', WindowHandle)
dev_display (Image)
//*获取字符区域、计算区域中心位置和区域的角度
threshold (Image, Regions, 3, 100)
closing_rectangle1 (Regions, RegionClosing, 3, 3)
connection (RegionClosing, ConnectedRegions)
select_shape (ConnectedRegions, SelectedRegions, ['height','width'], 'and', [42.431,25.367], [47.661,31.514])
union1 (SelectedRegions, RegionUnion)
smallest_rectangle2 (RegionUnion, Row, Column, Phi, Length1, Length2)
gen_rectangle2 (Rectangle, Row, Column, Phi, Length1, Length2)
area_center (Rectangle, Area, Row1, Column1)
//*将图片中的字符区域移动到窗体中央
vector_angle_to_rigid (Row1, Column1, Phi, Height/2, Width/2, 0, HomMat2D)
affine_trans_image (Image, ImageAffineTrans, HomMat2D, 'constant', 'false')
affine_trans_region (Rectangle, RegionAffineTrans, HomMat2D, 'nearest_neighbor')
//*显示截取的字符区域
reduce_domain (ImageAffineTrans, RegionAffineTrans, ImageReduced)
dev_display (ImageReduced)
//*读取字符
create_text_model_reader ('auto', 'Universal_Rej.occ', TextModel)
find_text (ImageReduced, TextModel, TextResultID)
get_text_object (Characters, TextResultID, 'all_lines')
smallest_rectangle2 (RegionAffineTrans, Row2, Column2, Phi1, Length11, Length21)
get_text_result (TextResultID, 'class', ResultValue)
tuple_sum (ResultValue, Sum)
四、用到的字符图片