zoukankan      html  css  js  c++  java
  • WindowsPhone8拍照功能实现简介

    WindowsPhone作为一款智能手机操作系统,支持APP中拍照是必不可少的,目前在WP8上的拍照主要有以下三种途径:

    1、使用CameraCaptureTask;

    2、使用PhotoCamera类;

    3、使用PhotoCaptureDevice类。

    下面简单介绍下这三种方法:

    第一个就是CameraCaptureTask了。做WP开发的都知道微软为了让开发者能够使用一些手机功能在framework中提供了大量的选择器和启动器,本文不谈论这个话题,但是要知道CameraCaptureTask就是属于选择器的一种。CameraCaptureTask用起来非常的简单,使用如下代码即可:

     1 //以下代码均来自MSDN
     2 //引用命名空间
     3 using Microsoft.Phone.Tasks;
     4 
     5 //定义变量
     6 CameraCaptureTask cameraCaptureTask;
     7 
     8 //页面构造函数中实例化
     9 cameraCaptureTask = new CameraCaptureTask();
    10 cameraCaptureTask.Completed += new EventHandler<PhotoResult>(cameraCaptureTask_Completed);
    11 
    12 //定义事件响应函数
    13 void cameraCaptureTask_Completed(object sender, PhotoResult e)
    14 {
    15     if (e.TaskResult == TaskResult.OK)
    16     {
    17         MessageBox.Show(e.ChosenPhoto.Length.ToString());
    18 
    19         System.Windows.Media.Imaging.BitmapImage bmp = new System.Windows.Media.Imaging.BitmapImage();
    20         bmp.SetSource(e.ChosenPhoto);
    21         myImage.Source = bmp;
    22     }
    23 }
    24 
    25 //在需要使用任务的地方调用
    26 cameraCaptureTask.Show();

    当cameraCaptureTask.Show执行时你会看到一个近似系统原生相机应用的页面,在这个页面中可以对拍摄进行一些个性的选择。当拍摄完成,点击了接受后cameraCaptureTask_Completed函数会执行,其中参数e的ChosenPhoto属性就是包含了相片信息的流,对这个流进行处理就可以显示或者保持照片了。CameraCaptureTask应该说是这三种方法中使用起来最简单的一个,它使用的拍摄UI以及一些功能(比如闪光灯切换、摄像头前后切换等)都是系统实现的,但是这个方法也是有弊端的,先看一段MSDN上的说明:

    重要说明重要说明:

    使用 CameraCaptureTask API 拍摄的照片始终会复制到手机的本机拍照中。如果客户已将其手机设置为自动上载,则会将这些照片复制到 SkyDrive,并且可能会不按照应用的设定而与更广泛的受众共享。出于此原因,如果您并不希望共享或上载应用拍摄的照片,例如临时图像或包含隐私信息的图像,请勿使用 CameraCaptureTask API。而是应该使用 PhotoCamera API 实现您自己的相机 UI。

    这段话中提到了隐私的问题,而除了隐私问题,CameraCaptureTask还有一个可能是所有选择器(似乎是所有吧,我自己也没有全部用过)都存在的问题,就是它会使得你的APP墓碑化。也许你不在乎这个问题,但是有些时候墓碑化会使得APP发生一些我们不希望发生的问题。出于不想墓碑化的考虑,我们可以使用PhotoCamera或者PhotoCaptureDevice来实现拍照。

    使用PhotoCamera来拍照。主要有一些几个部分:

    UI上核心的是VideoBrush,是用来显示摄像头内容的。而CompositeTransform则是用来随着手机转动而旋转VideoBrush的。

    1     <Canvas.Background>
    2                 <VideoBrush x:Name="viewfinderBrush" >
    3                     <VideoBrush.RelativeTransform>
    4                         <CompositeTransform x:Name="previewTransform"
    5                             CenterX=".5"
    6                             CenterY=".5" />
    7                     </VideoBrush.RelativeTransform>
    8                 </VideoBrush>
    9             </Canvas.Background>

    后台代码中需要定义相机的各种事件:

     1      //Code for initialization, capture completed, image availability events; also setting the source for the viewfinder.
     2         protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
     3         {
     4 
     5             // Check to see if the camera is available on the device.
     6             if ((PhotoCamera.IsCameraTypeSupported(CameraType.Primary) == true) ||
     7                  (PhotoCamera.IsCameraTypeSupported(CameraType.FrontFacing) == true))
     8             {
     9                 // Initialize the camera, when available.
    10                 if (PhotoCamera.IsCameraTypeSupported(CameraType.FrontFacing))
    11                 {
    12                     // Use front-facing camera if available.
    13                     cam = new Microsoft.Devices.PhotoCamera(CameraType.FrontFacing);
    14                 }
    15                 else
    16                 {
    17                     // Otherwise, use standard camera on back of device.
    18                     cam = new Microsoft.Devices.PhotoCamera(CameraType.Primary);
    19                 }
    20 
    21                 // Event is fired when the PhotoCamera object has been initialized.
    22                 cam.Initialized += new EventHandler<Microsoft.Devices.CameraOperationCompletedEventArgs>(cam_Initialized);
    23 
    24                 // Event is fired when the capture sequence is complete.
    25                 cam.CaptureCompleted += new EventHandler<CameraOperationCompletedEventArgs>(cam_CaptureCompleted);
    26 
    27                 // Event is fired when the capture sequence is complete and an image is available.
    28                 cam.CaptureImageAvailable += new EventHandler<Microsoft.Devices.ContentReadyEventArgs>(cam_CaptureImageAvailable);
    29 
    30                 // Event is fired when the capture sequence is complete and a thumbnail image is available.
    31                 cam.CaptureThumbnailAvailable += new EventHandler<ContentReadyEventArgs>(cam_CaptureThumbnailAvailable);
    32 
    33                 // The event is fired when auto-focus is complete.
    34                 cam.AutoFocusCompleted += new EventHandler<CameraOperationCompletedEventArgs>(cam_AutoFocusCompleted);
    35 
    36                 // The event is fired when the viewfinder is tapped (for focus).
    37                 viewfinderCanvas.Tap += new EventHandler<GestureEventArgs>(focus_Tapped);
    38 
    39                 // The event is fired when the shutter button receives a half press.
    40                 CameraButtons.ShutterKeyHalfPressed += OnButtonHalfPress;
    41 
    42                 // The event is fired when the shutter button receives a full press.
    43                 CameraButtons.ShutterKeyPressed += OnButtonFullPress;
    44 
    45                 // The event is fired when the shutter button is released.
    46                 CameraButtons.ShutterKeyReleased += OnButtonRelease;
    47 
    48                 //Set the VideoBrush source to the camera.
    49                 viewfinderBrush.SetSource(cam);
    50             }
    51             else
    52             {
    53                 // The camera is not supported on the device.
    54                 this.Dispatcher.BeginInvoke(delegate()
    55                 {
    56                     // Write message.
    57                     txtDebug.Text = "A Camera is not available on this device.";
    58                 });
    59 
    60                 // Disable UI.
    61                 ShutterButton.IsEnabled = false;
    62                 FlashButton.IsEnabled = false;
    63                 AFButton.IsEnabled = false;
    64                 ResButton.IsEnabled = false;
    65             }
    66         }

    在这些事件中,最为重要的是Initialized、CaptureImageAvailable和CaptureThumbnailAvailable这三个事件。而CameraButtons.ShutterKeyHalfPressed、CameraButtons.ShutterKeyPressed和CameraButtons.ShutterKeyReleased三个事件就是手机物理拍照按键的事件了。

    Initialized意味着相机初始化完毕,此时可以设置闪光灯模式、相机分辨率等。

     1   void _camera_Initialized(object sender, CameraOperationCompletedEventArgs e)
     2         {
     3             if (e.Succeeded)
     4             {
     5                 this.Dispatcher.BeginInvoke(delegate()
     6                 {
     7                     // 初始化闪光灯模式
     8                     _camera.FlashMode = FlashMode.Off;
     9                     btnFlash.Content = "闪光灯:Off";
    10 
    11                     // 初始化分辨率设置
    12                     _camera.Resolution = _camera.AvailableResolutions.ElementAt<Size>(_currentResolutionIndex);
    13                     btnResolution.Content = "分辨率:" + _camera.AvailableResolutions.ElementAt<Size>(_currentResolutionIndex);
    14 
    15                     lblMsg.Text = "主摄像头初始化成功";
    16                 });
    17             }
    18         }

    CaptureImageAvailable是当相片流得到后触发的。此时参数e的ImageStream就是图片流,可以按照需要保存。

     1 void cam_CaptureImageAvailable(object sender, Microsoft.Devices.ContentReadyEventArgs e)
     2         {
     3             string fileName = savedCounter + ".jpg";
     4 
     5             try
     6             {   // Write message to the UI thread.
     7                 Deployment.Current.Dispatcher.BeginInvoke(delegate()
     8                 {
     9                     txtDebug.Text = "Captured image available, saving picture.";
    10                 });
    11 
    12                 // Save picture to the library camera roll.
    13                 library.SavePictureToCameraRoll(fileName, e.ImageStream);
    14 
    15                 // Write message to the UI thread.
    16                     Deployment.Current.Dispatcher.BeginInvoke(delegate()
    17                 {
    18                     txtDebug.Text = "Picture has been saved to camera roll.";
    19 
    20                 });
    21 
    22                 // Set the position of the stream back to start
    23                 e.ImageStream.Seek(0, SeekOrigin.Begin);
    24 
    25                 // Save picture as JPEG to isolated storage.
    26                 using (IsolatedStorageFile isStore = IsolatedStorageFile.GetUserStoreForApplication())
    27                 {
    28                     using (IsolatedStorageFileStream targetStream = isStore.OpenFile(fileName, FileMode.Create, FileAccess.Write))
    29                     {
    30                         // Initialize the buffer for 4KB disk pages.
    31                         byte[] readBuffer = new byte[4096];
    32                         int bytesRead = -1;
    33 
    34                         // Copy the image to isolated storage. 
    35                         while ((bytesRead = e.ImageStream.Read(readBuffer, 0, readBuffer.Length)) > 0)
    36                         {
    37                             targetStream.Write(readBuffer, 0, bytesRead);
    38                         }
    39                     }
    40                 }
    41 
    42                 // Write message to the UI thread.
    43                 Deployment.Current.Dispatcher.BeginInvoke(delegate()
    44                 {
    45                     txtDebug.Text = "Picture has been saved to isolated storage.";
    46 
    47                 });
    48             }
    49             finally
    50             {
    51                 // Close image stream
    52                 e.ImageStream.Close();
    53             }
    54 
    55         }

    CaptureThumbnailAvailable的代码和上面的几乎一样,只不过这个事件中参数e的ImageStream是拍照缩略图的流文件。

    到此为止相机的准备工作就差不多了,那么当我真正要拍照时怎么做呢?以使用相机的物理拍照按键为例:

    1  private void OnButtonFullPress(object sender, EventArgs e)
    2         {
    3             if (_camera != null)
    4             {
    5                 _camera.CaptureImage();
    6             }
    7         }

    就是简单的一个CaptureImage方法即可。它会触发我们之前设置好的PhotoCamera的各种事件,最终得到图片流。

    在使用CaptureThumbnailAvailable时随着手机的转动,如果不在代码中调整CompositeTransform的角度,会使得VideoBrush中显示的内容发生偏转。一般都是在页面的OnOrientationChanged事件中处理,具体的就不写出来了,请参考PhotoCamera的例子:

    http://code.msdn.microsoft.com/Basic-Camera-Sample-52dae359

    总体来说PhotoCamera是可以在APP中执行的,不需要墓碑化。而且使用起来也比较简单,它也可以设置是否对焦等相机的基本功能。但是似乎它存在一个致命的问题,说似乎是因为我找了很多办法都无法解决,在stack overflow上看到有人说这个确实解决不了。这个问题就是当你旋转手机时,即使你用代码的方式让VideoBrush显示正确了,但是拍摄出来的照片仍旧旋转角度不对,也就是说VideoBrush中看到的不是真正拍摄出来的。如果哪位看官有办法解决这个问题,还望不吝赐教。

    最后说下PhotoCaptureDevice,其实我个人认为,搞定这个其它两个都可以不使用了。习惯看MSDN的同志应该都有看到这个类的使用,构建一个功能完全的拍照应用就应该也必须使用它。

    先来设置必要的变量:

    1    //设置摄像头是正面还是背面
    2    public CameraSensorLocation cameraSensorLocation { get; set; }
    3    //摄像头
    4    public PhotoCaptureDevice curPhotoCaptureDevice { get; private set; }
    5    //捕获序列
    6    private CameraCaptureSequence cameraCaptureSequence;
    7    //当前摄像头像素,目前采用的是最大像素
    8    private Windows.Foundation.Size captureResolution;

    先看下下面的代码:

     1       //检查手机是否支持摄像头
     2             if (PhotoCaptureDevice.AvailableSensorLocations.Contains(CameraSensorLocation.Back) ||
     3                 PhotoCaptureDevice.AvailableSensorLocations.Contains(CameraSensorLocation.Front))
     4             {
     5                 // 初始化摄像头信息
     6                 if (PhotoCaptureDevice.AvailableSensorLocations.Contains(CameraSensorLocation.Back))
     7                 {
     8                     //使用后置摄像头
     9                     BackSupportedResolutions = PhotoCaptureDevice.GetAvailableCaptureResolutions(CameraSensorLocation.Back);
    10                     this.cameraSensorLocation = CameraSensorLocation.Back;
    11                     this.captureResolution = BackSupportedResolutions[0];
    12                 }
    13                 else
    14                 {
    15                     //使用前置摄像头
    16                     FrontSupportedResolutions = PhotoCaptureDevice.GetAvailableCaptureResolutions(CameraSensorLocation.Front);
    17                     this.cameraSensorLocation = CameraSensorLocation.Front;
    18                     this.captureResolution = FrontSupportedResolutions[0];
    19                 }
    20         }

          首先用PhotoCaptureDevice的静态方法来判定手机是否支持前置和后置摄像头。然后如果支持后置一般默认使用后置摄像头,当然这个是可以切换的。你需要得到一个相机的分辨率captureResolution,这个是在初始化相机时使用的,我使用是相机所支持的最大像素,只要拍摄出来的图片会比较大。以及一个相机位置cameraSensorLocation(前置或者后置)。

         接下来初始化相机:

    1    //如果摄像头已经加载,则不重复加载
    2    if (this.curPhotoCaptureDevice != null)
    3      {
    4          return false;
    5      }
    6    //创建PhotoCaptureDevice
    7    this.cameraSensorLocation = cameraSensorLocation;
    8    this.curPhotoCaptureDevice = await PhotoCaptureDevice.OpenAsync(this.cameraSensorLocation, captureResolution);

     当相机初始化完成后,可以指定相机的属性,比如下面的。这些属性是比较多的,具体需要请参靠MSDN。

    1  curPhotoCaptureDevice.SetProperty(KnownCameraGeneralProperties.PlayShutterSoundOnCapture, true);
    2  curPhotoCaptureDevice.SetProperty(KnownCameraGeneralProperties.AutoFocusRange, AutoFocusRange.Infinity);

    设置完属性就需要设置捕获序列。捕获序列是很重要的一个东西,在创建完捕获序列后还可以设置帧的属性,比如可以设置相机场景模式:例如人物啊、风景啊,用过数码相机的都应该知道是怎么回事。这个就是PhotoCaptureDevice的强大之处,你可以创建一个非常不错的拍摄应用。最后在拍照前需要准备好捕获序列。

    1 //捕获序列是发送给手机 CPU 的工作单位。当发起捕获时,使用它定义希望发生的内容。
    2 //使用照片捕获设备上的方法创建捕获序列。需要指定的唯一参数是希望包括在序列中的帧的数量。
    3 //在此版本中,该值将始终为 1。当发起捕获时,将会立即捕获帧。
    4 this.cameraCaptureSequence = this.curPhotoCaptureDevice.CreateCaptureSequence(1);
    5 //设置照相机场景模式
    6 this.cameraCaptureSequence.Frames[0].DesiredProperties[KnownCameraPhotoProperties.SceneMode]
    7                               = CameraSceneMode.Portrait;
    8 await this.curPhotoCaptureDevice.PrepareCaptureSequenceAsync(this.cameraCaptureSequence);

    最后看一下拍照的核心部分:

    1   MemoryStream thumbnailStream = new MemoryStream();
    2   MemoryStream imageStream = new MemoryStream();
    3 
    4   his.cameraCaptureSequence.Frames[0].ThumbnailStream =  thumbnailStream.AsOutputStream();
    5   this.cameraCaptureSequence.Frames[0].CaptureStream = imageStream.AsOutputStream();
    6   //最终获得图片的位置
    7   await this.cameraCaptureSequence.StartCaptureAsync();

    捕获序列的ThumbnailStream是拍照的缩略图流,而CaptureStream则是正常图片流。它们保存在了我们定义的thumbnailStream和imageStream中。这样你就可以自由的处理图片流了。

    说了这么多,基本上PhotoCaptureDevice的拍照就完成了。等等,我好像忘记了什么。上面说到PhotoCamera在最终得到图片后旋转角度可能有问题,那么PhotoCaptureDevice中呢?其实在PhotoCaptureDevice中如果不专门设置也会有问题的,但是就因为PhotoCaptureDevice多了下面的属性设置可以使得拍照出来的流内容在编码前得到旋转。具体角度也是需要计算的,这个和PhotoCamera差不多。

    1  this.curPhotoCaptureDevice.SetProperty(KnownCameraGeneralProperties.EncodeWithOrientation, 90);

    下面给大家提供个MSDN上PhotoCaptureDevice的例子,这个例子很好的演示了如何使用PhotoCaptureDevice。

    http://code.msdn.microsoft.com/Basic-Lens-sample-359fda1b

  • 相关阅读:
    PCLVisualizer::addSphere 运行报错解决方案
    八叉树 (转载)
    Django 迁移错误 Cannot add foreign key constraint,字段类型自动变成Bigint(20)
    Django之Model字段详解
    FineReport如何连接hadoop,hive,Impala数据库,Kerberos认证
    图片自适应容器的几种方法
    【windows】查看电池使用情况
    【C++】C++复合变量的定义及易错点
    【c++】C++自定义类注意事项
    金字塔池化Spatial Pyramid Pooling
  • 原文地址:https://www.cnblogs.com/klkucan/p/3440251.html
Copyright © 2011-2022 走看看