Reading QRCode with Windows 8 RC

I have been looking around the web for reading a QR Code from the camera of their Windows 8 device.

No luck I found nothing that was looking in that direction.

So I just take the ZXing barcode scanning library from Codeplex, modified it a bit, but the biggest part was around getting the output of the webcam as stream to get frames from it and send it over to this barcode library.

First problem, Windows 8 doesn’t support getting frames out of Video stream from managed code.

I am not really in the mood of writing C++ right now, so I just used the MediaCapture.CapturePhotoToStreamAsync method to get periodically the image from the webcam and send it over to the QRCodeReader.

Second problem, the stream received by the CapturePhotoToStreamAsync method is a BMP format in my case, and Windows 8 doesn’t expose a lot of ways to manipulate bits within the image, especially in the case I need luminance from the BMP ARGB format.

I ended up skipping the first 54 bytes of the stream (BMP header bytes) and then reading against all values to create a luminance byte array to give to the QRDecoder (that only work with luminance actually).

The last trick I tried to sue was to display a preview video stream on the screen while getting images from the photo stream, but even though Windows 8 doesn’t crash when creating, initializing and reading from multiple MediaCapture objects, it actually doesn’t display anything on the video stream part.

At the end the solution I am sharing here is displaying the same image used for scanning he barcode, which explain quite some latency between displays.

Feel free to use it and to share it for optimizing it a little bit :)

UPDATE: I just completely rewrote the code in a more WinRT (async await) way, which gives the app far more responsive for scanning QR Codes!!

Code sample here.

29 thoughts on “Reading QRCode with Windows 8 RC

  1. edison

    hey I am glad that you giving out the interesting codes. I have tried the codes but it isn’t work. Why? Please help me out. Thanks

  2. Benjamin Post author

    Hey there,

    Just saying it’s not working will not help me to help you :)
    Do you any error message? What is actually not working?

    Cheers, Ben

  3. edison

    An exception of type ‘com.google.zxing.ReaderException’ occurred in Silverlight.DLL but was not handled in user code

  4. Benjamin Post author

    The ZXing library is raising an exception when the given image is not containing a readable QR code, which is pointing out that your Visual Studio setup is configured to break on any exception, handled or not.
    Please into the Debug menu -> Exceptions…, and uncheck all checkboxes in the Thrown column.
    That should do the trick :)

  5. Kishore

    Hi Benjamin,

    I am getting exception like “An unhandled exception of type ‘System.Exception’ occurred in mscorlib.dll.Additional information: Exception from HRESULT: 0xC00DABE0″

    Thanks,
    Kishore

  6. Marwan

    Hello Benjamin,

    I was trying to get the QRCodeReader, to decode an image that i added to the project, So hear’s what i’ve done so far:


    StorageFile st = await Package.Current.InstalledLocation.GetFileAsync(@"Alec-Bradley-QR-Code.bmp");
    _imageStream = new InMemoryRandomAccessStream();
    Stream s = await st.OpenStreamForReadAsync();
    s.CopyTo(_imageStream.AsStreamForWrite());
    _datareader = new DataReader(_imageStream.GetInputStreamAt((ulong)54));

    The problem is when getting “luminanceBits[]” I have 2 more/less bits, and that is driving the code to throw an exception “attempted to access data outside the valid range”.
    I am running out of ideas here….. don’t no if skipping 54 bits is the wrong thing or the way i am getting the stream. What’s wrong ?

    (Thank you !)

  7. Marwan

    Hello,

    I managed to get the InMemoryAccessStream from the image and this is what i’ve done:


    StorageFile file = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync("Alec-Bradley-QR-Code.bmp");
    _imageStream = new InMemoryRandomAccessStream();
    using (var inputStream = await file.OpenReadAsync())
    {
    await RandomAccessStream.CopyAsync(inputStream, _imageStream);
    }
    _imageStream.Seek(0);

    After getting the stream and the bitmapImage (i get the width and height of the image), I reset the following variables => ‘_dimensions’ ‘_encodingProps’ .

    Now I’m getting this error after decoding the bar-code : ” Index was outside the bounds of the array.” Will keep this topic updated ;)

  8. Suman

    Hi Guys,

    I am trying the same. Do you guys have any success in scanning the QR code image?
    I am trying with a sample image which has QR code in it.

    Thanks & Regards,
    Suman

  9. theCake

    Hello,
    Great work porting this library to WinRT! However, I was unable to run the sample app. It crashes with an unhandled exception in the _capture.InitializeAsync() line…
    Nevertheless, I wrapped the encoding features of your zxing port into a new library, which I plan to publish on my blog soon.. It features a Task to generate QR codes in one line of code.. If you might want to see the code, or prefer a way to credit you as the one who did the complicated part, just drop me an email =)
    I’d really like to wrap the decoding mechanism as well, but..

    greetings,
    ~theCake

  10. Chris

    Hello,

    I have tried your sample but the problem is that I always get the “Exception of type ‘com.google.zxing.ReaderException’ was thrown.” at the QRCodeReader decode and the result is always an empty string. I have not managed to read a QR Barcode yet! Do you have any clue why I can’t get a result?

    @”theCake” : You should not run the project to a simulator. Check it!

  11. Chris

    Thanx for the response!

    Yes I have already made these changes.

    The problem is that in the ReedSolomonDecoder.cs the findErrorLocations functions always throws an “throw new ReedSolomonException(“Error locator degree does not match number of roots”)” because the int e is never equal to the numErrors and then everything breaks!

    Can you figure out what is going on?

  12. Adi

    Hi Benjamin I am using this code

    using (IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read))
    {
    // Set the image source to the selected bitmap
    BitmapImage bitmapImage = new BitmapImage();
    bitmapImage.DecodePixelHeight = decodePixelHeight;
    bitmapImage.DecodePixelWidth = decodePixelWidth;
    bitmapImage.SetSource(fileStream);
    var reader = new Windows.Storage.Streams.DataReader(fileStream.GetInputStreamAt(0));
    await reader.LoadAsync((uint)fileStream.Size);
    byte[] pixels = new byte[fileStream.Size];
    reader.ReadBytes(pixels);
    com.google.zxing.LuminanceSource source = new RGBLuminanceSource(pixels, bitmapImage.DecodePixelWidth,
    bitmapImage.DecodePixelHeight);
    var binarizer = new HybridBinarizer(source);
    var binBitmap = new BinaryBitmap(binarizer);
    var qrRead = new com.google.zxing.oned.MultiFormatUPCEANReader(zxingHints); ;
    var results = qrRead.decode(binBitmap);
    }

    I am trying your project for decoding barcode, but I am getting exception in decode function of OneDReader class. Any help?

  13. MrT

    Hi,

    I’ve tried to use the code to scan barcodes instead of QR codes but I keep getting reader errors. Do you know if there are any issues with the MultiFormatReader?

  14. Benjamin Post author

    Hi folks,
    This code is provided as an example, and therefore you should have to check against your image binary content to see where is the issue is coming from.
    As far as I looked at it, the ZXing lib is working pretty fine even with an image opened from the file-system :)

    Cheers, Ben

  15. Vivek Deshmukh

    hi Benjamin,

    I go through your sample code which is absolutely fine for camera reading.

    but I want to encode QRCode depends on user inputs.
    I know how to call encode function but how to form image after getting BitMatrix in WIRT ?

    Could you please guide me for this ?

  16. Scruff.R

    Hi Benjamin,

    thanks for your great work and the help you provide to users astonishingly quick!

    There is only one silly question, which might seem a bit off topic:
    Wouldn’t it be possible to change the project output “Silverlight.DLL” to something more expressive like e.g. bsQRLib or bsQRLibrary_ZXingPort?
    “Silverlight” is a very general term and a registered trade mark, so using it could lead to confusion (what exactly does this DLL do?) or other problems.

    This said, I really don’t want to appear petty, but rather help to earn your project the respect it deserves, by suggesting to use your OWN NAME (hence the bs prefix) and set it seperate from other projects that might also use “Silverlight” in any other form.

    Cheers,

    Scruff.R

  17. Benjamin Post author

    Hello Vivek,

    This is acutally something put aside from C# here, for good performance reasons; this points to doing this in C++ instead.
    Don’t forget that my lazyness led me to do it this way, but the best way would still be to do all the image handling in C++ and then only do the image QRCode mapping in C#.

    Still, if wou want ot have a look at doing bitmap matrix manipulation in C# you definitely need t have a look here, this project actually is an implementation of the missing pieces of Bitmap manipulation in C#.

    Cheers,
    Ben

  18. Benjamin Post author

    Hello Scruff,

    That’s a good point, I’ll be doing something around this soon (next 2 weeks time I hope); check back then for new package :)

    Cheers,
    Ben

  19. Scruff.R

    Hi Ben,

    now some C# stuff.
    While playing a bit with your code, I stumbled over the really slow RGBLuminanceSource(WritableBitmap, int, int) and RGBLuminanceSource(byte[], int, int) didn’t work for me either (same as e.g. post of Adi 16.8. 11:55).
    After reading some comments dealing with these problem in the original ZXing.Net forum, too, I slightly tweaked your code of RGBLuminanceSource into an ARGBLuminanceSource and avoided the time consuming GetPixeli(x,y) by reusing the (byte[], int, int) constructor.

    public ARGBLuminanceSource(byte[] d, int W, int H) : base(W, H)
    {
    __width = W;
    __height = H;
    int pixelCount = W * H; // can forget about 2D (might save time building product W*H over and over again)
    int posSource = 0; // incremenal cursor instead of permanently calculating position might save time
    // additionally rids us of the need to know number of bytes per pixel (add or remove an ++)
    int r, g, b; // declare only once (might save time?)

    // In order to measure pure decoding speed, we convert the entire image to a greyscale array
    // up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
    luminances = new sbyte[pixelCount];

    // drop extra loop (might save time?)
    for (int posTarget = 0; posTarget > 2);
    }
    }
    }

    public ARGBLuminanceSource(WriteableBitmap d, int W, int H) : this(d.ToByteArray(), W, H) { }

    Cheers,
    Scruff.R

  20. Linda Rawson

    I am also receiving the following error:

    An exception of type ‘com.google.zxing.ReaderException’ occurred in Silverlight.DLL but was not handled in user code

    All of the debug exceptions in the thrown column are not checked.

    I put a try catch around this statement in mainpage.xaml.cs

    Result r = c.decode(bbm);

    if (!String.IsNullOrEmpty(r.Text))
    {
    result = r.Text;
    }

    In the hopes it would ignore the error.

    Any other ideas? I am running VS2012.

    Thanks! You really did a great job here!

  21. Linda Rawson

    I can never get it to read a QR Code. The same as @Chris.

    Here is the image file: http://sensorytech.net/photo%20(63).jpg

    But I got around the getPixel slowness by converting the WriteableBmp into a byte[]

    Stream stream = wb.PixelBuffer.AsStream();
    byte[] pixels = new byte[stream.Length];
    stream.Seek(0, 0);
    stream.Read(pixels, 0, pixels.Length);

    com.google.zxing.LuminanceSource source = new RGBLuminanceSource(pixels, wb.PixelWidth, wb.PixelHeight);

  22. Linda Rawson

    The problem has to do with the colors. From a bitmap using zxing library my first color of my image at (x,y) = (0,0) = R:78, G:10, B:6 but from the writeablebitmap at (0,0) = R:109,G:47,B:24. I am thinking it is the wrong colorspace. Like one is BGR and the other is RGB.

    The FinderPatternInfo never finds any possible centers so never locates the bar code.

    It fails at this line:

    // A winner?
    if (foundPatternCross(stateCount))

    Because the colors are wrong. I am trying to work through the issue but if you guys are better image processors than I am that would be great.

  23. Linda Rawson

    Okay I can finally read a bar code. Feel free to enhance this code but here is how I did it. I added a routine to take the Writeablebitmap into the Sbyte array of luminances.

    private sbyte[] GetLuminance(WriteableBitmap modifiedImage)
    {
    int r = 0, a = 0, i = 0;
    int h = modifiedImage.PixelHeight;
    int w = modifiedImage.PixelWidth;
    Stream stream = modifiedImage.PixelBuffer.AsStream();
    byte[] StreamBuffer = new byte[stream.Length];
    //byte[] tempbuffer = new byte[64000];
    stream.Seek(0, 0);
    stream.Read(StreamBuffer, 0, StreamBuffer.Length);

    sbyte[] luminances = new sbyte[w * h];
    int currentX = 0;
    int currentY = 0;

    for (i = 0; i < StreamBuffer.Length – 4; i = i + 4)
    {
    var a1 = StreamBuffer[i + 3];
    var r1 = StreamBuffer[i + 2];
    var g1 = StreamBuffer[i + 1];
    var b1 = StreamBuffer[i + 0];

    a = 0xff;

    currentX = (i / 4) % w;
    currentY = (i / 4) / w;
    int offset = currentY * w;

    luminances[offset + currentX] = (sbyte)(((int)r1) << 16 | ((int)g1) << 8 | ((int)b1));
    }
    //stream.Seek(0, 0);
    //stream.Write(StreamBuffer, 0, StreamBuffer.Length);
    stream.Dispose();
    modifiedImage.Invalidate();
    return luminances;
    }

    Then I created an overridden method in RGBLuminanceSource.cs

    public RGBLuminanceSource(sbyte[] d, int W, int H, bool Is8Bit)
    : base(W, H)
    {
    __width = W;
    __height = H;
    luminances = d;
    }

    Then I call it like this:

    StorageFile file = await StorageFile.GetFileFromPathAsync(pImageFilePath);

    WriteableBitmap wb = new WriteableBitmap(1, 1);
    wb = await wbm.FromStream(await file.OpenReadAsync());

    com.google.zxing.LuminanceSource source = new RGBLuminanceSource(GetLuminance(wb), wb.PixelWidth, wb.PixelHeight,true);
    var binarizer = new HybridBinarizer(source);
    var binBitmap = new BinaryBitmap(binarizer);

    returnValue = reader.decode(binBitmap).Text;

    Yeah! So excited!

  24. Benjamin Post author

    Hello Linda,

    I came up the the same conclusion; I actually found a really faster method to get Bitmap bytes using the BitmapDecoder object.
    But for some reasons the bits given by the BitmapDecoder class against my old slow parsing with DataReader is not giving me the same Bitmap bytes at all!

    This means additional time would be needed to find an acccurate way to either get those bits right or to find the good values for building up the luminance table used by ZXing.

    Cheers,
    Ben

  25. Linda Rawson

    I need some more time to mess with it but I had this code commented out. It is probably something along these lines where you have to switch the colors.

    // Decode image format
    /*var decoder = await BitmapDecoder.CreateAsync(await file.OpenReadAsync());
    var transform = new BitmapTransform();
    var pixelData = await decoder.GetPixelDataAsync(decoder.BitmapPixelFormat, decoder.BitmapAlphaMode, transform, ExifOrientationMode.RespectExifOrientation, ColorManagementMode.ColorManageToSRgb);

    // Swap R and B channels since it’s reversed
    var pixels = pixelData.DetachPixelData();
    for (var i = 0; i < pixels.Length; i += 4)
    {
    var r = pixels[i];
    var b = pixels[i + 2];
    pixels[i] = b;
    pixels[i + 2] = r;
    }*/

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Powered by Sweet Captcha
Verify your real existence,
Drag the dress to the hanger
  • captcha
  • captcha
  • captcha
  • captcha