I'm trying to scan a QR-Code from WebCam in WinUI 3. I've installed ZXing and AForge Video but these libraries doesn't work properly in WinUI 3. Are there any solutions or more compatible alternatives?
ZXing (ZXing.NET is a port) is completely platform agnostic, so it works fine with any technology as long as you can capture a "bitmap" (RGB, etc.) of some kind whatever that means on a given platform.
Here is WinUI3 (make sure you have the latest WinUI3 nugets installed) sample application that does two things:
The code could be easily changed to read any barcode (EAN13, etc.) that XZing supports. It contains a thin adaptation layer between XZing and WinRT's SoftwareBitmap (that I have copied from XZing.NET code):
Here is the XAML for a WinUI3 page:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Click="Button_Click">Toggle Capture</Button>
<TextBox x:Name="textBox" Grid.Row="1" />
<MediaPlayerElement
x:Name="player"
Grid.Row="2"
Width="600"
Height="600"
AutoPlay="True" />
</Grid>
And the code in a WinUI3 page (you need the XZing.NET nuget package installed):
public sealed partial class MainPage : Page
{
private readonly SoftwareBitmapBarcodeReader _reader;
private MediaCapture _capture;
private MediaFrameReader _frameReader;
private MediaSource _mediaSource;
public MainPage()
{
InitializeComponent();
// set various xzing options (beware, all formats like All_1D can divide perf by orders of magnitude)
_reader = new SoftwareBitmapBarcodeReader
{
AutoRotate = true
};
_reader.Options.PossibleFormats = new[] { BarcodeFormat.QR_CODE };
_reader.Options.TryHarder = true;
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
if (_capture == null)
{
await InitializeCaptureAsync();
return;
}
await TerminateCaptureAsync();
}
private async Task InitializeCaptureAsync()
{
// get first capture device (change this if you want)
var sourceGroup = (await MediaFrameSourceGroup.FindAllAsync())?.FirstOrDefault();
if (sourceGroup == null)
return; // not found!
// init capture & initialize
_capture = new MediaCapture();
await _capture.InitializeAsync(new MediaCaptureInitializationSettings
{
SourceGroup = sourceGroup,
SharingMode = MediaCaptureSharingMode.SharedReadOnly,
MemoryPreference = MediaCaptureMemoryPreference.Cpu, // to ensure we get SoftwareBitmaps
});
// initialize source
var source = _capture.FrameSources[sourceGroup.SourceInfos[0].Id];
// create reader to get frames & pass reader to player to visualize the webcam
_frameReader = await _capture.CreateFrameReaderAsync(source, MediaEncodingSubtypes.Bgra8);
_frameReader.FrameArrived += OnFrameArrived;
await _frameReader.StartAsync();
_mediaSource = MediaSource.CreateFromMediaFrameSource(source);
player.Source = _mediaSource;
}
private void OnFrameArrived(MediaFrameReader sender, MediaFrameArrivedEventArgs args)
{
var bmp = sender.TryAcquireLatestFrame()?.VideoMediaFrame?.SoftwareBitmap;
if (bmp == null)
return;
var result = _reader.Decode(bmp);
if (result != null)
{
// found a QR CODE
DispatcherQueue.TryEnqueue(() =>
{
textBox.Text = result.BarcodeFormat + ": " + result.Text;
});
}
}
private async Task TerminateCaptureAsync()
{
player.Source = null;
_mediaSource?.Dispose();
_mediaSource = null;
if (_frameReader != null)
{
_frameReader.FrameArrived -= OnFrameArrived;
await _frameReader.StopAsync();
_frameReader?.Dispose();
_frameReader = null;
}
_capture?.Dispose();
_capture = null;
}
}
// this is the thin layer that allows you to use XZing over WinRT's SoftwareBitmap
public class SoftwareBitmapBarcodeReader : BarcodeReader<SoftwareBitmap>
{
public SoftwareBitmapBarcodeReader()
: base(bmp => new SoftwareBitmapLuminanceSource(bmp))
{
}
}
// from https://github.com/micjahn/ZXing.Net/blob/master/Source/lib/BitmapLuminanceSource.SoftwareBitmap.cs
public class SoftwareBitmapLuminanceSource : BaseLuminanceSource
{
protected SoftwareBitmapLuminanceSource(int width, int height)
: base(width, height)
{
}
public SoftwareBitmapLuminanceSource(SoftwareBitmap softwareBitmap)
: base(softwareBitmap.PixelWidth, softwareBitmap.PixelHeight)
{
if (softwareBitmap.BitmapPixelFormat != BitmapPixelFormat.Gray8)
{
using SoftwareBitmap convertedSoftwareBitmap = SoftwareBitmap.Convert(softwareBitmap, BitmapPixelFormat.Gray8);
convertedSoftwareBitmap.CopyToBuffer(luminances.AsBuffer());
return;
}
softwareBitmap.CopyToBuffer(luminances.AsBuffer());
}
protected override LuminanceSource CreateLuminanceSource(byte[] newLuminances, int width, int height)
=> new SoftwareBitmapLuminanceSource(width, height) { luminances = newLuminances };
}
And here is the result on a QR Code that encodes "Hello World":