我们前面有一个版本,没有整理,只能是500万海康gige工业相机无驱动取像成功(黑白相机gm)。
这里,版本更新了,可以不是500万,200万,80万也可以,你可以去试一试,我手头没有200万,80万的相机,有使用的,可以反馈一下结果,也就是说,写了一个通用版本,黑白相机,海康gige工业相机,你可以不安装任何驱动就能取像,所以呢?你可以移植成c语言版本,也可以在linux下,没有驱动的情况下,跑起来,想起来,还是很开心的,更新如下:
第一,界面(为了放入截图,我轻微整理了一下):
第二,代码(不引用加载用任何动态库,打开c#,copy进去直接用):
1,获取ip地址按钮:
//get local ip
byte[] localip;
byte[] camip;
byte portgao8wei;
byte portdi8wei;
private void button1_Click(object sender, EventArgs e)
{
//iplocal = IPAddress.Parse("192.168.21.123");
//ipremote = IPAddress.Parse("192.168.21.54");
iplocal = IPAddress.Parse(textBox1.Text);
localip = iplocal.GetAddressBytes();
ipremote = IPAddress.Parse(textBox2.Text);
camip = ipremote.GetAddressBytes();
int value = Convert.ToInt32(textBox11.Text);
portgao8wei= (byte)(value >> 8);
portdi8wei = (byte)(value);
}
UdpClient GVCPsocket = new UdpClient(3956);
2,gvcp通过3956第一次通信,按钮代码:
//gvcp tongguo3956 diyici tongxin
private void button2_Click(object sender, EventArgs e)
{
byte[] sendbuff = new byte[12] { 0x42, 0x01, 0x00, 0x02, 0x00, 0x00, 0, 1, 0, 0, 0, 0 };
IPEndPoint remotetarget = new IPEndPoint(ipremote, 3956);
IPEndPoint localtarget = new IPEndPoint(iplocal, 3956);
GVCPsocket.Send(sendbuff, 12, remotetarget);
Thread.Sleep(200);
byte[] buf = GVCPsocket.Receive(ref localtarget);//44,45,46,47
textBox3.Text = buf[44].ToString()+"."+buf[45].ToString()+"."+buf[46].ToString()+"."+buf[47].ToString();
}
3,gvcp通过3956获取宽度,按钮代码:
private void button3_Click(object sender, EventArgs e)
{
IPEndPoint remotetarget = new IPEndPoint(ipremote, 3956);
IPEndPoint localtarget = new IPEndPoint(iplocal, 3956); ;
byte[] sendbuff = new byte[12] { 0x42, 0x01, 0x00, 0x80, 0x00, 0x04,
0x00, 0x91, 0x00, 0x03, 0x03, 0x60 };
GVCPsocket.Send(sendbuff, 12, remotetarget);
Thread.Sleep(200);
byte[] camwidth = GVCPsocket.Receive(ref localtarget);//width=0x0a20=2592
int width = (camwidth[10] << 8) | camwidth[11];
textBox4.Text = width.ToString();
imgW = width;
}
int imgW = 0;
int imgH = 0;
4,gvcp通过3956获取高度,按钮代码:
private void button4_Click(object sender, EventArgs e)
{
IPEndPoint localtarget = new IPEndPoint(IPAddress.Any, 3956);
IPEndPoint remotetarget = new IPEndPoint(ipremote, 3956);
// IPEndPoint remotetarget = new IPEndPoint(IPAddress.Parse("192.168.21.54"), 3956);
byte[] sendbuff = new byte[12] { 0x42, 0x01, 0x00, 0x80, 0x00, 0x04,
0x00, 0x92, 0x00, 0x03, 0x03, 0xa0 };
GVCPsocket.Send(sendbuff, 12, remotetarget);
Thread.Sleep(200);
byte[] camheight = GVCPsocket.Receive(ref localtarget);//h=0x0798=1944
int height = (camheight[10] << 8) | camheight[11];
textBox5.Text = height.ToString();
imgH = height;
}
5,gvcp通过3956获取offsetx,按钮代码:
private void button6_Click(object sender, EventArgs e)
{
IPEndPoint localtarget = new IPEndPoint(IPAddress.Any, 3956);
IPEndPoint remotetarget = new IPEndPoint(ipremote, 3956);
// IPEndPoint remotetarget = new IPEndPoint(IPAddress.Parse("192.168.21.54"), 3956);
byte[] sendbuff = new byte[12] { 0x42, 0x01, 0x00, 0x80, 0x00, 0x04,
0x00, 0x93, 0x00, 0x03, 0x03, 0x0e0 };
GVCPsocket.Send(sendbuff, 12, remotetarget);
Thread.Sleep(200);
byte[] camoffsetx = GVCPsocket.Receive(ref localtarget);//1
int offsetx = (camoffsetx[10] << 8) | camoffsetx[11];
textBox7.Text = offsetx.ToString();
}
6,gvcp通过3956获取offsety,按钮代码:
private void button5_Click(object sender, EventArgs e)
{
IPEndPoint localtarget = new IPEndPoint(IPAddress.Any, 3956);
IPEndPoint remotetarget = new IPEndPoint(ipremote, 3956);
//IPEndPoint remotetarget = new IPEndPoint(IPAddress.Parse("192.168.21.54"), 3956);
byte[] sendbuff = new byte[12] { 0x42, 0x01, 0x00, 0x80, 0x00, 0x04,
0x00, 0x94, 0x00, 0x03, 0x04, 0x20};
GVCPsocket.Send(sendbuff, 12, remotetarget);
Thread.Sleep(100);
byte[] camoffsety = GVCPsocket.Receive(ref localtarget);//1
int offsety = (camoffsety[10] << 8) | camoffsety[11];
textBox6.Text = offsety.ToString();
}
7,gvcp通过3956获取mono,按钮代码:
private void button7_Click(object sender, EventArgs e)
{
IPEndPoint localtarget = new IPEndPoint(IPAddress.Any, 3956);
IPEndPoint remotetarget = new IPEndPoint(ipremote, 3956);
// IPEndPoint remotetarget = new IPEndPoint(IPAddress.Parse("192.168.21.54"), 3956);
byte[] sendbuff = new byte[12] { 0x42, 0x01, 0x00, 0x80, 0x00, 0x04,
0x00, 0x95, 0x00, 0x03, 0x06, 0x10 };
GVCPsocket.Send(sendbuff, 12, remotetarget);
Thread.Sleep(50);
byte[] cammono8 = GVCPsocket.Receive(ref localtarget);//=01080001
int mono8 = (cammono8[10] << 8) | cammono8[11];
if(1==mono8)
textBox8.Text = "0x01080001";
}
8,gvcp通过3956设置心跳,按钮代码:
bool 可以控制 = false;
bool 可以设置心跳 = false;
string 反馈信息 = "";
private void button8_Click(object sender, EventArgs e)
{
反馈信息 = "";
textBox9.Text = "";
IPEndPoint localtarget = new IPEndPoint(IPAddress.Any, 3956);
IPEndPoint remotetarget = new IPEndPoint(ipremote, 3956);
byte[] sendbuff = new byte[12] { 0x42, 0x01, 0x00, 0x80, 0x00, 0x04,
0x01, 0x28, 0x00, 0x00, 0x0a, 0x00 };
GVCPsocket.Send(sendbuff, 12, remotetarget);
Thread.Sleep(50);
byte[] retfree = GVCPsocket.Receive(ref localtarget);// //如果返回如此[11]=0,its free can be control
if (retfree[11] == 0)
{
可以控制 = true;
反馈信息 += "返回为零,可以控制寄存器"+"\r\n";
}
if (可以控制)
{
//开始控制,向寄存器写入0x02
sendbuff = new byte[16] { 0x42, 0x01, 0x00, 0x82, 0x00, 0x08,
0x01, 0x29, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x02 };
GVCPsocket.Send(sendbuff, 16, remotetarget);
Thread.Sleep(50);
retfree = GVCPsocket.Receive(ref localtarget);// //如果返回如此[11]=1,开始设置心跳
if (retfree[11] == 1)
{
可以设置心跳 = true;
反馈信息 += "控制寄存器写入0x02成功" + "\r\n";
}
}
if (可以设置心跳)
{
byte 增加1 = 0x2d;
for (int i = 0; i < 5; i++)//怎样判断gvcpReply.status==success?如果在5次内设置成功了,就要跳出来
{
byte[] sendbuff16 = new byte[16] { 0x42, 0x01, 0x00, 0x82, 0x00, 0x08,
0x01, 增加1, 0x00, 0x00, 0x09, 0x38, 0x00, 0x00, 0x27, 0x10 };
GVCPsocket.Send(sendbuff16, 16, remotetarget);
Thread.Sleep(500);
byte[] ret12 = GVCPsocket.Receive(ref localtarget);
if (ret12[11] == 1)
{
反馈信息 += "心跳写入成功" + "\r\n";
//成功了?
i = 5;
break;
}
增加1++;
}
}
backgroundWorker1.RunWorkerAsync();//启动心跳
}
9,我们这里第八步,使用了一个异步线程backgroundWorker1,代码如下:
bool m_bgvsp = false;
Socket socketRxRaw;
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
byte hello增加1 = 0x2d;
IPEndPoint remotetarget = new IPEndPoint(ipremote, 3956);
m_bgvsp = true;
while (m_bgvsp)
{
byte[] sendbuff16 = new byte[16] { 0x42, 0x01, 0x00, 0x82, 0x00, 0x08,
0x01, hello增加1, 0x00, 0x00, 0x09, 0x38, 0x00, 0x00, 0x27, 0x10 };
GVCPsocket.Send(sendbuff16, 16, remotetarget);
hello增加1++;
Thread.Sleep(50);
}
}
10,gvsp取像,按钮代码如下:
byte[][] buffer5038848 = new byte[2][];
byte[] m_buff9000 = new byte[9000];
private void button9_Click(object sender, EventArgs e)
{
if (m_bgvsp)
{
//1,开始控制,向寄存器写入GevSCPHostPort
IPEndPoint remotetarget = new IPEndPoint(ipremote, 3956);
byte[] sendbuff16 = new byte[16] { 0x42, 0x01, 0x00, 0x82, 0x00, 0x08,
0x01, 0x2e, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, portgao8wei, portdi8wei};//写入端口port=63378
//0x01, 0x2e, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0f7, 0x92};//写入端口port=63378
GVCPsocket.Send(sendbuff16, 16, remotetarget);//返回1,才可以下一步
Thread.Sleep(50);
textBox11.Enabled = false;
//2,开始控制,向寄存器写入GevSCDA
sendbuff16 = new byte[16] { 0x42, 0x01, 0x00, 0x82, 0x00, 0x08,
//0x01, 0x2f, 0x00, 0x00, 0x0d, 24, 192, 168, 21, 123};
0x01, 0x2f, 0x00, 0x00, 0x0d, 24, localip[0], localip[1], localip[2], localip[3]};
//写入ip192.168.20.48
GVCPsocket.Send(sendbuff16, 16, remotetarget);//返回1,才可以下一步
Thread.Sleep(50);
反馈信息 += "GevSCPHostPort写入63378成功" + "\r\n";
// textBox10.Enabled = false;
/* 3, <Integer Name="ExposureTime_RegAddr">
<Value>
0x00030b04
</Value>*/
sendbuff16 = new byte[16] {0x42, 0x01, 0x00, 0x82, 0x00, 0x08,
0x03, 0x2f, 0x00, 0x03, 0x0b, 0x04,0,0,0x61,0x0a8};
//写入曝光值25000
// 0x03, 0x2f, 0x00, 0x03, 0x0b, 0x04,0,0,0x3a,0x98}; //写入曝光值15000
GVCPsocket.Send(sendbuff16, 16, remotetarget);//返回1,才可以下一步
Thread.Sleep(50);
反馈信息 += "GevSCDA写入192, 168, 21, 123成功" + "\r\n";
//4,开始控制,向寄存器写入 var gevSCPSPacketSize = (await Gvcp.GetRegister(nameof(GvcpRegister.GevSCPSPacketSize))).pValue;//地址0x0d04
sendbuff16 = new byte[16] {0x42, 0x01, 0x00, 0x82, 0x00, 0x08,
0x01, 0x31, 0x00, 0x00, 0x0d, 0x04, 64, 0, 0x1f, 0x0e4};
//写入mtu8164
GVCPsocket.Send(sendbuff16, 16, remotetarget);//返回1,才可以下一步
Thread.Sleep(50);
反馈信息 += "写入曝光值25000成功" + "\r\n";
//5,开始控制,向寄存器写入 acquisitionStart.SetValueAsync(1).ConfigureAwait(false)) as GvcpReply
sendbuff16 = new byte[16] {0x42, 0x01, 0x00, 0x82, 0x00, 0x08,
0x01, 0x32, 0x00, 0x03, 0x08, 4, 0, 0, 0, 1};
//写入mtu8164
GVCPsocket.Send(sendbuff16, 16, remotetarget);//返回1,才可以下一步,进入异步线程接受图像
Thread.Sleep(50);
反馈信息 += "写入mtu8164成功" + "\r\n";
IPEndPoint localtarget = new IPEndPoint(IPAddress.Any, 3956);
byte[] ret12 = GVCPsocket.Receive(ref localtarget);
if (ret12[11] == 0x01)
{
反馈信息 += "写入acquisitionStart==1成功" + "\r\n";
//buffer5038848[0] = new byte[2592 * 1944];
//buffer5038848[1] = new byte[5038848];
buffer5038848[0] = new byte[imgW * imgH];
buffer5038848[1] = new byte[imgW * imgH];
一帧分包 = (imgW * imgH) / 8128;
帧分包余数 = imgW * imgH - 一帧分包 * 8128;
//destptrbig = Marshal.AllocHGlobal(5038848);
destptrbig = Marshal.AllocHGlobal(imgW * imgH);
destptrbig1 = Marshal.AllocHGlobal(imgW * imgH);
//初始化接受套接字
// 设置UDP服务器的端点
socketRxRaw = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
// socketRxRaw.Bind(new IPEndPoint(IPAddress.Any, 63378));// ProtocolType.Udp
int hello= Convert.ToInt32(textBox11.Text);
socketRxRaw.Bind(new IPEndPoint(IPAddress.Any,hello ));
// IPEndPoint remotetargetRx = new IPEndPoint(IPAddress.Parse("192.168.20.54"), 63378);
// IPEndPoint localtargetRx = new IPEndPoint(IPAddress.Any, 63378);
socketRxRaw.ReceiveTimeout = 1000;
//One full hd image with GVSP2.0 Header as default, it will be updated for image type
//socketRxRaw.ReceiveBufferSize = (int)(1920 * 1100);
socketRxRaw.ReceiveBufferSize = (int)(2* 2500 * 2024);
创建缓冲区以接收数据
//byte[] bytess = new byte[socketRxRaw.ReceiveBufferSize];
//EndPoint remoteEP = (EndPoint)groupEP;
//接受第0帧,测试帧
// socketRxRaw.Receive(m_buff9000);
// 等待广播数据
//int bytesRec = socketRxRaw.ReceiveFrom(bytess, ref remoteEP);
//if (bytess[7]==0)
{
backgroundWorker2.RunWorkerAsync();
// timer1.Start();
反馈信息 += "gvsp开始取像" + "\r\n";
textBox9.Text += 反馈信息;
}
}
}
}
11,这里第十步,使用了异步线程2,backgroundWorker2,其调用代码如下(这里是接收图像步骤,这里不仅仅500万相机可以用,这里没写死,写的通用了):
bool yongzhen = true;
int 一帧分包 = 0;
int 帧分包余数 = 0;
int frame = 0;
private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
{
// IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, 63378);
//EndPoint remoteEP=(EndPoint)groupEP;
frame = 0;
do
{
try
{
int changdu = socketRxRaw.Receive(m_buff9000);//与函数ReceiveFrom结果一样
if (changdu > 1000)
{
int packetIDperframe = (m_buff9000[6] << 8) | m_buff9000[7];
if (packetIDperframe == 0) continue;
int jiou = frame % 2;
switch (jiou)
{
case 0:
{
//if (packetIDperframe == 620)//一帧分包
if (packetIDperframe == 一帧分包 + 1)//一帧分包
{
//System.Runtime.InteropServices.Marshal.Copy(m_buff9000, 8, destptrbig + (packetIDperframe - 1) * 8128, 7616);//结果与上面一样,有横线 }
// 帧分包余数
System.Runtime.InteropServices.Marshal.Copy(m_buff9000, 8, destptrbig + (packetIDperframe - 1) * 8128, 帧分包余数);
System.Runtime.InteropServices.Marshal.Copy(destptrbig, buffer5038848[0], 0, imgH * imgW);
showbuffer2pict(buffer5038848[0], imgW, imgH, pictureBox1);//不是bmp z序的,正常的202302061722
//System.Runtime.InteropServices.Marshal.Copy(destptrbig, buffer5038848[0], 0, 5038848);
//showbuffer2pict(buffer5038848[0], 2592, 1944, pictureBox1);//不是bmp z序的,正常的202302061722
frame++;
}
else
{
//destptrbig202403301518,为什么c#有横线,c版本没有,代码是一致的
System.Runtime.InteropServices.Marshal.Copy(m_buff9000, 8, destptrbig + (packetIDperframe - 1) * 8128, 8128);//结果与上面一样,有横线 }
}
}
break;
case 1:
{
//if (packetIDperframe == 620)//一帧分包
if (packetIDperframe == 一帧分包 + 1)//一帧分包
{
//System.Runtime.InteropServices.Marshal.Copy(m_buff9000, 8, destptrbig + (packetIDperframe - 1) * 8128, 7616);//结果与上面一样,有横线 }
// 帧分包余数
System.Runtime.InteropServices.Marshal.Copy(m_buff9000, 8, destptrbig1 + (packetIDperframe - 1) * 8128, 帧分包余数);
System.Runtime.InteropServices.Marshal.Copy(destptrbig1, buffer5038848[0], 0, imgH * imgW);
showbuffer2pict(buffer5038848[0], imgW, imgH, pictureBox1);//不是bmp z序的,正常的202302061722
//System.Runtime.InteropServices.Marshal.Copy(destptrbig, buffer5038848[0], 0, 5038848);
//showbuffer2pict(buffer5038848[0], 2592, 1944, pictureBox1);//不是bmp z序的,正常的202302061722
frame++;
}
else
{
//destptrbig202403301518,为什么c#有横线,c版本没有,代码是一致的
System.Runtime.InteropServices.Marshal.Copy(m_buff9000, 8, destptrbig1 + (packetIDperframe - 1) * 8128, 8128);//结果与上面一样,有横线 }
}
}
break;
default:
break;
}
}
}
catch (Exception ee)
{ }
} while (yongzhen);
//
}
12,下面是如何关闭相机:即就是“gvsp取像结束”按键的代码:
private void button10_Click(object sender, EventArgs e)
{
IPEndPoint remotetarget = new IPEndPoint(ipremote, 3956);
//关闭相机控制,向寄存器写入0x00
byte[] sendbuff = new byte[16] { 0x42, 0x01, 0x00, 0x82, 0x00, 0x08,
0x01, 0x29, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 };
GVCPsocket.Send(sendbuff, 16, remotetarget);
Thread.Sleep(50);
//duchulai shi 0
IPEndPoint localtarget = new IPEndPoint(IPAddress.Any, 3956);
// IPEndPoint remotetarget = new IPEndPoint(ipremote, 3956);
// byte[]
sendbuff = new byte[12] { 0x42, 0x01, 0x00, 0x80, 0x00, 0x04,
0x01, 0x28, 0x00, 0x00, 0x0a, 0x00 };
GVCPsocket.Send(sendbuff, 12, remotetarget);
Thread.Sleep(50);
byte[] retfree = GVCPsocket.Receive(ref localtarget);// //如果返回如此[11]=0,its free can be control
if (retfree[11] == 0)
{
yongzhen = false;
backgroundWorker2.Dispose();
反馈信息 += "gvsp取像结束" + "\r\n";
textBox9.Text += 反馈信息;
}
}
13,关于海康相机曝光的控制:使用了一个文本框来更改,代码如下:
private void textBox10_TextChanged(object sender, EventArgs e)//genggai baoguang202407231428
{
//float value = (float)(Convert.ToDouble(textBox3.Text));//改为880000
//int hello = device.MV_CC_SetExposureTime_NET(value);//感觉有效果202208060942
int value = Convert.ToInt32(textBox10.Text);
byte gao8wei = (byte)(value >> 8);
byte di8wei = (byte)(value);
/* 3, <Integer Name="ExposureTime_RegAddr">
<Value>
0x00030b04
</Value>*/
byte[] sendbuff16 = new byte[16] {0x42, 0x01, 0x00, 0x82, 0x00, 0x08,
//0x03, 0x2f, 0x00, 0x03, 0x0b, 0x04,0,0,0x61,0x0a8};
0x03, 0x2f, 0x00, 0x03, 0x0b, 0x04,0,0,gao8wei,di8wei};
//写入曝光值25000
IPEndPoint remotetarget = new IPEndPoint(ipremote, 3956);
// 0x03, 0x2f, 0x00, 0x03, 0x0b, 0x04,0,0,0x3a,0x98}; //写入曝光值15000
GVCPsocket.Send(sendbuff16, 16, remotetarget);//返回1,才可以下一步
Thread.Sleep(50);
}
以为不多,写了不少!还有最后一个图像显示函数:
void showbuffer2pict(byte[] buffer, int ww, int hh, PictureBox destImg)
{
int mod = ww % 4;//解决四位对齐问题20150716
int temproiw = ww + (4 - mod) % 4;//其实这都是和显示相关,处理图像其实不必考虑,
//顯示
byte[] cutvalues = new byte[temproiw * hh * 3];
int bytes = temproiw * hh * 3;
Bitmap cutPic24 = new Bitmap(temproiw, hh, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
BitmapData _cutPic = cutPic24.LockBits(new Rectangle(0, 0, temproiw, hh), ImageLockMode.ReadWrite,
cutPic24.PixelFormat);
IntPtr ptr = _cutPic.Scan0;//得到首地址
for (int i = 0; i < hh; i++)
{
for (int j = 0; j < ww; j++)
{
int n = i * ww + j;
//int m = 3 * n;
int m = 3 * (i * temproiw + j);
cutvalues[m] = buffer[n];
cutvalues[m + 1] = buffer[n];
cutvalues[m + 2] = buffer[n];
}
}
System.Runtime.InteropServices.Marshal.Copy(cutvalues, 0, ptr, bytes);
cutPic24.UnlockBits(_cutPic);
destImg.Image = cutPic24;
}
最后,拍了个视频,供参考。
这个程序,c,c++版本都可以跑,我不懂linux,否则,我会去倒腾,哈哈!
linux一般会驱动千兆网卡,但一般不会驱动gige相机,想想就兴奋,要注意的是,linux下,可以用png图像显示或存盘,我在c语言下试过支持png图像的包,可以用,存了好几张图像,都是ok的。
我们的图像在byte【】数组里,linux下c或c++,你用指针,一个char*指针,他和这个byte【】是一样的,用过opencv,你就知道。