Hands-On Lab Photo Fun 应用程序

 

 

Hands-On Lab

Photo Fun 应用程序

 

 

Lab version:    1.0.0

Last updated:          2/29/2012

 


 

内容

课程概述 . 3

实验 . 4

任务 1 – 捕捉和显示 live camera feed. 5

任务2 – 处理 live camera feed. 10

课程总结 . 14

 


 

 

 

由于微软提供了公共规格定义,所有的Windows Phone设备必须有500万像素以上的摄像头。在Windows Phone 7.0中,应用程序想调用摄像头只能使用CameraCaptureTask chooser。一个应用程序可以使用chooser来得到内置摄像头的照片。

新的Windows Phone Mango版本提供了更多摄像头的集成功能,包括在Microsoft.Devices.PhotoCamera类中,该类提供了摄像头的细粒度控件,并且可以让你的应用程序直接访问摄像头空间和原始数据。

本次实验演示了如何使用PhotoCamera API来捕捉live camera feed和操作相机显示预览。本次实验也会演示如何访问一些其他摄像头控件,包括摄像头按钮。

提示:这次实验针对WindowsPhone Mango版本,最终RTM版本可能略有不同。

课程目标

本次实验将帮助您完成下列目标:

·                 编写应用程序来捕捉live camera feed 并且在屏幕上显示预览

·                 通过摄像头捕捉照片并且保存到设备的media library中

·                 在一个单独的线程中执行图片处理操作,避免阻塞UI

 

前提条件

您在开始本次动手实验前,请先确认达到下列前提条件:

·        Microsoft Visual Studio 2010 或者 Microsoft Visual C# Express2010, 和 Windows® Phone 7 Codenamed “Mango” Developer Tools,下载地址: http://go.microsoft.com/?linkid=9772716

·        如何创建 Windows® Phone 7应用程序的支持
(如果您是新的Windows® Phone 7开发者,您可以首先阅读: http://msdn.microsoft.com/en-us/gg266499).

 

实验提纲

这个动手实验将包括一个完整的实验,包括下列的任务:

1.      捕捉和显示live camera feed 和在摄像头中得到照片.

2.       在一个单独的线程中执行图片处理操作

 

预计完成时间

完成该实验预计花费15到30分钟的时间。

 

 

该实验的起始点工程文件包括在实验安装目录的Source\Begin目录下。

该解决方案中包括一个实现了所有用户界面的工程。当您完成本次实验时,您会实现所有用户界面相关的应用程序逻辑。

在本次实验中,我们将学习:

·                 如何捕捉live camera feed并显示到应用程序中

·                 如何获得照片并且将其保存在media library中

·                 如何在live camera feed和快照中实现不同的效果

 

任务 1 – 捕捉和显示 live camera feed

这次任务将显示如何使用Microsoft.Devices.PhotoCamera 类,并为摄像头的正确使用提供一些指导。

提示:使用DeviceStatus类需要添加对Microsoft.Phone程序集的引用。该引用将会默认出现在Windows Phone 7 Mango的应用程序工程中。

当你的应用程序导航到某个页面或离开时,Windows Phone 页面的导航模块会调用PhoneApplicationPageOnNavigatedToOnNavigatedFrom 方法。当预览界面显示时,这些方法将被调用。。

使用这两个方法来创建和释放资源,能够让页面访问摄像头并且实现预览。你会在OnNavigatedTo方法中创建PhotoCamera类的实例,在OnNavigatedFrom方法中释放。

1.      打开 MainPage.xaml.cs 文件,并且声明一个PhotoCamera类型的私有变量:

C#

public partial class MainPage : PhoneApplicationPage

{

    private PhotoCameracamera; //The device's camera

   private PhotofunDataContextPhotofunDataContext

   {

       get { returnthis.DataContext asPhotofunDataContext; }

   }

    publicMainPage()

    {

        InitializeComponent();

    }

}

提示:PhotofunDataContext属性会提供一个DataContext属性的强类型封装,方便后续代码中使用。

2.      重载 OnNavigatedTo 方法,当页面活动时,创建一个PhotoCamera的实例:

C#

protected override voidOnNavigatedTo(System.Windows.Navigation.NavigationEventArgse)

{

    if(null == camera)

    {

        camera = new PhotoCamera();

    }

 

    base.OnNavigatedTo(e);

}

重载 OnNavigatedFrom 方法,当页面隐藏时,释放摄像头资源:

C#

protected override voidOnNavigatedFrom(System.Windows.Navigation.NavigationEventArgse)

{

    camera.Dispose();

    camera = null;

 

    base.OnNavigatedFrom(e);

}

3.      摄像头的初始化需要一些时间。当初始化完成时,PhotoCamera类会触发Initialized事件。在OnNavigatedTo方法中添加摄像头的初始化Initialized事件处理方法:

C#

protected override voidOnNavigatedTo(System.Windows.Navigation.NavigationEventArgse)

{

    if (null ==camera)

    {

        camera = new PhotoCamera();

 

        camera.Initialized += camera_Initialized;

    }

 

    base.OnNavigatedTo(e);

}

camera_Initialized 事件处理函数中, 指定摄像头的分辨率,并准备UI来显示视频。AvailableResolutions列表按照每个分辨率的像素数量(宽X高)进行排序,因此第一个分辨率是最低的分辨率。本次实验将使用手机的CPU为live camera feed提供多种效果,但不包括性能优化。为了提高性能我们在这里使用最低分辨率。

为了保证视频在整个屏幕上显示,更新矩形的宽和高.

C#

private void camera_Initialized(object sender, CameraOperationCompletedEventArgse)

{

    if(e.Succeeded)

    {

        varres = from resolution incamera.AvailableResolutions

                          where resolution.Width == 640

                          select resolution;

 

        camera.Resolution = res.First();

        this.Dispatcher.BeginInvoke(delegate()

        {

            ShowRegularVideo();

        });

    }

}

 

private void ShowRegularVideo()

{

    videoRectangle.Width = this.ActualWidth;

    videoRectangle.Height = this.ActualHeight;

}

提示:PhotoCamera没有使用UI线程来触发事件。所以,任何针对UI的修改必须使用Dispatcher.BeginInvoke().

4.      为了显示 live camera feed, 使用名为 viewfinderBrushVideoBrush, 已经声明在XAML中了。 这个 VideoBrush 是在Windows Phone “Mango”中加入的一个新类型的Brush. 它可以被用在任意一个可以使用Brush类的地方,用来“绘制”一个视频流。在这个应用程序中,视频流来自camera live feed。为了将live feed连接到viewfinderBrush上,调用brush的SetSource扩展方法:

C#

protected override voidOnNavigatedTo(System.Windows.Navigation.NavigationEventArgse)

{

   

    camera.Initialized += camera_Initialized;

 

    viewfinderBrush.SetSource(camera);

}

5.      在运行该应用程序之前, 你必须告诉Windows Phone 7你的应用程序使用摄像头。修改 Properties\WMAppManifest.xml f文件,并添加摄像头兼容性声明。如果没有应用程序manifest中的声明,当你尝试访问摄像头时,该应用程序将会抛出异常。在Capabilities元素中,添加ID_CAP_ISV_CAMERA如下实例:

XML

<Capabilities>

    <Capability Name="ID_CAP_ISV_CAMERA" />

</Capabilities>

6.      现在,您的应用程序可以显示live camera feed了,接下来添加对于照片快照的支持,并将照片保存到 media library中。当用户按下摄像头按钮时,摄像头CameraButtons类的ShutterKeyPressed事件将被触发。然后第一步是初始化照片捕捉操作。类是一个static类,用来帮助将所有的摄像头按钮事件集中在一个单独的地方。在添加Initialized事件之后,在OnNavigatedTo方法中添加事件处理方法。

C#

...

camera = newPhotoCamera();

 

camera.Initialized += camera_Initialized;

 

CameraButtons.ShutterKeyPressed += camera_ButtonFullPress;

 

viewfinderBrush.SetSource(camera);

...

事件处理函数简单地调用摄像头来初始化一次照片拍摄。

C#

private void camera_ButtonFullPress(object sender, EventArgse)

{

   camera.CaptureImage();

}

7.      因为CameraButtons类是static类,如果开发者忘记取消对这些事件的订阅,将会引发这些应用程序的一些不确定行为。找到OnNavigatedFrom方法,并将下列代码添加到方法的开始部分:

C#

CameraButtons.ShutterKeyPressed -=camera_ButtonFullPress;

8.      在摄像头拍摄照片之后,如果应用程序拍摄完成,将产生CaptureImageAvailable事件,并且将照片以stream的方式传递给应用程序。

OnNavigatedTo方法中,添加 CaptureImageAvailable 事件:

C#

...

camera.ButtonFullPress += camera_ButtonFullPress;

 

camera.CaptureImageAvailable += camera_CaptureImageAvailable;

 

viewfinderBrush.SetSource(camera);

...

The CaptureImageAvailable 事件处理函数会得到图片的文件流,并将其传递给SavePicture方法。

C#

private void camera_CaptureImageAvailable(object sender, ContentReadyEventArgse)

{

   Dispatcher.BeginInvoke(delegate()

   {

       SavePicture(e.ImageStream);

   });

}

SavePicture 方法会将图片刘转换为WriteableBitmap, 该对象最终会被保存到media library中。注意  WritableBitmap派生自 DependencyObject。 SavePicture 方法也会更新UI (例如当一个预览被添加)。因为UI 和 DependencyObjects 只能通过Dispatcher线程可以被访问和操作,所以CaptureImageAvailable事件处理函数中使用Dispatcher来调用SavePicture方法。

C#

private void SavePicture(StreamimageStream)

{

   WriteableBitmap bitmap = CreateWriteableBitmap(imageStream);

 

   SaveCapturedImage(bitmap);

}

 

private WriteableBitmap CreateWriteableBitmap(Stream imageStream)

{

   WriteableBitmap bitmap = new WriteableBitmap((int)camera.Resolution.Width,(int)camera.Resolution.Height);

 

   imageStream.Position = 0;

   //Load the captured image stream to the bitmap

   bitmap.LoadJpeg(imageStream);

   return bitmap;

}

SaveCapturedImage 方法创建临时的 MemoryStream, 从WriteableBitmap对象中加载图片数据,并最终将刘传递给MediaLibrary ,保存该图像:

C#

private void SaveCapturedImage(WriteableBitmap imageToSave)

{

   MemoryStream stream = new MemoryStream();

   imageToSave.SaveJpeg(stream, imageToSave.PixelWidth, imageToSave.PixelHeight,0, 100);

 

   //Take the stream back to its beginning because it will be read again

   //when saving to the library

   stream.Position = 0;

 

   //Save picture to device media library

   MediaLibrary library = new MediaLibrary();

   string fileName = string.Format("{0:yyyy-MM-dd-HH-mm-ss}.jpg",DateTime.Now);

   library.SavePicture(fileName, stream);

}

9.       在SaveCapturedImage方法中访问Media Library时,你的应用程序必须声明 ID_CAP_MEDIALIB 兼容性。再次修改 Properties\WMAppManifest.xml 文件添加media library 兼容性声明。在Capabilities元素中,添加下列元素:

XML

<Capabilities>

    ...

    <Capability Name="ID_CAP_ISV_CAMERA" />

    <Capability Name="ID_CAP_MEDIALIB" />

    ...

</Capabilities>

 

10.   当用户拍照时,你应该显示预览界面。在MainPage.xaml中,我们为PhotofunDataContext类的Previews属性添加数据绑定, 来绑定页面。数据绑定超过了本次实验的内容,简单来说,就是当我们添加照片到Previews集合时,UI会自动显示这些照片。

修改 SavePicture 方法将捕捉照片添加到Previews集合中:

C#

private void SavePicture(StreamimageStream)

{

   WriteableBitmap bitmap = CreateWriteableBitmap(imageStream);

 

   PhotofunDataContext.Previews.Add(bitmap);

 

   SaveCapturedImage(bitmap);

}

 

应用程序现在能够使用VideoBrush将live camerafeed绘制到应用程序界面上了。 这也可以拍照并将照片保存到MediaLibrary中,还可以在拍照时预览。

 

 

任务 2 – 处理live camera feed

上面的任务展示了如何显示一个live camera feed和拍摄照片。本次任务将显示:

·                 在向用户显示最终结果之前,为照片添加灰度效果(End解决方案中将包括其他容易实现的附加效果)

·                 将这些工作放到单独的线程中,避免阻塞UI。

本次任务将使用PhotoFun工程中EffectsFrames文件家中的文件。

提示:这些实验中的文件并不是本次实验的重点,所以不在这里解释了。

1.      我们要做的第一件事是,在将照片保存到media library前,为拍摄的照片添加灰度效果。
打开 MainPage.xaml.cs 文件,修改SavePicture 方法,调用 ProcessCapturedImage 方法.

C#

private void SavePicture(Stream imageStream)

{

   WriteableBitmap bitmap = CreateWriteableBitmap(imageStream);

 

   WriteableBitmap processedBitmap =ProcessCapturedImage(bitmap);

 

   PhotofunDataContext.Previews.Add(processedBitmap);

 

   SaveCapturedImage(processedBitmap);

}

 

private WriteableBitmap ProcessCapturedImage(WriteableBitmap bitmap)

{

    IEffect processingEffect = new GrayScaleEffect();

    int[] pixels= processingEffect.ProcessImage(bitmap.Pixels);

 

    WriteableBitmap imageToSave= new WriteableBitmap(bitmap.PixelWidth, bitmap.PixelHeight);

    pixels.CopyTo(imageToSave.Pixels, 0);

    return imageToSave;

}

ProcessCapturedImage 请求  GrayScaleEffect 处理 WriteableBitmap 并返回一个像素的数组用来保存已经修改后的图片. 然后拷贝这些像素到新的 WriteableBitmap 中,然后返回到 SavePicture

Note: Adding support for more effects is very simple. The Endsolution uses an extensible mechanism with multiple effects that manipulate theimage (grayscale, sepia, negative) or add overlays. The following steps useonly the grayscale effect, but apply to all the effects in the solution.

2.               下一步将这些效果呈现在live camera feed 中,用来显示在用户界面上。你不能使用 SetSource方法来重定向摄像头输出到屏幕的方式,因为我们无法在视频显示之前提供额外的操作。

幸运的是,PhotoCamera类提供preview buffer。Preview buffer是指定时间中摄像头看到的影像。在 GetPreviewBuffer 方法下面,调用 PhotoCameraGetPreviewBufferArgb32方法。这个方法生成一个byte数组,你可以容易的添加任意一种效果。

Note:  PhotoCamera提供了一系列方法提供各种格式的preview buffer。我们选择 GetPreviewBufferArgb32 方法因为它将返回IEffect支持的格式,尽管该格式较其他格式稍微有些慢。

在你调用 GetPreviewBufferArgb32之前,你需要初始化一个byte数组用来保存buffer.你需要使用PhotoCameraPreviewBufferResolution属性计算buffer的尺寸。

C#

private virtual int[] GetPreviewBuffer()

{

    int[] pixelData = new int[(int)(camera.PreviewBufferResolution.Width* camera.PreviewBufferResolution.Height)];

   camera.GetPreviewBufferArgb32(pixelData);

    return pixelData;

}

提示:虽然这种方法不是立即显示,但这种方法适合目前的代码,没有支持多种效果的方法,来展现效果是困难的。下面的步骤,将简单介绍这些方法。

该解决方案的End中演示了在live preview中模拟某种效果的实现。它捕捉了previewbuffer,并在显示最终结果前,加入一种效果。CPU控制对于这个过程进行多次处理,可以将每次捕捉的preview buffer作为一帧。这将创建一种将效果加入了live camera preview的错觉,当我们获得快照时,调整它们,并在获取新的快照时尽可能快地更新它们。

 

 

当用户选择某种效果时,我们才使用这种技术。如果使用这种技术来附加的效果比SetSource 来重定向视频显示要慢的多。

提示:我们将使用多线程技术来控制视频的模拟。但是本次实验不涉及关于多线程技术、同步机制的描述。

事不宜迟,打开 Source\End 目录下的解决方案文件,打开MainPage.xaml.cs 文件.

3.      camera_Initialized 事件处理方法调用新的 EffectSelected 方法, 如果没有选择效果,该方法将live camera feed绘制到用户界面上,当某些效果被选择后,一些更复杂的视频仿真机制也将被使用。

C#

private void EffectSelected()

{

    bool pumpSelectedFramesStarted = pumpEffectedFrames;

    varselectedEffect = PhotofunDataContext.SelectedEffect;

 

    pumpEffectedFrames = selectedEffect!= null && selectedEffect.Effect != null && !(selectedEffect.Effect is EchoEffect);

 

    if(!pumpEffectedFrames)

    ShowRegularVideo();

    else

    ShowProcessedPreview();

 

    FrameImage.Visibility = PhotofunDataContext.SelectedFrame.FrameUri!= null ? Visibility.Visible: Visibility.Collapsed;

 

    //Start the imageprocessing if it hasn't started yet

    if(pumpEffectedFrames && !pumpSelectedFramesStarted)

    {

        StartImageProcessing();

    }

}

EffectSelected 调用 ShowProcessedPreview 方法来准备显示区域,调用StartImageProcessing 方法开始模拟。ShowProcessedPreview 简单地隐藏videoRectangle 元素,并显示 MainImage element来替代. MainImage 元素会绑定到 WriteableBitmap 对象上。

4.               StartImageProcessing 方法首先创建WriteableBitmap ,使其绑定到MainImage 元素上,然后在一个单独的线程中触发 PumpEffectFrames 方法。PumpEffectFrames 方法控制程序循环,直到页面被切换到后台。在循环中的每一次操作,PushProcessedFramemethod 被调用并在界面上绘制一个新的帧。

C#

private void PushProcessedFrame(PhotoCamera phCam)

{

    if (pumpEffectedFrames && !capturingImage)

    {

        int[] previewBuffer = GetPreviewBuffer();

 

        //Process the preview image

        IEffect effect = newGrayScaleEffect();

        int[] imageData = effect.ProcessImage(previewBuffer);

 

        //Copy to WriteableBitmap
        imageData.CopyTo(previewWriteableBitmap.Pixels,0);

        previewWriteableBitmap.Invalidate();

    }

}

在应用程序添加效果之后,PushProcessedFrame 更新绑定到MainImage元素上的 WriteableBitmap。

 

在本次实验中,您学习了如何直接将摄像头集成到应用程序中。PhotoCamera API提供了通过摄像头按钮和其他事件,来显示livecamera feed。

你也可以看到应用程序的示例来连接摄像头,在返回图像前,通过较低层次的接口来处理图片。

 


发布了23 篇原创文章 · 获赞 8 · 访问量 4万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章