• 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

  • 相关阅读:
    BZOJ4849[Neerc2016]Mole Tunnels——模拟费用流+树形DP
    BZOJ3638[Codeforces280D]k-Maximum Subsequence Sum&BZOJ3272Zgg吃东西&BZOJ3267KC采花——模拟费用流+线段树
    BZOJ3291Alice与能源计划——匈牙利算法+模拟费用流
    BZOJ2151种树——模拟费用流+链表+堆
    CF1117D Magic Gems
    CF1117C Magic Ship
    CF1117A Best Subsegment
    Loj 2028 随机序列
    Loj 504 ZQC的手办
    Luogu 3806 点分治1
  • 原文地址:https://www.cnblogs.com/hebeiDGL/p/3487539.html
Copyright © 2020-2023  润新知