通用二维码 Windows
QR code in Universal Windows
如何正确预览视频和识别二维码(任何带有 ZXing 的代码)?
在 windows phone 8.1 中,我使用 MediaCapture 开始预览并使用 VideoEffect 识别二维码。
在通用 Windows VideoEffect 中不起作用。
我认为您使用 MediaCapture
和 ZXing
库是正确的,但我认为它应该可以使用相机预览帧。我的想法是初始化相机,然后获取预览帧并将其变为WriteableBitmap
,最后使用Zxing
Api来分析这个WriteableBitmap
。
这是我的示例:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="4*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<CaptureElement x:Name="PreviewControl" Stretch="Uniform" Grid.Row="0" />
<Button Content="Click Me" Click="Button_Click" Grid.Row="1" />
<TextBlock x:Name="txtDecoderType" Grid.Row="2" />
<TextBlock x:Name="txtDecoderContent" Grid.Row="3" />
</Grid>
我的代码中的关键点在这里:
public MainPage()
{
this.InitializeComponent();
NavigationCacheMode = NavigationCacheMode.Required;
// Useful to know when to initialize/clean up the camera
Application.Current.Suspending += Application_Suspending;
Application.Current.Resuming += Application_Resuming;
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
_displayOrientation = _displayInformation.CurrentOrientation;
_displayInformation.OrientationChanged += DisplayInformation_OrientationChanged;
await InitializeCameraAsync();
await _mediaCapture.VideoDeviceController.FocusControl.FocusAsync();
await GetPreviewFrameAsSoftwareBitmapAsync();
}
private readonly DisplayInformation _displayInformation = DisplayInformation.GetForCurrentView();
private DisplayOrientations _displayOrientation = DisplayOrientations.Portrait;
// Rotation metadata to apply to the preview stream (MF_MT_VIDEO_ROTATION)
// Reference: http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh868174.aspx
private static readonly Guid RotationKey = new Guid("C380465D-2271-428C-9B83-ECEA3B4A85C1");
// Prevent the screen from sleeping while the camera is running
private readonly DisplayRequest _displayRequest = new DisplayRequest();
// For listening to media property changes
private readonly SystemMediaTransportControls _systemMediaControls = SystemMediaTransportControls.GetForCurrentView();
// MediaCapture and its state variables
private MediaCapture _mediaCapture;
private bool _isInitialized = false;
private bool _isPreviewing = false;
// Information about the camera device
private bool _mirroringPreview = false;
private bool _externalCamera = false;
private async void Application_Suspending(object sender, SuspendingEventArgs e)
{
// Handle global application events only if this page is active
// See official sample
}
private async void Application_Resuming(object sender, object o)
{
// Handle global application events only if this page is active
// See official sample
}
protected override async void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
// Handling of this event is included for completenes, as it will only fire when navigating between pages and this sample only includes one page
// See official sample
}
private async void SystemMediaControls_PropertyChanged(SystemMediaTransportControls sender, SystemMediaTransportControlsPropertyChangedEventArgs args)
{
// See official sample
}
private async void DisplayInformation_OrientationChanged(DisplayInformation sender, object args)
{
// See official sample
}
private async void MediaCapture_Failed(MediaCapture sender, MediaCaptureFailedEventArgs errorEventArgs)
{
// See official sample
}
private async Task InitializeCameraAsync()
{
Debug.WriteLine("InitializeCameraAsync");
if (_mediaCapture == null)
{
// Attempt to get the back camera if one is available, but use any camera device if not
var cameraDevice = await FindCameraDeviceByPanelAsync(Windows.Devices.Enumeration.Panel.Back);
if (cameraDevice == null)
{
Debug.WriteLine("No camera device found!");
return;
}
// Create MediaCapture and its settings
_mediaCapture = new MediaCapture();
// Register for a notification when something goes wrong
_mediaCapture.Failed += MediaCapture_Failed;
var settings = new MediaCaptureInitializationSettings { VideoDeviceId = cameraDevice.Id };
// Initialize MediaCapture
try
{
await _mediaCapture.InitializeAsync(settings);
_isInitialized = true;
}
catch (UnauthorizedAccessException)
{
Debug.WriteLine("The app was denied access to the camera");
}
// If initialization succeeded, start the preview
if (_isInitialized)
{
// Figure out where the camera is located
if (cameraDevice.EnclosureLocation == null || cameraDevice.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Unknown)
{
// No information on the location of the camera, assume it's an external camera, not integrated on the device
_externalCamera = true;
}
else
{
// Camera is fixed on the device
_externalCamera = false;
// Only mirror the preview if the camera is on the front panel
_mirroringPreview = (cameraDevice.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Front);
}
await StartPreviewAsync();
}
}
}
private async Task StartPreviewAsync()
{
Debug.WriteLine("StartPreviewAsync");
// Prevent the device from sleeping while the preview is running
_displayRequest.RequestActive();
// Register to listen for media property changes
_systemMediaControls.PropertyChanged += SystemMediaControls_PropertyChanged;
// Set the preview source in the UI and mirror it if necessary
PreviewControl.Source = _mediaCapture;
PreviewControl.FlowDirection = _mirroringPreview ? FlowDirection.RightToLeft : FlowDirection.LeftToRight;
// Start the preview
await _mediaCapture.StartPreviewAsync();
_isPreviewing = true;
// Initialize the preview to the current orientation
if (_isPreviewing)
{
await SetPreviewRotationAsync();
}
}
private async Task SetPreviewRotationAsync()
{
// Only need to update the orientation if the camera is mounted on the device
if (_externalCamera) return;
// Calculate which way and how far to rotate the preview
int rotationDegrees = ConvertDisplayOrientationToDegrees(_displayOrientation);
// The rotation direction needs to be inverted if the preview is being mirrored
if (_mirroringPreview)
{
rotationDegrees = (360 - rotationDegrees) % 360;
}
// Add rotation metadata to the preview stream to make sure the aspect ratio / dimensions match when rendering and getting preview frames
var props = _mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview);
props.Properties.Add(RotationKey, rotationDegrees);
await _mediaCapture.SetEncodingPropertiesAsync(MediaStreamType.VideoPreview, props, null);
}
private async Task StopPreviewAsync()
{
// See official sample
}
private async Task GetPreviewFrameAsSoftwareBitmapAsync()
{
// Get information about the preview
var previewProperties = _mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview) as VideoEncodingProperties;
// Create the video frame to request a SoftwareBitmap preview frame
var videoFrame = new VideoFrame(BitmapPixelFormat.Bgra8, (int)previewProperties.Width, (int)previewProperties.Height);
// Capture the preview frame
using (var currentFrame = await _mediaCapture.GetPreviewFrameAsync(videoFrame))
{
// Collect the resulting frame
SoftwareBitmap previewFrame = currentFrame.SoftwareBitmap;
WriteableBitmap wbmap = new WriteableBitmap((int)previewProperties.Width, (int)previewProperties.Height);
previewFrame.CopyToBuffer(wbmap.PixelBuffer);
qrcode(wbmap);
}
}
private async Task CleanupCameraAsync()
{
// See official sample
}
private static async Task<DeviceInformation> FindCameraDeviceByPanelAsync(Windows.Devices.Enumeration.Panel desiredPanel)
{
// See official sample
}
private static int ConvertDisplayOrientationToDegrees(DisplayOrientations orientation)
{
// See official sample
}
private async void qrcode(WriteableBitmap bmp)
{
IBarcodeReader reader = new BarcodeReader();
// detect and decode the barcode inside the bitmap
var result = reader.Decode(bmp);
// do something with the result
if (result != null)
{
txtDecoderType.Text = result.BarcodeFormat.ToString();
txtDecoderContent.Text = result.Text;
}
else
{
await GetPreviewFrameAsSoftwareBitmapAsync();
}
}
我已经用 Lumia 950 测试了这段代码,它有时在我身边运行得很快而且很好,但有时它就是无法解码预览帧。所以这里只是简单的示例,并不是100%正确就可以在正式项目中使用。我这里是尝试用Zxing
api和官方的Camera preview frame sample, so if you have any problem about get preview frame, you can look into this sample. There is also a Zxing sample for QR code,你也可以下载看看
补充:您需要在清单文件中启用 Internet(Client)
、Microphone
、Webcam
功能。
所以,我的解决方案
创建新的 WinRT 项目“Windows 运行时组件(通用 Windows)
在 WinRT 中创建
class CaptureVideoEffect : IBasicVideoEffect (from Windows.Media.Effects)
- 将效果附加到 MediaCapture
await capture.AddEffectAsync(MediaStreamType.VideoPreview, typeof(CaptureVideoEffect).FullName, ...);
现在在 CaptureVideoEffect.ProcessFrame(ProcessVideoFrameContext 上下文)中您可以获得实时视频输出
在CaptureVideoEffect.ProcessFrame中如果前一帧未完成解码则跳过帧
获取图像为字节[]
var frameSize = inputFrameBitmap.PixelWidth * inputFrameBitmap.PixelHeight * 4;
var frameBuffer = new Windows.Storage.Streams.Buffer((uint)frameSize);
inputFrameBitmap.CopyToBuffer(frameBuffer);
byte[] bitmap = frameBuffer.ToArray();
- 解码
result = barcodeReader.Decode(bitmap, inputFrameBitmap.PixelWidth, inputFrameBitmap.PixelHeight, BitmapFormat.RGB32);
public sealed class BarcodeReader
{
public Result Decode([ReadOnlyArray] byte[] rawRGB, int width, int height, BitmapFormat format);
}
如何正确预览视频和识别二维码(任何带有 ZXing 的代码)?
在 windows phone 8.1 中,我使用 MediaCapture 开始预览并使用 VideoEffect 识别二维码。
在通用 Windows VideoEffect 中不起作用。
我认为您使用 MediaCapture
和 ZXing
库是正确的,但我认为它应该可以使用相机预览帧。我的想法是初始化相机,然后获取预览帧并将其变为WriteableBitmap
,最后使用Zxing
Api来分析这个WriteableBitmap
。
这是我的示例:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="4*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<CaptureElement x:Name="PreviewControl" Stretch="Uniform" Grid.Row="0" />
<Button Content="Click Me" Click="Button_Click" Grid.Row="1" />
<TextBlock x:Name="txtDecoderType" Grid.Row="2" />
<TextBlock x:Name="txtDecoderContent" Grid.Row="3" />
</Grid>
我的代码中的关键点在这里:
public MainPage()
{
this.InitializeComponent();
NavigationCacheMode = NavigationCacheMode.Required;
// Useful to know when to initialize/clean up the camera
Application.Current.Suspending += Application_Suspending;
Application.Current.Resuming += Application_Resuming;
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
_displayOrientation = _displayInformation.CurrentOrientation;
_displayInformation.OrientationChanged += DisplayInformation_OrientationChanged;
await InitializeCameraAsync();
await _mediaCapture.VideoDeviceController.FocusControl.FocusAsync();
await GetPreviewFrameAsSoftwareBitmapAsync();
}
private readonly DisplayInformation _displayInformation = DisplayInformation.GetForCurrentView();
private DisplayOrientations _displayOrientation = DisplayOrientations.Portrait;
// Rotation metadata to apply to the preview stream (MF_MT_VIDEO_ROTATION)
// Reference: http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh868174.aspx
private static readonly Guid RotationKey = new Guid("C380465D-2271-428C-9B83-ECEA3B4A85C1");
// Prevent the screen from sleeping while the camera is running
private readonly DisplayRequest _displayRequest = new DisplayRequest();
// For listening to media property changes
private readonly SystemMediaTransportControls _systemMediaControls = SystemMediaTransportControls.GetForCurrentView();
// MediaCapture and its state variables
private MediaCapture _mediaCapture;
private bool _isInitialized = false;
private bool _isPreviewing = false;
// Information about the camera device
private bool _mirroringPreview = false;
private bool _externalCamera = false;
private async void Application_Suspending(object sender, SuspendingEventArgs e)
{
// Handle global application events only if this page is active
// See official sample
}
private async void Application_Resuming(object sender, object o)
{
// Handle global application events only if this page is active
// See official sample
}
protected override async void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
// Handling of this event is included for completenes, as it will only fire when navigating between pages and this sample only includes one page
// See official sample
}
private async void SystemMediaControls_PropertyChanged(SystemMediaTransportControls sender, SystemMediaTransportControlsPropertyChangedEventArgs args)
{
// See official sample
}
private async void DisplayInformation_OrientationChanged(DisplayInformation sender, object args)
{
// See official sample
}
private async void MediaCapture_Failed(MediaCapture sender, MediaCaptureFailedEventArgs errorEventArgs)
{
// See official sample
}
private async Task InitializeCameraAsync()
{
Debug.WriteLine("InitializeCameraAsync");
if (_mediaCapture == null)
{
// Attempt to get the back camera if one is available, but use any camera device if not
var cameraDevice = await FindCameraDeviceByPanelAsync(Windows.Devices.Enumeration.Panel.Back);
if (cameraDevice == null)
{
Debug.WriteLine("No camera device found!");
return;
}
// Create MediaCapture and its settings
_mediaCapture = new MediaCapture();
// Register for a notification when something goes wrong
_mediaCapture.Failed += MediaCapture_Failed;
var settings = new MediaCaptureInitializationSettings { VideoDeviceId = cameraDevice.Id };
// Initialize MediaCapture
try
{
await _mediaCapture.InitializeAsync(settings);
_isInitialized = true;
}
catch (UnauthorizedAccessException)
{
Debug.WriteLine("The app was denied access to the camera");
}
// If initialization succeeded, start the preview
if (_isInitialized)
{
// Figure out where the camera is located
if (cameraDevice.EnclosureLocation == null || cameraDevice.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Unknown)
{
// No information on the location of the camera, assume it's an external camera, not integrated on the device
_externalCamera = true;
}
else
{
// Camera is fixed on the device
_externalCamera = false;
// Only mirror the preview if the camera is on the front panel
_mirroringPreview = (cameraDevice.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Front);
}
await StartPreviewAsync();
}
}
}
private async Task StartPreviewAsync()
{
Debug.WriteLine("StartPreviewAsync");
// Prevent the device from sleeping while the preview is running
_displayRequest.RequestActive();
// Register to listen for media property changes
_systemMediaControls.PropertyChanged += SystemMediaControls_PropertyChanged;
// Set the preview source in the UI and mirror it if necessary
PreviewControl.Source = _mediaCapture;
PreviewControl.FlowDirection = _mirroringPreview ? FlowDirection.RightToLeft : FlowDirection.LeftToRight;
// Start the preview
await _mediaCapture.StartPreviewAsync();
_isPreviewing = true;
// Initialize the preview to the current orientation
if (_isPreviewing)
{
await SetPreviewRotationAsync();
}
}
private async Task SetPreviewRotationAsync()
{
// Only need to update the orientation if the camera is mounted on the device
if (_externalCamera) return;
// Calculate which way and how far to rotate the preview
int rotationDegrees = ConvertDisplayOrientationToDegrees(_displayOrientation);
// The rotation direction needs to be inverted if the preview is being mirrored
if (_mirroringPreview)
{
rotationDegrees = (360 - rotationDegrees) % 360;
}
// Add rotation metadata to the preview stream to make sure the aspect ratio / dimensions match when rendering and getting preview frames
var props = _mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview);
props.Properties.Add(RotationKey, rotationDegrees);
await _mediaCapture.SetEncodingPropertiesAsync(MediaStreamType.VideoPreview, props, null);
}
private async Task StopPreviewAsync()
{
// See official sample
}
private async Task GetPreviewFrameAsSoftwareBitmapAsync()
{
// Get information about the preview
var previewProperties = _mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview) as VideoEncodingProperties;
// Create the video frame to request a SoftwareBitmap preview frame
var videoFrame = new VideoFrame(BitmapPixelFormat.Bgra8, (int)previewProperties.Width, (int)previewProperties.Height);
// Capture the preview frame
using (var currentFrame = await _mediaCapture.GetPreviewFrameAsync(videoFrame))
{
// Collect the resulting frame
SoftwareBitmap previewFrame = currentFrame.SoftwareBitmap;
WriteableBitmap wbmap = new WriteableBitmap((int)previewProperties.Width, (int)previewProperties.Height);
previewFrame.CopyToBuffer(wbmap.PixelBuffer);
qrcode(wbmap);
}
}
private async Task CleanupCameraAsync()
{
// See official sample
}
private static async Task<DeviceInformation> FindCameraDeviceByPanelAsync(Windows.Devices.Enumeration.Panel desiredPanel)
{
// See official sample
}
private static int ConvertDisplayOrientationToDegrees(DisplayOrientations orientation)
{
// See official sample
}
private async void qrcode(WriteableBitmap bmp)
{
IBarcodeReader reader = new BarcodeReader();
// detect and decode the barcode inside the bitmap
var result = reader.Decode(bmp);
// do something with the result
if (result != null)
{
txtDecoderType.Text = result.BarcodeFormat.ToString();
txtDecoderContent.Text = result.Text;
}
else
{
await GetPreviewFrameAsSoftwareBitmapAsync();
}
}
我已经用 Lumia 950 测试了这段代码,它有时在我身边运行得很快而且很好,但有时它就是无法解码预览帧。所以这里只是简单的示例,并不是100%正确就可以在正式项目中使用。我这里是尝试用Zxing
api和官方的Camera preview frame sample, so if you have any problem about get preview frame, you can look into this sample. There is also a Zxing sample for QR code,你也可以下载看看
补充:您需要在清单文件中启用 Internet(Client)
、Microphone
、Webcam
功能。
所以,我的解决方案
创建新的 WinRT 项目“Windows 运行时组件(通用 Windows)
在 WinRT 中创建
class CaptureVideoEffect : IBasicVideoEffect (from Windows.Media.Effects)
- 将效果附加到 MediaCapture
await capture.AddEffectAsync(MediaStreamType.VideoPreview, typeof(CaptureVideoEffect).FullName, ...);
现在在 CaptureVideoEffect.ProcessFrame(ProcessVideoFrameContext 上下文)中您可以获得实时视频输出
在CaptureVideoEffect.ProcessFrame中如果前一帧未完成解码则跳过帧
获取图像为字节[]
var frameSize = inputFrameBitmap.PixelWidth * inputFrameBitmap.PixelHeight * 4;
var frameBuffer = new Windows.Storage.Streams.Buffer((uint)frameSize);
inputFrameBitmap.CopyToBuffer(frameBuffer);
byte[] bitmap = frameBuffer.ToArray();
- 解码
result = barcodeReader.Decode(bitmap, inputFrameBitmap.PixelWidth, inputFrameBitmap.PixelHeight, BitmapFormat.RGB32);
public sealed class BarcodeReader
{
public Result Decode([ReadOnlyArray] byte[] rawRGB, int width, int height, BitmapFormat format);
}