Qt C++与Halcon联合开发实现简易OCR字符识别

效果图如下:
原图像
菜单栏点击读取的图片
识别后的图像(窗体左上角为识别的字符)
菜单栏点击字符读取后显示的图片(左上角为识别的字符)
一、开发平台搭建:
本人用开发平台如下:
1> Visual Studio 2017
2> Qt5.12(包含MVSC编译器)
3> Halcon18.05-64Bit

二、Qt项目的创建
1>新建Qt Widget项目>>选择MainWindow类>>选择MSVC2017编译器进行源码编译。
编译器选择
2>打开Halcon的安装目录,找到并拷贝lib和include文件夹到所创建的Qt项目文件夹中。
Qt项目文件夹
3>在Qt项目.pro文件夹中添加如下代码,用于包含与Halcon相关的C++头文件和lib库。
.pro文件
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(), &centerRow, &centerCol);

    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)

四、用到的字符图片
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值