DALSA网口线扫相机SDK开发详解例程(C#版)

首先吐槽一句,官方的demos写的真的不好,坑爹啊。对于小白来说,开发官方demos为我所用太难了。为什么呢?因为它Dalsa的DALSA.SaperaLT.SapClassBasic.dll中,不仅有采图的代码库,还有用于显示的UI库(它不是用Winform的PictureBox显示图片,而是用它自家的UI显示图片),demos把采图程序和UI库杂糅在一起,而且隐藏了少部分细节。

后来我在网上狂搜资料,搜到了两个大佬的两篇好文章:

dalsa 8k线阵网口相机c#开发

https://blog.csdn.net/baidu_30028771/article/details/64628784

DALSA相机SDK不完全教程

http://www.cnblogs.com/lgyup/p/4313332.html

文章一的代码是一个完整的例子,是可以直接采到图的。文章二的代码缺少关键的GetCameraInfo()方法,是不能直接运行的,但是这篇文章的讲解更全面、深入,可以说两篇都是必备的啦。

我为什么要写这两篇文章呢?因为我想集合这两家之长,再加入一点自己的经验、代码,并且提供完整的源代码方便大家开发。很忏愧 ,只做了一点微小的贡献。

我的开发硬件、软件信息:

操作系统:windows 10、windows 7 64bit都有

线扫相机:Dalsa Linea Mono 4k 26 kHz GigE (LA-GM-04K08A)

IDE :Visual studio 2013

第一篇文章我直接运行代码报错了,是到了跟Dalsa相关的dll的语句时报错的。Win 10系统很扯淡,报错的提示一点都看不懂,我换Win 7的系统后,也报错,但是明确把错误原因找出来了。其实是Dalsa的dll中有低版本.Net Framework的代码,导致不兼容。解决的办法,网上一大推,核心就一句:在app.config的合适位置,加这句话useLegacyV2RuntimeActivationPolicy=”true”。

如果没有app.config文件,你就需要创建这个文件。

我的app.config文件内容如下:

现在你需要把我上文提到的两篇博客仔细看一下了。

……

看完了吗?看完了的话,接着往下看我的文章。

首先学习一下相机配置文件(.ccf)如何生成:

① 打开相机软件Sapera CamExpert,确保相机已经正常工作,然后自己改变到合适的参数;

② 点击软件左上角的File——Save As…,选择文件夹路径,修改文件名。
DALSA网口线扫相机SDK开发详解例程(C#版)_第1张图片
DALSA网口线扫相机SDK开发详解例程(C#版)_第2张图片
因为我是用Halcon显示图片,因此我添加了两个dll引用。除了app.config之外,我所有自己编写的代码全部在Form1.cs中。其实相当于我把全部源代码一字不漏全告诉你了。

我的Form1界面如下:
DALSA网口线扫相机SDK开发详解例程(C#版)_第3张图片
(点击Init初始化,会弹出该线阵相机的型号)

然后点击“snap”的话,它会连续采集15张图,并保存,如下:
DALSA网口线扫相机SDK开发详解例程(C#版)_第4张图片
Form1.cs的全部内容如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using DALSA.SaperaLT.SapClassBasic;
using HalconDotNet;

namespace WinDalsa
{
    public partial class Form1 : Form
    {
        private SapLocation m_ServerLocation;   // 设备的连接地址
        private SapAcqDevice m_AcqDevice;       // 采集设备
        private SapBuffer m_Buffers;            // 缓存对象
        private SapAcqDeviceToBuf m_Xfer;       // 传输对象

        public Form1()
        {
            InitializeComponent();
        }


        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            DestroyObjects();
            DisposeObjects();
        }


        private void btn_init_Click(object sender, EventArgs e)
        {
            CreateNewObjects();
        }

        private void btn_setting_Click(object sender, EventArgs e)
        {
            //设置曝光值,为了设置的值不超限,需要获取曝光值的允许范围(主要是最大值)
            double valuetemp = GetMaxValue("ExposureTime");
            if (valuetemp > 0)
            {
                m_AcqDevice.SetFeatureValue("ExposureTime", valuetemp);
            }

            m_AcqDevice.SetFeatureValue("Gain", "9.9");
        }


        // 获得相机部分属性的值
        private void btn_getValue_Click(object sender, EventArgs e)
        {
            string deviceModelName;
            string deviceUserId;
            string pixelFormat;
            string triggerMode;

            double acquisitionLineRate;  //行频和曝光时间不能设置为int类型
            double exposureTime;
            double gain;
            int width;
            int height;
            int sensorWidth;
            int sensorHeight;


            m_AcqDevice.GetFeatureValue("DeviceModelName", out deviceModelName);  //Linea M4096-7um
            m_AcqDevice.GetFeatureValue("DeviceUserID", out deviceUserId);  //空
            m_AcqDevice.GetFeatureValue("PixelFormat", out pixelFormat);  //Mono8
            m_AcqDevice.GetFeatureValue("TriggerMode", out triggerMode);  //Off

            m_AcqDevice.GetFeatureValue("AcquisitionLineRate", out acquisitionLineRate);  //10000.0
            m_AcqDevice.GetFeatureValue("ExposureTime", out exposureTime);  //70.0
            m_AcqDevice.GetFeatureValue("Gain", out gain);  //9.0
            m_AcqDevice.GetFeatureValue("Width", out width);  //4096
            m_AcqDevice.GetFeatureValue("Height", out height);  //2800
            m_AcqDevice.GetFeatureValue("SensorWidth", out sensorWidth);  //4096
            m_AcqDevice.GetFeatureValue("SensorHeight", out sensorHeight);  //1

        }


        #region 单步采集、连续采集、冻结采集
        private void btn_snap_Click(object sender, EventArgs e)
        {

            //Snap()只采集一张,如果是Snap(15)则连续采集15张
            m_Xfer.Snap(15);//m_Xfer.Snap(m_Buffers.Count)
        }

        private void btn_grab_Click(object sender, EventArgs e)
        {
            m_Xfer.Grab();
        }

        private void btn_freeze_Click(object sender, EventArgs e)
        {
            m_Xfer.Freeze(); //还有m_Xfer.Abort()的用法;

        }

        #endregion



        //得到所有连接的相机信息,并将它们加入到ArrayList里面去
        public bool GetCameraInfo(out string sCameraName, out int nIndex)
        {
            sCameraName = "";
            nIndex = 0;

            int serverCount = SapManager.GetServerCount();
            int GenieIndex = 0;
            System.Collections.ArrayList listServerNames = new System.Collections.ArrayList();

            bool bFind = false;
            string serverName = "";
            for (int serverIndex = 0; serverIndex < serverCount; serverIndex++)
            {
                if (SapManager.GetResourceCount(serverIndex, SapManager.ResourceType.AcqDevice) != 0)
                {
                    serverName = SapManager.GetServerName(serverIndex);
                    listServerNames.Add(serverName);
                    GenieIndex++;
                    bFind = true;
                }
            }

            int count = 1;
            string deviceName = "";
            foreach (string sName in listServerNames)
            {
                deviceName = SapManager.GetResourceName(sName, SapManager.ResourceType.AcqDevice, 0);
                count++;
            }

            sCameraName = serverName;
            nIndex = GenieIndex;

            return bFind;
        }



        private bool CreateNewObjects()
        {

            string Name;
            int Index;
            bool RTemp = GetCameraInfo(out Name, out Index);
            if (RTemp)
            {
                MessageBox.Show(Name);
            }
            else
            {
                MessageBox.Show("Get camera info false!");
                return false;
            }


            m_ServerLocation = new SapLocation(Name, 0);


            // 创建采集设备,new SapAcqDevice()的括号中第二个参数既可以写配置文件路径,也可以写false,猜测false是用相机当前的设置
            //m_AcqDevice = new SapAcqDevice(m_ServerLocation, false);
            m_AcqDevice = new SapAcqDevice(m_ServerLocation, @"C:\Users\xh6300\Desktop\dalsa_win7_develop\gray\T_Linea_M4096-7um_Default_Default_2800.ccf");


            if (m_AcqDevice.Create() == false)
            {
                DestroyObjects();
                DisposeObjects();
                return false;
            }
            // 创建缓存对象
            if (SapBuffer.IsBufferTypeSupported(m_ServerLocation, SapBuffer.MemoryType.ScatterGather))
            {
                m_Buffers = new SapBufferWithTrash(2, m_AcqDevice, SapBuffer.MemoryType.ScatterGather);
            }
            else
            {
                m_Buffers = new SapBufferWithTrash(2, m_AcqDevice, SapBuffer.MemoryType.ScatterGatherPhysical);
            }
            if (m_Buffers.Create() == false)
            {
                DestroyObjects();
                DisposeObjects();
                return false;
            }

            m_AcqDevice.SetFeatureValue("AcquisitionLineRate", 20000.0); //注意:行频在相机工作时不能设置(曝光、增益可以),最好在初始化阶段设置

            // 创建传输对象
            m_Xfer = new SapAcqDeviceToBuf(m_AcqDevice, m_Buffers);
            m_Xfer.XferNotify += new SapXferNotifyHandler(m_Xfer_XferNotify);
            m_Xfer.XferNotifyContext = this;
            m_Xfer.Pairs[0].EventType = SapXferPair.XferEventType.EndOfFrame;
            m_Xfer.Pairs[0].Cycle = SapXferPair.CycleMode.NextWithTrash;
            if (m_Xfer.Pairs[0].Cycle != SapXferPair.CycleMode.NextWithTrash)
            {
                DestroyObjects();
                DisposeObjects();
                return false;
            }
            if (m_Xfer.Create() == false)
            {
                DestroyObjects();
                DisposeObjects();
                return false;
            }
            return true;
        }


        private void DestroyObjects()
        {
            if (m_Xfer != null && m_Xfer.Initialized)
                m_Xfer.Destroy();
            if (m_Buffers != null && m_Buffers.Initialized)
                m_Buffers.Destroy();
            if (m_AcqDevice != null && m_AcqDevice.Initialized)
                m_AcqDevice.Destroy();
        }

        private void DisposeObjects()
        {
            if (m_Xfer != null)
            { m_Xfer.Dispose(); m_Xfer = null; }
            if (m_Buffers != null)
            { m_Buffers.Dispose(); m_Buffers = null; }
            if (m_AcqDevice != null)
            { m_AcqDevice.Dispose(); m_AcqDevice = null; }
        }


        private static int picCountNum = 0; //统计采集了多少张图,有利于理解m_Xfer.Snap(15)中15的意思

        void m_Xfer_XferNotify(object sender, SapXferNotifyEventArgs argsNotify)
        {
            //首先需判断此帧是否是废弃帧,若是则立即返回,等待下一帧(但这句话似乎有时候m_Xfer.Snap(n)时会导致丢帧,可以注释掉试试)
            if (argsNotify.Trash) return;

            //获取m_Buffers的地址(指针),只要知道了图片内存的地址,其实就能有各种办法搞出图片了(例如转成Bitmap)
            IntPtr addr;
            m_Buffers.GetAddress(out addr);

            //观察buffer中的图片的一些属性值,语句后注释里面的值是可能的值
            int count = m_Buffers.Count;  //2
            SapFormat format = m_Buffers.Format;  //Uint8
            double rate = m_Buffers.FrameRate;  //30.0,连续采集时,这个值会动态变化
            int height = m_Buffers.Height;  //2800
            int weight = m_Buffers.Width;  //4096
            int pixd = m_Buffers.PixelDepth;  //8

            //显示实时帧率
            UpdateFrameRate();
            lbl_FrameRate.BeginInvoke(new Action(() => { lbl_FrameRate.Text = m_Buffers.FrameRate.ToString();}));

            //利用halcon从内存中采集图片并保存
            HObject ImageTemp = null;
            HOperatorSet.GenImage1(out ImageTemp, "byte", 4096, 2000, addr);//取内存数据,生成图像,halcon实现

            hWindowControl1.HalconWindow.SetPart(0, 0, 2000, 4096);
            HOperatorSet.DispObj(ImageTemp, hWindowControl1.HalconWindow);
            picCountNum++;
            HOperatorSet.WriteImage(ImageTemp, "bmp", 0, "C:\\Users\\xh6300\\Desktop\\tt\\" + picCountNum);

        }


        private void UpdateFrameRate()
        {
            if (m_Xfer.UpdateFrameRateStatistics())
            {
                float framerate = 0.0f;
                SapXferFrameRateInfo stats = m_Xfer.FrameRateStatistics;

                if (stats.IsBufferFrameRateAvailable)
                    framerate = stats.BufferFrameRate;
                else if (stats.IsLiveFrameRateAvailable && !stats.IsLiveFrameRateStalled)
                    framerate = stats.LiveFrameRate;

                m_Buffers.FrameRate = framerate;
            }
        }



        //获得相机参数的最大值(行频和曝光时间是近似倒数的关系,获得参数最大值可以防止设置参数超限)
        private double GetMaxValue(string featureName)
        {
            SapFeature feature = new SapFeature(m_ServerLocation);
            if (!feature.Create())
                return -1;
            if (!m_AcqDevice.GetFeatureInfo(featureName, feature))
                return -1;
            double maxValue = 0;
            if (!feature.GetValueMax(out maxValue))
                return -1;
            return maxValue;
        }


        //这个一般用得少,最小值一般是很小的数(比如Gain最小0.125,width最小128),我们一般不会设置这样的数
        private double GetMinValue(string featureName)
        {
            SapFeature feature = new SapFeature(m_ServerLocation);
            if (!feature.Create())
                return -1;
            if (!m_AcqDevice.GetFeatureInfo(featureName, feature))
                return -1;
            int minValue = 0;
            if (!feature.GetValueMin(out minValue))
                return -1;
            return minValue;
        }
    }
}

当你完全理解了这篇文章(以及我提到的两篇),Dalsa网口线阵相机的开发基本就没啥问题了,当然这时候你返回去看官方demos会有新的收获,比如我上面的UpdateFrameRate()就是从官方demos中剥离出来的,该函数可以得到采图时的实际帧率。

你可能感兴趣的:(C#,机器视觉,c#,ui,开发语言)