图片采集以及转存—DALSA相机SDK开发(不再涉及halcon或opencv等)
借助DALSA提供的官方SDK,以及部分,完成相机的信息获取、相机连接、内存裸数据采集、转存图片一系列流程,不再像之前一样涉及到halcon或者opencv来处理图像,而是只使用DALSA的官方SDK来进行开发。
本项目做的是C#控制台项目,没有界面,如果要界面的话,网上也有代码可以借鉴,可以添加halcon的空间HalconWindow,来直接用halcon的HOperatorSet来放;
也可以不使用halcon,只用winForm内自带的pictureBox控件,但是那样的话就需要将图片先转成bitMap,并且使用调色板来放置。
我不作Form窗体,界面的事不再赘述,因为我是自动连续采集(Grab),自动停止(Freeze),开始和结束靠的是控制程序发过来的信号,使用Socket通信实现。
1.全局变量
private SapLocation m_ServerLocation; // 设备的连接地址
private SapAcqDevice m_AcqDevice; // 采集设备
private SapBuffer m_Buffers; // 缓存对象
private SapAcqDeviceToBuf m_Xfer; // 传输对象
private String urlFromJava;
private Boolean socketStatu = true;
前四个是DALSA的DLL内的对象,后两个是用于自动触发和停止用的变量。
2.main方法调用
static void Main(string[] args)
{
Program p = new Program();
p.socketServer();//开始Socket通信服务端,监听等待java发送的开始采集信号
}
很简单,因为main方法是static静态的,但是我socketServer()方法不是静态的,所以调用的时候需要实例化项目再调用,要不然就是socketServer()直接调用了。
3.触发
private void socketServer()
{
Console.WriteLine("开启Socket通信--C#服务端");
Socket soo = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ip = IPAddress.Parse("127.0.0.1");
IPEndPoint ipend = new IPEndPoint(ip, 61666);
soo.Bind(ipend);
soo.Listen(10);
byte[] by = new byte[1024];
while (true)
{
Socket so = soo.Accept();
int len = so.Receive(by);//Send发送 Receive接收
Console.WriteLine("len=" + len);
if (socketStatu)
{
urlFromJava = Encoding.UTF8.GetString(by, 0, len);
Console.WriteLine("赋值地址");
}
if (len > 5)
{
Console.WriteLine("urlFromJava=" + urlFromJava);
CreateNewObjects();//初始化相机
Console.WriteLine("java字符串:" + urlFromJava + "-----C#开始采集");
m_Xfer.Grab();
socketStatu = false;
}
else if (len == 3)
{
m_Xfer.Freeze();
Thread.Sleep(5000);
m_Xfer.Abort();
DestroyObjects();
DisposeObjects();
picCountNum = 0;
socketStatu = true;
Console.WriteLine("java字符串:" + urlFromJava + "-----C#结束采集");
}
}
}
C#这边程序是Socket服务器端,接收来java程序(Scoket客户端,负责控制)的信号,根据收到信息不同进行开始采集或者结束采集。
urlFromJava这个变量是一个地址,java端来告诉采集程序保存地址。地址传过来之后,第一步初始化相机,连接相机,然后Grab(),开始采集。
关闭的时候,执行Freez()停止采集,线程等待5秒,目的是停止采集后,将还存在内存地址通道中的裸数据都取出来,如果freeze之后直接释放,就拿不到还在地址上的数据了,缓存对象释放之后,将本次采集所有对象摧毁。
4.初始化并连接相机
private bool CreateNewObjects()
{
Console.WriteLine("相机初始化");
string Name;
int Index;
//获取相机详细信息
bool RTemp = GetCameraInfo(out Name, out Index);
if (RTemp)
{
Console.WriteLine("相机" + Name);
}
else
{
Console.WriteLine("未获取到相机信息!");
return false;
}
m_ServerLocation = new SapLocation(Name, 0);
// 创建采集设备,new SapAcqDevice()的括号中第二个参数写配置文件路径,配置文件为ccf格式
m_AcqDevice = new SapAcqDevice(m_ServerLocation, @"C:\Users\Administrator\Desktop\T_Linea_M4096-7um_Default_Default.ccf");
//当参数为false时表示采用相机当前设备
//m_AcqDevice = new SapAcqDevice(m_ServerLocation, false);
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;
}
获取相机信息,加载相机配置文件(用相机专家调整好参数后导出ccf文件),加载参数,m_Xfer.XferNotify += new SapXferNotifyHandler(m_Xfer_XferNotify);这一句是核心,这是回调函数,就靠它采图了。后面会告诉你们m_Xfer_XferNotify的实现。
记下来一次介绍初始化中调用的几个子方法,分别是GetCameraInfo、DestroyObjects、DisposeObjects、m_Xfer_XferNotify、UpdateFrameRate。
5.获取相机信息
public bool GetCameraInfo(out string sCameraName, out int nIndex)
{
Console.WriteLine("开始获取相机信息");
sCameraName = "";
nIndex = 0;
//查询相机数量
int serverCount = SapManager.GetServerCount();
int GenieIndex = 0;
//实例化一个list,作为容器
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;
}
6.回调函数m_Xfer_XferNotify
private static int picCountNum = 0;
void m_Xfer_XferNotify(object sender, SapXferNotifyEventArgs argsNotify)
{
if (argsNotify.Trash) return;
//获取m_Buffers的地址(指针)
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; //1000
int weight = m_Buffers.Width; //4096
int pixd = m_Buffers.PixelDepth; //8
//显示实时帧率
UpdateFrameRate();
picCountNum++;
m_Buffers.Save(urlFromJava + picCountNum + ".bmp", "-format bmp");
}
这个sava方法就是从SDK中提取出来的,给上参数,就可以实现图片的保存,不用借助任何其他的技术方法,SDK本身就可以。
7.更新实时帧率
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;
}
}
8.dispose和destory摧毁以及释放
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; }
}