通用二维码 Windows

QR code in Universal Windows

如何正确预览视频和识别二维码(任何带有 ZXing 的代码)?

在 windows phone 8.1 中,我使用 MediaCapture 开始预览并使用 VideoEffect 识别二维码。

在通用 Windows VideoEffect 中不起作用。

Recognize by timer with CapturePhotoToStorageFileAsync慢慢来。

我认为您使用 MediaCaptureZXing 库是正确的,但我认为它应该可以使用相机预览帧。我的想法是初始化相机,然后获取预览帧并将其变为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)MicrophoneWebcam 功能。

所以,我的解决方案

  1. 创建新的 WinRT 项目“Windows 运行时组件(通用 Windows)

  2. 在 WinRT 中创建

class CaptureVideoEffect : IBasicVideoEffect (from Windows.Media.Effects)
  1. 将效果附加到 MediaCapture
await capture.AddEffectAsync(MediaStreamType.VideoPreview, typeof(CaptureVideoEffect).FullName, ...);
  1. 现在在 CaptureVideoEffect.ProcessFrame(ProcessVideoFrameContext 上下文)中您可以获得实时视频输出

  2. 在CaptureVideoEffect.ProcessFrame中如果前一帧未完成解码则跳过帧

  3. 获取图像为字节[]

var frameSize = inputFrameBitmap.PixelWidth * inputFrameBitmap.PixelHeight * 4;
var frameBuffer = new Windows.Storage.Streams.Buffer((uint)frameSize);
inputFrameBitmap.CopyToBuffer(frameBuffer);
byte[] bitmap = frameBuffer.ToArray();
  1. 解码
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);
}