有關 Nokia Imaging SDK 的初級使用,能夠參考:Nokia Imaging SDK濾鏡使用入門html
本文的主題:windows
一、 如何 PhotoCaptureDevice 類使用,以及如何在 MediaElement 控件上顯示攝像頭預覽。架構
二、 如何經過 Nokia Imaging SDK 提供的濾鏡,運用到攝像頭的預覽縮略圖圖片流上。相片拍照完成後async
,再爲高像素圖片運用用戶選擇的濾鏡效果。ide
三、經過本工程,開發者能夠快速預覽 Nokia Imaging SDK 所提供 53 種濾鏡 的大體效果,能夠在post
實際的開發過程當中,快速選擇相應的濾鏡進行使用。ui
本文的內容:url
一、首先提供一個基本的代碼架構(basic文件夾下)。接下來的步驟是基於這個基本代碼工程進行操做的。「基本代碼工程」spa
實現了照片拍攝(使用 WP8 中的高級拍照 PhotoCaptureDevice 類)的基本功能,拍照完成後跳轉到照片預覽頁面。調試
基本代碼的運行如圖:
注意:本文不會對講述 PhotoCaptureDevice 類的使用。參考 MSDN。
二、打開 basic 文件夾下的基本工程。在基本工程裏面,已經添加好 Nokia Imaging SDK的相關類庫。
工程目錄及相關類和頁面功能的說明:
三、由於工程完成後的代碼量較大,爲了更好的完成本工程,先介紹一下下面步驟將實現的功能。
1)單擊屏幕,顯示濾鏡選擇列表。包括 Nokia Imaging SDK 提供的 53個濾鏡。
2)從濾鏡列表選擇濾鏡後,攝像頭取景框實時顯示:
3)單擊拍攝按鈕,從 MainPage.xaml 頁面跳轉到ImagePreviewPage.xaml ,顯示通過濾鏡處理的圖片(截圖選擇的是「魔術筆濾鏡」):
四、在工程的根目錄下新建一個文件夾「FilterComponent」,用來存儲接下來的代碼文件。
五、由於 MediaElement 有兩個方法:
public void SetSource(MediaStreamSource mediaStreamSource); public void SetSource(Stream stream);
這裏咱們使用第一個方法。
在 FilterComponent 文件夾下新建一個 CameraStreamSource類,該類繼承自 MediaStreamSource類。把這個CameraStreamSource
類用來做爲 MediaElement 控件的數據源。
注:這裏只列出重點方法的解釋,並未貼出所有代碼及註釋。具體代碼請查看Code_Snippets文件夾下的 CameraStreamSource.cs 文件。
六、重寫上面定義的CameraStreamSource類中的兩個重要方法:
protected override void OpenMediaAsync() protected override void GetSampleAsync(MediaStreamType mediaStreamType)
1)OpenMediaAsync 方法:收集實例化 MediaStreamDescription 對象的集合所需的元數據,而後對其進行實例化。能夠把這個方法理解成,
當初始化爲 MediaElement 視頻流時會調用 OpenMediaAsync(),在這個方法裏面,設置流數據信息,包括了流數據,視頻寬高,視頻時間,
當初始化完成後,調用 ReportOpenMediaCompleted(mediaSourceAttributes, mediaStreamDescriptions) 方法,來通知 MediaElement
控件,MediaStreamSource已經打開,並提供相關視頻流信息。該方法僅會在視頻信息初始化時調用一次。
具體的解釋能夠參考工程的代碼註釋。
2)GetSampleAsync 方法:致使 MediaStreamSource 準備一個 MediaStreamSample,它描述要由媒體管線呈現的下一個媒體示例。能夠經過 ReportGetSampleCompleted 和 ReportGetSampleProgress 響應此方法。當爲 MediaElement 控件提供視頻流的每一幀時調用,此時咱們可以
得到圖像的幀數據,此時咱們爲該幀添加用戶選擇的濾鏡效果。
七、在 FilterComponent 文件夾下,新建一個 FilterItem 類,該類有5個成員:
public class FilterItem { public int Index { get; set; } public string Name { get; set; } /// <summary> /// 引用當前用戶選擇的濾鏡效果 /// </summary> public static FilterItem CurrentFilterItem { get; set; } /// <summary> /// 建立濾鏡效果 /// </summary> /// <param name="index">當 index 不爲空時,設置 index 指定的濾鏡,若是爲空,則指定 /// CurrentFilterItem.Index 指定的濾鏡</param> /// <returns></returns> public static List<IFilter> CreateInstance(int? index = null) { … } /// <summary> /// 濾鏡列表數據源 /// </summary> public static readonly List<FilterItem> FilterSource = new List<FilterItem> { … } }
該類的做用是爲 MainPage 頁面的濾鏡列表提供數據源。其中CreateInstance() 方法經過Switch 語句來建立相應的濾鏡實體。
注:這裏只列出重點方法的解釋,並未貼出所有代碼及註釋。具體代碼請查看Code_Snippets文件夾下的 FilterItem.cs 文件。
八、在 FilterComponet 文件夾下,新建一個 NokiaImagingSDKEffects 類,在這個類中,咱們主要添加一個
public async Task GetNewFrameAndApplyEffect(IBuffer frameBuffer, Size frameSize)
該方法的做用是爲預覽視頻流的幀添加濾鏡。該方法的所有代碼:
/// <summary> /// 爲預覽視頻流的幀添加濾鏡 /// </summary> /// <param name="frameBuffer">幀數據</param> /// <param name="frameSize">幀尺寸</param> /// <returns></returns> public async Task GetNewFrameAndApplyEffect(IBuffer frameBuffer, Size frameSize) { var scanlineByteSize = (uint)frameSize.Width * 4; // 4 bytes per pixel in BGRA888 mode var bitmap = new Bitmap(frameSize, ColorMode.Bgra8888, scanlineByteSize, frameBuffer); // 經過 _photoCaptureDevice 建立一個新的 image source _cameraPreviewImageSource = new CameraPreviewImageSource(_photoCaptureDevice); if (FilterItem.CurrentFilterItem != null) { _filterEffect = new FilterEffect(_cameraPreviewImageSource) { Filters = FilterItem.CreateInstance() }; var renderer = new BitmapRenderer(_filterEffect, bitmap); await renderer.RenderAsync(); } else { var renderer = new BitmapRenderer(_cameraPreviewImageSource, bitmap); await renderer.RenderAsync(); } }
注:這裏只列出重點方法的解釋,並未貼出所有代碼及註釋。具體代碼請查看Code_Snippets文件夾下的 NokiaImagingSDKEffects.cs 文件。
九、在 FilterComponent 文件夾下建立一個 MainPage_Realtime.cs 文件,該文件存放的類是 MainPage 類的分部類,使用關鍵字 partial
關鍵字把新代碼和基礎代碼工程的 MainPage 類進行區分:
public partial class MainPage : PhoneApplicationPage { … }
在該文件中添加三個全局變量:
private MediaElement _mediaElement = null; private NokiaImagingSDKEffects _cameraEffect = null; private CameraStreamSource _cameraStreamSource = null;
並添加一個 InitializeEffect() 方法來初始這些變量。
十、修改 MainPage 類。
1)爲 MainPage.xaml 頁面中,DataTemplate 中爲 Image 控件添加一個 Loaded 事件,當該控件加載完成後,添加預覽濾鏡圖片:
<Image Loaded="Image_Loaded"/>
MainPage_Realtime.cs 文件中添加該 Image_Loaded 方法:
/// <summary> /// 當濾鏡列表模版中的 Image 加載完成後,添加相應的濾鏡預覽圖 /// </summary> private async void Image_Loaded(object sender, RoutedEventArgs e) { Image image = sender as Image; // 讀取根目錄下的示例圖片 Stream _imageSource = App.GetResourceStream(new Uri("Sample.jpg", UriKind.Relative)).Stream; // 獲取當前 Item 的 DataContext FilterItem item = image.DataContext as FilterItem; WriteableBitmap writeableBitmap = new WriteableBitmap(100, 100); // 爲示例圖片添加相應的濾鏡效果 using (var source = new StreamImageSource(_imageSource)) using (var filterEffect = new FilterEffect(source) { Filters = FilterItem.CreateInstance(item.Index).ToArray() }) using (var renderer = new WriteableBitmapRenderer(filterEffect, writeableBitmap)) { await renderer.RenderAsync(); } // 顯示到頁面中 image.Source = writeableBitmap; }
2)在 MainPage.xaml 頁面,爲 ListBox 中 DataTemplate 模版中的 Border 控件添加 Tap="Border_Tap" 事件,來把用戶選擇的濾鏡效
果設置爲全局共享變量,該事件在 MainPage 的分部類中實現:
/// <summary> /// 用戶單擊濾鏡列表中的濾鏡項 /// </summary> private void Border_Tap(object sender, System.Windows.Input.GestureEventArgs e) { //e.Handled = true; FilterItem.CurrentFilterItem = (sender as Border).DataContext as FilterItem; }
2)在 MainPage.xaml 給名爲 LayoutRoot 的 grid 添加一個 Tap="LayoutRoot_Tap" 事件,來動態切換濾鏡列表的顯示與隱藏,
該事件在 MainPage 的分部類中實現:
/// <summary> /// 單擊屏幕時,切換濾鏡列表 /// </summary> private void LayoutRoot_Tap(object sender, System.Windows.Input.GestureEventArgs e) { e.Handled = true; if (listbox_preview.Visibility == System.Windows.Visibility.Collapsed) { listbox_preview.Visibility = System.Windows.Visibility.Visible; } else { listbox_preview.Visibility = System.Windows.Visibility.Collapsed; } }
3)添加完上面代碼,在 MainPage.xaml.cs 文件中,修改 OnNavigatedTo() 方法,把 BackgroundVideoBrush.SetSource(_photoCaptureDevice);
替換爲 MainPage 分部類種添加的 InitializeEffect(); 當頁面導航到 MainPage 頁面後,初始化相應的控件和濾鏡。
一樣,在 OnNavigatingFrom() 中,添加 MainPage分部類 中的 Uninitialize_Realtime();方法,以當頁面導航離開時,釋放資源。
注:這裏只列出重點方法的解釋,並未貼出所有代碼及註釋。具體代碼請查看Code_Snippets文件夾下的 MainPage_Realtime.cs 文件。
十一、ImagePreviewPage.xaml.cs 頁面中,爲 PhotoCaptureDevice 類捕獲的高分辨率圖片添加用戶選擇的濾鏡效果。由於高分辨率圖片保存在了 ImageDataContext.Singleton.ImageStream 屬性中,因此直接對它添加濾鏡效果,而後顯示在頁面上:
protected override async void OnNavigatedTo(NavigationEventArgs e) { ImageDataContext dataContext = ImageDataContext.Singleton; if (dataContext.ImageStream != null) { // 顯示默認沒有濾鏡效果的圖片 //BitmapImage bi = new BitmapImage(); //bi.SetSource(dataContext.ImageStream); //image.Source = bi; dataContext.ImageStream.Seek(0, SeekOrigin.Begin);//嚴重注意!!!!!!!! using (var source = new StreamImageSource(dataContext.ImageStream)) using (var filterEffect = new FilterEffect(source) { Filters = FilterItem.CreateInstance() }) using (var renderer = new JpegRenderer(filterEffect)) { Windows.Storage.Streams.IBuffer buf = await renderer.RenderAsync(); Stream stream = System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBufferExtensions.AsStream(buf); BitmapImage bi = new BitmapImage(); bi.SetSource(stream); image.Source = bi; } } base.OnNavigatedTo(e); }
代碼工程完成後,運行效果:
上面的 gif 圖片快速演示了 Nokia Imging SDK 中的 53 種濾鏡,而且給取景器快速運用其中對比比較明顯的幾個濾鏡,拍照
後,便可獲得通過濾鏡處理的全像素照片。
總結:
本實驗代碼量比較多,沒有所有粘貼工程代碼,而是先在 basic 文件夾下提供一個用 PhotoCaptureDeVice類實現的拍
照功能的基本代碼工程,而後在該文件夾下的 Code_Snippets 文件夾中,提供相應的代碼片斷。動手實驗文檔中主要講解了重點代碼的做用,
而且 Nokia Imaging SDK 提供的 所有 53種濾鏡,都只是設置了默認值,具體參數的調整已經添加到工程的代碼註釋上。
有關 Nokia Imaging SDK 濾鏡使用流程,請參考相關文檔
代碼工程下載:http://pan.baidu.com/s/1sj4RKo1
提示:
一、由於本實驗會使用到手機攝像頭,因此建議經過真機調試
二、在運行源代碼時,會出現一個編譯錯誤: Nokia Imaging SDK does not support the AnyCPU target platform.
由於 Nokia Imaging SDK 支持託管代碼和本地代碼,因此在編譯前須要進行設置:
1)在模擬器上運行時:菜單 -> 生成 -> 配置管理器 -> 活動解決方案平臺 -> x86 2)在真機上運行時: 菜單 -> 生成 -> 配置管理器 -> 活動解決方案平臺 -> ARM