How To Scan QRCode For UWP (4)

QR Code的全稱是Quick Response Code,中文翻譯爲快速響應矩陣圖碼,有關它的簡介能夠查看維基百科。 我準備使用ZXing.Net來實現掃描二維碼的功能,ZXing.Net在Codeplex上有介紹能夠參考https://zxingnet.codeplex.com/。html

能夠經過Nuget來引入ZXing.uwp到項目中,解碼QR code的API很是簡單,以下所示:git

public Result Decode(WriteableBitmap barcodeBitmap);

看上去實現解碼功能很easy,只須要調用 LowLagPhotoCapture.CaptureAsync 獲取Camera捕獲的IRandomAccessStream對象,而後構造一個新的 WriteableBitmap 對象 調用WriteableBmp.SetSourceAsync 將捕獲的流設置到 WriteableBitmap 裏面。最後調用BarcodeReader.Decode來解碼QR code。github

然而事情每每並不是那麼順利,拿着平板對着屏幕掃了半天,也不見可以Quick Response。看來下ZXing.Net寫的實例代碼,跟我寫的沒有啥區別,一樣也是很難解碼QR code。 接着我嘗試去解碼一個靜態的二維碼圖片,發現成功率100%,並且成功解碼出來都是毫秒級別的。因而我又嘗試去調試了下Decode的實現源碼,發現我拍照的圖片分辨率是1920 * 1080,那張靜態圖片的分辨率是300 * 300。 因而我須要作的就是將拍照的圖片進行裁剪,裁剪出來的圖片的大小跟中間的那個框的大小差很少。關於如何去裁剪圖片能夠參考另一片隨筆 How To Crop Bitmap For UWPapp

爲了能方便測試反覆掃描二維碼,我還寫了一個簡單的ResultPage,就一個文本框顯示解碼後的文本,一個按鈕點擊繼續掃描。dom

實現代碼也是很是簡單:async

 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 }

 掃描二維碼的具體實現代碼以下:ide

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 」 異常,由於以前沒有中止。這個問題困擾了我一段時間。post

實現代碼:測試

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。ui

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

相關文章
相關標籤/搜索