Display Webcam Feed in WPF
I wanted to display my webcam feed in a WPF App. But although the below code is fairly simple, finding a good, recently updated and working library for this purpose and getting it installed took much more time than expected.
So for this reason I put it on my website so you don't have to go through the same struggle.
The app can also scan the webcam feed for a QR Code or other types and act as a barcode scanner if needed.
The library I ended up using is Emgu CV. You need to install three Emgu packages and the ZXing package for reading QR codes from NuGet into your project.
packages.config
' in Visual Studio and select 'Migrate packages.config to PackageReference
' if you are getting errors during installation.
See Emgu Issues on GitHub for more information.
Furthermore there was an issue with a missing library named '
cvextern.dll
'. It was not added to the project initially.
So it is included in the source files just to be sure.
It is the 64 bit version. So make sure you set the platform target to x64 in '
Properties > Build > Platform target
' in your project.
View source on GitHub
Download the App
Code Snippets
The XAML of the MainWindow.
<Window x:Class="CaptureWebcam.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Title="Capture Webcam Feed" Height="380" Width="370" Background="#566895"> <StackPanel> <Image Name="feedImage" Grid.Row="0" Grid.Column="0" Stretch="Fill" /> <Image Name="Image1" Grid.Row="0" Grid.Column="0" Stretch="Fill" Visibility="Collapsed" /> <TextBlock Name="TextBlock1" Grid.Row="1" Grid.Column="0" Foreground="White" Margin="10, 20, 10, 0" HorizontalAlignment="Center" TextAlignment="Center" FontWeight="Bold" FontSize="12" /> <TextBlock Name="TextBlock2" Grid.Row="1" Grid.Column="0" Foreground="White" Margin="10, 5, 10, 10" HorizontalAlignment="Center" TextAlignment="Center" FontSize="10" /> </StackPanel> </Window>
And the MainWindow C# code to make it all work.
using System; using System.Windows; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Timers; using System.Windows.Media.Imaging; using System.Threading.Tasks; using System.Media; using System.Reflection; using ZXing; using ZXing.Common; using Emgu.CV; using Emgu.CV.Structure; using Emgu.CV.CvEnum; namespace CaptureWebcam { public partial class MainWindow : Window { VideoCapture capture; Timer timer; public MainWindow() { InitializeComponent(); //the fps of the webcam int cameraFps = 30; //init the camera capture = new VideoCapture(); //set the captured frame width and height (default 640x480) capture.Set(CapProp.FrameWidth, 1024); capture.Set(CapProp.FrameHeight, 768); //create a timer that refreshes the webcam feed timer = new Timer() { Interval = 1000 / cameraFps, Enabled = true }; timer.Elapsed += new ElapsedEventHandler(timer_Tick); } private async void timer_Tick(object sender, ElapsedEventArgs e) { //there is a qr code image visible if (feedImage.Visibility == Visibility.Collapsed) { timer.Stop(); //the delay time you want to display the qr code in the ui for await Task.Run(() => Task.Delay(2500)); //set the image visibility this.Dispatcher.Invoke(() => { feedImage.Visibility = Visibility.Visible; Image1.Visibility = Visibility.Collapsed; }); timer.Start(); } this.Dispatcher.Invoke(() => { var mat1 = capture.QueryFrame(); var mat2 = new Mat(); //flip the image horizontally CvInvoke.Flip(mat1, mat2, FlipType.Horizontal); //convert the mat to a bitmap var bmp = mat2.ToImage().ToBitmap(); //copy the bitmap to a memorystream var ms = new MemoryStream(); bmp.Save(ms, ImageFormat.Bmp); //display the image on the ui feedImage.Source = BitmapFrame.Create(ms); //try to find a qr code in the feed string qrcode = FindQrCodeInImage(bmp); if (!string.IsNullOrEmpty(qrcode)) { //set the found text in the qr code in the ui TextBlock1.Text = qrcode; TextBlock2.Text = $"Last scan: {DateTime.Now.ToLongDateString()} at {DateTime.Now.ToShortTimeString()}."; //play a sound to indicate qr code found var player_ok = new SoundPlayer(GetStreamFromResource("sound_ok.wav")); player_ok.Play(); //hide the feed image feedImage.Visibility = Visibility.Collapsed; } }); } private string FindQrCodeInImage(Bitmap bmp) { //decode the bitmap and try to find a qr code var source = new BitmapLuminanceSource(bmp); var bitmap = new BinaryBitmap(new HybridBinarizer(source)); var result = new MultiFormatReader().decode(bitmap); //no qr code found in bitmap if (result == null) { return null; } //create a new qr code image var writer = new BarcodeWriter { Format = BarcodeFormat.QR_CODE, Options = new EncodingOptions { Height = 300, Width = 300 } }; //write the result to the new qr code bmp image var qrcode = writer.Write(result.Text); //make the bmp transparent qrcode.MakeTransparent(); //show the found qr code in the app var stream = new MemoryStream(); qrcode.Save(stream, ImageFormat.Png); //display the new qr code in the ui Image1.Source = BitmapFrame.Create(stream); Image1.Visibility = Visibility.Visible; //and/or save the new qr code image to disk if needed try { //qrcode.Save($"qr_code_{DateTime.Now.ToString("yyyyMMddHHmmss")}.gif", ImageFormat.Gif); } catch { //handle disk write errors here } //return the found qr code text return result.Text; } private static Stream GetStreamFromResource(string filename) { var assembly = Assembly.GetExecutingAssembly(); return assembly.GetManifestResourceStream(string.Format("{0}.Resources.{1}", assembly.GetName().Name, filename)); } } }