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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章