在过去的十年中手机集成的相机逐渐取代传统口袋中的相机。
开发一个提供先进拍照和图片处理的应用程序取决于两个方面 手机硬件和系统平台提供的照片捕获
API。诺基亚的新的系列设备与新的 Windows Phone8 先进的照片捕获 API 为你提供能够调节拍照
设备各种参数的权限,比如曝光时间、ISO、焦点位置和白平衡。
引用一个扩展演示示例应用程序,本文描述了如何初始化和使用相机,如何读取参数,该设备支持的问题
,如何设置参数,以及如何捕获和存储照片。
Windows Phone 8 增强的照片捕获 API
WP8 发布了全新的照片捕获 API,包含了可以高度配置相机参数。新的相机特性主要包括下面:
—获得或调整支持的相机参数:
static CameraCapturePropertyRange SupportedPropertyRange(CameraSensorLocation sensor, Guid propertyId)
static IReadOnlyList<object> GetSupportedPropertyValues(CameraSensorLocation sensor, Guid propertyId)
object GetProperty(Guid propertyId)
void SetProperty(Guid propertyId, object value)
—调用自动对焦,自动白平衡,自动曝光:
static bool IsFocusSupported(CameraSensorLocation sensor)
static bool IsFocusRegionSupported(CameraSensorLocation sensor)
IAsyncOperation<CameraFocusStatus> FocusAsync()
IAsyncOperation<CameraFocusStatus> ResetFocusAsync()
—检查、设置预览、拍摄的分辨率
static IReadOnlyList<Size> GetAvailablePreviewResolutions()
static IReadOnlyList<Size> GetAvailableCaptureResolutions()
IAsyncAction SetPreviewResolutionAsync(Size value)
IAsyncAction SetCaptureResolutionAsync(Size value)
—读取预览缓冲区:
event TypedEventHandler<ICameraCaptureDevice, Object> PreviewFrameAvailable
void GetPreviewBufferArgb(out int[] pixels)
void GetPreviewBufferY(out byte[] pixels)
void GetPreviewBufferYCbCr(out byte[] pixels)
—创建捕获序列,然后用来捕捉帧:
CameraCaptureSequence CreateCaptureSequence(uint numberOfFrames)
IAsyncAction PrepareCaptureSequenceAsync(CameraCaptureSequence sequence)
获取相机参数主要基于三种不同的方式。预览、读取捕获的分辨率和通过专门的同步方法设置。
同样对焦的操作 API 中也有相应的特定方法。图像特定参数的调整依赖于方法中标注参数的 GUID,
来声明参数值所支持的范围,和方法设置所支持的值。
图像拍照指定的参数取值一般都是范围值或者既定的数组值,比如 ISO,指定在一个最大值和一个
最小值,在这之间的值都是合法的。数组类型的参数由可能的值的列表组成,特别是设备上合法的枚举值。
WMAppManifest.xml 中添加ID_CAP_ISV_CAMERA 权限声明。
另外,保存图片到图片库需要添加
ID_CAP_MEDIALIB_PHOTO。
Nokia Lumia 相机参数
下面的表格列出了 Lumia820 和 920 设备上与图片相关的主要的相机参数。
设备 | Nokia Lumia 820 | Nokia Lumia 920 | ||
摄像头 | Front | Back | Front | Back |
自动对焦范围 | Infinity | Auto, Macro, Normal, Full, Hyperfocal, Infinity | Infinity | Auto, Macro, Normal, Full, Hyperfocal, Infinity |
预览分辨率 | 640x480 | 800x448, 640x480 | 1280x720, 1024x768 | 1280x720, 1024x768 |
捕获分辨率 | 640x480 | 3264x2448, 3552x2000, 2592x1936, 2592x1456, 2048x1536, 640x480 | 1280x960, 1280x720, 640x480 | 3264x2448, 3552x2000, 2592x1936, 2592x1456, 2048x1536, 640x480 |
曝光补偿 (EV) | -12...12 | -12...12 | -12...12 | -12...12 |
曝光时间 (microseconds) | 1...33333 | 1...500000 | 1...33333 | 1...500000 |
Flash mode | Off | Auto, On, Off | Off | Auto, On, Off |
光照对焦模式 | Off | Auto, On, Off | Off | Auto, On, Off |
ISO | 100...800 | 100...800 | 100...800 | 100...800 |
手动对焦位置 | No | Yes, 1000 positions | No | Yes, 1000 positions |
场景模式 | Auto, Sport, Night, Backlit | Auto, Macro, Sport, Night, Night Portrait, Backlit | Auto, Sport, Night, Backlit | Auto, Macro, Sport, Night, Night Portrait, Backlit |
预设白平衡 | Cloudy, Daylight, Fluorescent, Tungsten | Cloudy, Daylight, Fluorescent, Tungsten | Cloudy, Daylight, Fluorescent, Tungsten |
Camera Explorer application
Camera Explorer 示例应用程序是关于 PhotoCaptureDevice 如何使用的部分示例。该应用程序包含一个实时
取景器,一个包含很多调节参数的 settings 页面,和一个预览页面并且提供保存捕获图片的方法。
Camera Explorer 工程的架构图:
除了 4个主要的 Windows Phone application 页面,还有一个单例的 DataContext 对象包含应用程序里广泛
使用的对象,比如一个 Settings 的实例和一个 PhotoCaptureDevice 的实例。
DataContext 类在不同的页面中被使用:
...
class DataContext : INotifyPropertyChanged
{
...
public static DataContext Singleton { ... }
public ObservableCollection<Parameter> Parameters { ... }
public PhotoCaptureDevice Device { ... }
public MemoryStream ImageStream { ... }
}
捕获照片
在 Mainpage 的 xaml 中使用一个 Canvas 元素作为取景框,使用一个 VideoBrush 作为背景画刷:
<phone:PhoneApplicationPage x:Class="CameraExplorer.MainPage" ... >
...
<Grid x:Name="LayoutRoot" ... >
...
<Grid x:Name="ContentPanel" ... >
<Canvas x:Name="VideoCanvas">
<Canvas.Background>
<VideoBrush x:Name="videoBrush"/>
</Canvas.Background>
</Canvas>
...
</Grid>
</Grid>
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar>
<shell:ApplicationBarIconButton Text="capture" Click="captureButton_Click" ... />
...
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>
...
</phone:PhoneApplicationPage>
在 MainPage 的 C# 代码中,在InitializeCamera 方法中初始化 PhotoCaptureDevice 对象,并且被
保存在 DataContext 中。在 XAML 中声明的捕获按钮事件中可以看到一个照片如何被捕获:
...
public partial class MainPage : PhoneApplicationPage
{
private CameraExplorer.DataContext _dataContext = CameraExplorer.DataContext.Singleton;
...
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
if (_dataContext.Device == null)
{
...
await InitializeCamera(CameraSensorLocation.Back);
...
}
videoBrush.RelativeTransform = new CompositeTransform()
{
CenterX = 0.5,
CenterY = 0.5,
Rotation = _dataContext.Device.SensorLocation == CameraSensorLocation.Back ?
_dataContext.Device.SensorRotationInDegrees :
- _dataContext.Device.SensorRotationInDegrees
};
videoBrush.SetSource(_dataContext.Device);
...
}
private async Task InitializeCamera(CameraSensorLocation sensorLocation)
{
Windows.Foundation.Size initialResolution = new Windows.Foundation.Size(640, 480);
PhotoCaptureDevice d = await PhotoCaptureDevice.OpenAsync(sensorLocation, initialResolution);
d.SetProperty(KnownCameraGeneralProperties.EncodeWithOrientation,
d.SensorLocation == CameraSensorLocation.Back ?
d.SensorRotationInDegrees : -d.SensorRotationInDegrees);
_dataContext.Device = d;
}
private async void captureButton_Click(object sender, EventArgs e)
{
if (!_manuallyFocused)
{
await AutoFocus();
}
await Capture();
}
private async Task AutoFocus()
{
if (!_capturing && PhotoCaptureDevice.IsFocusSupported(_dataContext.Device.SensorLocation))
{
...
await _dataContext.Device.FocusAsync();
...
_capturing = false;
}
}
private async Task Capture()
{
if (!_capturing)
{
_capturing = true;
MemoryStream stream = new MemoryStream();
CameraCaptureSequence sequence = _dataContext.Device.CreateCaptureSequence(1);
sequence.Frames[0].CaptureStream = stream.AsOutputStream();
await _dataContext.Device.PrepareCaptureSequenceAsync(sequence);
await sequence.StartCaptureAsync();
_dataContext.ImageStream = stream;
...
}
...
}
...
}
显示预览并且保存到 Camera roll
显示一个捕获的照片的预览很容易。Camera Explorer 程序中使用一个 Image 的 XAML 元素使用一个
BitmapImage 对象实现。另外,保存图片到 camera roll 也很容易。仅仅需要记住在
MediaLibrary.SavePictureToCameraRoll(string name, Stream source) 方法中使用的图片流属性必须指定
为图片数据的开始。
<phone:PhoneApplicationPage x:Class="CameraExplorer.PreviewPage" ... >
...
<Grid x:Name="LayoutRoot" ... >
...
<Grid x:Name="ContentPanel" ... >
<Image x:Name="image" Stretch="UniformToFill"/>
</Grid>
</Grid>
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar>
<shell:ApplicationBarIconButton Text="save" Click="saveButton_Click" ... />
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>
...
</phone:PhoneApplicationPage>
...
public partial class PreviewPage : PhoneApplicationPage
{
private CameraExplorer.DataContext _dataContext = CameraExplorer.DataContext.Singleton;
private BitmapImage _bitmap = new BitmapImage();
protected override void OnNavigatedTo(NavigationEventArgs e)
{
...
_bitmap.SetSource(_dataContext.ImageStream);
image.Source = _bitmap;
...
}
private void saveButton_Click(object sender, EventArgs e)
{
try
{
_dataContext.ImageStream.Position = 0;
MediaLibrary library = new MediaLibrary();
library.SavePictureToCameraRoll("CameraExplorer_" + DateTime.Now.ToString() + ".jpg",
_dataContext.ImageStream);
}
catch (Exception)
{
...
}
...
}
}
调整 Camera 的参数
Camera Explorer 工程架构中的参数分不到 ArrayParameters 和RangeParameters 中,两者都继承自基类 Parameter。
这种工程的参数架构提供一种简单并且可扩展的方式来处理参数并且把它们直接 绑定到屏幕中的 UI 控件,比如 ComboBox,
Slider 和 Text。本文并没有涉及绑定,你可以参考程序的源码。
下面是简化的 Parameter 基类:
...
public abstract class Parameter : INotifyPropertyChanged
{
...
protected Parameter(PhotoCaptureDevice device, string name)
{
...
}
public PhotoCaptureDevice Device { ... }
public string Name { ... }
public bool Supported { ... }
public abstract void Refresh();
...
}
上面只是 Wiki 的部分内容。
: