I have recently been working on an application for a client that needs to read QR codes. This has lead to some interesting findings. There are a couple of approaches you can use. One is to take a picture and evaluate it for a code and the other is more like the Bing Vision feature. Both of them can be accomplished by leveraging theSilverlight ZXing library from Codeplex.

In order to have QR code images to test I would suggest going to QRStuff.com. It is a site where you can freely generate QR images that you can prove out your app against. This is how I generated the image at the top of this post. If your code is working at the end you should be able to easily get back to this site. Wink! Wink!

So how do we write this code? I am going to take a quick look at both approaches and we can see which one we prefer at the end. For full disclosure I actually borrowed the code for both from other sites, although I don’t have references to them.

Approach 1

The first approach opens a camera object and then you press the shutter button in order to focus and capture an image of code. You will be asked to accept or retake the image. The main component of this code is a CameraCaptureTask instance which you will then need an even to show.

private CameraCaptureTask camera = new CameraCaptureTask();



private void ScanButton_Click( object sender, RoutedEventArgs e)

{

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

camera.Show();

}





Your code then needs to capture the completed event for the CameraCaptureTask and then pass the image to code which will check it for a proper QR code.

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;



bmp.SetSource(e.ChosenPhoto);

textBlock1.Text = string .Empty;

WriteableBitmap wbmp = new WriteableBitmap(bmp);

string recognizedBarcode = string .Empty;



if (BarcodeHelper.TryToRecognizeQRcode(wbmp, out recognizedBarcode))

textBlock1.Text = recognizedBarcode;

else

textBlock1.Text = "Unrecognizable barcode! " + recognizedBarcode;

}

}





Below is the code within the BarcodeHelper which actually processes the image.

public static bool TryToRecognizeQRcode(WriteableBitmap wb, out string qrCode)

{

var zxhints = new Dictionary< object , object >()

{

{ DecodeHintType.TRY_HARDER, true },

};

// create reader instance

var reader = new com.google.zxing.qrcode.QRCodeReader();

return TryToRecognize(wb, reader, null , out qrCode);

}





My experience with this code is that it doesn’t recognize the codes as often as it does. This give the illusion of being less accurate since each time you accept an image it lets you know if it recognized it or not.

Approach 2

The second approach involves using the preview buffer for the phone’s camera. To start you need to create a PhoneApplicationPage and strip it down to bare bones (no titles) and add rectangle object to the main grid with a VideoBrush fill. It also needs a ListBox to display the results.

<Grid x:Name= "LayoutRoot" Background= "Transparent" >

<Rectangle x:Name= "_previewRect"

Margin= "0"

Height= "800"

Width= "600"

HorizontalAlignment= "Center"

VerticalAlignment= "Center" >

<Rectangle.Fill>

<VideoBrush x:Name= "_previewVideo" >

<VideoBrush.RelativeTransform>

<CompositeTransform

x:Name= "_previewTransform" CenterX= ".5" CenterY= ".5" />

</VideoBrush.RelativeTransform>

</VideoBrush>

</Rectangle.Fill>

</Rectangle>

<ListBox Margin= "10" x:Name= "foundList" FontSize= "30" FontWeight= "ExtraBold" SelectionChanged= "foundList_SelectionChanged" />

</Grid>





The page is then driven off of a PhotoCamera object and a DispatchTimer. Below is the initial setup within the page.

private readonly DispatcherTimer _timer;

private readonly ObservableCollection< string > _matches;



private PhotoLuminanceSource _luminance;

private QRCodeReader _reader;

private PhotoCamera _photoCamera;



public ScanView()

{

InitializeComponent();



_matches = new ObservableCollection< string >();

foundList.ItemsSource = _matches;



_timer = new DispatcherTimer();

_timer.Interval = TimeSpan.FromMilliseconds(250);

_timer.Tick += (o, arg) => ScanPreviewBuffer();

}



protected override void OnNavigatedTo(NavigationEventArgs e)

{

App app = Application.Current as App;

app.ScannedValue = string .Empty;



_photoCamera = new PhotoCamera();

_photoCamera.Initialized += OnPhotoCameraInitialized;

_previewVideo.SetSource(_photoCamera);



CameraButtons.ShutterKeyHalfPressed += (o, arg) => _photoCamera.Focus();



base .OnNavigatedTo(e);

}



private void OnPhotoCameraInitialized( object sender, CameraOperationCompletedEventArgs e)

{

int width = Convert.ToInt32(_photoCamera.PreviewResolution.Width);

int height = Convert.ToInt32(_photoCamera.PreviewResolution.Height);



_luminance = new PhotoLuminanceSource(width, height);

_reader = new QRCodeReader();



Dispatcher.BeginInvoke(() =>

{

_previewTransform.Rotation = _photoCamera.Orientation;

_timer.Start();

});

}





Ultimately we get back down to the ZXing library again with a few improvements to the processing. The code for the decoding is actually a simple native call to a QRCodeReader’s decode method and passing it the bitmap from the camera buffer.

private void ScanPreviewBuffer()

{

try

{

_photoCamera.GetPreviewBufferY(_luminance.PreviewBufferY);

var binarizer = new HybridBinarizer(_luminance);

var binBitmap = new BinaryBitmap(binarizer);

var result = _reader.decode(binBitmap);

Dispatcher.BeginInvoke(() => DisplayResult(result.Text));

}

catch

{

}

}



private void DisplayResult( string text)

{

if (!_matches.Contains(text))

_matches.Add(text);

}





In the end approach #2 gives a better user experience and can handle more garbage cluttering the media that is displaying the QR code. When it comes down to it, both QR code interpretations could be used with either camera sampling. The ZXing code from approach #1 is probably more flexible in the long run if you have more dynamic conditions to read the images under, but it is going to take more work and greater understanding of the libraries. As usual pick the code sample that works best for your situation.