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 UWP。app
爲了能方便測試反覆掃描二維碼,我還寫了一個簡單的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