Namespaces

Variants
Actions

Please note that as of October 24, 2014, the Nokia Developer Wiki will no longer be accepting user contributions, including new entries, edits and comments, as we begin transitioning to our new home, in the Windows Phone Development Wiki. We plan to move over the majority of the existing entries over the next few weeks. Thanks for all your past and future contributions.

Optical Reader Library for Windows Phone 8

From Wiki
Jump to: navigation, search
Featured Article
09 Feb
2014

This article shows how to use the Optical Reader Library. It explains both how to integrate the Optical Reader Task into a Windows Phone 8 app in order to read QR and other barcodes, and how to extend the library to support other processors for image enhancement and decoding.

WP Metro Icon File.png
WP Metro Icon Multimedia.png
SignpostIcon XAML 40.png
WP Metro Icon WP8.png
Article Metadata
Code ExampleTested with
SDK: Windows Phone 8.0 SDK
Devices(s): Nokia Lumia 720, Nokia Lumia 1020, Nokia Lumia 1520
Compatibility
Platform(s):
Windows Phone 8
Dependencies: Nokia Imaging SDK v1.0.272.0, ZXing.NET v0.12.0.0
Platform Security
Capabilities: ID_CAP_ISV_CAMERA
Article
Created: shyoty (15 Jan 2014)
Last edited: joaocardoso (23 Jul 2014)

Contents

Introduction

Creating a functional 1D or 2D barcode reader (or other optical reader) may consume a considerable amount of development time. Partly, this is because the camera systems (sensors and lenses) on different devices have different capabilities; for example, minimum focus distance and view angle. The time taken may also be impacted by constraints on the types of codes that need to be read, the licensing of decoder libraries that can be used, or the amount of "quality improvement" that needs to be performed on an image before it can be decoded.

The Optical Reader Library attempts to address these issues: it provides an optical code reading Task that is easy to integrate and simple to use, that is already optimized to use the best camera parameters for different devices, and which can be extended to support different image preprocessing and optical code decoders if needed.

By default, the Optical Reader Library enhances image quality (for example, by increasing contrast) using the Nokia Imaging SDK (v1.0.272.0) and decodes the image with ZXing.NET v0.12.0.0 (Apache License 2.0).

This article creates a very simple demo app to show how to use the Optical Reader Task. It also provides information on how to extend the Optical Reader Task's processing capabilities.

Optical reader demo app (after decoding a QR code).

Downloads

You can download relevant files here:

The Optical Reader Library source project uses Nuget Automatic Package Restore to retrieve the Nokia Imaging SDK and ZXing.NET libraries from Nuget.org. If you choose to build the library from scratch, make sure that you have Nuget Package Manager 2.7 or later installed. To do this, open Microsoft Visual Studio Express for Windows Phone and navigate to Tools - Extensions and Updates - Updates - Visual Studio Gallery, and check if there is a Nuget update available.


Using the Optical Reader Task in a Windows Phone 8 app

Using the Microsoft Visual Studio Express 2012 for Windows Phone, create a new Visual C# Windows Phone App project. In this document, we'll call our app "OpticalReaderDemo".

Installing the library from local Nuget package

The easiest way to add the Optical Reader Library to the project is to install it from a locally saved Nuget package. Therefore, download the library Nuget package and place it somewhere on your hard drive, for example C:\Temp\ , and click Tools - Library Package Manager - Package Manager Settings - Package Manager - Package Sources, and add C:\Temp\ as a new package source by clicking on the plus icon. Make sure that the newly added package source is ticked as active. Close the dialog by clicking OK.

Next, right-click on the OpticalReaderDemo project in the Solution Explorer panel, and select Manage Nuget Packages. On the left of the opening dialog, select Online, and under that, select C:\Temp. You should now see Optical Reader Library and an Install button. Click on Install. This installs the library and its dependencies (Nokia Imaging SDK v1.0.272.0 and ZXing.NET v0.12.0.0) to the project.

Adding the required capabilities

As the optical reader task uses the PhotoCaptureDevice, you need to add the ID_CAP_ISV_CAMERA capability to the project. In the Solution Explorer panel, open OpticalReaderDemo/Properties/WMAppManifest.xml, and on the Capabilities tab check ID_CAP_ISV_CAMERA. Although there may be other capabilities checked too by default, no other capabilities are required by this demo, nor by the library.

Invoking the optical reader task from code

Next, open the automatically generated MainPage.xaml and add an Image element for showing detected optical code thumbnail, a TextBlock element for displaying some information about the optical code, and a Button element for launching the optical reader task.

  1. <phone:PhoneApplicationPage
  2.     x:Class="OpticalReaderDemo.MainPage"
  3.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  4.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  5.     xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
  6.     xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
  7.     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  8.     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  9.     mc:Ignorable="d"
  10.     FontFamily="{StaticResource PhoneFontFamilyNormal}"
  11.     FontSize="{StaticResource PhoneFontSizeNormal}"
  12.     Foreground="{StaticResource PhoneForegroundBrush}"
  13.     SupportedOrientations="Portrait" Orientation="Portrait"
  14.     shell:SystemTray.IsVisible="False">
  15.  
  16.     <!--LayoutRoot is the root grid where all page content is placed-->
  17.     <Grid x:Name="LayoutRoot" Background="Transparent">
  18.         <Grid.RowDefinitions>
  19.             <RowDefinition Height="Auto"/>
  20.             <RowDefinition Height="*"/>
  21.         </Grid.RowDefinitions>
  22.  
  23.         <!--TitlePanel contains the name of the application and page title-->
  24.         <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
  25.             <TextBlock Text="OPTICAL READER DEMO" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
  26.             <TextBlock Text="main page" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
  27.         </StackPanel>
  28.  
  29.         <!--ContentPanel - place additional content here-->
  30.         <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
  31.             <Grid.RowDefinitions>
  32.                 <RowDefinition/>
  33.                 <RowDefinition Height="Auto"/>
  34.                 <RowDefinition Height="Auto"/>
  35.             </Grid.RowDefinitions>
  36.  
  37.             <!-- Image to show a thumbnail of the detected optical code -->
  38.             <Image Grid.Row="0" x:Name="ResultImage"/>
  39.  
  40.             <!-- TextBlock to show information about the detected optical code -->
  41.             <TextBlock Grid.Row="1" x:Name="ResultTextBlock" TextAlignment="Center" Margin="24"/>
  42.  
  43.             <!-- Button to launch the OpticalReaderTask -->
  44.             <Button Grid.Row="2" Margin="24" Content="launch optical reader task" Click="LaunchTaskButton_Click"/>
  45.         </Grid>
  46.     </Grid>
  47.  
  48. </phone:PhoneApplicationPage>

Next, switch to the code-behind-file MainPage.xaml.cs and add an instance of the optical reader task as a member, and also add a member for detected optical reader results. In the constructor page add an event handler for the task's Completed event, and catch the optical reader result in it to the member. Implement the handler for the task launch button Click event, simply by calling the task's Show method. This navigates the application to the optical reader viewfinder. Finally, as the Completed event handler function is called before the application navigates back from the viewfinder to this page, the result (if anything was found) is available for the page to use in the OnNavigatedTo method.

  1. using Microsoft.Phone.Controls;
  2. using System;
  3. using System.Windows;
  4. using System.Windows.Navigation;
  5.  
  6. namespace OpticalReaderDemo
  7. {
  8.     public partial class MainPage : PhoneApplicationPage
  9.     {
  10.         private OpticalReaderLib.OpticalReaderTask _task = new OpticalReaderLib.OpticalReaderTask();
  11.         private OpticalReaderLib.OpticalReaderResult _result = null;
  12.  
  13.         public MainPage()
  14.         {
  15.             InitializeComponent();
  16.  
  17.             _task.Completed += OpticalReaderTask_Completed;
  18.         }
  19.  
  20.         private void OpticalReaderTask_Completed(object sender, OpticalReaderLib.OpticalReaderResult e)
  21.         {
  22.             _result = e;
  23.         }
  24.  
  25.         private void LaunchTaskButton_Click(object sender, RoutedEventArgs e)
  26.         {
  27.             _task.Show();
  28.         }
  29.  
  30.         protected override void OnNavigatedTo(NavigationEventArgs e)
  31.         {
  32.             base.OnNavigatedTo(e);
  33.  
  34.             if (_result != null && _result.TaskResult == Microsoft.Phone.Tasks.TaskResult.OK)
  35.             {
  36.                 ResultImage.Source = _result.Thumbnail;
  37.                 ResultTextBlock.Text = String.Format("{0} ({1} bytes)\n\n{2}",
  38.                     _result.Format,
  39.                     _result.Data != null ? _result.Data.Length : 0,
  40.                     _result.Text);
  41.             }
  42.  
  43.             _result = null;
  44.         }
  45.     }
  46. }

Running the application

Reading optical codes mostly only makes sense on a device, not on an emulator. Therefore, in order to run the application, connect a Windows Phone 8 device to your computer, select Device and ARM as the build target, and hit F5 to start running the application on the device.

Application main page, click on the button to navigate to the optical reader viewfinder Optical reader viewfinder, reading a QR-code QR-code was read, application has navigated back to the main page


Configuring the optical reader task

The Optical Reader Task API contains properties for configuring the reader. Probably the most important and useful configuration property is the ObjectSize, which you can use to set the target optical code size in millimeters. The library reader viewfinder will automatically zoom the camera preview so that a given size optical code fits the reader target rectangle when the device is at an optimal reading distance (minimum focus distance) from the code. This is very useful since it means that the UI implicitly guides the user of the application to position the device on the optimal reading distance where the device can still focus, but at the same time the maximum amount of sensor resolution is used to capture the code. How much to zoom on different devices is based on compiled-in information (camera focal length, sensor size) about a range of Nokia Lumia devices.

  1. namespace OpticalReaderLib
  2. {
  3.     // Summary:
  4.     //     Optical reader task is a Windows Phone chooser task implementation that allows
  5.     //     easy and quick integration of 1D and 2D optical code reading functionality.
  6.     public class OpticalReaderTask : ChooserBase<OpticalReaderResult>, IDisposable
  7.     {
  8.         public OpticalReaderTask();
  9.  
  10.         // Summary:
  11.         //     Reader camera focus interval, meaning the time that needs to pass without
  12.         //     the reader finding anything before it attempts to re-focus the lens.  Default
  13.         //     is 2500 milliseconds.
  14.         public TimeSpan FocusInterval { get; set; }
  15.         //
  16.         // Summary:
  17.         //     Target object real-life millimeter size. This affects the zoom factor used
  18.         //     in the reader viewfinder.  Default is no zoom.
  19.         public Size ObjectSize { get; set; }
  20.         //
  21.         // Summary:
  22.         //     Processor to use for processing the frames.  Zxing processor is used if no
  23.         //     processor is set explicitly.
  24.         public IProcessor Processor { get; set; }
  25.         //
  26.         // Summary:
  27.         //     Set to true to require the user to confirm a found result by tapping on a
  28.         //     result preview. If false, the first result found will be used automatically.
  29.         //      Default is false.
  30.         public bool RequireConfirmation { get; set; }
  31.         //
  32.         // Summary:
  33.         //     Set to true to see debug frames in the reader viewfinder. Default is false,
  34.         //     meaning that debug frames are not displayed.  Debug frames are meant to be
  35.         //     used while developing normalizers, enhancers and processor, in order to get
  36.         //     visual feedback on how the frame is modified during processing.  Debug frames
  37.         //     are not meant to be displayed in final consumer applications.
  38.         public bool ShowDebugInformation { get; set; }
  39.  
  40.         public void Dispose();
  41.         //
  42.         // Summary:
  43.         //     Show the optical reader viewfinder.  Application is navigated to an optical
  44.         //     reader viewfinder page and the Completed event is fired when user either
  45.         //     navigates away from the viewfinder or if an optical code is detected.
  46.         public override void Show();
  47.     }
  48. }


Extending the Optical Reader processing capabilities

Processors take in camera frames and return information about any optical codes they detected in the frame. In addition to the optical reader task, the Optical Reader Library contains interfaces that can be implemented to create custom processors.

  1. using System;
  2. using System.Threading.Tasks;
  3. using Windows.Foundation;
  4.  
  5. namespace OpticalReaderLib
  6. {
  7.     // Summary:
  8.     //     Frame processor implementation interface.  Processors are all-in-one frame
  9.     //     decoding systems. Depending on the implementation, they may use normalizers,
  10.     //     enhancers and decoders to help in the job.
  11.     public interface IProcessor
  12.     {
  13.         // Summary:
  14.         //     Fired when there is a debugging frame available.  Debug frames are meant
  15.         //     to be used while developing normalizers, enhancers and processor, in order
  16.         //     to get visual feedback on how the frame is modified during processing.  Debug
  17.         //     frames are not meant to be displayed in final consumer applications.
  18.         event EventHandler<DebugFrameEventArgs> DebugFrameAvailable;
  19.  
  20.         // Summary:
  21.         //     Attempts to decode an optically encoded code from the frame by processing
  22.         //     it.
  23.         //
  24.         // Parameters:
  25.         //   frame:
  26.         //     Frame to process.
  27.         //
  28.         //   area:
  29.         //     Interesting frame area.
  30.         //
  31.         //   rotation:
  32.         //     Frame rotation, how many degrees it should be rotated clockwise.
  33.         //
  34.         // Returns:
  35.         //     Processing result or null if no code was found.
  36.         Task<ProcessResult> ProcessAsync(Frame frame, Rect area, double rotation);
  37.     }
  38. }

Processors may further utilize other interfaces and constructs from the library, for example:

  • OpticalReaderLib.INormalizer: Normalizers crop areas from frames and rotate them.
  • OpticalReaderLib.IEnhancer: Enhancers attempt to improve frame image quality so that decoding is easier.
  • OpticalReaderLib.IDecoder: Decoders attempt to find and decode optically encoded information from frames.

The library contains built-in basic implementations for all the interfaces. So, for example, if you want to implement a simple custom processor that attempts to improve the camera frame image quality before feeding the frames to a ZXing.NET decoder, you can use the built-in BasicNormalizer and ZxingDecoder, and add a custom IEnhancer implementation between them.

  1. using System.Threading.Tasks;
  2.  
  3. namespace OpticalReaderDemo
  4. {
  5.     public class CustomProcessor : OpticalReaderLib.BasicProcessor
  6.     {
  7.         public CustomProcessor() : base(new OpticalReaderLib.ZxingDecoder())
  8.         {
  9.             Normalizer = new OpticalReaderLib.BasicNormalizer();
  10.             Enhancer = new CustomEnhancer();
  11.         }
  12.     }
  13. }
  1. using System;
  2. using System.Threading.Tasks;
  3.  
  4. namespace OpticalReaderDemo
  5. {
  6.     /// <summary>
  7.     /// Dumb little custom enhancer that turns all frame pixels to either white or black
  8.     /// depending on whether the grayscale value of the pixel exceeds a threshold or not,
  9.     /// rotating the threshold for adjacent frames.
  10.     /// </summary>
  11.     class CustomEnhancer : OpticalReaderLib.IEnhancer
  12.     {
  13.         private uint _threshold = 0;
  14.         private uint _jump = 16;
  15.         private uint _min = 48;
  16.         private uint _max = 160;
  17.  
  18.         public async Task<OpticalReaderLib.EnhanceResult> EnhanceAsync(OpticalReaderLib.Frame frame)
  19.         {
  20.             if (frame.Format == OpticalReaderLib.FrameFormat.Gray8)
  21.             {
  22.                 _threshold = _threshold + _jump;
  23.  
  24.                 if (_threshold > _max)
  25.                 {
  26.                     _threshold = _min;
  27.                 }
  28.  
  29.                 var f = await Task.Run<OpticalReaderLib.Frame>(() =>
  30.                 {
  31.                     for (int i = 0; i < frame.Buffer.Length; i++)
  32.                     {
  33.                         frame.Buffer[i] = (byte)(frame.Buffer[i] < _threshold ? 0x00 : 0xff);
  34.                     }
  35.  
  36.                     return frame;
  37.                 });
  38.  
  39.                 return new OpticalReaderLib.EnhanceResult()
  40.                 {
  41.                     Frame = f
  42.                 };
  43.             }
  44.             else
  45.             {
  46.                 throw new Exception("Dumb little custom enhancer only supports Gray8 encoded frames");
  47.             }
  48.         }
  49.     }
  50. }


Further reading

For detailed description on the library public APIs, see the library documentation (included in the downloads).

This page was last modified on 23 July 2014, at 16:06.
1683 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×