先日のエントリの最後に触れていた話です。
試してみると実はそこまで難しくなくバーコード(QRコード)を読み取ることができました。簡単になった最大の理由は、Microsoftが提供しているUWPのサンプルです。
カメラを使ってPreviewFrame
を取得するサンプルアプリがあります。ここにあるコードを必要なところだけコピーしてきたらほぼ終わりです。このとき、カメラを使うためにはアプリの機能でカメラ
とマイク
を有効にする必要があります。また、上のサンプルにあるように撮影した画像を保存する場合はピクチャライブラリ
など保存先へのアクセスが必要です。今回のバーコードリーダーのサンプルでは保存処理は除外し、インメモリで処理するため、保存先へのアクセスは必要ありません。
次に、UWPでも使えるZXing.Net
(mobileではない方)を追加します。project.json
のdependencies の中に次の行を追加します。
"dependencies": { "ZXing.Net": "0.14.0.1" },
そして肝心のコードですが、1秒おきにフォーカスを合わせて写っている画像をQRCodeとして読み取りを試みるようにしました。
private async Task TryDecodePreviewAsync() { await mediaCapture.VideoDeviceController.FocusControl.FocusAsync(); // Get information about the preview var previewProperties = mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview) as VideoEncodingProperties; if (previewProperties == null) return; // このVideoFrameのFormatとBarcodeReaderのフォーマットで一致するのがGray8しかない var videoFrame = new VideoFrame(BitmapPixelFormat.Gray8, (int)previewProperties.Width, (int)previewProperties.Height); // Capture the preview frame using (var currentFrame = await mediaCapture.GetPreviewFrameAsync(videoFrame)) { // 結果フレームを取得 var previewFrame = currentFrame.SoftwareBitmap; // 結果をbyte配列に変換。DecodeメソッドはWriteableBitmapも受け付けるが、 // こちらはUIスレッド上でないと生成できないのであきらめる。 var buffer = new byte[4 * previewFrame.PixelWidth * previewFrame.PixelHeight]; previewFrame.CopyToBuffer(buffer.AsBuffer()); var barcodeReader = new BarcodeReader {AutoRotate = true}; var r = barcodeReader.Decode(buffer, previewFrame.PixelWidth, previewFrame.PixelHeight, BitmapFormat.Gray8); Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { FrameInfoTextBlock.Text = string.Format("{0}x{1} {2}", previewFrame.PixelWidth, previewFrame.PixelHeight, previewFrame.BitmapPixelFormat); ResultText.Text = r?.Text ?? ""; }).FireAndForget(); } }
FireAndForget
は拡張メソッドです。
public static class TaslExtensions { public static void FireAndForget(this Task task) { task.ContinueWith(t => { Debug.WriteLine(t.Exception);}, TaskContinuationOptions.OnlyOnFaulted); } public static void FireAndForget(this IAsyncAction action) { action.AsTask().FireAndForget(); } }
previewFrame
を取得するまではサンプルそのままで、そこからZXing.Net
を使います。コメントにあるように、BarcodeReader
に渡す画像データはWriteableBitmap
か、byte配列に付加情報を追加したものが選べるのですが、WriteableBitmap
はUIスレッドから生成しないといけないので、previewFrame
をbyte配列に変換しています。
実行するとこんな感じです。
実行したサンプル一式をGitHubに公開しています。