最近在做一个威视IPC的视觉跟踪项目,因为实际操作跟本人无关,只是因为兴趣做点小研究而已,因为平台主要是用C#的,那视觉处理库无疑选择Emgu会比较理想一点,Emgu是OpenCV的一个C#封装,网上放出来的资料并不多见,搜索耗费不少的时间,Emgu的入门好像网上有些挺好的文章,在此不赘述。

站在用户的角度思考问题,与客户深入沟通,找到抚宁网站设计与抚宁网站推广的解决方案,凭借多年的经验,让设计与互联网技术结合,创造个性化、用户体验好的作品,建站类型包括:网站设计、网站制作、企业官网、英文网站、手机端网站、网站推广、域名注册、网页空间、企业邮箱。业务覆盖抚宁地区。
本来项目的要求应该是要实时的,但使用Emgu好像挺难实时的,且不说实时视频帧很难保证,就Emgu的一句图像比较函数在我i5的机器下就花掉了100多ms,然而接近实时也并非不可能的,例如使用更好的CPU或使用显卡运算,也许存在更好的视觉处理库,方法应该不少的。我的项目实际要求是统计物体运动轨迹再作一些简单的判断而已,所以我采取一种恶心的方式,将视频数据流存放到一个Queue中,再开一条线程慢慢处理这此数据,反正我只需要事后得知结果而已,保证原始数据的实时显得更重要一点。
事先声明一点,因为开发的原因,没法在办公室里调试摄像头,我建了一个ImageStream的类,用于封装采集到的视频数据,因为Emgu中使用的是Image
先上ImageStream类的代码,如下:
using Emgu.CV;
using Emgu.CV.Structure;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QImageClass
{
///
/// 用于保存Image数据流的类
///
public class ImageStream
{
///
/// 时间值
///
public DateTime m_DateTime;
///
/// 源文件名
///
public string m_SrcFileName;
///
/// 图像流
///
public MemoryStream m_ImageStream = null;
///
/// 构造函数
///
///
///
public ImageStream(DateTime dt, byte[] pBuf, string srcFileName = null)
{
this.m_DateTime = dt;
this.m_ImageStream = new MemoryStream(pBuf);
this.m_SrcFileName = srcFileName;
}
///
/// 转换成为Emgu的图像
///
///
public Image ToEmguImage()
{
Image img = Image.FromStream(this.m_ImageStream);
return new Image((Bitmap)(img));
}
///
/// 根据时间作为文件名
///
///
public string ToFileName()
{
string file = this.m_DateTime.Year.ToString("D4") + "-" +
this.m_DateTime.Month.ToString("D2") + "-" +
this.m_DateTime.Day.ToString("D2") + "-" +
this.m_DateTime.Hour.ToString("D2") + "-" +
this.m_DateTime.Minute.ToString("D2") + "-" +
this.m_DateTime.Second.ToString("D2") + "-" +
this.m_DateTime.Millisecond.ToString("D3");
return file;
}
}
} 类中的m_DateTime跟m_SrcFileName只是作一个数据源的识别参数而已,为的是调试上的方便。
图像运动检测我封装成为了一个Poser类,使用Add(ImageStream im)将图像数据加入到处理队列里,然后自行在ProcessThread的线程中处理,Poser的代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Collections;
using System.Threading;
using Emgu.CV;
using Emgu.CV.Structure;
using System.Diagnostics;
using Emgu.CV.VideoSurveillance;
using Emgu.CV.CvEnum;
using System.Drawing;
namespace QImageClass
{
///
/// Image序列处理类
///
public class ImagePoser
{
///
/// 图像流数据链表
///
private Queue _ImageStreamList = new Queue();
///
/// 退出线程的标志
///
private bool _QuitThreadFlag = true;
///
/// 加入序列的总数量
///
private int _TotalImageCount = 0;
///
/// 已经处理完毕的数量
///
private int _FinishedCount = 0;
///
/// 互斥锁
///
private Mutex _WaitMutex = new Mutex();
///
/// 前景与背景检测器
///
private FGDetector _ForeGroundDetector = null;
///
/// 默认构造函数
///
public ImagePoser()
{
}
///
/// 开始进行图像处理
///
public void Start()
{
this._QuitThreadFlag = false;
Thread thread = new Thread(new ThreadStart(this.ProcessThread));
thread.Name = "ImagePoserThread";
thread.Is true;
thread.Start();
}
///
/// 停止图像处理线程
///
public void Stop()
{
this._QuitThreadFlag = true;
}
///
/// 退出条件
///
///
protected virtual bool StopCondition()
{
Queue fifo = new Queue();
return false;
}
///
/// 添加图像数据
///
///
public void Add(ImageStream p_w_picpathStream)
{
this._WaitMutex.WaitOne();
this._ImageStreamList.Enqueue(p_w_picpathStream);
this._TotalImageCount++;
Debug.WriteLine("Poser Add : " + this._TotalImageCount.ToString());
this._WaitMutex.ReleaseMutex();
}
///
/// 总共需要处理的数量
///
///
public int GetTotalCount()
{
return this._TotalImageCount;
}
///
/// 已经处理完毕的数量
///
///
public int GetBeFinishedCount()
{
return this._FinishedCount;
}
///
/// 获取当前未处理的数量
///
///
public int GetUnFinishedCount()
{
this._WaitMutex.WaitOne();
int nListCount = this._ImageStreamList.Count;
this._WaitMutex.ReleaseMutex();
return nListCount;
}
///
/// 图像处理线程
///
private void ProcessThread()
{
//前景检测器
if (this._ForeGroundDetector == null)
{
this._ForeGroundDetector = new FGDetector(FORGROUND_DETECTOR_TYPE.FGD);
}
while (!this._QuitThreadFlag)
{
ImageStream im = null;
this._WaitMutex.WaitOne();
if (this._ImageStreamList.Count == 0)
{
this._WaitMutex.ReleaseMutex();
Thread.Sleep(1);
continue;
}
Stopwatch st = new Stopwatch();
st.Start();
//抽取出一组ImageStream
im = this._ImageStreamList.Dequeue();
this._FinishedCount++;
this._WaitMutex.ReleaseMutex();
//转换成为OpenCV所使用的图片格式
Image tagImage = (im.ToEmguImage()).Resize(0.5, INTER.CV_INTER_LINEAR);
//tagImage.Save("E:\\" + im.ToFileName() + ".bmp");//保存Bmp格式文件
//运动检测
//////////////////////////////////////////////////////////////////////////
//高斯处理
tagImage.SmoothGaussian(3);
//获取前景,将其转成为灰度图
_ForeGroundDetector.Update(tagImage);
Image foreGroundMark = _ForeGroundDetector.ForegroundMask;
//foreGroundMark.Save("E:\\" + im.ToFileName() + ".bmp");//保存Bmp格式文件
//连续区域的边缘点集
Contour contour = foreGroundMark.FindContours();
if (contour != null)
{
//Rectangle rect = contour.BoundingRectangle;
//Image resImg = new Image(foreGroundMark.Size);
//绘画边缘点集
foreach (Point p in contour)
{
tagImage.Draw(new CircleF(p, 2.0f), new Bgr(Color.Red), 1);
}
//绘画绑定矩形
tagImage.Draw(contour.BoundingRectangle, new Bgr(Color.Green), 1);
}
//保存处理后的图片
tagImage.Save("E:\\" + im.ToFileName() + ".bmp");//保存Bmp格式文件
//计算图像处理时间
st.Stop();
Debug.WriteLine("Poser处理耗时 : " + st.ElapsedMilliseconds.ToString() + "ms\r\n");
Thread.Sleep(1);
}
}
}
} 使用时,开启一个线程(应该没难度吧?),使用类似如下的代码
////// 填充数据流 /// private void Thread1() { //读取资源文件 EmunFileReader reader = new EmunFileReader("D:\\TEST_JPG - 副本", ".jpg"); string[] fileList = reader.GetFileList(); int nCount = reader.GetFileCount(); //图像处理器 ImagePoser poser = new ImagePoser(); poser.Start(); foreach (string s in fileList) { //将图片转成为ImageStream Debug.WriteLine(s); Image img = Image.FromFile(s); ImageStream ism = new ImageStream(DateTime.Now, ImageConvert.ImageToBytes(img, ImageFormat.Jpeg), s); poser.Add(ism); Thread.Sleep(1); } while (true) { if (poser.GetTotalCount() == nCount && poser.GetBeFinishedCount() == nCount) { poser.Stop(); break; } else { Thread.Sleep(1); } } }
至于原始图片,大家可以自行寻找,我是用PS来P出一个会动的物体,原图跟结果图像都放在附件里,大家可以自己下载下来玩一下。
分享题目:一个基于Emgu的运动检测实例
分享URL:http://www.jxjierui.cn/article/ppesde.html


咨询
建站咨询
