一、項目需求
AR播放器:將一系列帶透明通道的圖片以一定的幀率連續顯示,疊加載攝像頭采集的畫面之上,並播放聲音。
此為最初級的AR技術,因為畫面是事先渲染好的,固定不變的,所以實際上並不能實現“互動”,當然,配合畫面擺出幾個動作拍個照片還是可以。
二、解決方案
1、WinForm還是WPF?
關於攝像頭操作,WinForm有很多開源類庫可以選擇,比如AForge,OpenCV等等;WPF則可以使用WPFMediaKit(也可以通過WindowsFormsHost來使用WinForm的控件,但是效率肯定不如WinForm高,另外在此不得不高度評價下WPFMediaKit,用金老師的話來說,完美!)。
另外,對於透明通道的支持,WinForm簡直讓人無語啊,為什么會自動以父級的背景作為背景?當你把Picture控件疊加在攝像頭控件上,你會驚喜的發現,這是什么鬼!當然,你可以使用強大的GDI來解決,不過那么多的毛邊和刺刺是腫么回事!然后不得不再次感嘆,WPF,完美!
雖然很多人拿WPF的效率說事,但是,在土豪客戶面前,這些都不是事兒嘛!CPU上I7,內存搞個16G,顯卡來個GTX9xx系列!能用錢解決的事情,那都不是事兒,當然,對我們這個初級的AR播放器來說,2GHz以上,4核的CPU就夠用了;內存2G有點壓力,4G應該足以應付,顯卡嘛,集成顯卡應該也沒問題。
2、動畫
2-1、Thread/Task/BackgroundWorker/Timer
使用WinForm的童鞋,首先想到的應該是這幾個玩意兒了,不過除了BackgroundWorker以外,需要跨線程操作UI,簡單介紹兩種方法:
Control.CheckForIllegalCrossThreadCalls=false;//不檢查線程沖突,雖然可以操作UI了,但是可能會產生難以預料的后果
Control.Invoke(委托,參數)//使用委托,需要刷新的控件Invoke一下
當然,WPF也可以使用,不過UI操作是醬紫滴:
Dispatcher.BeginInvoke(()=>{//做你該做的事情!});
2-2、DispatcherTimer/CompositionTarget
不得不說,WPF有許多好用的玩意兒,用起來可比WinForm舒服的多!
簡單上一段代碼:
1 var timer=new DispatcherTimer(); 2 timer.Interval=TimeSpan.FromMilliseconds(1000/25);// fps:25/s 3 var sources=new List<BitmapImage>();//加載序列幀圖片,省略代碼 4 var index=0; 5 6 timer.Tick+=(s,e)=> 7 { 8 image.Source=sources[index++];//Image控件,疊加在攝像頭控件之上 9 10 if(index>sources.Count-1) 11 { 12 index=0; 13 } 14 }; 15 16 timer.Start(); 17 //使用MediaPlayer播放音樂
其中:timer.Interval = TimeSpan.FromMilliseconds(1000 / 25);
因為渲染圖片需要一定時間,圖片大小不同,耗時不同,所以實際上達不到25幀每秒,很難與聲音同步!
至於CompositionTarget.Rendering,是UI線程的回調,幀率固定為60/s(Silverlight倒是可以設置幀率),有可能阻塞UI線程,導致畫面卡頓,還是不用為妙,當然,這玩意兒在某些場景還是很好用的!
2-3、Animation
終於到了Animation出場的時候,上代碼:
var ani=new Int32Anitiation(0,images.Count,TimeSpan.FromMilliseconds(images.Count * 1000.0 / fps)); //設置幀率 Timeline.SetDesiredFrameRate(ani, fps); ani.CurrentTimeInvalidated +=(s,e)=> { //更新圖片 img.Source=new BitmapImage(new Uri(images[ImageIndex])); }; this.BeginAnimation(ImageIndexProperty, ani);
使用Animation會出現掉幀、跳幀的情況,例如由1直接變為3,又或者連續幾個3,這樣保證了總的時長不變,幀率也就相對穩定,也就可以與音頻同步。
而使用DispatcherTimer,比如有600幀,希望的幀率為30(也就是一秒30張圖片),理論總時長為600/30=20 (s),音頻文件按此時長來制作,Interval=1000/30 -常量(Tick事件的耗時),在耗時較大情況下(圖片文件較大,設備性能不行等原因),此值接近0甚至小於0,所以根本無法到達要求的幀率,實際總時長將大於理論時長,也就無法與音頻同步!
