Nokia Imaging SDK 的高级使用—实时滤镜拍照

有关 Nokia Imaging SDK 的初级使用,可以参考:Nokia Imaging SDK滤镜使用入门

本文的主题:

1、如何 PhotoCaptureDevice 类使用,以及如何在 MediaElement 控件上显示摄像头预览

2、如何通过 Nokia Imaging SDK 提供的滤镜,运用到摄像头的预览缩略图图片流上。相片拍照完成后

为高像素图片运用用户选择的滤镜效果。

3、通过本工程,开发者可以快速预览 Nokia Imaging SDK 所提供 53 种滤镜 的大致效果,可以在

实际的开发过程中,快速选择相应的滤镜进行使用。

本文的内容:

1、首先提供一个基本的代码架构(basic文件夹下)。接下来的步骤是基于这个基本代码工程进行操作的。“基本代码工程”

实现了照片拍摄(使用 WP8 中的高级拍照 PhotoCaptureDevice 类)的基本功能,拍照完成后跳转到照片预览页面。

基本代码的运行如图:

注意:本文不会对讲述 PhotoCaptureDevice 类的使用。参考 MSDN

2、打开 basic 文件夹下的基本工程。在基本工程里面,已经添加好 Nokia Imaging SDK的相关类库。

工程目录及相关类和页面功能的说明:

3、因为工程完成后的代码量较大,为了更好的完成本工程,先介绍一下下面步骤将实现的功能。

1)单击屏幕,显示滤镜选择列表。包括 Nokia Imaging SDK 提供的 53个滤镜。

2)从滤镜列表选择滤镜后,摄像头取景框实时显示:

3)单击拍摄按钮,从 MainPage.xaml 页面跳转到ImagePreviewPage.xaml ,显示经过滤镜处理的图片(截图选择的是“魔术笔滤镜”):

4、在工程的根目录下新建一个文件夹“FilterComponent”,用来存储接下来的代码文件。

5、因为 MediaElement 有两个方法:

public void SetSource(MediaStreamSource mediaStreamSource);

public void SetSource(Stream stream);

这里我们使用第一个方法。

在 FilterComponent 文件夹下新建一个 CameraStreamSource类,该类继承自 MediaStreamSource类。把这个CameraStreamSource

类用来作为 MediaElement 控件的数据源。

注:这里只列出重点方法的解释,并未贴出全部代码及注释。具体代码请查看Code_Snippets文件夹下的 CameraStreamSource.cs 文件。

6、重写上面定义的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 控件提供视频流的每一帧时调用,此时我们能够

获得图像的帧数据,此时我们为该帧添加用户选择的滤镜效果。

7、在 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 文件。

8、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 文件。

9、在 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() 方法来初始这些变量。

10、修改 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 文件。

11、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 

提示:

1、因为本实验会使用到手机摄像头,所以建议通过真机调试

2、在运行源代码时,会出现一个编译错误: Nokia Imaging SDK does not support the AnyCPU target platform.

因为 Nokia Imaging SDK 支持托管代码和本地代码,所以在编译前需要进行设置:

1)在模拟器上运行时:菜单 -> 生成 -> 配置管理器 -> 活动解决方案平台 -> x86 2)在真机上运行时:  菜单 -> 生成 -> 配置管理器 -> 活动解决方案平台 -> ARM

更多有关说明请参考: http://developer.nokia.com/Resources/Library/Lumia/#!nokia-imaging-sdk/adding-libraries-to-the-project.html

原文地址:https://www.cnblogs.com/hebeiDGL/p/3487539.html