Archive

Posts Tagged ‘ZXing’

Silverlight for Windows Phone 7: How to use ZXing 7.1 in Silverlight

November 22nd, 2011 No comments

    ZXing is an open source library for processing of multi-format 1D/2D barcode images. Using a built-in camera on your mobile phone you can scan a barcode (i.e. make a picture containing the barcode) and decode it by exploiting ZXing library. As the result you may have a recognized and decoded string value of barcode. The library supports such formats as traditional UPC-A and UPC-E, EAN-8 and EAN-13, trendy QR Code and etc. The library initially is implemented in Java, but there is a port to C#. However, if you decide to use this port in Silverlight Windows Phone 7 project, you’ll run into some problems with compatibility as Silverlight provides a “limited” C#. To be more specific, the C# port of ZXing actively utilizes such non-generic collections as Hashtable and ArrayList from System.Collections namespace, but almost everything under this namespace was completely removed from Silverlight. For details, please read the following article – Non-Generic Collections to be Removed from Silverlight. Hashtable and ArrayList can be replaced with System.Collections.Generic.Dictionary<Object, Object> and System.Collections.Generic.List<Object> respectively. Instances are received as System.Collections.ArrayList.Synchronized() can be smoothly turned into usual instances, that are not thread safe, because multithreading isn’t used in the library. If it’s necessary to provide thread safety, it can be done outside the library at the stage of instantiating of and accessing to the barcode readers contained in ZXing library. Just follow the rule – each thread must allocate its own object.

Furthermore, the ZXing C# port uses Color, Rectangle and Bitmap classes from System.Drawing, which is unavailable in Silverlight as well. As the replacement we can consider System.Windows.Media.Color, System.Windows.Rect and System.Windows.Media.Imaging.WriteableBitmap respectively, but they aren’t absolutely compatible with the preceding ones and some kind of adjustment code is required.

System.SerializableAttribute isn’t accessible too. We can just get rid of it. A number of other small changes are required as well.

As the result, I made a Silverlight port of ZXing 7.1 from C# port. You can download it here or here. Where the original code was replaced, I left commentaries kind of “here was .net follower” 🙂

A few words of how to use it. In my application I try reading and decoding barcodes of UPC-A and UPC-E formats. For my needs I implemented a very simple wrapper around ZXing libraryBarcodeHelper, probably, it may be useful for somebody else (the helper is in downloadable solution). The wrapper is shown below. In the method TryToRecognizeBarcode you can see how interaction with ZXing is usually being put into practice.
Update: other methods, TryToRecognizeQRcode and TryToRecognizeDataMatrix were added to the BarcodeHelper. They allow to recognize QR and Data Matrix codes, respectively.

using System;
using System.Collections.Generic;
using System.Windows.Media.Imaging;

using com.google.zxing;

namespace Helpers
{
    public static class BarcodeHelper
    {
        public static bool TryToRecognizeBarcode(WriteableBitmap wb, out string barCode)
        {            
            // set some recognition settings
            var zxhints = new Dictionary<object, object>() 
                { 
                    { DecodeHintType.TRY_HARDER, true }, 
                    { DecodeHintType.POSSIBLE_FORMATS, new List<Object>() { BarcodeFormat.UPC_A, BarcodeFormat.UPC_E } } 
                };

            // create reader instance
            var reader = new com.google.zxing.oned.MultiFormatUPCEANReader(zxhints);
            return TryToRecognize(wb, reader, zxhints, out barCode);
        }

        public static bool TryToRecognizeQRcode(WriteableBitmap wb, out string qrCode)
        {
            // create reader instance
            var reader = new com.google.zxing.qrcode.QRCodeReader();
            return TryToRecognize(wb, reader, null, out qrCode);            
        }

        public static bool TryToRecognizeDataMatrix(WriteableBitmap wb, out string dtm)
        {
            // create reader instance
            var reader = new com.google.zxing.datamatrix.DataMatrixReader();
            return TryToRecognize(wb, reader, null, out dtm);             
        }

        private static bool TryToRecognize(WriteableBitmap wb, Reader reader, Dictionary<object, object> zxhints, out string output)
        {
            bool res = false;
            output = null;
            try
            {
                var luminiance = new RGBLuminanceSource(wb, wb.PixelWidth, wb.PixelHeight);
                var binarizer  = new com.google.zxing.common.HybridBinarizer(luminiance);
                var binBitmap  = new com.google.zxing.BinaryBitmap(binarizer);

                // recognize
                var results = reader.decode(binBitmap, zxhints); // exception will be thrown if reader cannot decode image.

                output = results.Text;
                res = true;
            }
            catch (Exception ex)
            {
            }
            return res;
        }
    }
}

Below is the sample code of usage taken from TestApplication (see downloadable solution):

WriteableBitmap wbmp = new WriteableBitmap(barcodeSample.Source as BitmapImage);
string recognizedOutput = string.Empty;
if (BarcodeHelper.TryToRecognizeBarcode(wbmp, out recognizedOutput))
// if (BarcodeHelper.TryToRecognizeQRcode(wbmp, out recognizedOutput))
// if (BarcodeHelper.TryToRecognizeDataMatrix(wbmp, out recognizedOutput))
    resultTexBlock.Text = recognizedOutput;
else
    resultTexBlock.Text = "Unrecognizable barcode!";

Hint: if the recognition failed on an image with a high resolution and, apparently, a big size, try reducing the image size in two times step by step until the image is recognized.

The following part of another simple test application demonstrates how to use camera to make picture containing barcode and recognize it then. You can find this small application in downloadable solution as well.

using System;
using System.Windows;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Tasks;
using System.Windows.Media.Imaging;
using System.IO;
using Helpers;

namespace WindowsPhoneApplication2
{
    public partial class MainPage : PhoneApplicationPage
    {
        private CameraCaptureTask camera = new CameraCaptureTask();

        // Constructor
        public MainPage()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            button1.IsEnabled = false;            

            camera.Completed += new EventHandler<PhotoResult>(camera_Completed);
            camera.Show();
        }

        void camera_Completed(object sender, PhotoResult e)
        {
            camera.Completed -= new EventHandler<PhotoResult>(camera_Completed);

            if (e.TaskResult == TaskResult.OK)
            {
                BitmapImage bmp = new BitmapImage();
                bmp.CreateOptions = BitmapCreateOptions.None; //Don't delay creation
                bmp.SetSource(e.ChosenPhoto);

                resultTexBlock.Text = string.Empty;

                WriteableBitmap wbmp = new WriteableBitmap(bmp);
                string recognizedBarcode = string.Empty;
                if (BarcodeHelper.TryToRecognizeBarcode(wbmp, out recognizedBarcode))
                    resultTexBlock.Text = recognizedBarcode;
                else
                    resultTexBlock.Text = "Unrecognizable barcode!";
            }            

            button1.IsEnabled = true;
        }
    }
}

If you find a bug, please, leave an appropriate comment here and the issue will be fixed in a timely manner.

Download ZXing7_1_Port.zip (Visual Studio 2010 Solution)

ps There is an alternative – Windows Phone 7 Silverlight ZXing Barcode Scanning Library, but, as I see, the version of ZXing they use isn’t up-to-date.

Related posts: