有关 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