1. 簡單的景深影像處理
在上篇文章中,我們討論了如何擷取像素點的深度值以及如何根據深度值產生影像。在之前的例子中,我們過濾掉了閾值之外的點。這就是一種簡單的影像處理,叫閾值處理。使用的閾值方法雖然有點粗糙,但是有用。更好的方法是利用機器學習來從每一幀影像資料中計算出閾值。Kinect深度值最大為4096mm,0值通常表示深度值不能確定,一般應該將0值過濾掉。微軟建議在開發中使用1220mm(4’)~3810mm(12.5’)範圍內的值。在進行其他深度影像處理之前,應該使用閾值方法過濾深度資料至1220mm-3810mm這一範圍內。
使用統計方法來處理深度影像資料是一個很常用的方法。閾值可以基於深度資料的平均值或者中值來確定。統計方法可以協助確定某一點是否是雜訊、陰影或者是其他比較有意義的物體,比如說使用者的手的一部分。有時候如果不考慮像素的視覺意義,可以對原始深度進行資料採礦。對景深資料處理的目的是進行形狀或者物體的識別。通過這些資訊,程式可以確定人體相對於Kinect的位置及動作。
1.1深度影像資料長條圖
長條圖是統計資料分布的一個很有效工具。在這裡我們關心的是一個景深影像圖中深度值的分布。長條圖能夠直觀地反映給定資料集中資料的分布狀況。從長條圖中,我們能夠看出深度值出現的頻率以及聚集分組。通過這些資訊,我們能夠確定閾值以及其他能夠用來對映像進行過濾的指標,使得能夠最大化的揭示深度影像圖中的深度資訊。為了展示這一點,接下來我們將會展示一副景深影像資料的長條圖,並通過長條圖,使用一些簡單的技術來過濾掉我們不想要的像素點。
首先建立一個新的項目。然後根據之前文章中講的步驟發現和初始化KinectSensor對象來進行深度影像資料處理,包括註冊DepthFrameReady事件。在添加實現深度長條圖之前,將UI介面更改為如下:
<Window x:Class="KinectDepthHistogram.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="800" Width="1200" WindowStartupLocation="CenterScreen"> <Grid> <StackPanel> <StackPanel Orientation="Horizontal"> <Image x:Name="DepthImage" Width="640" Height="480" /> <Image x:Name="FilteredDepthImage" Width="640" Height="480" /> </StackPanel> <ScrollViewer Margin="0,15" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> <StackPanel x:Name="DepthHistogram" Orientation="Horizontal" Height="300" /> </ScrollViewer> </StackPanel> </Grid></Window>
建立長條圖的方法很簡單,就是建立一系列的矩形元素,然後將它添加到名為DepthHistogram的StackPanel元素中,由於DepthHistogram對象的Orientation屬性設定為Horizontal,所以這些矩形會水平排列。大多數應用程式計算長條圖只是用來進行中間過程處理用,如果想要將長條圖展現出來,則需要在繪圖上面做些工作。下面的代碼展現了如何繪製長條圖:
private void KinectDevice_DepthFrameReady(object sender, DepthImageFrameReadyEventArgs e){ using (DepthImageFrame frame = e.OpenDepthImageFrame()) { if (frame != null) { frame.CopyPixelDataTo(this._DepthPixelData); CreateBetterShadesOfGray(frame, this._DepthPixelData); CreateDepthHistogram(frame, this._DepthPixelData); } }} private void CreateDepthHistogram(DepthImageFrame depthFrame, short[] pixelData){ int depth; int[] depths = new int[4096]; double chartBarWidth = Math.Max(3, DepthHistogram.ActualWidth / depths.Length); int maxValue = 0; DepthHistogram.Children.Clear(); //計算並擷取深度值.並統計每一個深度值出現的次數 for (int i = 0; i < pixelData.Length; i++) { depth = pixelData[i] >> DepthImageFrame.PlayerIndexBitmaskWidth; if (depth >= LoDepthThreshold && depth <= HiDepthThreshold) { depths[depth]++; } } //尋找最大的深度值 for (int i = 0; i < depths.Length; i++) { maxValue = Math.Max(maxValue, depths[i]); } //繪製長條圖 for (int i = 0; i < depths.Length; i++) { if (depths[i] > 0) { Rectangle r = new Rectangle(); r.Fill = Brushes.Black; r.Width = chartBarWidth; r.Height = DepthHistogram.ActualHeight * (depths[i] / (double)maxValue); r.Margin = new Thickness(1, 0, 1, 0); r.VerticalAlignment = System.Windows.VerticalAlignment.Bottom; DepthHistogram.Children.Add(r); } }}