如何使用QT框架进行算子SDK二次开发
QT 作为一个界面框架,被越来越多的应用软件的开发者青睐,其主要优势是跨平台,除了跨平台的优势就是它是完全面向对象的,这一点比MFC要好很多,可以看到越来越来的开发者开始使用QT做C++界面开发,本文介绍如果在QT框架中使用海康机器视觉算子包SDK进行二次开发。 海康机器视觉算子SDK是一个封装程度非常高的机器视觉API集合,它包含了诸如模板匹配,几何查找,几何测量,读码,字符识别,深度学习等模块,开发者调用这些模块并不需要具有深厚的算法基础(可以说是零基础,门外汉也能开发视觉应用),开发者也不需要了解具体的算法实现就能完成视觉应用的开发。

如何使用QT框架进行算子SDK二次开发

使用QT进行算子SDK二次开发需要了解那些知识?

1.1          首先需要了解SDK的安装目录下各个文件的作用。

用户在安装完Vision Master软件之后,默认会在C盘Program Files(x86)目录下安装算子开发包,典型的路径是这样的:

C:\Program Files (x86)\MVDAlgorithmSDK

在此目录下,应该会看到以下文件夹

下面分别讲一下图中各个文件夹的作用。

1.1.1 头文件和静态库

使用C++语言进行算子SDK二次开发,最为常见的方式是使用静态库.lib加头文件.h的方式,因此Includes和Libraries这两个文件夹是我们所关心的。后面在开发环境配置中会重点介绍着两个文件夹如何引用。

1.1.2 C#需要引用的库

其中ReferencedAssemblies文件夹中用户也可以发现大量的DLL,这些DLL是使用C#语言进行算子二次开发才需要用到的,对于C++开发者来说不需要关心,所以不必关注 。

1.1.3 运行时依赖库

Runtime文件夹非常重要,这是使用算子SDK开发的应用程序运行时需要依赖的动态库,缺少它您开发的应用程序将无法运行,如果您想要在一台没有安装算子SDK软件包的电脑上运行您的二次开发程序,您可以将Runtime文件下的x64文件夹(如果是32位系统,就是win32文件夹)下的所有文件复制到你开发的exe所在目录即可。对于在一台已经安装了算子SDK开发包的电脑上运行您开发的视觉应用程序,则完全不必进行前面所说的操作,因为Runtime的路径已经写到环境变量中,程序运行时依赖的动态库会首先从环境变量中查找。

1.1.4 帮助文件

  Documents文件夹存放的时开发需要查询的开发的帮助文档,这个文档对于视觉应用开发者来说是非常重要的,算子类库的用法和各种内置数据类型可以通过查询文档知道。

Samples文件夹存放的是二次开发示例程序,如果您需要快速入门,可以直接跳到这个文件夹内查看相关的示例程序。

1.1.5 环境检测等实用工具

MVDTools文件夹存放是环境的检测工具之类的应用程序,视觉应用的二次开发不必了解这个。

1.2          具备基本的C++编程基础知识

首先,具备基础的C++编程知识,算子SDK开发需要掌握的C++知识点并不多,并不需要对C++各种知识面面俱到的了解。这里对其中涉及到的一些基础知识点做一些简单介绍,以便在二次开发过程中能很好的理解算子SDK的用法。

在算子SDK中,开发者会经常遇到I开头的类,例如IMvdImage, IMvdShape, IHPFeaturePatMatchTool , 这个IInterface的首字母,所以IMvdImage, IMvdShape等都是抽象类,是不可以直接实例化的,所以类似下面这样的语句是无法通过编译的。

IMvdImage image

你也不可以像下面这样写,也是编译通不过的:

IMvdImage *pImage = new IMvdImage();

以IMVDImage的使用举例来说,正确的做法是这样的:

//读取D盘下的图像文件testImage.bmp
IMvdImage *pImage;
CreateImageInstance(&pImage);
pImage.InitImage(“D:\\testImage.bmp”,MVD_PIXEL_MONO_08);

在算子SDK中绝大多数类都是抽象类,都不能直接实例化,需要显式的调用CreateXXXInstance(XXX指代某个具体类,如Image类),然后才能对其进行操作,否则就会出现空引用,从而引发程序异常。

1.3          了解算子SDK在底层做了哪些工作?

1.3.1 算子SDK 简化了算子的初始化流程

算子SDK的二次开发简化了算子的初始化工作。在算子初始化过程中,需要对算子进行加密校验和解密,然后根据算子的能力集进行内存分配,除此之外,还需要对常用的运行参数设置初始的默认值,这些工作对于视觉应用的开发者来说较为繁琐和重复,因此算子SDK对这些流程都做了封装,开发者只需要一行代码就可以实现算子的初始,大大降低了开发者调用底层机器视觉算法的难度和负担。

1.3.2 算子SDK简化了参数设置和参数获取

算子SDK的二次开发简化了算子的参数设置,算子的运行参数在底层实际上是对应不同的结构体,在开发者不了解这些运行参数的数据类型和结构体的情况下,想要直接设置底层算子的运行参数难度就很大。但算子SDK对这参数设置这部分也做了很好的封装,用户只需要调用算子工具的SetParam函数接口,就可以在不了解底层算子运行参数结构体的情况下,也能将参数设置进去。SetParam函数提供了两个参数,一个参数是paramName, 一个参数是paramValue,开发者只需要查阅帮助文件,找到对应工具的参数表格,就可以设置参数。例如:设置快速模板匹配工具的最小匹配得分:

FastFeatureMatchTool1.SetParam(“MinScore”,”0.65”);

同样,开发者通过GetParam接口也能很方便的获取算子工具的当前参数值。

1.3.3 算子SDK以面向对象的方式封装了算子工具

有过Halcon视觉开发经验的开发者都知道,底层算子的调用灵活性很高,但是完全是面向过程的,不能很好的做到代码的重用,用户需要自己使用C++设计模式去构建自己的算子类库,而我们的算子SDK就是封装好的算子类库,算子实例化之后,通过暴露的接口API设置其属性,调用其方法就能控制算子工具,而不需要程序自己去管理算子工具的内部状态,视觉应用的开发者只需要将更多的精力放在业务逻辑层,而不必关系算子内部是如何工作的。

算子SDK二次开发QT开发环境配置

使用Qt框架开发桌面应用程序,开发者通常有两种选择,一种是在Qt官方提供的集成开发环境QtCreator中进行开发,优势是配置引用相对简单,通过一个pro文件,qmake工具会自动生成makefile, 另外,QtCreator的代码实时纠错提醒以及高度集成的帮助文件系统,开发者遇到不了解的API只需要按下F1就能自动导航到帮助文档对应的条目,这个是很多开发者喜欢QtCreator的原因,但是QtCreator的调试工具做得确实不如VisualStudio, 调试时不如VisualStudio方便,VisualStudio可以很方便的查看但变量的值,而在QtCreator中,对于用户自定义的数据类型,往往只能看到这个变量的指针,而不能查看到具体的变量值。因此有一部分开发者选择VisualStudio作为集成开发环境,下面分别介绍这两种环境下如何配置算子SDK的二次开发。

2.1 使用QtCreator作为集成开发环境

创建Qt工程后,在pro文件中,需要添加一些配置,pro文件的典型写法如下截图所示:

如图所示,在pro文件的第1行,QT += core gui axcontainer 表示我们需要用到头文件,特别的axcontainer ,这个是算子SDK开发需要用到ActiveX控件,所以必须要添加axcontianer.

在pro文件的第22行,必须在工程的根目录下放上mvrenderactivexlib.cpp文件,pro文件的第25行,必须在工程的根目录下放上mvrenderactivexlib.h头文件。

在pro文件的第29行,定义了一个宏 MVD_INSTALL_DIR, 将算子包的安装目录指定当前工作目录的往上4级,其实就是C:/Program Files (x86)/MVDAlogrithmSDK 这个路径,由于pro文件中配置的路径不允许存在空格或者中文字符,所以用$quote( )包含起来。

在pro的第31行,指定工程引用到的头文件。这个时候我们前面定义的MVD_INSTALL_DIR就起到作用了,可以缩短头文件路径的写法。

接着我们还需要指定静态库的路径和依赖的动态库路径,如下图所示:

在pro文件的第43行,指定win64工程需要链接的动态库,写法是:

         LIBS += -L$$静态库路径 –l静态库名称

如图所示,MVDShapeCpp.lib和MVDImageCpp.lib 是必须用到的库,无论你用到什么算法工具,这个都是必不可少的,而MVDAlmightyPatMatchCpp.lib, MVDPreproMaskCpp.lib, MVDCircleFindCpp.lib 则是根据工程是否用到来决定是否添加,没有到相关的算子工具,则无需添加。

在pro文件的第52行,指定的是win32工程需要链接的静态库。

至此,pro文件就配置好了,开发者可以参照这个pro文件的写法来配置自己的工程,写法是基本一致的,只需要修改一下MVD_INSTALL_DIR这个宏指代的MVDAlgorithmSDK的安装路径就好了,然后再根据自己的需求,添加需要用到静态库(修改上面pro文件的44-47行,54-56行的内容即可)。

配置完pro文件,接着是添加用到的渲染控件,打开Qt Designer, 在窗口设计器中,把ActiveX控件的容器QAxWidget 拖到窗口中,如下图所示:

                                                 添加QAxWidget容器到窗体中

添加了ActiveX控件的容器后,我们就需要添加具体的ActiveX控件了,在窗体设计器中,对着拖进去的QAxWidget这个容器右键,选择需要加Activ控件,如下图所示:

    

在弹出右键菜单中选择“设置控件”,然后在弹出的对话框中,输入Mv,会过滤掉很多不需要选择的控件,选择MvRenderActivex Control。

好了,到了这一步,基本上QT的配置就完成,还需要在工程的头文件中添加一些常用的头文件,例如,在mainwindow.h头文件中添加算子SDK二次开发常用的头文件。如下:

                                     添加算子SDK常用头文件

2.2 使用VisualStudio作为集成开发环境

我们以VisualStudio 2017为例,在VS中开发Qt应用程序,就不需要什么pro文件,只需要在工程设置中设置头文件路径和静态库路径。

                                                设置附加包含目录

接着编辑附加包含目录,这些附加的包含目录是必不可少的,必须添加,否则程序编译会报错,分别是

$(QTDIR)\include\ActiveQt;

$(MVDALGO_DEV_ENV)\Includes\Algorithm; $(MVDALGO_DEV_ENV)\Includes\MvRenderOcx;

$(MVDALGO_DEV_ENV)\Includes\VisionDesiner;

添加ActiveQt目录是因为需要用到AcitveX容器,Algorithm, MvRenderOcx, VisionDesigner这几个头文件目录包含了渲染控件,以及所有算子需要用到的头文件,如下图所示:

                                                   编辑附加头文件路径

设置完了附加头文件路径,接着就是设置附加静态库路径和链接时需要链接的静态库名称,如下图所示:

编辑附加库目录,如下图所示:

切换到链接器的“输入”,设置链接的静态库名称,如下图所示:

链接的静态库Qt5AxBased.lib,Qt5AxContainerd.lib 是必不可少的(注意这里库名称后面带有d,表明工程配置是Debug生成模式,如果用户工程配置的是Release模式,则不需要带d),因为需要用到ActiveX容器。MVDShapeCpp.lib, MVDImageCpp.lib 也是必不可少的。而MVDCircleFindCpp.lib则需要根据开发者是否用到做取舍,如果没用到则不必添加,如果用到其他的算子工具,必须在这里添加其他算子工具的静态库名称。

典型的算子工具调用流程

算子工具的典型调用流程如下:

我们以调用圆查找算子工具为例(例子只是为了解释说明调用流程,不具有实际意义),典型的代码如下:

//创建一个矩形框作为圆查找ROI
IMvdRectangleF *pRect;
CreateRectangleInstance(&pRect,currentImage->GetWidth()/2,            currentImage->GetHeight()/2,100,100);
//创建一个圆查找工具实例
ICircleFindTool *pCircleFindTool;
CreateCircleFindToolInstance(&pCircleFindTool);
//设置输入图像
pCircleFindTool->SetInputImage(currentImage);
//设置圆查找ROI
pCircleFindTool->SetROI(pRect);
//运行圆查找工具
pCircleFindTool->Run();
//获取圆查找结果
ICircleFindResult *pResult = pCircleFindTool->GetResult();
if(pResult!=nullptr)
{
      MVD_POINT_F cent = pResult->GetCircleCenter();
      qDebug()<<"CentX="<<cent.fX<<"CentY="<<cent.fY;
}
else
{
      qDebug()<<"CircleFindTool NG";
}


如何渲染算子工具的运行结果

算子工具运行完成后,我们可以获取其结果,将结果组装成各种IMvdShape,然后将Shape添加到控件的Shape列表中。添加后渲染控件就会自动渲染图形结果了。我们还是用代码来说明问题。

首先,编写一个函数,用来显示图像和渲染Shape,代码如下:

/**
 * @brief 在控件上显示图像
 * @param pImage 输入图像
 */
void MainWindow::ShowImage(IMvdImage *pImage)
{
    if(pImage!=nullptr)
    {
        long long nInImgPtr = (long long)(currentImage);
        QVariant vInputImg(nInImgPtr);           ui->axWidgetRender->dynamicCall("MV_LoadImageFromObject(const QVariant& varImageObj)",                     vInputImg);
        ui->axWidgetRender->dynamicCall("MV_Display()");
        pPreproMaskTool->SetInputImage(currentImage);
    }
}

/**
 * @brief 添加图形源
 * @param pShape 输入图形
 */
void MainWindow::AddShape(IMvdShape *pShape)
{
    QVariant qVarHandle(reinterpret_cast<long long>(pShape));
    MVD_SHAPE_HANDLE handle = 0;
    MVD_SHAPE_HANDLE* pHandle = &handle;
    QVariant pQVarHandle((long long)(pHandle));
    ui->axWidgetRender->dynamicCall("MV_AddShapeEx(const QVariant&, const QVariant&)",                                   qVarHandle, pQVarHandle);
}

为了简化清除图形和显示图形的写法,我们再定义两个宏,一个用来清除控件上的所有图形,一个用来刷新控件的显示。

#define CLEAR_SHAPES ui->axWidgetRender->dynamicCall("MV_ClearShapes()")
#define MVD_DISPLAY ui->axWidgetRender->dynamicCall("MV_Display()")

这样我们只需要在需要清除图形的地方添加CLEAR_SHAPES; 需要刷新控件显示的地方添加MVD_DISPLAY。

好了,渲染算子的运行结果功能就完备了,再举一个完整的简单例子来说明渲染控件的用法。

举例:调用圆查找功能算子,并渲染圆查找结果。

try 
{
        //清除图形
        CLEAR_SHAPES;
        //显示图像
        ShowImage(currentImage);
        //实例化一个扇环区域,扇环中心100,100,内径40,外径80,起始角//度0,终止角度360
        IMvdAnnularSectorF *pAnnualarsector;               CreateAnnularSectorInstance(&pAnnualarsector,MVD_POINT_F{100,100},40,80,0,360);  
        //实例化圆查找工具
        ICircleFindTool *pcirFindTool;
        CreateCircleFindToolInstance(&pcirFindTool);
        //设置输入图像
        pcirFindTool->SetInputImage(currentImage);
        //设置ROI
        pcirFindTool->SetROI(pAnnualarsector);
        //算子运行
        pcirFindTool->Run();
	//获取结果
       ICircleFindResult *pCirleFindResult = pcirFindTool->GetResult();
       if(pCirleFindResult!=nullptr)
       {
         //在控件上渲染查找到的圆
         IMvdCircleF *pCircle;
         CreateCircleInstance(&pCircle,MVD_POINT_F{0,0},10);
         pCircle->SetCenter(pCirleFindResult->GetCircleCenter();    
         pCircle->SetRadius(pCirleFindResult->GetCircleRadius();
         pCircle->SetBorderColor(MVD_COLOR{0x7f,0xff,0,0});
         AddShape(pCircle);
      }
      MVD_DISPLAY;        
} 
catch (IMVDException &ex) 
{
     QString errorMessage = QString::fromLocal8Bit("执行算法模块发生异常,返回错误码") +                          QString::number(ex.GetErrorCode(),16);
     QMessageBox::warning(this,"Warning",errorMessage);
}


版权声明:本文为V社区用户原创内容,转载时必须标注文章的来源(V社区),文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:v-club@hikrobotics.com 进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
上一篇

开发一个自己的VM模块(二)

下一篇

如何集成第三方算子(如OpenCv,Halcon)到VisionMaster软件中

评论请先登录 登录
全部评论 0
Lv.0
3
创作
8
粉丝
15
获赞
相关阅读
  • 工程经验系列:视觉定位(标定设计,及应用)
    2022-07-01
  • VM4.2正式发布!
    2022-07-04
  • 线阵产品不定行高使用方式介绍
    2022-07-13
  • RGB-D相机SDK使用说明
    2022-07-11
  • 工业读码相机比对功能介绍
    2022-07-07

请升级浏览器版本

您正在使用的浏览器版本过低,请升级最新版本以获得更好的体验。

推荐使用以下浏览器