其实工业相机里面大部分不同的知名厂家的相机完全可以通过一个共用类来访问,这一点有在使用halcon小助手连接相机上体现。比如说常用的GigE接口的相机几乎都适应于halcon提供的GigE接口类。
以下分享一个我使用大华相机动态链接库开发的一个共用相机类。
环境:
大华相机的dll:
共用类:
public class CamCommon
{
#region 属性相关
//搜索到的设备集合
static List<IDeviceInfo> li = Enumerator.EnumerateDevices();
List<IGrabbedRawData> m_frameList = new List<IGrabbedRawData>(); /* 图像缓存列表 */
Thread renderThread = null; /* 显示线程 */
bool m_bShowLoop = true; /* 线程控制变量 */
Mutex m_mutex = new Mutex(); /* 锁,保证多线程安全 */
private Graphics _g = null;
bool m_bShowByGDI; /* 是否使用GDI绘图 */
const int DEFAULT_INTERVAL = 40;
Stopwatch m_stopWatch = new Stopwatch(); /* 时间统计器 */
/* 设备对象 大华官方SDK */
private ThridLibray.IDevice m_dev;
/// <summary>
/// 相机ID
/// </summary>
public string userID;
#endregion
#region 委托事件相关
/// <summary>
/// 给需要显示图像的地方绑定,图像回调委托
/// </summary>
/// <param name="image"></param>
public delegate void DaHuaImageShow(HObject image);
/// <summary>
/// 图像回调显示函数
/// </summary>
public event DaHuaImageShow evetAsynchImage;
#endregion
#region 构造相关
/// <summary>
/// 根据UserID 名称自动对应创建实例化 并
/// </summary>
/// <param name="userID"></param>
public CamCommon(string userID){
this.userID = userID;
if (li.Count > 0)
{
for (int i = 0; i < li.Count; i++)
{/// 如果要获取该相机设置名称, 可以用li[i].Key 返回的就是设置名
if (li[i].Name == userID) { // 表示找到了该相机就打开连接
/* 获取搜索到的第一个设备 连接到相机实例化 */
m_dev = Enumerator.GetDeviceByIndex(i);
}
}
}
}
#endregion
#region 一般方法相关
/// <summary>
/// 获取连接到的设备名
/// </summary>
/// <returns>返回设备集合</returns>
public static List<string> DeviceNames() {
List<string> DeviceNameList = new List<string>();
if (li.Count > 0)
{
for (int i = 0; i < li.Count; i++)
{
DeviceNameList.Add(li[i].Name);
}
}
return DeviceNameList;
}
/// <summary>
/// 图像采集函数 ,线程
/// </summary>
private void ShowThread()
{
while (m_bShowLoop)
{
if (m_frameList.Count == 0)
{
Thread.Sleep(10);
continue;
}
/* 图像队列取最新帧 */
m_mutex.WaitOne();
IGrabbedRawData frame = m_frameList.ElementAt(m_frameList.Count - 1);
m_frameList.Clear();
m_mutex.ReleaseMutex();
/* 主动调用回收垃圾 */
GC.Collect();
/* 控制显示最高帧率为25FPS */
if (false == isTimeToDisplay())
{
continue;
}
try
{
/* 图像转码成bitmap图像 */
var bitmap = frame.ToBitmap(false);
Bitmap bitmap1 = null;
m_bShowByGDI = true;
if (m_bShowByGDI)
{
/// 事件触发,通过委托进行窗口间转换
HObject image = BitmapToHImage(bitmap);
/// 添加事件通过绑定事件的方式进行图像传播
if (evetAsynchImage!=null) { /// 避免没有接受事件者触发
evetAsynchImage(image);
}
image.Dispose();
bitmap.Dispose();
}
}
catch (Exception exception)
{
Catcher.Show(exception);
}
}
}
/* 判断是否应该做显示操作 */
private bool isTimeToDisplay()
{
m_stopWatch.Stop();
long m_lDisplayInterval = m_stopWatch.ElapsedMilliseconds;
if (m_lDisplayInterval <= DEFAULT_INTERVAL)
{
m_stopWatch.Start();
return false;
}
else
{
m_stopWatch.Reset();
m_stopWatch.Start();
return true;
}
}
/// <summary>
/// 打开相机
/// </summary>
/// <returns></returns>
public bool openCam() {
try
{
/* 注册链接事件 */
m_dev.ConnectionLost += OnConnectLoss;
/* 打开设备 */
while (!m_dev.Open())
{
MessageBox.Show("连接相机失败");
Thread.Sleep(5000);
return false;
}
///* 设置图像格式 */
//using (IEnumParameter p = m_dev.ParameterCollection[ParametrizeNameSet.ImagePixelFormat])
//{
// p.SetValue("Mono8");
//}
///* 设置曝光 */
///*using (IFloatParameter p = m_dev.ParameterCollection[ParametrizeNameSet.ExposureTime])
//{
// p.SetValue(20000);
//}*/
///* 设置增益 */
//using (IFloatParameter p = m_dev.ParameterCollection[ParametrizeNameSet.GainRaw])
//{
// p.SetValue(1.0);
//}
/// 开启图像采集函数线程
renderThread = new Thread(new ThreadStart(ShowThread));
renderThread.IsBackground = true;
renderThread.Start();
/* 设置缓存个数为8(默认值为16) */
m_dev.StreamGrabber.SetBufferCount(8);
/* 注册码流回调事件 图像回调函数 */
m_dev.StreamGrabber.ImageGrabbed += OnImageGrabbed;
}
catch {
MessageBox.Show("连接相机失败");
return false;
}
return true;
}
/// <summary>
/// 开始连续采集
/// </summary>
public void openConsecutive() {
try
{
if (m_dev != null) {
/* 开启码流 */
if (!m_dev.GrabUsingGrabLoopThread())
{
MessageBox.Show(@"开启码流失败");
return;
}
}
}
catch { }
}
/// <summary>
/// 关闭连续采集
/// </summary>
public void closeConsecutive() {
try
{
if (m_dev != null)
{
m_dev.ShutdownGrab(); /* 停止码流 */
}
}
catch { }
}
/// <summary>
/// 关闭相机
/// </summary>
public void closeCam() {
try
{
if (m_dev == null)
{
throw new InvalidOperationException("Device is invalid");
}
m_dev.StreamGrabber.ImageGrabbed -= OnImageGrabbed; /* 反注册回调 */
m_dev.ShutdownGrab(); /* 停止码流 */
m_dev.Close(); /* 关闭相机 */
}
catch {
MessageBox.Show("关闭失败!","Error");
}
}
/// <summary>
/// 码流数据回调
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnImageGrabbed(Object sender, GrabbedEventArgs e)
{
m_mutex.WaitOne();
m_frameList.Add(e.GrabResult.Clone());
m_mutex.ReleaseMutex();
}
/// <summary>
/// 相机掉线重连函数
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnConnectLoss(object sender, EventArgs e)
{
/// 先关闭所有信息
m_dev.ShutdownGrab();
m_dev.Dispose();
m_dev = null;
///重新创建实例绑定时间 开始连接
/* 设备搜索 */
List<IDeviceInfo> li = Enumerator.EnumerateDevices();
if (li.Count > 0)
{
for (int i = 0; i < li.Count; i++)
{
if (li[i].Name == userID)
{ // 表示找到了该相机就打开连接
/* 获取搜索到的第一个设备 连接到相机实例化 */
m_dev = Enumerator.GetDeviceByIndex(i);
// 实例化之后打开相机并开启实时采集
/* 打开设备 只尝试打开3次 如果打开3次失败则关闭 */
int ii = 0;
while (!m_dev.Open())
{
ii += 1;
Thread.Sleep(200);
if (ii >= 3) {
return;
}
}
/ 开启实时采集
/* 设置缓存个数为8(默认值为16) */
m_dev.StreamGrabber.SetBufferCount(8);
/* 注册码流回调事件 */
m_dev.StreamGrabber.ImageGrabbed += OnImageGrabbed;
/* 开启码流 or 开启实时采集*/
m_dev.GrabUsingGrabLoopThread();
}
}
}
else {
MessageBox.Show("未找到"+userID+ "的相机,请检查相机接口!");
}
}
/// <summary>
/// 图像转换
/// </summary>
/// <param name="SrcImage"></param>
/// <returns></returns>
public static HObject BitmapToHImage(Bitmap SrcImage)
{
HObject Hobj;
HOperatorSet.GenEmptyObj(out Hobj);
Point po = new Point(0, 0);
Size so = new Size(SrcImage.Width, SrcImage.Height);
//template.Width, template.Height
Rectangle ro = new Rectangle(po, so);
Bitmap DstImage = new Bitmap(SrcImage.Width, SrcImage.Height, PixelFormat.Format8bppIndexed);
DstImage = SrcImage.Clone(ro, PixelFormat.Format8bppIndexed);
int width = DstImage.Width;
int height = DstImage.Height;
Rectangle rect = new Rectangle(0, 0, width, height);
System.Drawing.Imaging.BitmapData dstBmpData = DstImage.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
//pImage.PixelFormat
int PixelSize = Bitmap.GetPixelFormatSize(dstBmpData.PixelFormat) / 8;
int stride = dstBmpData.Stride;
//重点在此
unsafe
{
int count = height * width;
byte[] data = new byte[count];
byte* bptr = (byte*)dstBmpData.Scan0;
fixed (byte* pData = data)
{
for (int i = 0; i < height; i++)
for (int j = 0; j < width; j++)
{
data[i * width + j] = bptr[i * stride + j];
}
HOperatorSet.GenImage1(out Hobj, "byte", width, height, new IntPtr(pData));
}
}
DstImage.UnlockBits(dstBmpData);
return Hobj;
}
/// <summary>
/// 开启硬件触发,其实触发模式设置为on既可以硬触发也可以软触发
/// </summary>
/// <returns></returns>
public bool openTriggerLinel()
{
return m_dev.TriggerSet.Open(TriggerSourceEnum.Line1);
}
/// <summary>
/// 开启软件触发
/// </summary>
/// <returns></returns>
public bool openSoftwareTrigger() {
return m_dev.TriggerSet.Open(TriggerSourceEnum.Software);
}
/// <summary>
/// 触发模式或软触发模式触发一次相机采集
/// </summary>
public void SoftwareTrigger() {
if (m_dev == null)
{
throw new InvalidOperationException("Device is invalid");
}
try
{
m_dev.ExecuteSoftwareTrigger();
}
catch (Exception exception)
{
Catcher.Show(exception);
}
}
#endregion
#region 相机参数相关
//定义一般参数; 格式:中文名,大华相机官方参数keys,类型
static string[,] prms =
{ { "设备型号", "DeviceModelName", "string" } ,
{ "图像格式","PixelFormat","Enumeration" },
{ "帧数","AcquisitionFrameCount","Integer" },
{ "曝光时间","ExposureTimeAbs","Float" },
{ "增益","GainRaw","Float" },
{ "灰度系数","Gamma","Float" },
{ "长","Width","Integer" },
{ "宽","Height","Integer" },
{ "触发模式","TriggerMode","Enumeration" }
};
/// <summary>
/// 获取一般参数的键
/// </summary>
/// <returns></returns>
public static string[] getPrmKeys()
{
int len = prms.GetLength(0);
string[] keys = new string[len];
for (int i = 0; i < len; i++)
{
keys[i] = prms[i,0];
}
return keys;
}
/// <summary>
/// 获取相机参数值
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public string[] getValue(string key)
{
string type = null;
string prmKey = null;
for (int i = 0; i < prms.GetLength(0); i++)
{
if (prms[i,0] == key)
{
type = prms[i,2];
prmKey = prms[i, 1];
break;
}
}
if (type == null)
{
return null;
}
return getPrmValue(prmKey, type);
}
/// <summary>
/// 可以通过传入通用参数key值和类型来获取参数
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
public string[] getPrmValue(string key, string type) {
//获取相机参数值
if ("string" == type)
{
using (IStringParameter p = m_dev.ParameterCollection[new StringName(key)])
{
string[] values = { p.GetValue() };
return values;
}
}
if ("Enumeration" == type)
{
using (IEnumParameter p = m_dev.ParameterCollection[new EnumName(key)])
{
List<string> list = new List<string>();
list = p.GetAllValues();
string[] values = new string[list.Count];
list.CopyTo(values, 0);
return values;
}
}
if ("Integer" == type)
{
using (IIntegraParameter p = m_dev.ParameterCollection[new IntegerName(key)])
{
string[] values = { p.GetValue().ToString() };
return values;
}
}
if ("Float" == type)
{
using (IFloatParameter p = m_dev.ParameterCollection[new FloatName(key)])
{
string[] values = { p.GetValue().ToString("f2") };
return values;
}
}
if ("Bool" == type)
{
using (IBooleanParameter p = m_dev.ParameterCollection[new BooleanName(key)])
{
string strText;
if (true == p.GetValue())
{
strText = "True";
}
else
{
strText = "False";
}
string[] values = { strText };
return values;
}
}
return null;
}
/// <summary>
/// 设置相机参数值
/// </summary>
/// <param name="key"></param>
public bool setValue(string key ,string value) {
string type = null;
string prmKey = null;
for (int i = 0; i < prms.GetLength(0); i++)
{
if (prms[i, 0] == key)
{
type = prms[i, 2];
prmKey = prms[i, 1];
break;
}
}
if (type == null)
{
return false;
}
return setPrmValue(prmKey, value,type);
}
/// <summary>
/// 可以通过传入通用参数key值和类型来设置参数
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
public bool setPrmValue(string key ,string value, string type) {
try
{
//设置相机参数值
if ("string" == type)
{
using (IStringParameter p = m_dev.ParameterCollection[new StringName(key)])
{
return p.SetValue(value);
}
}
if ("Enumeration" == type)
{
using (IEnumParameter p = m_dev.ParameterCollection[new EnumName(key)])
{
return p.SetValue(value);
}
}
if ("Integer" == type)
{
using (IIntegraParameter p = m_dev.ParameterCollection[new IntegerName(key)])
{
return p.SetValue(int.Parse(value));
}
}
if ("Float" == type)
{
using (IFloatParameter p = m_dev.ParameterCollection[new FloatName(key)])
{
return p.SetValue(float.Parse(value));
}
}
if ("Bool" == type)
{
using (IBooleanParameter p = m_dev.ParameterCollection[new BooleanName(key)])
{
return p.SetValue(bool.Parse(value));
}
}
}
catch (Exception)
{
MessageBox.Show("设置参数异常!");
}
return false;
}
#endregion
}
Demo演示:
窗体设计:
代码:
public partial class Form1 : Form
{
//当前的相机设备
CamCommon cam;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//加载参数列表
string[] prm = CamCommon.getPrmKeys();
this.comboBox1.Items.AddRange(prm);
//加载设备
List<string> DeviceNames = CamCommon.DeviceNames();
if (DeviceNames.Count ==0)
{
MessageBox.Show("未找到设备!");
return;
}
foreach (string name in DeviceNames)
{
comboBox_DevList.Items.Add(name);
}
comboBox_DevList.Text = DeviceNames[0];
//设置只读控件
this.comboBox_DevList.DropDownStyle = ComboBoxStyle.DropDownList;
this.comboBox1.DropDownStyle = ComboBoxStyle.DropDownList;
//控件绑定事件
this.btnOpen.Click += btnOpen_Click;
this.button1.Click += button1_Click;
this.button2.Click += button2_Click;
this.button3.Click += button3_Click;
this.button4.Click += button4_Click;
this.comboBox1.TextChanged += comboBox1_TextChanged;
this.comboBox2.Validated += comboBox2_Validated;
}
#region 控件事件
//打开设备
private void btnOpen_Click(object sender, EventArgs e)
{
string CamID = comboBox_DevList.Text;
try
{
cam = new CamCommon(CamID);
for (int i = 0; !cam.openCam(); i++) { Thread.Sleep(500); }/// 绑定回调函数事件
cam.evetAsynchImage += ShowImage; /// 图像显示函数
MessageBox.Show("当前设备连接成功!");
EnaledTrue();
}
catch (Exception)
{
MessageBox.Show("当前设备连接失败!");
}
}
//开始采集
private void button1_Click(object sender, EventArgs e)
{
if (cam == null)
{
return;
}
cam.openConsecutive();
this.button1.Enabled = false;
this.button2.Enabled = true;
}
//停止采集
private void button2_Click(object sender, EventArgs e)
{
if (cam == null)
{
return;
}
cam.closeConsecutive();
this.button1.Enabled = true;
this.button2.Enabled = false;
}
//软触发,这里将触发模式参数设为on即可,也可以直接调用CamCommon类中提供的方法打开软触发
private void button4_Click(object sender, EventArgs e)
{
if (cam == null)
{
return;
}
cam.openSoftwareTrigger();
cam.SoftwareTrigger();
}
//关闭相机
private void button3_Click(object sender, EventArgs e)
{
if (cam == null)
{
return;
}
cam.closeCam();
cam = null;
EnaledTrue();
this.comboBox2.Items.Clear();
this.comboBox2.Text = "";
}
//获取参数
private void comboBox1_TextChanged(object sender, EventArgs e)
{
string key = this.comboBox1.Text;
if (cam == null)
{
return;
}
string[] values = cam.getValue(key);
this.comboBox2.Items.AddRange(values);
this.comboBox2.Text = values[0];
}
private void comboBox2_Validated(object sender, EventArgs e)
{
if (cam == null)
{
return;
}
string key = this.comboBox1.Text;
string value = this.comboBox2.Text;
if (cam.setValue(key, value))
{
MessageBox.Show("设置成功!");
}
else
{
MessageBox.Show("设置失败!");
}
}
#endregion
#region 通用方法相关
//打开控件状态
public void EnaledTrue() {
this.button1.Enabled = true;
this.button2.Enabled = true;
}
public void ShowImage(HObject img) {
HTuple hv_WidthCam = null, hv_HeightCam = null;
//判断来图
if (img == null)
{
return;
}
HOperatorSet.GetImageSize(img, out hv_WidthCam, out hv_HeightCam);
HOperatorSet.SetPart(hWindowControl1.HalconWindow, 0, 0, hv_HeightCam, hv_WidthCam);
HOperatorSet.DispObj(img, hWindowControl1.HalconWindow);
img.Dispose();
}
#endregion
}