簡要說明:
通過對示例圖片分別運用 LomoFilter和SketchFilter兩個濾鏡(Nokia Imaging SDK 內置),
來學習濾鏡的基本使用和參數的設置。本工程的代碼量比較少,也很容易理解。
版本 NokiaImagingSDK 1.0.272.0
下面是這個 demo 的完成步驟:
1)通過 Visual Studio 2012 新建一個Windows Phone 8 的工程,命名為“FilterParametersSample”:
2) 右鍵單擊工程的“引用”,選擇“管理 NuGet程序包”:
在右上角搜索框輸入“Nokia Imaging SDK”,並安裝該 SDK:
3)因為本次實驗使用模擬器調試,所以需要在“配置管理器”選擇“x86”(Visual Studio菜單 -> 生成 -> 配置管理器):
注:關於添加 Nokia Imaging SDK后 VS 的設置請參考:Download and add the libraries to the project
4)在項目的根目錄下添加一張示例圖片 “Sample.jpg”:
5)在 MainPage.xaml 頁面中添加一個 Image 控件(顯示原圖)和兩個 Button 控件,單擊 Button 后,分別跳轉
到 LomoFilterPage.xaml 和 SketchFilterPage.xaml頁面。用下面的 XAML 替換掉 MainPage 頁面中名為 LayoutRoot 的 grid:
<!--LayoutRoot 是包含所有頁面內容的根網格--> <Grid x:Name="LayoutRoot" Background="Transparent"> <Image Source="/Sample.jpg" Margin="117,82,89,231"/> <Button Content="LomoFilter" HorizontalAlignment="Left" Margin="25,669,0,0" VerticalAlignment="Top"
Width="209" Click="LomoFilter_Button_Click"/> <Button Content="Sketch" HorizontalAlignment="Left" Margin="239,669,0,0"
VerticalAlignment="Top" Width="205" Click="SketchFilter_Button_Click"/> </Grid>
在 MainPage.xaml.cs 頁面添加相應的按鈕事件:
// Lomo (自由濾鏡) private void LomoFilter_Button_Click(object sender, RoutedEventArgs e) { NavigationService.Navigate(new Uri("/Pages/LomoFilterPage.xaml", UriKind.Relative)); } // Sketch 素描濾鏡
private void SketchFilter_Button_Click(object sender, RoutedEventArgs e) { NavigationService.Navigate(new Uri("/Pages/SketchFilterPage.xaml", UriKind.Relative)); }
6)新建一個名為“Pages”的文件夾,在頁面中分別添加兩個頁面:
LomoFilterPage.xaml 和 SketchFilterPage.xaml,分別用來處理 Lomo 濾鏡和 Sketch濾鏡。
7)首先打開 SketchFilterPage.xaml 頁面,在 XAML 頁面添加一個 Image 控件和兩個RadioButton 控件,
分別用來顯示預覽圖和切換濾鏡模式:
<Grid x:Name="LayoutRoot" Background="Transparent" Tap="LayoutRoot_Tap"> <Image x:Name="PreviewImage"/> <Grid x:Name="gridControls" VerticalAlignment="Bottom" Background="#44ffffff" > <Grid.Resources> <Style TargetType="TextBlock"> <Setter Property="Margin" Value="20,0,0,0"/> <Setter Property="Foreground" Value="#ffffffff"/> <Setter Property="FontWeight" Value="ExtraBold"/> </Style> </Grid.Resources> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> </Grid.RowDefinitions> <TextBlock Text="素描模式:"/> <StackPanel Orientation="Horizontal" Grid.Row="1" Background="{StaticResource PhoneAccentBrush}"> <RadioButton Content="Gray" GroupName="Mode" Checked="Gray_RadioButton_Checked"/> <RadioButton Content="Color" GroupName="Mode" Checked="Color_RadioButton_Checked"/> </StackPanel> </Grid> </Grid>
上面的 gridControls 用來作為“操作面板”,當單擊 LayoutRoot 控件時,切換它的隱藏和顯示:
// 切換控制面板 private void LayoutRoot_Tap(object sender, System.Windows.Input.GestureEventArgs e) { e.Handled = true; if (gridControls.Visibility == System.Windows.Visibility.Collapsed) { gridControls.Visibility = System.Windows.Visibility.Visible; } else { gridControls.Visibility = System.Windows.Visibility.Collapsed; } }
8)另外在 SketchFilterPage.xaml.cs 頁面中,聲明三個變量:
// 使用濾鏡 SketchMode _sketchMode = SketchMode.Gray; Stream _imageStreamOrigin;// 保存圖片的原始數據 WriteableBitmap _writeBitmap; //輸出結果
9)在構造函數中,讀取示例圖片的流,用來稍后添加濾鏡:
public SketchFilterPage() { InitializeComponent(); // 獲取 xap 包中的 圖片 StreamResourceInfo StreamInfo = App.GetResourceStream(new Uri("Sample.jpg", UriKind.Relative)); // 先把未經過濾鏡處理的示例圖片顯示出來 BitmapImage bitmapImage = new BitmapImage(); bitmapImage.SetSource(StreamInfo.Stream); _writeBitmap = new WriteableBitmap(bitmapImage); PreviewImage.Source = _writeBitmap; // 保存示例圖片的流,用於接下來添加濾鏡 _imageStreamOrigin = StreamInfo.Stream; }
10)添加兩個 RadioButton 的 Checked 事件,用來為圖片運用不同的濾鏡效果:
// 灰色 private void Gray_RadioButton_Checked(object sender, RoutedEventArgs e) { _sketchMode = SketchMode.Gray; CreatePreviewImage(); } // 彩色 private void Color_RadioButton_Checked(object sender, RoutedEventArgs e) { _sketchMode = SketchMode.Color; CreatePreviewImage(); }
11)最后添加 CreatePreviewImage() 方法,對圖片運用濾鏡效果:
public async void CreatePreviewImage() { if (_imageStreamOrigin == null || _writeBitmap == null) { return; } _imageStreamOrigin.Position = 0; // 運用這個濾鏡效果 var filters = new IFilter[] { new SketchFilter(_sketchMode) }; using (var source = new StreamImageSource(_imageStreamOrigin)) using (var filterEffect = new FilterEffect(source) { Filters = filters }) using (var renderer = new WriteableBitmapRenderer(filterEffect, _writeBitmap)) { await renderer.RenderAsync(); PreviewImage.Source = _writeBitmap; } }
12)當分別點擊“草圖濾鏡”(SketchFilter)的兩個 RadioButton 時,運行效果:
13) 接下來在LomoFilterPage.xaml 頁面中,給示例圖片添加 LomoFilter:
和上面一樣,首先添加一個 Image 控件,用來顯示圖片預覽;然后添加兩個 Silder控件,分別調整濾鏡的亮度、
飽和度;最后添加兩組 RadioButton控件,分別用來調整濾鏡的“光暈”模式和“顏色風格”,相應的 XAML如下:
<!--LayoutRoot 是包含所有頁面內容的根網格--> <Grid x:Name="LayoutRoot" Background="Transparent" Tap="LayoutRoot_Tap"> <Image x:Name="PreviewImage"/> <Grid x:Name="gridControls" Visibility="Collapsed" VerticalAlignment="Bottom" Background="#44ffffff" > <Grid.Resources> <Style TargetType="TextBlock"> <Setter Property="Margin" Value="20,0,0,0"/> <Setter Property="Foreground" Value="#ffffffff"/> <Setter Property="FontWeight" Value="ExtraBold"/> </Style> </Grid.Resources> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> </Grid.RowDefinitions> <TextBlock Text="亮度:"/> <Slider Grid.Row="1" Value="0.5" Minimum="0" Maximum="1" ValueChanged="brightnessSlider_ValueChanged"/> <TextBlock Grid.Row="2" Text="飽和度:"/> <Slider Grid.Row="3" Value="0.5" Minimum="0" Maximum="1" ValueChanged="saturationSlider_ValueChanged"/> <TextBlock Grid.Row="4" Text="光暈:"/> <StackPanel Orientation="Horizontal" Grid.Row="5"> <RadioButton Content="Low" GroupName="LomoVignetting" Checked="lowRadioButton_Checked"/> <RadioButton Content="Medium" GroupName="LomoVignetting" Checked="medRadioButton_Checked"/> <RadioButton Content="High" GroupName="LomoVignetting" Checked="highRadioButton_Checked"/> </StackPanel> <TextBlock Grid.Row="6" Text="風格:"/> <StackPanel Orientation="Horizontal" Grid.Row="7"> <RadioButton Content="Red" GroupName="Style" Checked="Red_RadioButton_Checked"/> <RadioButton Content="Green" GroupName="Style" Checked="Green_RadioButton_Checked"/> <RadioButton Content="Blue" GroupName="Style" Checked="Blue_RadioButton_Checked"/> <RadioButton Content="Yellow" GroupName="Style" Checked="Yellow_RadioButton_Checked"/> </StackPanel> </Grid> </Grid>
14)在相應的 C# 頁面,首先聲明 6個變量,分別保存原始圖片流,和濾鏡的設置參數:
// 使用濾鏡 double _brightness = 0.5; double _saturation = 0.5; LomoVignetting _lomoVignetting = LomoVignetting.Medium; //光暈 LomoStyle _lomoStyle = LomoStyle.Neutral; Stream _imageStreamOrigin;// 保存圖片的原始數據 WriteableBitmap _writeBitmap;// 輸出結果
15)在構造函數中,首先讀取示例圖片的流,顯示未經過濾鏡處理的圖片:
public LomoFilterPage() { InitializeComponent(); // 獲取 xap 包中的 圖片 StreamResourceInfo StreamInfo = App.GetResourceStream(new Uri("Sample.jpg", UriKind.Relative)); // 先把未經過濾鏡處理的示例圖片顯示出來 BitmapImage bitmapImage = new BitmapImage(); bitmapImage.SetSource(StreamInfo.Stream); _writeBitmap = new WriteableBitmap(bitmapImage); PreviewImage.Source = _writeBitmap; // 保存示例圖片的流,用於接下來添加濾鏡 _imageStreamOrigin = StreamInfo.Stream; }
16)然后定義一個修改濾鏡的方法,每當 Silder 控件或者 RadioButton 控件修改濾鏡參數的時候,調用一次這個方法:
public async void CreatePreviewImage() { if (_imageStreamOrigin == null || _writeBitmap == null) { return; } _imageStreamOrigin.Position = 0; // 運用這個濾鏡效果 var filters = new IFilter[] { new LomoFilter(_brightness, _saturation, _lomoVignetting, _lomoStyle) }; using (var source = new StreamImageSource(_imageStreamOrigin)) using (var filterEffect = new FilterEffect(source) { Filters = filters }) using (var renderer = new WriteableBitmapRenderer(filterEffect, _writeBitmap)) { await renderer.RenderAsync(); PreviewImage.Source = _writeBitmap; } }
改進上面 CreatePreviewImage() 方法:
因為在給圖片運用濾鏡效果的時候,比如在拖動 Silder 的過程中,因為上一次方法調用還沒有完成,
也就是當代碼執行到 await renderer.RenderAsync(); 時,異步線程的操作還沒有執行完,下一次
代碼再次執行,就可能會引發 線程安全 的問題,所以用一個小技巧修改一下上面的代碼,使用一個全局
的 IsBusying 布爾值,控制線程的調用:

bool IsBusying = false; public async void CreatePreviewImage() { if (_imageStreamOrigin == null || _writeBitmap == null) { return; } if (IsBusying) { return; // 如果正在運用一組參數,則返回 } IsBusying = true; _imageStreamOrigin.Position = 0; // 運用這個濾鏡效果 var filters = new IFilter[] { new LomoFilter(_brightness, _saturation, _lomoVignetting, _lomoStyle) }; using (var source = new StreamImageSource(_imageStreamOrigin)) using (var filterEffect = new FilterEffect(source) { Filters = filters }) using (var renderer = new WriteableBitmapRenderer(filterEffect, _writeBitmap)) { await renderer.RenderAsync(); //Windows.Foundation.IAsyncOperation<WriteableBitmap> ia = renderer.RenderAsync(); //ia.Cancel(); PreviewImage.Source = _writeBitmap; } IsBusying = false; }
當然,在調用復雜的情況時,可以使用 System.Threading.Semaphore 類 來限制訪問同一資源的線程數量,
或者 System.Threading.AutoResetEvent 控制線程的事件執行。
17)下面添加 SIider 和 RadioButton 控件的事件觸發的代碼,下面的代碼雖然比較多,但很容易理解:

#region 參數調整 private void brightnessSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { _brightness = 1.0 - e.NewValue; CreatePreviewImage(); } private void saturationSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { _saturation = e.NewValue; CreatePreviewImage(); } protected void lowRadioButton_Checked(object sender, System.Windows.RoutedEventArgs e) { _lomoVignetting = LomoVignetting.Low; CreatePreviewImage(); } protected void medRadioButton_Checked(object sender, System.Windows.RoutedEventArgs e) { _lomoVignetting = LomoVignetting.Medium; CreatePreviewImage(); } protected void highRadioButton_Checked(object sender, System.Windows.RoutedEventArgs e) { _lomoVignetting = LomoVignetting.High; CreatePreviewImage(); } private void Red_RadioButton_Checked(object sender, RoutedEventArgs e) { _lomoStyle = LomoStyle.Red; CreatePreviewImage(); } private void Green_RadioButton_Checked(object sender, RoutedEventArgs e) { _lomoStyle = LomoStyle.Green; CreatePreviewImage(); } private void Blue_RadioButton_Checked(object sender, RoutedEventArgs e) { _lomoStyle = LomoStyle.Blue; CreatePreviewImage(); } private void Yellow_RadioButton_Checked(object sender, RoutedEventArgs e) { _lomoStyle = LomoStyle.Yellow; CreatePreviewImage(); } #endregion
18)運行工程:
19) 總結:本實驗代碼量比較少,也很容易理解。有關 Nokia Imaging SDK 濾鏡使用流程,請參考相關文檔。
工程下載鏈接: http://pan.baidu.com/s/1eXV4q
在運行源代碼時,會出現一個編譯錯誤: Nokia Imaging SDK does not support the AnyCPU target platform.
因為 Nokia Imaging SDK 支持托管代碼和本地代碼,所以在編譯前需要進行設置:
1)在模擬器上運行時:菜單 -> 生成 -> 配置管理器 -> 活動解決方案平台 -> x86 2)在真機上運行時: 菜單 -> 生成 -> 配置管理器 -> 活動解決方案平台 -> ARM