I love barcodes. In my line of work (image processing and document imaging), barcodes play an integral part in high quality automation. As such, I've developed code from the lowest level of encoding and decoding barcodes up to using a number of libraries for working with barcodes.
The biggest problem with libraries is cost versus functionality. Most of the commercial libraries tend to be expensive and also separate their licenses between 1D and 2D barcodes such that obtaining full functionality is even more expensive. Free barcode libraries rarely support 2D barcodes, which is a deal breaking 9 times out of 10 for me, and when they do the symbology support is weak at best. This is understandable because 2D barcodes typically involve terribly complex encoding and decoding logic, but it's still frustrating to drop thousands of dollars for a commercial library that often includes awkward licensing limitations.
In my endless search for the best library, I found Spire.Barcode. This is a freely available encoding and decoding library that supports both 1D and 2D barcodes. Further, the recognition quality is surprisingly good. That said, there is one huge con:
- Barcode recognition returns an array of strings.
Why is this a con? Well, there are two questions which are common in barcode recognition that are not directly supported:
- What barcode symbology was used to detected the value?
- Where is the zone from which the barcode was detected?
As far as I can tell, #2 is impossible with Spire.Barcode as it is, but it's also the less common of the two questions. #1 is possible, after a fashion, which the following code snippet will handle. I wrote a barcode wrapper around Spire.Barcode that extends the functionality (with a primary focus on my WPF general imaging library) and answers question #1 in the process.
The trick, if you want to call it a trick, is to scan for each symbology separately, and store the results along with the symbology in a flattened collection. For convenience when there are multiple pages, the page index is also stored. The only downside to this approach is that it's slower. Barcode recognition in general is tricky to make fast, so adding inefficiencies to the process may be prohibitive. Spire.Barcode is not the fastest library I've used (that accolade goes to Vintasoft and Atalasoft), but it's not the slowest either.
Miscellaneous Stuff
The code snippet is self-contained barring the reference to Spire.Barcode, a supported barcode type enumeration, and a support method that converts BitmapSource
to Bitmap
. Even though Spire.Barcode uses a flags enumeration for its barcode types, I found that combining types with the bitwise OR operator does not work as well as I'd prefer. When combining all of the types, I received a "not supported" exception, which is silly. Further, the flags are set up in such a way that you might accidentally read unexpected barcodes (matching Codabar when looking for 3 of 9 or 128, for example). As such, I pared down the supported types to common ones I work with using another enumeration. All it does is limit the Spire enumeration:
[Flags]
public enum SupportedBarcodeType : long
{
Code39Extended = BarCodeType.Code39Extended,
Code93Extended = BarCodeType.Code93Extended,
Code128 = BarCodeType.Code128,
DataMatrix = BarCodeType.DataMatrix,
QRCode = BarCodeType.QRCode,
Pdf417 = BarCodeType.Pdf417,
}
In the interests of transparency, Imaging.GetBitmap is defined as follows:
/// <summary>
/// Loads a Bitmap from a BitmapSource.
/// </summary>
public static Bitmap GetBitmap(BitmapSource image)
{
using (var stream = new MemoryStream())
{
var encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(image));
encoder.Save(stream);
// Bitmap objects created from a stream expect the stream
// to stay open for the lifetime of the bitmap. We don't
// want to manage the stream too, so copy the bitmap to
// eliminate that dependency.
using (var bitmap = new Bitmap(stream))
{
return new Bitmap(bitmap);
}
}
}
Enjoy!