假设我们手上有了一款相机,可以按照如下流程进行测试
登录
http://www.mindvision.com.cn/
注意,如果这里报错,提示没有C#环境,建议你新建一个工程,到联网模板里去找,选择C#应用程序,VS2010会自动下载相关的模板
回到我们的工程,设置FirstStep为启动项目
运行即可
然后我们可以新建一个工程,模仿这个demo去实现上面这个功能,实现对C#的学习
选择C#窗体程序,如果你没有C#环境,可以选择左下角的联机模板,创建C#,会自动下载。
确定即可。
进入相机驱动的安装目录
(这里直接引用他们的demo里的dll,其实也可以自己写一个项目,模仿MDSDK项目,然后项目属性的输出类似改为类库,再引用即可。建议熟悉后再尝试,现在继续往下看)
右键cs文件
可以看到下面的InitializeComponent函数,这个函数里面设定了控件(如按钮,文字等)的属性,包括名称、位置、尺寸等等,还有相关的调用方案,一般是自动生成,不用我们修改。
先添加四个按钮,点击设计界面,如果你没有这个标签页,双击cs文件能打开这个标签。
我们打开工具箱,从中拖几个按钮过去
找到公共控件的Button
拖过去即可,然后再移动鼠标到右下角,把我们的窗口拖到合适大小
再添加4个按钮,如图:
双击某一个按钮,进入代码界面
可以看到自动生成的点击事件函数:
复制如下内容到这个函数里
if (m_Grabber != IntPtr.Zero)
MvApi.CameraShowSettingPage(m_hCamera, 1);
CameraShowSettingPage是进入相机设置界面。这里表示当相机指针不为Zero,进入设置界面。m_Grabber 在相机初始化时会被赋值。
using MVSDK;
using CameraHandle = System.Int32;
protected IntPtr m_Grabber = IntPtr.Zero;
protected CameraHandle m_hCamera = 0;
IntPtr:表示指针或句柄的平台特定整数类型,这里你可以认为是定义了一个整数类型的相机句柄,后面会用这个m_Grabber 来操作相机,如打开关闭相机等。
按钮1的点击事件完成,为了更容易识别,我们把按钮1设置为中文名称,右键按钮
将名称“button1”
改为“设置”
效果如下:
再改第二个名称
四个都改完后的效果
然后我们分别给各个按钮添加点击事件
双击第二个“播放”按钮,添加代码:
添加一个方法即可。
if (m_Grabber != IntPtr.Zero)
MvApi.CameraGrabber_StartLive(m_Grabber);
表示启动相机。
if (m_Grabber != IntPtr.Zero)
MvApi.CameraGrabber_StopLive(m_Grabber);
可以看到,由于迈德威视的相机SDK封装的比较完善,我们只需要一个方法就可以实现很多调用了。
但是最后一个抓图(截图)方法会比较复杂一些,因为涉及到一些关于文件保存的问题。双击抓图按钮,添加代码:
其实主要还是SaveImage和SaveAsBmp方法,然后将文件保存到指定路径中
if (m_Grabber != IntPtr.Zero)
{
IntPtr Image;
if (MvApi.CameraGrabber_SaveImage(m_Grabber, out Image, 2000) ==
CameraSdkStatus.CAMERA_STATUS_SUCCESS)
{
string filename = System.IO.Path.Combine(
AppDomain.CurrentDomain.BaseDirectory.ToString(),
string.Format("{0}.bmp", System.Environment.TickCount));
MvApi.CameraImage_SaveAsBmp(Image, filename);
MvApi.CameraImage_Destroy(Image);
MessageBox.Show(filename);
}
else
{
MessageBox.Show("Snap failed");
}
}
System.IO.Path.Combine:将路径与文件名合并起来,如C:\ABC.BMP
{0}:占位符,代表后面的第一个变量,类似于打印时的%d
四个按钮的事件写完了。
定时器控件用于底部状态栏数据的更新,这一块不是很重要,了解一下即可。
拖过去
双击上面那个闹钟,进入代码界面,写定时器事件
if (m_Grabber != IntPtr.Zero)
{
tSdkGrabberStat stat;
MvApi.CameraGrabber_GetStat(m_Grabber, out stat);
string info = String.Format(
"| Resolution:{0}*{1} | DispFPS:{2} | CapFPS:{3} |",
stat.Width, stat.Height, stat.DispFps, stat.CapFps);
StateLabel.Text = info;
}
状态栏的信息用于获取相机采集到的画面的宽高和帧率等信息。
可以发现有一个地方标红了,
是标签控件,相当于状态栏的地方,说明标签没有添加,这时候我们可以把标签添加过去。
相机的回调用于不断的刷新图像,我们在窗口设定了一个Image,然后不断的更新这个Image。
protected pfnCameraGrabberFrameCallback m_FrameCallback;
m_FrameCallback = new pfnCameraGrabberFrameCallback(CameraGrabberFrameCallback);
然后我们再定义CameraGrabberFrameCallback
注意这里的第二个和第三个参数,pFrameBuffer是得到的帧缓存,在获取image时需要传入该参数;pFrameHead是图像帧头信息,用于获取图像格式。
private void CameraGrabberFrameCallback(
IntPtr Grabber,
IntPtr pFrameBuffer,
ref tSdkFrameHead pFrameHead,
IntPtr Context)
{
}
我们在函数体中添加如下代码:
GC.Collect();//强制进行即时垃圾回收,防止内存溢出
int w = pFrameHead.iWidth;
int h = pFrameHead.iHeight;
Boolean gray = (pFrameHead.uiMediaType ==
(uint)MVSDK.emImageFormat.CAMERA_MEDIA_TYPE_MONO8);
Bitmap Image = new Bitmap(w, h,
gray ? w : w * 3,
gray ? PixelFormat.Format8bppIndexed : PixelFormat.Format24bppRgb,
pFrameBuffer);
if (gray)
{
Image.Palette = m_GrayPal;
}
this.Invoke((EventHandler)delegate
{
DispWnd.Image = Image;
DispWnd.Refresh();
});
在这里对image进行绘制,首先要判断相机的类型(彩色or黑白),然后进行Bitmap绘制,最后输出到DispWnd中。
可以看到有一些标红的地方,
首先pixel format是像素格式,它需要System.Drawing.Imaging的引用。
然后是m_GrayPal,该参数类型是定义组成调色板的颜色的数组,在初始化相机时会被赋值,当确定相机是黑白相机(gray为真)时,对image的图像调色板palette属性进行赋值。
我们添加System.Drawing.Imaging引用和m_GrayPal初始化即可。
using System.Drawing.Imaging;
protected ColorPalette m_GrayPal;
然后DispWnd,这个是一个PictureBox,顾名思义——图片盒子,就是用来显示我们图像的控件名称,我们回到设计窗口,将PictureBox拖过去。
我们在窗口构造函数处添加相机的初始化代码
代码很好理解,创建相机的状态参数status,设备列表数组DevList,并获取已连接相机的数量,如果数组不为空则返回数组的长度,否则返回0。
private void InitCamera()
{
CameraSdkStatus status = 0;
tSdkCameraDevInfo[] DevList;
MvApi.CameraEnumerateDevice(out DevList);
int NumDev = (DevList != null ? DevList.Length : 0);
}
if (NumDev < 1)
{
MessageBox.Show("未扫描到相机”);
return;
}
else if (NumDev == 1)
{
status = MvApi.CameraGrabber_Create(out m_Grabber, ref DevList[0]);
}
else
{
status = MvApi.CameraGrabber_CreateFromDevicePage(out m_Grabber);
}
if (status == 0)
{
MvApi.CameraGrabber_GetCameraDevInfo(m_Grabber, out m_DevInfo);
MvApi.CameraGrabber_GetCameraHandle(m_Grabber, out m_hCamera);
MvApi.CameraCreateSettingPage(m_hCamera, this.Handle, m_DevInfo.acFriendlyName, null, (IntPtr)0, 0);
MvApi.CameraGrabber_SetRGBCallback(m_Grabber, m_FrameCallback, IntPtr.Zero);
tSdkCameraCapbility cap;
MvApi.CameraGetCapability(m_hCamera, out cap);
if (cap.sIspCapacity.bMonoSensor != 0)
{
MvApi.CameraSetIspOutFormat(m_hCamera, (uint)MVSDK.emImageFormat.CAMERA_MEDIA_TYPE_MONO8);
Bitmap Image = new Bitmap(1, 1, PixelFormat.Format8bppIndexed);
m_GrayPal = Image.Palette;
for (int Y = 0; Y < m_GrayPal.Entries.Length; Y++)
m_GrayPal.Entries[Y] = Color.FromArgb(255, Y, Y, Y);
}
}
上图是有一个相机的时候执行的操作,其中标红的地方是因为m_DevInfo
没有定义,添加后即可
protected tSdkCameraDevInfo m_DevInfo;
MvApi.CameraSetMirror(m_hCamera, 1, 1);
MvApi.CameraGrabber_StartLive(m_Grabber);
如果if (status == 0)不通过,说明相机打开失败,我们需要进行提醒
else
{
MessageBox.Show(String.Format("打开相机失败,原因:{0}", status));
}
相机的初始化也已经完成,最后一步是设置窗口的关闭事件,需要将相机的活动销毁
右键窗口空白处
双击右侧空白处,即可自动添加代码,然后写销毁事件即可
点击运行