你的位置:首页 > ASP.net教程

[ASP.net教程]UWP中二维码扫描如何实现及问题


二维码扫描的实现,简单的来说可以分三步走:“成像”、“截图”与“识别”。

UWP开发中,最常用的媒体工具非MediaCapture莫属了,下面就来简单介绍一下如何利用MediaCapture来实现扫描和截图并且利用Zxing识别二维码,以及会遇到的问题和需要注意的地方。

1. 初始化与成像

 1 private async void InitMediaCaptureAsync() 2     { 3       //寻找后置摄像头 4       var allVideoDevices = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture); 5       var cameraDevice = allVideoDevices.FirstOrDefault(x => x.EnclosureLocation != null && x.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Back); 6  7       if (cameraDevice == null) 8       { 9         Debug.WriteLine("No camera device found!");10 11         return;12       }13 14       var settings = new MediaCaptureInitializationSettings15       {16         StreamingCaptureMode = StreamingCaptureMode.Video,17         //必须,否则截图的时候会很卡很慢18         PhotoCaptureSource = PhotoCaptureSource.VideoPreview,19         VideoDeviceId = cameraDevice.Id20       };21 22       _mediaCapture = new MediaCapture();23 24       try25       {26         await _mediaCapture.InitializeAsync(settings);27         _initialized = true;//初始化成功28       }29       catch (UnauthorizedAccessException)30       {31         Debug.WriteLine("The app was denied access to the camera");32       }33       catch (Exception ex)34       {35         Debug.WriteLine("Exception when initializing MediaCapture with {0}: {1}", cameraDevice.Id, ex.ToString());36       }37 38       if (_initialized)39       {40         var focusControl = _mediaCapture.VideoDeviceController.FocusControl;41 42         if (focusControl.Supported)43         {44           var focusSettings = new FocusSettings()45           {46             Mode = focusControl.SupportedFocusModes.FirstOrDefault(f => f == FocusMode.Continuous),47             DisableDriverFallback = true,48             AutoFocusRange = focusControl.SupportedFocusRanges.FirstOrDefault(f => f == AutoFocusRange.FullRange),49             Distance = focusControl.SupportedFocusDistances.FirstOrDefault(f => f == ManualFocusDistance.Nearest)50           };51 52           //设置聚焦,最好使用FocusMode.Continuous,否则影响截图会很模糊,不利于识别53           focusControl.Configure(focusSettings);54         }55 56         captureElement.Source = _mediaCapture;57         captureElement.FlowDirection = FlowDirection.LeftToRight;58 59         try60         {61           await _mediaCapture.StartPreviewAsync();62           _previewing = true;63         }64         catch (Exception ex)65         {66           Debug.WriteLine("Exception when starting the preview: {0}", ex.ToString());67         }68 69         if (_previewing)70         {71           try72           {73             if (_mediaCapture.VideoDeviceController.FlashControl.Supported)74             {75               //关闭闪光灯76               _mediaCapture.VideoDeviceController.FlashControl.Enabled = false;77             }78           }79           catch80           {81           }82 83           if (focusControl.Supported)84           {85             //开始聚焦86             await focusControl.FocusAsync();87           }88         }89       }90     }

View Code

2. 截图与识别

1 private void InitTimer()2     {3       _timer = new DispatcherTimer();4       //每50毫秒截一次图5       _timer.Interval = TimeSpan.FromMilliseconds(50);6       _timer.Tick += _timer_Tick;7       _timer.Start();8     }

View Code
 1 private async void _timer_Tick(object sender, object e) 2     { 3       using (var stream = new InMemoryRandomAccessStream()) 4       { 5         var previewProperties = _mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview) as VideoEncodingProperties; 6         //将截图写入内存流中 7         await _mediaCapture.CapturePhotoToStreamAsync(ImageEncodingProperties.CreateJpeg(), stream); 8  9         //利用Zxing识别,成功:停止timer;失败:继续10         var reader = new BarcodeReader();11         var bitmapWriteable = new WriteableBitmap((int)previewProperties.Width, (int)previewProperties.Height);12         bitmapWriteable.SetSource(stream);13         var result = reader.Decode(bitmapWriteable);14 15         if (!string.IsNullOrEmpty(result.Text))16         {17           _timer.Stop();18         }19       }20     }

View Code

这里顺便说一下如何安装Zxing,打开nuget管理器 命令窗口输入 Install-Package ZXing.Net ,回车; 关于Zxing如何使用,到网上搜索一下有很多教程,这里不再赘述

3. 问题与优化

A) 截图有响声

使用CapturePhotoToStreamAsync来截取图片有的时候会有“咔擦咔擦”声,很影响用户体验,最理想的做法是找到一种能从视频流中直接截取图片的方法,在这里不得不说一句MediaCapture真的真的很强大,MediaCapture给我们提供了直接从视频流中取出其中一帧的方法GetPreviewFrameAsync,于是我把代码进行了如下修改,即流畅又没有烦人的“咔擦咔擦”声

 1 private async void _timer_Tick(object sender, object e) 2     { 3       var previewProperties = _mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview) as VideoEncodingProperties; 4  5       using (var videoFrame = new VideoFrame(BitmapPixelFormat.Rgba8, (int)previewProperties.Width, (int)previewProperties.Height)) 6       { 7         using (var currentFrame = await _mediaCapture.GetPreviewFrameAsync(videoFrame)) 8         { 9           using (var previewFrame = currentFrame.SoftwareBitmap)10           {11             var buffer = new Windows.Storage.Streams.Buffer((uint)(4 * previewFrame.PixelWidth * previewFrame.PixelHeight));12             previewFrame.CopyToBuffer(buffer);13 14             using (var stream = buffer.AsStream().AsRandomAccessStream())15             {16               //利用Zxing识别,成功:停止timer;失败:继续17               var reader = new BarcodeReader();18               var bitmapWriteable = new WriteableBitmap((int)previewProperties.Width, (int)previewProperties.Height);19               bitmapWriteable.SetSource(stream);20               var result = reader.Decode(bitmapWriteable);21 22               if (!string.IsNullOrEmpty(result.Text))23               {24                 _timer.Stop();25               }26             }27           }28         }29       }      30     }

View Code

顺便提一下记得要使用如下两个命名空间

using System.IO;using System.Runtime.InteropServices.WindowsRuntime;

否则无法实现buffer.AsStream().AsRandomAccessStream()

B) 连续聚焦

并不是所有机型都支持连续聚焦的(FocusMode.Continuous),这个时候只能自己实现间断性持续聚焦了

C) 截图之后图片处理

有的时候为了实现一些功能(比如说扫描框)或者提高识别率,我们需要对截取出来的图片进行一些二次处理,或剪裁或缩放或旋转,我们可以使用BitmapDecoder和BitmapEncoder来实现

using (var stream = buffer.AsStream().AsRandomAccessStream()){   var decoder = await BitmapDecoder.CreateAsync(BitmapDecoder.JpegDecoderId, stream);   var destStream = new InMemoryRandomAccessStream();   var encoder = await BitmapEncoder.CreateForTranscodingAsync(destStream, decoder);                 //剪裁   encoder.BitmapTransform.Bounds = new BitmapBounds() { X = 0, Y = 0, Width = 100, Height = 100 };   //缩放   encoder.BitmapTransform.ScaledWidth = 100;   encoder.BitmapTransform.ScaledHeight = 100;   //旋转   encoder.BitmapTransform.Rotation = BitmapRotation.Clockwise90Degrees;   await encoder.FlushAsync();   await destStream.FlushAsync();
}

D) 挂起和唤醒

另外值得注意的是,程序在Suspending和Resuming还有Activated时出现的一系列状态转换,这时候很容易引起bug,需要处理好避免crash。

4. 最后

识别出来的字符串处理一般也就超链接和普通文本两种,当然也可以增加条码扫描功能,识别出的是编码,不管怎样,大家可以根据项目具体需求做相应的处理。