zoukankan      html  css  js  c++  java
  • How To Scan QRCode For UWP (4)

    QR Code的全称是Quick Response Code,中文翻译为快速响应矩阵图码,有关它的简介可以查看维基百科。 我准备使用ZXing.Net来实现扫描二维码的功能,ZXing.Net在Codeplex上有介绍可以参考https://zxingnet.codeplex.com/。

    可以通过Nuget来引入ZXing.uwp到项目中,解码QR code的API非常简单,如下所示:

    public Result Decode(WriteableBitmap barcodeBitmap);

    看上去实现解码功能很easy,只需要调用 LowLagPhotoCapture.CaptureAsync 获取Camera捕获的IRandomAccessStream对象,然后构造一个新的 WriteableBitmap 对象 调用WriteableBmp.SetSourceAsync 将捕获的流设置到 WriteableBitmap 里面。最后调用BarcodeReader.Decode来解码QR code。

    然而事情往往并非那么顺利,拿着平板对着屏幕扫了半天,也不见能够Quick Response。看来下ZXing.Net写的实例代码,跟我写的没有啥区别,同样也是很难解码QR code。 接着我尝试去解码一个静态的二维码图片,发现成功率100%,而且成功解码出来都是毫秒级别的。于是我又尝试去调试了下Decode的实现源码,发现我拍照的图片分辨率是1920 * 1080,那张静态图片的分辨率是300 * 300。 于是我需要做的就是将拍照的图片进行裁剪,裁剪出来的图片的大小跟中间的那个框的大小差不多。关于如何去裁剪图片可以参考另外一片随笔 How To Crop Bitmap For UWP

    为了能方便测试反复扫描二维码,我还写了一个简单的ResultPage,就一个文本框显示解码后的文本,一个按钮点击继续扫描。

    实现代码也是非常简单:

     1 using Windows.UI.Xaml;
     2 using Windows.UI.Xaml.Controls;
     3 using Windows.UI.Xaml.Navigation;
     4 
     5 namespace ScanQRCode
     6 {
     7     public sealed partial class ResultPage : Page
     8     {
     9         public ResultPage()
    10         {
    11             this.InitializeComponent();
    12         }
    13 
    14         protected override void OnNavigatedTo(NavigationEventArgs e)
    15         {
    16             txtResult.Text = e.Parameter.ToString();
    17         }
    18 
    19         private void btnScan_Click(object sender, RoutedEventArgs e)
    20         {
    21             Frame.Navigate(typeof(MainPage));
    22         }
    23     }
    24 }

     扫描二维码的具体实现代码如下:

    private async Task StartScanQRCode()
            {
                try
                {
                    Result _result = null;
                    while (_result == null && lowLagPhotoCapture != null && IsCurrentUIActive)
                    {
                        var capturedPhoto = await lowLagPhotoCapture.CaptureAsync();
                        if (capturedPhoto == null)
                        {
                            continue;
                        }
                        using (var stream = capturedPhoto.Frame.CloneStream())
                        {
                            //calculate the crop square's length.
                            var pixelWidth = capturedPhoto.Frame.Width;
                            var pixelHeight = capturedPhoto.Frame.Height;
                            var rate = Math.Min(pixelWidth, pixelHeight) / Math.Min(ActualWidth, ActualHeight);
                            var cropLength = focusRecLength * rate;
    
                            // initialize with 1,1 to get the current size of the image
                            var writeableBmp = new WriteableBitmap(1, 1);
                            var rect = new Rect(pixelWidth / 2 - cropLength / 2,
                                pixelHeight / 2 - cropLength / 2, cropLength, cropLength);
                            using (var croppedStream = await ImageHelper.GetCroppedStreamAsync(stream, rect))
                            {
                                writeableBmp.SetSource(croppedStream);
                                // and create it again because otherwise the WB isn't fully initialized and decoding
                                // results in a IndexOutOfRange
                                writeableBmp = new WriteableBitmap((int)cropLength, (int)cropLength);
                                croppedStream.Seek(0);
                                await writeableBmp.SetSourceAsync(croppedStream);
                            }
    
                            _result = ScanBitmap(writeableBmp);
                        }
                    }
    
                    if (_result != null)
                    {
                        Frame.Navigate(typeof(ResultPage), _result.Text);
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("Exception when scaning QR code" + ex.Message);
                }
            }
    
     private Result ScanBitmap(WriteableBitmap writeableBmp)
            {
                var barcodeReader = new BarcodeReader
                {
                    AutoRotate = true,
                    Options = { TryHarder = true }
                };
    
                return barcodeReader.Decode(writeableBmp);
            }

    在处理窗体VisibilityChanged/Application.Current.Suspending/OnNavigatedTo/OnNavigatedFrom事件引发的时候,需要很好控制是需要开启Camera还是要关闭销毁Camera。本示例主要靠IsCurrentUIActive属性来判断,当IsCurrentUIActive为false的时候不能去调用LowLagPhotoCapture.CaptureAsync,否则就会抛“ A media source cannot go from the stopped state to the paused state ”异常,之后又无法调用MediaCapture.StopPreviewAsync来停止预览。重新初始化MediaCapture,再次去调用LowLagPhotoCapture.CaptureAsync又会抛出“ Hardware MFT failed to start streaming due to lack of hardware resources ” 异常,因为之前没有停止。这个问题困扰了我一段时间。

    实现代码:

    private bool IsCurrentUIActive
            {
                // UI is active if
                // * We are the current active page.
                // * The window is visible.
                // * The app is not suspending.
                get { return _isActivePage && !_isSuspending && Window.Current.Visible; }
            }

    根据当前UI是否为Active来决定是启动Camera还是销毁Camera。

    private async Task SwitchCameraOnUIStateChanged()
            {
                // Avoid reentrancy: Wait until nobody else is in this function.
                while (!_setupTask.IsCompleted)
                {
                    await _setupTask;
                }
    
                if (_isUIActive != IsCurrentUIActive)
                {
                    _isUIActive = IsCurrentUIActive;
    
                    Func<Task> setupAsync = async () =>
                    {
                        if (IsCurrentUIActive)
                        {
                            await StartCameraAsync();
                        }
                        else
                        {
                            await CleanupCameraAsync();
                        }
                    };
                    _setupTask = setupAsync();
                }
    
                await _setupTask;
            }

    完整代码已上传到github

    https://github.com/supperwu/UWP_Simple/tree/master/ScanQRCode

  • 相关阅读:
    聚焦LSMIMO的四大层面,浅谈5G关键技术
    基于LiteOS Studio零成本学习LiteOS物联网操作系统
    使用LiteOS Studio图形化查看LiteOS在STM32上运行的奥秘
    GaussDB(DWS)应用实践丨负载管理与作业排队处理方法
    GaussDB(DWS)磁盘维护:vacuum full执行慢怎么办?
    从物理空间到数字世界,数字孪生打造智能化基础设施
    Lab 4 : OpenFlow
    SDN控制器拓扑发现(一)
    pxe dhcp
    RyuBook1.0案例二:Traffic Monitor项目源码分析
  • 原文地址:https://www.cnblogs.com/supperwu/p/7280405.html
Copyright © 2011-2022 走看看