最近在做一個(gè)威視IPC的視覺(jué)跟蹤項(xiàng)目,因?yàn)閷?shí)際操作跟本人無(wú)關(guān),只是因?yàn)榕d趣做點(diǎn)小研究而已,因?yàn)槠脚_(tái)主要是用C#的,那視覺(jué)處理庫(kù)無(wú)疑選擇Emgu會(huì)比較理想一點(diǎn),Emgu是OpenCV的一個(gè)C#封裝,網(wǎng)上放出來(lái)的資料并不多見(jiàn),搜索耗費(fèi)不少的時(shí)間,Emgu的入門(mén)好像網(wǎng)上有些挺好的文章,在此不贅述。
站在用戶(hù)的角度思考問(wèn)題,與客戶(hù)深入溝通,找到撫寧網(wǎng)站設(shè)計(jì)與撫寧網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶(hù)體驗(yàn)好的作品,建站類(lèi)型包括:網(wǎng)站設(shè)計(jì)、網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、域名注冊(cè)、網(wǎng)頁(yè)空間、企業(yè)郵箱。業(yè)務(wù)覆蓋撫寧地區(qū)。
本來(lái)項(xiàng)目的要求應(yīng)該是要實(shí)時(shí)的,但使用Emgu好像挺難實(shí)時(shí)的,且不說(shuō)實(shí)時(shí)視頻幀很難保證,就Emgu的一句圖像比較函數(shù)在我i5的機(jī)器下就花掉了100多ms,然而接近實(shí)時(shí)也并非不可能的,例如使用更好的CPU或使用顯卡運(yùn)算,也許存在更好的視覺(jué)處理庫(kù),方法應(yīng)該不少的。我的項(xiàng)目實(shí)際要求是統(tǒng)計(jì)物體運(yùn)動(dòng)軌跡再作一些簡(jiǎn)單的判斷而已,所以我采取一種惡心的方式,將視頻數(shù)據(jù)流存放到一個(gè)Queue中,再開(kāi)一條線(xiàn)程慢慢處理這此數(shù)據(jù),反正我只需要事后得知結(jié)果而已,保證原始數(shù)據(jù)的實(shí)時(shí)顯得更重要一點(diǎn)。
事先聲明一點(diǎn),因?yàn)殚_(kāi)發(fā)的原因,沒(méi)法在辦公室里調(diào)試攝像頭,我建了一個(gè)ImageStream的類(lèi),用于封裝采集到的視頻數(shù)據(jù),因?yàn)镋mgu中使用的是Image<TColor>的類(lèi)型,這里邊會(huì)有一些圖像格式轉(zhuǎn)換的工作需要注意。將視頻數(shù)據(jù)Byte[]轉(zhuǎn)成為Image<TColor>不算太難吧,也就是一句話(huà)的事而已,可能需要注意JPEG跟BMP格式的。因?yàn)槭悄M,我將電腦上JPG圖片轉(zhuǎn)成為Byte[]數(shù)據(jù)流,放到ImageStream里,開(kāi)一個(gè)線(xiàn)程塞圖像數(shù)據(jù),一個(gè)線(xiàn)程處理圖像,大概流程就是這樣。
先上ImageStream類(lèi)的代碼,如下:
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 { /// <summary> /// 用于保存Image數(shù)據(jù)流的類(lèi) /// </summary> public class ImageStream { /// <summary> /// 時(shí)間值 /// </summary> public DateTime m_DateTime; /// <summary> /// 源文件名 /// </summary> public string m_SrcFileName; /// <summary> /// 圖像流 /// </summary> public MemoryStream m_ImageStream = null; /// <summary> /// 構(gòu)造函數(shù) /// </summary> /// <param name="dt"></param> /// <param name="pBuf"></param> public ImageStream(DateTime dt, byte[] pBuf, string srcFileName = null) { this.m_DateTime = dt; this.m_ImageStream = new MemoryStream(pBuf); this.m_SrcFileName = srcFileName; } /// <summary> /// 轉(zhuǎn)換成為Emgu的圖像 /// </summary> /// <returns></returns> public Image<Bgr, Byte> ToEmguImage() { Image img = Image.FromStream(this.m_ImageStream); return new Image<Bgr, Byte>((Bitmap)(img)); } /// <summary> /// 根據(jù)時(shí)間作為文件名 /// </summary> /// <returns></returns> 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; } } }
類(lèi)中的m_DateTime跟m_SrcFileName只是作一個(gè)數(shù)據(jù)源的識(shí)別參數(shù)而已,為的是調(diào)試上的方便。
圖像運(yùn)動(dòng)檢測(cè)我封裝成為了一個(gè)Poser類(lèi),使用Add(ImageStream im)將圖像數(shù)據(jù)加入到處理隊(duì)列里,然后自行在ProcessThread的線(xiàn)程中處理,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 { /// <summary> /// Image序列處理類(lèi) /// </summary> public class ImagePoser { /// <summary> /// 圖像流數(shù)據(jù)鏈表 /// </summary> private Queue<ImageStream> _ImageStreamList = new Queue<ImageStream>(); /// <summary> /// 退出線(xiàn)程的標(biāo)志 /// </summary> private bool _QuitThreadFlag = true; /// <summary> /// 加入序列的總數(shù)量 /// </summary> private int _TotalImageCount = 0; /// <summary> /// 已經(jīng)處理完畢的數(shù)量 /// </summary> private int _FinishedCount = 0; /// <summary> /// 互斥鎖 /// </summary> private Mutex _WaitMutex = new Mutex(); /// <summary> /// 前景與背景檢測(cè)器 /// </summary> private FGDetector<Bgr> _ForeGroundDetector = null; /// <summary> /// 默認(rèn)構(gòu)造函數(shù) /// </summary> public ImagePoser() { } /// <summary> /// 開(kāi)始進(jìn)行圖像處理 /// </summary> public void Start() { this._QuitThreadFlag = false; Thread thread = new Thread(new ThreadStart(this.ProcessThread)); thread.Name = "ImagePoserThread"; thread.Is true; thread.Start(); } /// <summary> /// 停止圖像處理線(xiàn)程 /// </summary> public void Stop() { this._QuitThreadFlag = true; } /// <summary> /// 退出條件 /// </summary> /// <returns></returns> protected virtual bool StopCondition() { Queue<int> fifo = new Queue<int>(); return false; } /// <summary> /// 添加圖像數(shù)據(jù) /// </summary> /// <param name="p_w_picpathStream"></param> 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(); } /// <summary> /// 總共需要處理的數(shù)量 /// </summary> /// <returns></returns> public int GetTotalCount() { return this._TotalImageCount; } /// <summary> /// 已經(jīng)處理完畢的數(shù)量 /// </summary> /// <returns></returns> public int GetBeFinishedCount() { return this._FinishedCount; } /// <summary> /// 獲取當(dāng)前未處理的數(shù)量 /// </summary> /// <returns></returns> public int GetUnFinishedCount() { this._WaitMutex.WaitOne(); int nListCount = this._ImageStreamList.Count; this._WaitMutex.ReleaseMutex(); return nListCount; } /// <summary> /// 圖像處理線(xiàn)程 /// </summary> private void ProcessThread() { //前景檢測(cè)器 if (this._ForeGroundDetector == null) { this._ForeGroundDetector = new FGDetector<Bgr>(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(); //轉(zhuǎn)換成為OpenCV所使用的圖片格式 Image<Bgr, Byte> tagImage = (im.ToEmguImage()).Resize(0.5, INTER.CV_INTER_LINEAR); //tagImage.Save("E:\\" + im.ToFileName() + ".bmp");//保存Bmp格式文件 //運(yùn)動(dòng)檢測(cè) ////////////////////////////////////////////////////////////////////////// //高斯處理 tagImage.SmoothGaussian(3); //獲取前景,將其轉(zhuǎn)成為灰度圖 _ForeGroundDetector.Update(tagImage); Image<Gray, Byte> foreGroundMark = _ForeGroundDetector.ForegroundMask; //foreGroundMark.Save("E:\\" + im.ToFileName() + ".bmp");//保存Bmp格式文件 //連續(xù)區(qū)域的邊緣點(diǎn)集 Contour<Point> contour = foreGroundMark.FindContours(); if (contour != null) { //Rectangle rect = contour.BoundingRectangle; //Image<Bgr, Byte> resImg = new Image<Bgr, Byte>(foreGroundMark.Size); //繪畫(huà)邊緣點(diǎn)集 foreach (Point p in contour) { tagImage.Draw(new CircleF(p, 2.0f), new Bgr(Color.Red), 1); } //繪畫(huà)綁定矩形 tagImage.Draw(contour.BoundingRectangle, new Bgr(Color.Green), 1); } //保存處理后的圖片 tagImage.Save("E:\\" + im.ToFileName() + ".bmp");//保存Bmp格式文件 //計(jì)算圖像處理時(shí)間 st.Stop(); Debug.WriteLine("Poser處理耗時(shí) : " + st.ElapsedMilliseconds.ToString() + "ms\r\n"); Thread.Sleep(1); } } } }
使用時(shí),開(kāi)啟一個(gè)線(xiàn)程(應(yīng)該沒(méi)難度吧?),使用類(lèi)似如下的代碼
/// <summary> /// 填充數(shù)據(jù)流 /// </summary> 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) { //將圖片轉(zhuǎn)成為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來(lái)P出一個(gè)會(huì)動(dòng)的物體,原圖跟結(jié)果圖像都放在附件里,大家可以自己下載下來(lái)玩一下。
名稱(chēng)欄目:一個(gè)基于Emgu的運(yùn)動(dòng)檢測(cè)實(shí)例
網(wǎng)站地址:http://chinadenli.net/article14/ppesde.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供用戶(hù)體驗(yàn)、定制開(kāi)發(fā)、網(wǎng)站制作、小程序開(kāi)發(fā)、動(dòng)態(tài)網(wǎng)站、移動(dòng)網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)