Thursday, October 07, 2010

Silverlight 4.0 Tutorial (9 of N): Using the WebCam as a Barcode Scanner

You can find a list of the previous tutorial posts here

Continuing our RegistrationBooth Application, we want to allow registered users to evaluate sessions, we don’t have a credentials store in our application so to identify the registered attendees we will use a simpler approach, in part2 we allowed printing a name tag for registered attendees, this name tag contains a barcode of the attendee id, in this post we will see how can we use the new Silverlight 4.0 WebCam functionality to be able to scan/read barcodes and extract the attendee id to identify the corresponding attendee.

During PDC09 Keynote, Scott Guthrie demonstrated a Silverlight Application that scans a book ISBN barcode and passes this ISBN to Amazon web service to retrieve the book information, we will reuse the code from this sample which can be downloaded from here.

This sample actually uses a code from a CodeProject Article (by Berend Engelbrecht) that detects barcodes inside an image. the code is located in the file BarcodeImaging.cs, so we will copy this file to our project.

The sample also has another class BarcodeCapture that derives from VideoSink, according to MSDN “To obtain video information from a video input device in Silverlight, you derive a custom video sink from VideoSink” , the important method to override in this class is OnSample which is called when the video device captures a complete video sample. the code in this method calls the BarcodeImaging method that scans the current sample bitmap for a barcode, if a barcode is found the BarcodeCapture class fires a custom event BarcodeDetected that propagates the detected barcode. so we will need to copy the BarcodeCapture.cs file to our project.

On the UI we will have two components, the first one is a rectangle that we will display the webcam stream inside it.

<Border x:Name="nameTagScannerArea" Padding="10" Grid.Column="1" Height="400" Grid.RowSpan="2" Background="{StaticResource greenBrush}" CornerRadius="30" d:IsHidden="True" >
        <StackPanel>
            <TextBlock TextAlignment="Center" FontSize="14.667" FontWeight="Bold" Foreground="White" >Place your name tag infront of the camera</TextBlock>
            <Rectangle x:Name="barcodeScanner" Fill="White" Width="400" Height="300"          />                  
     </StackPanel>
    </Border>

The second UI component is another rectangle that will be shown when a barcode is detected, the rectangle will display a simple welcome message to the user, we will add more functionality later.

<Border x:Name="welcomeArea" Padding="10" Grid.Column="1" Height="150" Grid.RowSpan="2" Background="{StaticResource orangeBrush}" CornerRadius="30" >
         <StackPanel Orientation="Horizontal">
         <TextBlock TextAlignment="Center" FontSize="14.667" VerticalAlignment="Center" FontWeight="Bold" Foreground="White" >Welcome </TextBlock>          
         <TextBlock x:Name="userName" TextAlignment="Center" FontSize="14.667" VerticalAlignment="Center" FontWeight="Bold" Foreground="White" ></TextBlock>          
         </StackPanel>
    </Border>

We will add two new visual states, the first one is “ScanningNameTag” in this state we display the barcode scanning area and the attendee list, here’s how the UI looks like in this state

ScanningNameTag State

The button “Back” moves the UI back to the Default state by using a MoveToStateAction behavior

 MoveToStateAction behavior to go to the Default State

The second state is “WelcomeAttendee”, in this state we show the welcome area and the attendee list, here’s how the UI looks like in this state

WelcomeAttendee State

As you can see we added a Good Bye Button (btnLogout), we will add a MoveToStateAction behavior to this button so that when we click it we move to the Default state

The logout button takes us back to the default state

when we click the  “Login” button, we will start the cam and move to the “ScanningNameTag” state

private void btnLogin_Click(object sender, RoutedEventArgs e)
        {
            if (ActivateCamera())
            {
                if (m_Capture == null)
                {
                    m_Capture = new BarcodeCapture();
                    m_Capture.BarcodeDetected += new EventHandler<BarcodeEventArgs>(OnBarcodeDetected);
                    m_Capture.CaptureSource = m_CaptureSource;
                }
                VisualStateManager.GoToState(this, "ScanningNameTag", true);
            }
        }

The code first activates the camera to start capturing by calling the ActivateCamera function shown below

bool ActivateCamera()
        {
            if (m_CaptureSource == null)
            {
                m_CaptureSource = new CaptureSource();
                m_CaptureSource.VideoCaptureDevice = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();

                VideoBrush previewBrush = new VideoBrush();
                previewBrush.Stretch = Stretch.Uniform;
                previewBrush.SetSource(m_CaptureSource);
                barcodeScanner.Fill = previewBrush;

                Size diff = new Size(double.MaxValue, double.MaxValue);
                Size wantedSize = new Size(640, 480);
                VideoFormat bestFormat = null;
                foreach (VideoFormat format in m_CaptureSource.VideoCaptureDevice.SupportedFormats)
                {
                    double x = Math.Abs(format.PixelWidth - wantedSize.Width);
                    double y = Math.Abs(format.PixelHeight - wantedSize.Height);

                    if (x < diff.Width && y < diff.Height)
                    {
                        bestFormat = format;
                        diff.Width = x;
                        diff.Height = y;
                    }
                }
                if (bestFormat != null)
                {
                    m_CaptureSource.VideoCaptureDevice.DesiredFormat = bestFormat;
                }
                if (CaptureDeviceConfiguration.RequestDeviceAccess() || CaptureDeviceConfiguration.AllowedDeviceAccess)
                {
                    m_CaptureSource.Start();
                    return true;
                }
                else
                    return false;
            }
            else
                return true;
        }

after starting the camera we create an instance of our VideoSink (BarcodeCapture) and link the capture source with our video sink, we must also register to the event BarcodeDetected to be notified when the BarcodeCapture video sink detects a barcode in the image.

Here is the event handler for the BarcodeDetected event

void OnBarcodeDetected(object sender, BarcodeEventArgs e)
        {
            if (e.Barcode.Length < 8)//barcode formate is ATTXXXXX
                return;

            int attendeeID=0;
            if (!Int32.TryParse(e.Barcode.Substring(3, 5), out attendeeID))
                return;

            Dispatcher.BeginInvoke(delegate()
            {
                RegistrationDomainContext context = this.Resources["registrationDomainContext"] as RegistrationDomainContext;
                context.Load(context.GetAttendeeQuery(attendeeID),
                        delegate(LoadOperation<Attendee> loadOperation)
                        {
                            m_CaptureSource.Stop();
                            m_Capture.Clear();
                            foreach (var attendee in loadOperation.Entities)
                            {
                                lblUserName.Text = string.Format(" {0} {1}", attendee.FirstName ,attendee.LastName);
                                break;
                            }
                            VisualStateManager.GoToState(this, "WelcomeAttendee", true);
                        },
                        null);
            });
        }

First We validate the barcode format, then we call a new domain service method that retrieves the attendee based on the id, note that we are using Dispatcher.BeginInvoke method so that our code runs on the main UI thread.

Let’s run the application, click on the Login button, the webcam will start, hold the name tag in front of the webcam, you may need to move the name tag closer/way from the webcam till it picks the barcode

Barcode scanning through the web cam

Once the barcode is detected, the application will move to the WelcomeAttendee state

5

Note: i used the Silverlight Barcode Library to generate the barcode, cause the previous method we used (the font 3 of 9) was not recognized by Berend library

You can download the source code from here.

See you in the next post.

8 comments:

desprado said...

great post!

Anonymous said...

Please tell me why this code didn't work if I use png\jpeg image from phone camera?

Harvinder Syan said...
This comment has been removed by the author.
Harvinder Syan said...
This comment has been removed by the author.
Harvinder Syan said...

hello sir.RegistrationBooth-9 does not contain any code. Kindly upload the code of webcam as barcode scanner...waiting for upload.....or email me on syan.hs@gmail.com

Tushar Parikh said...

Hi,
I have downloaded RegistrationBooth code and built it but its throw error on machine.

Could you please provide complete SharePoint Solution package for this feature.

Thanks in advance.

Regards,
Tushar Parikh

Anonymous said...

Where I can find the code? Thanks

Joseph C. Bartlett said...

Hey there, friend if you loooking something to scan i have the right place for you
http://www.barcodemart.co.id/web/pubcategory/scanning-barcode-1-3-01.html here you can find
everything about scaning, printing pos software and many other things visit us you wont be sorry.