技术标签: visual studio code 文件名
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文件内容如下:
<?xml version="1.0" encoding="utf-8"?>现在你需要把我上文提到的两篇博客仔细看一下了。
……
看完了吗?看完了的话,接着往下看我的文章。
首先学习一下相机配置文件(.ccf)如何生成:
① 打开相机软件Sapera CamExpert,确保相机已经正常工作,然后自己改变到合适的参数;
② 点击软件左上角的File——Save As…,选择文件夹路径,修改文件名。
我的解决方案资源结构如下:
因为我是用Halcon显示图片,因此我添加了两个dll引用。除了app.config之外,我所有自己编写的代码全部在Form1.cs中。其实相当于我把全部源代码一字不漏全告诉你了。
我的Form1界面如下:
(点击Init初始化,会弹出该线阵相机的型号)
然后点击“snap”的话,它会连续采集15张图,并保存,如下:
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中剥离出来的,该函数可以得到采图时的实际帧率。
作者:xh6300
出处:http://www.cnblogs.com/xh6300/
寻找最大数(三)时间限制:1000 ms | 内存限制:65535 KB难度:2描述给出一个整数N,每次可以移动2个相邻数位上的数字,最多移动K次,得到一个新的整数。求这个新的整数的最大值是多少。输入多组测试数据。每组测试数据占一行,每行有两个数N和K (1 ≤ N≤ 10^18; 0 ≤ K ≤ 100).输出每组测试数据的
单链表的c语言实现文章目录单链表的c语言实现定义单链表结构体初始化单链表操作头插法创建单链表尾插法创建单链表计算单链表的长度获取单链表中元素新元素插入单链表删除链表中某元素单链表的整表删除打印出整个单链表链表是通过一组任意的存储单元来存储线性表中的数据元素,这些存储单元可以是连续的也可以是不连续的。为了建立起数据元素之间的关系,对于每个数据元素除了存放数据元素自身的信息外,还必须有包含的指示...
Window表示一个窗口的概念,在日常开发中直接接触Window的机会并不多,但是在某些特殊时候我们需要在桌面上显示一个类似悬浮窗的东西,那么这效果就需要用到Window来实现。Window是一个抽象类,它的具体实现是pHoneWindow。创建一个Window是一个很简单的事,只需要通过WindowManager即可完成。WindowManager是外界访问Window的入口,Window的具体...
DataFrame.drop_duplicates(subset=None, keep='first', inplace=False)参数解释:subset: 列名,默认所有的列 keep: 是否保留{‘first’, ‘last’, False},keep= 'first' 表示去重时每组重复数据保留第一条数据,其余数据丢弃; keep='last' 表示去重时每组重复数据保留最后一条...
第一阶段:从源代码到可执行程序第一个helloworld程序:#include int main(int argc, char** argv){printf("hello world\n");return 0;}编译接生成可执行程序(arm
case1:由于rs.next()遍历查询结果时,下标是从“1”开始,而不是是从“0”开始,导致出错case2:public class infoDAO {public ResultSet rs;DBManager db=new DBManager();public List jquery(String sql) throws SQLException{rs=db.execut
同一个场景,同时存在透明和不透明物体,存在各种遮挡关系。正常渲染流程:渲染不透明物体时,开启深度测试和深度写入,开启渲染透明物体时,开启深度测试,关闭深度写入。两种状态,互相切换。 深度测试与开启就3种情况:(以下透明物体称为A,不透明物体称为B)1、始终开启深度测试和深度写入(默认状态) 此时,如果A在前,B在后,深度测试时,B不能通过测试,B的像素直接被丢弃,因此B不能参与混合,也就不能实现透明效果。2、渲染透明物体时,直接...
1.题目描述对于表达式n^2+n+41,当n在(x,y)范围内取整数值时(包括x,y)(-39<=x<50),判定该表达式的值是否为素数2.输入描述:输入数据有多组,每组占一行,由两个整数x,y组成,当x=0,y=0时,表示输入结束,该行不做处理。3.输出描述:对于每个给定范围内的取值,如果表达式的值都为素数,则输出"OK",否则请输出“Sorry”,每组输出占一行。4.输入例子:0 10 05.输出例子:OK6.解题思路:1、首先依据题意保证x、y不同
漏洞漏洞原因是由于框架对控制器名没有进行足够的校验导致在没有开启强制路由的情况下可以构造恶意语句执行远程命令受影响的版本包括5.0和5.1版本。漏洞影响范围Thinkphp 5.1.0 - 5.1.31Thinkphp 5.0.5 - 5.0.23漏洞复现通过phpinfo函数写出phpinfo()的信息 http://localhost/thinkphp5.1beta/public/index.php?s=index/\think\Container/invokefunction
目录01,常用的辅助类CountDownLatchCyclicBarrierSemaphore02,java.util.concurrent.locks.ReadWriteLock:读写锁03,java.util.concurrent.BlockQueue:阻塞对列01,常用的辅助类先在我们来说一说java.util.concurrent包下的三个常用辅助类CountDownLatchCountDownLatch:计数器(下计数器):允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同
NRF5脱机烧录器 STM8 STM32离线编程器 GD32 MM32 NXP LPC编程器
1.java平台可以直接与单片机里面的4G网络相对接吗?怎么进行对接的 ?