您的位置:首页 > 其它

如何用MediaCapture解决二维码扫描问题

2017-10-19 17:19 197 查看
原文:如何用MediaCapture解决二维码扫描问题(有修改)

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

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

1. 初始化与成像

private async void InitMediaCaptureAsync()
{   //选择后置摄像头
var cameraDevice = await FindCameraDeviceByPanelAsync(Windows.Devices.Enumeration.Panel.Back);
if (cameraDevice == null)
{
System.Diagnostics.Debug.WriteLine("No camera device found!");
return;
}
var settings = new MediaCaptureInitializationSettings
{
StreamingCaptureMode = StreamingCaptureMode.Video,
//MediaCategory = MediaCategory.Other,
//AudioProcessing = AudioProcessing.Default,
PhotoCaptureSource = PhotoCaptureSource.VideoPreview,
AudioDeviceId = string.Empty,
VideoDeviceId = cameraDevice.Id
};
await _mediaCapture.InitializeAsync(settings);
//摄像头旋转90度
//_mediaCapture.SetPreviewRotation(VideoRotation.Clockwise90Degrees);
var fff = _mediaCapture.MediaCaptureSettings;
capturePreview.Source = _mediaCapture;
await _mediaCapture.StartPreviewAsync();

var focusControl = _mediaCapture.VideoDeviceController.FocusControl;

if (focusControl.Supported)
{
var focusSettings = new FocusSettings()
{
Mode = focusControl.SupportedFocusModes.FirstOrDefault(f => f == FocusMode.Continuous),
DisableDriverFallback = true,
AutoFocusRange = focusControl.SupportedFocusRanges.FirstOrDefault(f => f == AutoFocusRange.FullRange),
Distance = focusControl.SupportedFocusDistances.FirstOrDefault(f => f == ManualFocusDistance.Nearest)
};

//设置聚焦,最好使用FocusMode.Continuous,否则影响截图会很模糊,不利于识别
focusControl.Configure(focusSettings);
}
if (focusControl.Supported)
{
//开始聚焦
await focusControl.FocusAsync();
}
if (_mediaCapture.VideoDeviceController.FlashControl.Supported)
{
//关闭闪光灯
_mediaCapture.VideoDeviceController.FlashControl.Enabled = false;
}
}


private static async Task<DeviceInformation> FindCameraDeviceByPanelAsync(Windows.Devices.Enumeration.Panel desired)
{

var allVideoDevices = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);

DeviceInformation desiredDevice = allVideoDevices.FirstOrDefault(x => x.EnclosureLocation != null && x.EnclosureLocation.Panel == desired);

return desiredDevice ?? allVideoDevices.FirstOrDefault();
}


2. 截图与识别

private void InitTimer()
{
_timer = new DispatcherTimer();
//每50毫秒截一次图
_timer.Interval = TimeSpan.FromMilliseconds(50);
_timer.Tick += _timer_Tick;
_timer.Start();
}


private async void _timer_Tick(object sender, object e)
{
using (var stream = new InMemoryRandomAccessStream())
{
var previewProperties = _mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview) as VideoEncodingProperties;
//将截图写入内存流中
await _mediaCapture.CapturePhotoToStreamAsync(ImageEncodingProperties.CreateJpeg(), stream);

//利用Zxing识别,成功:停止timer;失败:继续
var reader = new BarcodeReader();
var bitmapWriteable = new WriteableBitmap((int)previewProperties.Width, (int)previewProperties.Height);
bitmapWriteable.SetSource(stream.CloneStream()); //bitmapWriteable.SetSource(stream);有问题
var result = reader.Decode(bitmapWriteable);

if (!string.IsNullOrEmpty(result.Text))
{
_timer.Stop();
}
}
}


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

3. 问题与优化

A) 截图有响声

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

async void _timer_Tick(object sender, object e)
{
var previewProperties = _mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview) as VideoEncodingProperties;

using (var videoFrame = new VideoFrame(BitmapPixelFormat.Rgba8, (int) previewProperties.Width, (int) previewProperties.Height))
{
using (var currentFrame = await _mediaCapture.GetPreviewFrameAsync(videoFrame))
{
using (var previewFrame = currentFrame.SoftwareBitmap)
{

WriteableBitmap bitmap = new WriteableBitmap(previewFrame.PixelWidth, previewFrame.PixelHeight);
previewFrame.CopyToBuffer(bitmap.PixelBuffer);

var buffer = new Windows.Storage.Streams.Buffer((uint)(4 * previewFrame.PixelWidth * previewFrame.PixelHeight));
previewFrame.CopyToBuffer(buffer);
//利用Zxing识别,成功:停止timer;失败:继续
var reader = new BarcodeReader();
var result = reader.Decode(bitmap.PixelBuffer.ToArray(), bitmap.PixelWidth, bitmap.PixelHeight, RGBLuminanceSource.BitmapFormat.Unknown);
//var result = reader.Decode(bitmapWriteable);

if (!string.IsNullOrEmpty(result.Text))
{
_timer.Stop();
}

}
}
}
}


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

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


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

B) 连续聚焦

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

C) 截图之后图片处理

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



var previewProperties = _mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview) as VideoEncodingProperties;

using (var videoFrame = new VideoFrame(BitmapPixelFormat.Rgba8, (int)previewProperties.Width, (int)previewProperties.Height))
{
using (var currentFrame = await _mediaCapture.GetPreviewFrameAsync(videoFrame))
{
using (var previewFrame = currentFrame.SoftwareBitmap)
{

WriteableBitmap bitmap = new WriteableBitmap(previewFrame.PixelWidth, previewFrame.PixelHeight);
previewFrame.CopyToBuffer(bitmap.PixelBuffer);

var buffer = new Windows.Storage.Streams.Buffer((uint)(4 * previewFrame.PixelWidth * previewFrame.PixelHeight));
previewFrame.CopyToBuffer(buffer);
imageShowPicture.Source = bitmap;//show the picture

//利用Zxing识别,成功:停止timer;失败:继续
var reader = new BarcodeReader();
var result = reader.Decode(bitmap.PixelBuffer.ToArray(), bitmap.PixelWidth, bitmap.PixelHeight, RGBLuminanceSource.BitmapFormat.Unknown);

using (var stream = buffer.AsStream() .AsRandomAccessStream())
{
//var decoder = await BitmapDecoder.CreateAsync(stream);

var destStream = new InMemoryRandomAccessStream();
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, destStream);
//var encoder = await BitmapEncoder.CreateForTranscodingAsync(destStream, decoder);

encoder.SetSoftwareBitmap(previewFrame);

//剪裁
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();

var bitmapImage = new BitmapImage();
bitmapImage.SetSource(destStream);
imageShowPicture.Source = bitmapImage;//show the picture

}
}
}
}

if (!string.IsNullOrEmpty(result.Text))
{
_timer.Stop();
}






D) 挂起和唤醒

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

4. 最后

识别出来的字符串处理一般也就超链接和普通文本两种,当然也可以增加条码扫描功能,识别出的是编码,不管怎样,大家可以根据项目具体需求做相应的处理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: