halcon引擎学习笔记(五)多线程并发执行外部函数

勇哥这套halcon引擎的学习笔记贴子共七篇,它是在官方指导文档《http://www.skcircle.com/?id=1343》的基础上学习编写而成的笔记。只是有一篇讲解怎么调用向量变量的没有加入,因为勇哥实在不知道这个向量变量有啥子用。以后如果搞明白了再加入吧。


2020/7/3勇哥注:

原来halcon中的向量就是个容器,跟c++标准模板库中的那个向量是一致的。第八篇加上来吧

halcon引擎学习笔记(八)使用向量变量


halcon引擎学习笔记(七)在在HDevEngine/C#中使用实时编译器JIT 

halcon引擎学习笔记(六)多线程并发执行外部函数,多窗口显示  

halcon引擎学习笔记(五)多线程并发执行外部函数  

halcon引擎学习笔记(四)调用时的错误处理   

halcon引擎学习笔记(三)执行本地或者外部程序   

halcon引擎学习笔记(二)执行Procedure程序,扩展名为hdvp的halcon函数   

halcon引擎学习笔记(一)执行hdev程序   

演示程序勇哥用的是halcon19.11,C#使用的是vs2013版本。

全部测试代码勇哥已经打包,请点击下载


如果你要调用的程序是一个hdev主程序,且当中没有外部函数或者自定义函数,则可以看(一)

如果你要调用的程序是一个hdev主程序,且当中有若干的外部函数或者自定义函数,则可以看(三)

如果你要调用的程序只是一个hdvp的外部函数,则可以看(二)

如果你需要多线程调用外部函数,则可以看(六)(七)

如果你的程序中用到向量变量,则可以看(八)


2020/10/15勇哥注:

勇哥最近寻遍halcon引擎类的功能,发现无法实现修改halcon程序并保存后,C#这边能实时运行修改后的halcon程序。

必须要退出C#程序后,再次执行才是跑的修改后的代码。

这真是个遗憾,因为机器正在做货时,重启C#程序是相当不方便的一件事。

如果有人知道怎么实现,麻烦告诉勇哥,非常感谢!


2020/12/18勇哥注:

由网友“小黄鱼”指出,HDevEngine 类有个UnloadProcedure方法调用后,可以实现不需要重启C#程序即可执行更新后的外部函数。

经我测试后,发现是有效的!

在这里非常感谢他的指点!!!


正文部分

========================


这篇说说创建多线程HDevEngine应用程序时要遵守的最重要的规则。当多个线程并行执行HDevelop程序时,每个线程必须创建其自己的相应HDevProgramCall实例。

对于HDevEngine的所有实例,外部过程路径和HDevelop显示操作的实现总是全局设置的。所以建议通过单独的HDevEngine实例来设置它们,以使代码更具可读性。

因为HDevelop的显示操作的实现只能全局设置,所以它必须是线程安全且可重入的。



多线程并发执行外部函数


它由四个线程组成:主线程负责图形用户界面(GUI)。

image.png


主线程还通过HDevelop过程训练形状模型,并通过创建和初始化其他三个线程来初始化应用程序:两个处理线程和所谓的控制线程,控制这两个处理线程。

控制线程获取图像并将其传递给处理线程,处理线程随后处理图像并传回结果。控制线程收集结果,但不显示结果本身,因为HALCON窗口中的所有活动都必须由创建的线程执行。




演示程序的运行效果

image.png

image.png



本文源代码


注意:下面的演示程序的源码需要扫码支付才可以看到。

如果你觉得不想支付,那么可以在CSDN下载中去下载,(https://download.csdn.net/download/suneggs/12556535

或者通过看后面的代码分析,其实也可以自己完成测试代码。


支付2元或购买VIP会员后,才能查看本内容!立即支付升级会员查询订单


源码解读


(一)按下初始化按钮后的初始化代码


HOperatorSet.SetSystem("parallelize_operators", "false");

首先,自动操作器并行化被关闭,否则这两种机制将使用超过可用内核/处理器数量的内核,从而减慢应用程序的速度,而不是加快应用程序的速度。如果系统具有两个以上的核心或处理器,可以设置true,勇哥测试过是可以的。


然后,我们创建一个HDevEngine实例,并设置设置外部过程路径。

HDevEngine MyEngine = new HDevEngine();
  string ProcedurePath = halconExamples + @"\hdevengine\procedures";
  ...
  MyEngine.SetProcedurePath(ProcedurePath);

执行外部过程train_shape_model创建模板。

HDevProcedureCall  ProcTrain;
  HDevProcedure Procedure = new HDevProcedure("train_shape_model");
  ProcTrain = new HDevProcedureCall(Procedure);
  ProcTrain.Execute();

取得返回参数ModelID,ModelContours

public HTuple ModelID;
public HXLD ModelContours;

ModelID = ProcTrain.GetOutputCtrlParamTuple("ModelID");
ModelContours = ProcTrain.GetOutputIconicParamXld("ModelContours");

创建并初始化两个处理线程

public class EngineThread{
    Thread WorkerObject = null;
    HDevProcedureCall ProcCall;
    HTuple ModelID;
    HXLD ModelContours;    
    public AutoResetEvent EngineIsReady;    
    public EngineThread(Form1 mainForm)    {
    ModelID = mainForm.ModelID;
    ModelContours = mainForm.ModelContours;
    EngineIsReady = new AutoResetEvent(true);
}
 
EngineThread  WorkerEngine1;        // processing 
threadEngineThread  WorkerEngine2;     // processing threadAutoResetEvent Engine1Ready;
AutoResetEvent Engine2Ready;

WorkerEngine1 = new EngineThread(this);
WorkerEngine1.Init();
Engine1Ready = WorkerEngine1.EngineIsReady;

WorkerEngine2 = new EngineThread(this);
WorkerEngine2.Init();
Engine2Ready = WorkerEngine2.EngineIsReady;

EngineThread类的源代码见上面。

这个类初始化时对外部函数detect_shape.hdvp进行了赋初值。

public void Init(){
  HDevProcedure Procedure = new HDevProcedure("detect_shape");
  ProcCall = new HDevProcedureCall(Procedure);
  ProcCall.SetInputCtrlParamTuple("ModelID", ModelID);
  ProcCall.SetInputIconicParamObject("ModelContours", ModelContours);
}

最后,我们初始化图像采集。句柄存储在窗体的变量中,以便控制线程可以访问它。

private HFramegrabber AcqHandle;
string ImagePath = halconExamples + "/images/cap_illumination";
AcqHandle=new HFramegrabber("File",1,1,0,0,0,0,"default",-1,"default",
-1,"default",ImagePath,"default",-1,-1);


(二)按下run按钮后的处理过程


private void RunButton_Click(object sender, System.EventArgs e)
{
  WorkerEngine1.Run();
  WorkerEngine2.Run();

EngineThread类相应的方法Run()创建并启动线程并设置“就绪”信号

public void Run()
{
  EngineIsReady.Set();
  WorkerObject = new Thread(new ThreadStart(Process));
  WorkerObject.Start();
}

然后,主线程启动控制线程

ControlThread = new Thread(new ThreadStart(Run));
ControlThread.Start();

从控制线程触发处理线程

控制线程的操作包含在方法Run中. 只要没有按下Stop,它就会一直等到某个处理线程准备就绪。

EngineThread WorkerEngine; // variable to switch between processing threads
public void Run()
{
  HImage Image;    
  while (!StopEventHandle.WaitOne(0, true))
  {        
      if (Engine1Ready.WaitOne(0, true))
        WorkerEngine = WorkerEngine1;        
    else if(Engine2Ready.WaitOne(0, true))
      WorkerEngine = WorkerEngine2;        
    else
      continue;

    Image = AcqHandle.GrabImageAsync(-1);
    WorkerEngine.SetImage(Image);

然后,它获取下一个图像并将其传递给处理线程,线程将其存储在成员变量。

private HImage          
InputImage = null;
public void SetImage(HImage Img)
{
  InputImage = Img;
}


(三)其它的处理逻辑

这是工作线程主要的工作,就是运行外部函数detect_shape.hdvp

public void Process()
{    
    while (!DelegatedStopEvent.WaitOne(0, true))
  {        
      if (InputImage == null)            
          continue;

    ProcCall.SetInputIconicParamObject("Image", InputImage);
    ProcCall.Execute();

为了传递结果,定义了一个数据结构ResultContainer类来存储相关数据:处理后的图像以及找到的cap的位置、方向和轮廓。

public class ResultContainer{
  public HImage  InputImage;
  public HXLD    FoundContours;
  public double  Row;
  public double  Column;
  public double  Angle;
}

执行该过程后,处理线程访问其结果,并将其与处理后的图像一起存储在结果类(“结果容器”)的新实例中。

ResultContainer Result;
HTuple ResultTuple;

Result = new ResultContainer();
Result.InputImage = InputImage;
Result.FoundContours = ProcCall.GetOutputIconicParamXld("ResultObject");
ResultTuple = ProcCall.GetOutputCtrlParamTuple("ResultData");
Result.Row = ResultTuple[0];
Result.Column = ResultTuple[1];
Result.Angle = ResultTuple[2];

然后,处理线程通过将结果容器追加到列表中,将其传递给控制线程。

ResultMutex.WaitOne();
ResultList.Add(Result);
ResultMutex.ReleaseMutex();

此列表是主线程的成员变量它由互斥锁保护,以便线程可以安全地访问它

ArrayList ResultList;
Mutex ResultMutex;
public EngineThread(Form1 mainForm)
{
  ResultList = mainForm.ResultList;
  ResultMutex = mainForm.ResultDataMutex;
}

控制线程不执行结果本身的显示,而是通过方法调用将其委托给主线程

for( ;Count > 0;Count--)
  Invoke(DelegatedDisplay);
    The necessary members are defined by the form.
    delegate void  FuncDelegate();
FuncDelegate DelegatedDisplay;
public MultiThreadingForm()
{
  DelegatedDisplay = new FuncDelegate(DisplayResults);
}

注意,自HALCON 12以来,所有HALCON可视化运算符都自动委托给正确的线程,如“Threading Issues with Graphics”一章中所述。


显示结果

实际显示由DisplayResults方法执行。每次调用时,它都会从结果列表中移除一个项,并显示处理后的图像和找到的cap的轮廓。然后,释放相应的HALCON内部内存。

public void DisplayResults()
{
  ResultDataMutex.WaitOne();
  Result = (ResultContainer) ResultList[0];
  ResultList.Remove(Result);
  ResultDataMutex.ReleaseMutex();

  Window.ClearWindow();
  Window.DispImage(Result.InputImage);
  Window.DispObj(Result.FoundContours);

  Result.InputImage.Dispose();
  Result.FoundContours.Dispose();
}


程序中用到的halcon外部函数


train_shape_model.hdvp 的参数

image.png

源码:

read_image (Image, './cap_illumination/cap_illumination_01.png')
gen_circle (Circle, 495, 630, 290)
reduce_domain (Image, Circle, ImageReduced)
* 
create_scaled_shape_model (ImageReduced, 'auto', 0, rad(360), 'auto', 0.8, 
1.2, 'auto', 'auto', 'use_polarity', 'auto', 'auto', ModelID)
get_shape_model_contours (ModelContours, ModelID, 1)
return ()


train_shape_model.hdvp的参数

image.png

源码:

read_image (Image, './cap_illumination/cap_illumination_01.png')
gen_circle (Circle, 495, 630, 290)
reduce_domain (Image, Circle, ImageReduced)
* 
create_scaled_shape_model (ImageReduced, 'auto', 0, rad(360), 'auto', 0.8, 
1.2, 'auto', 'auto', 'use_polarity', 'auto', 'auto', ModelID)
get_shape_model_contours (ModelContours, ModelID, 1)
return ()


detect_shape.hdvp的参数

image.png

源码:

find_scaled_shape_model (Image, ModelID, 0, rad(360), 0.98, 1.02, 0.4, 1, 
1, 'least_squares_high', [5,3], 0.9, Rows, Columns, Angles, Scale, Score)
if (|Rows| > 0)
    Row := Rows[0]
    Column := Columns[0]
    Angle := Angles[0]
    vector_angle_to_rigid (0, 0, 0, Row, Column, Angle, HomMat2D)
    affine_trans_contour_xld (ModelContours, ResultObject, HomMat2D)
    DetectionSuccessful := 'true'
else
    Row := -1
    Column := -1
    Angle := -1
    DetectionSuccessful := 'false'
endif
ResultData := [Row,Column,Angle]
return ()



--------------------- 

作者:hackpig

来源:www.skcircle.com

版权声明:本文为博主原创文章,转载请附上博文链接!


本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2024年4月    »
1234567
891011121314
15161718192021
22232425262728
2930
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864