直接使用提供的代码框架进行修改,是最快得到效果的方法;但是这样的灵活性较差,而且真正的程序员从来都不会停滞在这一步:我们需要的是“将框架解析到最小化、理清楚每个构建之间的关系”,只有这样才能灵活运用。
一、准备工作
1、高拍仪已经接通,如果需要的话,还要安装驱动;
2、vs2012编程环境,能够编写Csharp和OpenCV程序(具体不清楚可以回过头来看配置);
3、
是DirectShow.net(http://directshownet.sourceforge.net/docs.html)的可使用类
![508489-20180831193303463-1986484462.png](https://i-blog.csdnimg.cn/blog_migrate/165c3691bbf581c3ef4250af9a26e2bd.png)
![logo](https://i-blog.csdnimg.cn/blog_migrate/5f1981f48ce47ab603225228cd8ad7c0.png)
它本身包含文档,有时间可以看一下。最新更新时间2010年。
4、
是由
![508489-20180831193304012-409114295.png](https://i-blog.csdnimg.cn/blog_migrate/16927eac0d3d25878a70be4977187818.png)
![508489-20180831193304209-491423623.png](https://i-blog.csdnimg.cn/blog_migrate/6231c9a7e53540f315ffde0f3cd27197.png)
生成的。这里可以先立足修改已经编辑成功的项目(具体原理将在下一课讲解)
二、配置程序
1、添加引用
![508489-20180831193304426-1038011652.png](https://i-blog.csdnimg.cn/blog_migrate/fd283831ed61d0a34bf94e620b21bdf3.png)
directshow.net的话,直接引用dll就可以了
![508489-20180831193304654-749519769.png](https://i-blog.csdnimg.cn/blog_migrate/1213c352408d3d0855674d49252f95ae.png)
2、拖动控件
使用Csharp编写界面,可以重复使用
![508489-20180831193304915-29559585.png](https://i-blog.csdnimg.cn/blog_migrate/85f98c5aef7b97659937a33cf82be0ff.png)
的定位功能
![508489-20180831193305076-1562961293.png](https://i-blog.csdnimg.cn/blog_migrate/9cb209be4dc9258473b523ddc0deee11.png)
以及Dock的停靠功能
![508489-20180831193305238-1584146256.png](https://i-blog.csdnimg.cn/blog_migrate/f6e81af03f6bc8b09b6afbb108d9efef.png)
图像显示的地方,肯定需要的是picturebox,不妨连同lena一起拷贝过来
![508489-20180831193305938-1339436566.jpg](https://i-blog.csdnimg.cn/blog_migrate/f65ec3ffc3c984603b6e03619c9da51c.jpeg)
由于采集处理是一个实时过程,我们采用timer控件来控制(关于是使用timer还是开线程,那种比较好,我们在框架融合的时候专门比较,并选择)
![508489-20180831193306367-1824525790.png](https://i-blog.csdnimg.cn/blog_migrate/0db41b40bad0ea420c1c87d7ca003f3d.png)
性Interval采用50即可,以为50*24>1000,一般来说还是有认为24帧以上比较连贯。
三、编写代码
1、添加头文件和引用,并添加capture.cs
![508489-20180831193306540-1601494609.png](https://i-blog.csdnimg.cn/blog_migrate/ea74827a68a94481182ceb40f601ab31.png)
![508489-20180831193306778-2024280620.jpg](https://i-blog.csdnimg.cn/blog_migrate/cdb024773eb01df1310f8cb0c4de3811.jpeg)
其中,Capture是一个专门对Directshow的采集设备的封装,里面有丰富的功能;是官方提供的代码,可以较为方向应用。
注意修改命名空间
![508489-20180831193307062-1473670933.jpg](https://i-blog.csdnimg.cn/blog_migrate/8ffd518c4f7a2217f98889f7f854e8dc.jpeg)
2、编写选择视频准备函数(注意这里默认设备为640*480),并且我在选择的时候默认选择了第2个(序号为1)的设备,因为我用的是笔记本,有内置摄像头
相关函数的操作,注意参考相关注释
//选择视频设备
public void InitVideoDevice()
{
try
{
if (cam != null)
cam.Dispose();
//读取参数
int VIDEODEVICE = 1; // zero based index of video capture device to use
const int VIDEOWIDTH = 640;// 是用默认(最大)分辨率
const int VIDEOHEIGHT = 480; // Depends on video device caps
const int VIDEOBITSPERPIXEL = 24; // BitsPerPixel values determined by device
cam = new Capture(VIDEODEVICE, VIDEOWIDTH, VIDEOHEIGHT, VIDEOBITSPERPIXEL, picPreview );
}
catch
{
MessageBox.Show("摄像头打开错误,请首先确保摄像头连接并至少支持1024*768分辨率!");
}
}
并且在form的init中进行调用,确保不能够出错
public FormMain()
{
InitializeComponent();
//构造摄像头数据
foreach (DsDevice ds in DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice))
{
cbCam.Items.Add(ds.Name);
}
//初始化摄像头
InitVideoDevice();
}
此时,已经可以预览,并且获得所有视频设备
![508489-20180831193307818-1506314275.jpg](https://i-blog.csdnimg.cn/blog_migrate/334292b9443f45c2e20ff9804e7c38cb.jpeg)
4、编写timer事件
为了将OpenCV的函数融入进去,必须自己编写timer事件。在这个timer事件中,最重要的操作就是
读取directshow.net产生的结果
调用OpenCV的函数进行处理
将处理的结果反馈到directshow.net中去显示出来
private void timer_Tick(object sender, EventArgs e)
{
// Release any previous buffer
if (m_ip != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(m_ip);
m_ip = IntPtr.Zero;
}
// capture image
try
{
m_ip = cam.Click();
}
catch
{
//do nothing,允许丢帧 TODO:是否改成继承上一帧更好
}
Bitmap b = new Bitmap(cam.Width, cam.Height, cam.Stride, PixelFormat.Format24bppRgb, m_ip);
// If the image is upsidedown
b.RotateFlip(RotateFlipType.RotateNoneFlipY);
srcImage = b;
if (picPreview.Image != null)
picPreview.Image.Dispose();
//调用clr+opencv图像处理模块
MemoryStream ms = new MemoryStream();
b.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
byte[] bytes = ms.GetBuffer();
Bitmap bitmap = client.testMethod(bytes);
//显示结果
picPreview.Image = bitmap;
}
这段代码比较关键
// Release any previous buffer
if (m_ip != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(m_ip);
m_ip = IntPtr.Zero;
}
首先判断指针是否为空
// capture image
try
{
m_ip = cam.Click();
}
catch
{
//do nothing,允许丢帧 TODO:是否改成继承上一帧更好
}
Bitmap b = new Bitmap(cam.Width, cam.Height, cam.Stride, PixelFormat.Format24bppRgb, m_ip);
// If the image is upsidedown
b.RotateFlip(RotateFlipType.RotateNoneFlipY);
srcImage = b;
而后通过调用Click()获得当前视频数据,并且将其转换为BitMap格式
if (picPreview.Image != null)
picPreview.Image.Dispose();
//调用clr+opencv图像处理模块
MemoryStream ms = new MemoryStream();
b.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
byte[] bytes = ms.GetBuffer();
Bitmap bitmap = client.testMethod(bytes);
最为关键的调用
client.testMethod
方法来进行图像处理,并将结果直接转换为bitmap,最后这个结果显示在另外一个picturebox上面。
如果你去看这个testMethod,是一个非常典型的“三明治”结构:包括将<byte>结构转换为Mat,调用OpenCV函数对Mat进行处理,将Mat结果返回出来。
Bitmap^ GOClrClass::testMethod(cli::array<unsigned char>^ pCBuf1)
{
将输入cli::array<unsigned char>转换为cv::Mat/
pin_ptr<System::Byte> p1 = &pCBuf1[0];
unsigned char* pby1 = p1;
cv::Mat img_data1(pCBuf1->Length,1,CV_8U,pby1);
cv::Mat img_object = cv::imdecode(img_data1,IMREAD_UNCHANGED);
if (!img_object.data)
return nullptr;
OpenCV的算法处理过程
cvtColor(img_object,img_object,COLOR_BGR2GRAY);
cvtColor(img_object,img_object,COLOR_GRAY2BGR);
Mat drawing = img_object.clone();
/将cv::Mat转换为Bitmap(只能传输cv_8u3格式数据)///
if (!drawing.data)
return nullptr;
Bitmap^ bitmap = MatToBitmap(drawing);
return bitmap;
}
而主要算法,只是一个灰度处理。(我首先将彩色图像转换为灰度,然后再将灰度转换为彩色,是为了保持3通道)
四、测试结果
为了显示结果,我添加了一个picResult的picturebox,则调用OpenCV的处理结果显示在右侧。
![508489-20180831193309154-1598585275.jpg](https://i-blog.csdnimg.cn/blog_migrate/c5063724a450ff09914fef7315a22287.jpeg)
。
![508489-20180831193309662-829572476.jpg](https://i-blog.csdnimg.cn/blog_migrate/339729d27e77ce663ab67becda103444.jpeg)
到此为止,基于
GOCW,创建一个实时视频程序的基本流程已经明晰了,如果感兴趣,相关的原理请关注后续博客。
感谢阅读至此,
希望有所帮助。
P.S小技巧:在tab选择到预览窗口的时候,才打开timer,这样能够保证最好效率。
private void tabControl_SelectedIndexChanged(object sender, EventArgs e)
{
if (tabControl.SelectedIndex == 1)//只有在预览的时候打开图像处理
timer.Enabled = true;
else
timer.Enabled = false;
}