本文主要通过Visual Studio调用海康摄像机抓取图像采集,要做到同步采集图像需要使用的多线程的操作,故而通过此项目来巩固C#多线程的实例化、调用、相关方法。
一、项目创建与准备
1.海康摄像机参数介绍
官方网址连接:海康机器人-机器视觉-产品详情 (hikrobotics.com)
(1)功能特性
-
支持自动或手动调节增益、曝光时间、LUT、Gamma校正等
-
彩色相机植入优异的图像插值算法,更好的颜色还原,并且支持自动平衡
-
千兆以太网接口,无中继情况下,最大传输距离可到100m
-
支持硬触发、软触发以及自由运行模式
-
兼容GigE Vision V1.2协议及GenlCam标准,无缝链接第三方软件
(2)参数信息
型号 | MV-CE013-50GM |
名称 | 130万像素网口面阵相机,RJ33,黑白 |
传感器类型 | CCD,全局快门 |
传感器型号 | Sharp RJ33 |
像元尺寸 | 3.75 μm × 3.75 μm |
靶面尺寸 | 1/3” |
分辨率 | 1280 × 960 |
最大帧率 | 30 fps @1280 × 960 Mono 8 |
动态范围 | 60 dB |
信噪比 | 37 dB |
增益 | 0 dB ~ 20 dB |
曝光时间 | 34 μs ~ 1 sec |
快门模式 | 支持自动曝光、手动曝光、一键曝光模式 |
黑白/彩色 | 黑白 |
像素格式 | Mono 8/10/10Packed/12/12Packed |
Binning | 不支持 |
下采样 | 不支持 |
镜像 | 支持水平镜像 |
具体详细参数,可以参考官网提供的资料。
2.创建项目
老样子,创建windows窗体项目
拖动一个HWindowControl窗口创建一个显示相机采集的图像,三个button按钮作用分别是获得相机句柄、打开相机、关闭相机功能。
二、程序编写
1. 创建点击事件
根据创建的按钮,双击进入点击事件,另外两个如法炮制:
private void button_get_Click(object sender, EventArgs e)
{
}
2.查看Halcon调取相机实现采集文档
Halcon调用相机代码:
open_framegrabber方法,就是激活相机进行采集,以下是提供的额参考文档,HALCON 虚拟图像采集接口允许您使用常规图像采集从文件中加载图像和图像序列 操作员open_framegrabber、grab_image、grab_image_async和close_framegrabber。可以测试和调试图像。
static void HOperatorSet.GrabImageAsync(out HObject image, HTuple acqHandle, HTuple maxDelay)方法的执行信息:
- 多线程类型:可重入(与非排他性运算符并行运行)。
- 多线程作用域:全局(可以从任何线程调用
)。HTuple AcqHandle;//相机句柄变量 Thread GrabRun; private void button_get_Click(object sender, EventArgs e) { HOperatorSet.OpenFramegrabber("GigEVision2", 0, 0, 0, 0, 0, 0, "progressive", -1, "default", -1, "false", "default", "MV-CE013-50GM", 0, -1,out AcqHandle); if (AcqHandle != null) { MessageBox.Show("获取句柄成功!"); } button_open.Enabled = true; button_get.Enabled = false; }
- 在不进行并行化的情况下进行处理。
static void HOperatorSet.CloseFramegrabber(HTuple acqHandle)方法执行信息与抓取方法相同,也是可以作用在多线程区域。
3.在C#中调用Halcon算子代码
(1)获取相机句柄
这个事件对应我们的获得相机句柄按钮,点击进入双击事件:
HTuple AcqHandle;//相机句柄变量
private void button_get_Click(object sender, EventArgs e)
{
//GigEVision2相机设备型号,AcqHandle相机句柄
HOperatorSet.OpenFramegrabber("GigEVision2", 0, 0, 0, 0, 0, 0, "progressive", -1, "default", -1, "false", "default", "MV-CE013-50GM", 0, -1,out AcqHandle);
if (AcqHandle != null)
{
MessageBox.Show("获取句柄成功!");
}
}
(2) 相机抓取图像
这个事件对应打开相机按钮,创建双击事件:
private void button_open_Click(object sender, EventArgs e)
{
//调用图像抓取方法
HOperatorSet.GrabImageAsync(out Image, AcqHandle, -1);
//设置图像显示区域
HTuple width, height;
HOperatorSet.GetImageSize(Image, out width, out height);
HOperatorSet.SetPart(hWindowControl1.HalconWindow, 0, 0, height - 1, width - 1);
//显示变量在hWindowControl窗口上
HOperatorSet.DispObj(Image, hWindowControl1.HalconWindow);
}
(3)关闭相机释放资源
为了及时关闭相机资源不占用,调用CloseFramegrabber方法释放句柄
private void button_closeAcq_Click(object sender, EventArgs e)
{
//释放相机句柄
HOperatorSet.CloseFramegrabber(AcqHandle);
}
在将上述做完之后,虽然可以成功运行并调取,但是可以明显的感受到,运行一次之后在此点击采集图像就会报错,所以开始引入多线程的概念。
三、多线程
1.概述
(原文连接:一文带你搞懂C#多线程的5种写法_c#创建多线程的几种方式-优快云博客)
-
使用Thread类通过ThreadStart(无参数)或ParameterizedThreadStart(一个输入参数)类型的委托创建一个Thread对象,开启一个新线程,执行该委托传递的任务,此时线程尚未处于运行状态。
-
调用Start()函数启动线程,当前线程继续执行。
-
调用Join()函数可以阻塞当前线程,直到调用Join()的线程终止。
-
调用Abort()方法,如需中止线程,在调用该方法的线程上抛出ThreadAbortException异常,以结束该线程
-
可以通过Thread.ResetAbort()方法阻止线程的中止。
在本项目中,我们会使用到Start()--开始和Abort()--终止方法。
2.创建线程
我们先创建一个线程变量,如果创建要报错的话,记得在头文件里面添加using System.Threading;就可以成功的创建了。
新建一个线程调用方法,我这里命名为Run,将图像采集的代码放进里面。
Thread GrabRun;//创建一个线程变量GrabRun
//相机采集线程
public void Run()
{
while (!isStop)
{
//调用图像抓取方法
HOperatorSet.GrabImageAsync(out Image, AcqHandle, -1);
//设置图像显示区域
HTuple width, height;
HOperatorSet.GetImageSize(Image, out width, out height);
HOperatorSet.SetPart(hWindowControl1.HalconWindow, 0, 0, height - 1, width - 1);
//显示变量在hWindowControl窗口上
HOperatorSet.DispObj(Image, hWindowControl1.HalconWindow);
}
}
在打开相机事件里面先初始化Thread线程变量,调用我们的Run方法,利用start()方法启用线程。
HObject Image;
private void button_open_Click(object sender, EventArgs e)
{
button_closeAcq.Enabled = true;
isStop = false;
GrabRun = new Thread(Run);
GrabRun.Start();
}
在关闭相机事件里面,我们调用Abort()方法终止线程,方便我们做循环使用,
// 点击事件关闭
bool isStop=false;
private void button_closeAcq_Click(object sender, EventArgs e)
{
isStop=true;
GrabRun.Abort();
GrabRun=null;
//释放相机句柄
HOperatorSet.CloseFramegrabber(AcqHandle);
button_open.Enabled = false;
button_closeAcq.Enabled = false;
button_get.Enabled = true;
}
这里的Button名.Enabled = false;代表初始化按钮不可使用,反之即可使用。
运行效果:
按下之后可以实时采集,至此多线程调用相机采集项目就告一段落。
本文主要还是简单梳理了一下如何利用多线程调用相机,多线程的几个常用调用方法,然后如何对线程进行定义、如何终止线程,如有任何问题还请各位大佬指出~~~
3.完整代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using HalconDotNet;
namespace C_调用窗口
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
button_open.Enabled = false;
button_closeAcq.Enabled = false;
}
HTuple AcqHandle;//创建相机句柄变量
Thread GrabRun;
private void button_get_Click(object sender, EventArgs e)
{
//GigEVision2相机设备型号,AcqHandle相机句柄
HOperatorSet.OpenFramegrabber("GigEVision2", 0, 0, 0, 0, 0, 0, "progressive", -1, "default", -1, "false", "default", "MV-CE013-50GM", 0, -1,out AcqHandle);
if (AcqHandle != null)
{
MessageBox.Show("获取句柄成功!");
}
button_open.Enabled = true;
button_get.Enabled = false;
}
//相机采集线程
public void Run()
{
while (!isStop)
{
//调用图像抓取方法
HOperatorSet.GrabImageAsync(out Image, AcqHandle, -1);
//设置图像显示区域
HTuple width, height;
HOperatorSet.GetImageSize(Image, out width, out height);
HOperatorSet.SetPart(hWindowControl1.HalconWindow, 0, 0, height - 1, width - 1);
//显示变量在hWindowControl窗口上
HOperatorSet.DispObj(Image, hWindowControl1.HalconWindow);
}
}
HObject Image;
private void button_open_Click(object sender, EventArgs e)
{
button_closeAcq.Enabled = true;
isStop = false;
GrabRun = new Thread(Run);
GrabRun.Start();
}
// 点击事件关闭
bool isStop=false;
private void button_closeAcq_Click(object sender, EventArgs e)
{
isStop=true;
GrabRun.Abort();
GrabRun=null;
//释放相机句柄
HOperatorSet.CloseFramegrabber(AcqHandle);
button_open.Enabled = false;
button_closeAcq.Enabled = false;
button_get.Enabled = true;
}
}
}