An OpenCV barcode and QR code scanner with ZBar

Today’s blog post on reading barcodes and QR codes with OpenCV is inspired by a question I received from PyImageSearch reader, Hewitt:

Hey Adrian, I really love the PyImageSearch blog. I look forward to your emails each week. Keep doing what you’re doing.

I have a question for you:

Does OpenCV have any modules that can be used to read barcodes or QR codes? Or do I need to use an entirely separate library?

Thanks Adrian.

Great question, Hewitt.

The short answer is no, OpenCV does not have any dedicated modules that can be used to read and decode barcodes and QR codes.

However, what OpenCV can do is facilitate the process of reading barcodes and QR codes, including loading an image from disk, grabbing a new frame from a video stream, and processing it.

Once we have the image or frame we can then pass it to a dedicated Python barcode decoding library such as a Zbar.

The ZBar library will then decode the barcode or QR code. OpenCV can come back in to perform any further processing and display the result.

If this sounds like a complicated process, it’s actually pretty straightforward. The ZBar library, along with its various forks and variations, have come a long way. One set of ZBar bindings in particular, pyzbar , is my personal favorite.

Inside today’s tutorial, I will show you how to read barcodes and QR codes with OpenCV and ZBar.

And as an added bonus, I’ll demonstrate how to deploy our barcode scanner to the Raspberry Pi as well!

To learn more about reading barcodes and QR codes with OpenCV and ZBar, just keep reading.

Looking for the source code to this post?
Jump right to the downloads section.

An OpenCV barcode and QR code scanner with ZBar

Today’s blog post is broken into four parts.

In the first part, I’ll show you how to install the ZBar library (with Python bindings).

The ZBar library will be used together with OpenCV to scan and decode barcodes and QR codes.

Once ZBar and OpenCV are properly configured I’ll demonstrate how to scan barcodes and QR codes in a single image.

Starting with a single image will give us the practice we need to prepare for the next step: Reading barcodes and QR codes in real-time with OpenCV and ZBar,

Finally, I’ll demonstrate how to deploy our real-time barcode scanner to the Raspberry Pi.

Installing ZBar (with Python bindings) for barcode decoding

A few weeks ago Satya Mallick from the LearnOpenCV blog posted a really great tutorial on using the ZBar library to scan barcodes.

The instructions to install ZBar in today’s post are largely based on his instructions, but with a few updates, the largest one being related to how we install the Python zbar  bindings themselves, ensuring we can:

  1. Use Python 3 (the official zbar  Python bindings only support Python 2.7)
  2. Detect and localize exactly where in the image the barcode is.

Installing the necessary software is an easy 3-step process.

Step 1: Install zbar  from the apt  or brew  repository

Installing ZBar for Ubuntu or Raspbian

Installing ZBar for Ubuntu can be accomplished with the following command:

Installing ZBar for macOS

Installing ZBar for macOS using brew is equally as easy (assuming you have Homebrew installed):

Step 2 (Optional): Create a virtual environment and install OpenCV

You have two options here:

  1. Use an existing virtual environment that has OpenCV ready to go (skip this step and head to Step 3).
  2. Or create a new, isolated virtual environment which involves installing OpenCV.

Virtual environments are a best practice for Python development and I highly encourage you to make use of them.

I elected to create a new, isolated Python 3 virtual environment and followed the Ubuntu (or macOS, depending on which machine I was using) OpenCV installation instructions linked on this page. The only change I made while following those instructions was to name my environment barcode :

Note: If you already have OpenCV installed on your system you can skip the OpenCV compile process and simply sym-link your cv2.so  bindings into the site-packages  directory of your new Python virtual environment.

Step 3: Install pyzbar

Now that I have a Python 3 virtual environment named barcode  on my machine, I activated the barcode  environment (yours might have a different name) and installed pyzbar :

If you are not using a Python virtual environment you can just do:

If you’re trying to install pyzbar  into the system version of Python make sure you use the sudo  command as well.

Decoding barcodes and QR codes with OpenCV in single images

Figure 1: Both QR and 1D barcodes can be read with our Python app using ZBar + OpenCV.

Before we implement real-time barcode and QR code reading, let’s first start with a single image scanner to get our feet wet.

Open up a new file, name it barcode_scanner_image.py  and insert the following code:

On Lines 2-4 we import our required packages.

Both pyzbar  and cv2  (OpenCV) need to be installed following the instructions in the previous section.

In contrast, argparse  is included with the Python install and it is responsible for parsing command line arguments.

We have one required command line argument for this script ( --image ) and it is parsed on Lines 7-10.

You’ll see at the end of this section how to run the script while passing a command line argument containing the input image path.

Now, let’s take the input image and put pyzbar  to work:

On Line 13, we load the input image  via its path (contained in our convenient args  dictionary).

From there, we call pyzbar.decode  to find and decode the barcodes  in the image  (Line 16). This is where all the magic of ZBar happens.

We aren’t finished yet — now we need to parse the information contained within the barcodes  variable:

Beginning on Line 19, we loop over the detected barcodes .

In this loop, we proceed to:

  1. Extracting the bounding box (x, y)-coordinates from the barcode.rect  object (Line 22), enabling us to localize and determine where in the input image the current barcode is.
  2. Draw draw a bounding box rectangle on the image  around the detected barcode  (Line 23).
  3. Decode the barcode  into a "utf-8"  string and extract the type of barcode (Lines 27 and 28). It is critical to call the .decode("utf-8")  function on the object to convert from a byte array to a string. You can experiment by removing/commenting it out to see what happens — I’ll leave this as an experiment for you to try.
  4. Format and draw the barcodeData  and barcodeType  on the image (Lines 31-33).
  5. And finally, output the same data and type information to the terminal for debugging purposes (Line 36).

Let’s test our OpenCV barcode scanner. You should use the “Downloads” section at the bottom of this blog post to download the code and example image.

From there, open up your terminal and execute this command:

As you can see in the terminal, all four of the barcodes were found and properly decoded!

Refer to Figure 1 for the processed image which has overlaid red rectangles and text for each barcode our software found.

Real-time barcode and QR code reading with OpenCV

In the previous section, we learned how to create a Python + OpenCV barcode scanner for single images.

Our barcode and QR code scanner worked well — but it raises the question, can we detect and decode barcode + QR codes in real-time?

To find out, open up a new file, name it barcode_scanner_video.py , and insert the following code:

On Lines 2-8 we import our required packages.

At this point, recalling the above explanation, you should recognize pyzbar , argparse , and cv2 .

We’ll also use VideoStream  to handle capturing video frames in an efficient, threaded approach. You can learn more about the VideoStream class here. And if you do not have imutils  installed on your system, just use the following command:

We’re going to parse one optional command line argument, --output , which contains the path to the output Comma Separated Values (CSV) file. This file will contain the timestamp and payload of each detected and decoded barcode from our video stream. If this argument isn’t specified, the CSV file will be placed in our current working directory with a name of "barcodes.csv"  (Lines 11-14).

From there, let’s initialize our video stream and open our CSV file:

On Lines 18 and 19 we initialize and start our VideoStream . You may either:

  • Use your USB webcam (uncomment Line 18 and comment Line 19)
  • Or if you’re using a Raspberry Pi (like me) you can use the PiCamera (uncomment Line 19 and comment Line 18).

I chose to use my Raspberry Pi PiCamera, as is shown in the next section.

We then pause for two seconds to allow the camera can warm up (Line 20).

We’ll be writing all barcodes we find to disk in a CSV file (but ensuring duplicates are not written). This is meant to be a (trivial) example of logging barcodes. You could, of course, do whatever you want once a barcode is detected and read such as:

  • Save it in a SQL database
  • Send it to a server
  • Upload it to the cloud
  • Send an email or text message

The actual action is arbitrary — we’re simply using the CSV file as an example.

Feel free to update the code to include any notification you may wish.

We open the csv  file for writing on Line 24. If you are modifying the code to append to the file, you can simply change the 2nd parameter from "w"  to "a"  (but you’ll have to search the file for duplicates in a different way).

We also initialize a set  for found  barcodes. This set will contain unique barcodes while preventing duplicates.

Let’s begin capturing + processing frames:

On Line 28 we start our loop and proceed to grab and and resize a  frame  from our video stream (Lines 31 and 32).

From there, we call pyzbar.decode  to detect and decode any QR + barcodes in the frame .

Let’s proceed to loop over the detected barcodes :

This loop should look very familiar if you read the previous section.

In fact, Lines 38-52 are identical to those the single image script. Please refer to the single image barcode detection and scanning section for a detailed review of this code block.

Lines 56-60 are new. On these lines, we check if we’ve found a unique (not previously found) barcode (Line 56).

If that’s the case, we write the timestamp and data to the csv  file (Lines 57-59). We also append the barcodeData  to a found  set as a simple way to handle duplicates.

In the remaining lines of the real-time barcode scanner script, we display the frame, check if the quit key is pressed, and perform cleanup:

On Line 63 we display the output frame .

Then on Lines 64-68, we check for keys and if "q"  is pressed, we break  out of the main execution loop.

Finally, we perform cleanup on Lines 72-74.

Building a barcode and QR code scanner on the Raspberry Pi

Figure 2: My Raspberry Pi barcode scanner project consists of a Raspberry Pi, PiCamera, 7-inch touchscreen, and battery pack.

What fun is a barcode scanner if I’m limited to my desk?

I decided that I’d like to take my barcode scanner with me using my Pi, touchscreen, and battery pack.

Shown in Figure 2 is my setup — the exact one I used for my mobile Pokedex deep learning project recently. If you’re looking to build your own with the exact peripherals shown, I’ve listed the products and links:

Building the system is really easy and I’ve made step-by-step instructions in this blog post.

Once your mobile ZBar barcode scanner is ready, use the “Downloads” section of this blog post to download the code associated with this blog post.

From there, open up a terminal on your Pi and launch the app with the following command (you’ll need a keyboard/mouse for this step but then you can disconnect and let the app run):

Now you can present barcodes to the camera and when you’re done, you can open the barcodes.csv  file (or if you’re so inclined you can execute tail -f barcodes.csv  in a separate terminal to view the data as it enters the CSV file live).

The first QR code that I tried is shown on a black background — it is very easy for ZBar to detect:

Figure 3: A QR code with the code “PyImageSearch” is recognized with our Python + ZBar application

Then I headed to my kitchen, with Pi, screen, and battery pack in hand, and found another QR code:

Figure 4: My website, “https://www.pyimagesearch.com/” is encoded in a QR code and recognized with ZBar and Python on my Raspberry Pi.

Success! It even works at many angles.

Now let’s try a QR code that is contains a JSON-blob of data:

Figure 5: An OpenCV barcode and QR scanner with ZBar decodes an image of a QR with ease. I deployed the project to my Raspberry Pi so I can take it on the go.

No match for my OpenCV + ZBar + Python barcode scanner project!

And finally, I tried a traditional 1-D barcode:

Figure 6: ZBar coupled with OpenCV and Python makes for a great Raspberry Pi barcode project. My name, “AdrianRosebrock” is encoded in this CODE128 barcode.

1-D barcodes are slightly more challenging for the system especially with a PiCamera which doesn’t support autofocus. That being said, I achieved a successful detection and decoding of this barcode as well.

You might have best luck with a USB webcam such as the Logitech C920 which has great autofocus. Alternatively, you can actually change the factory focus on your PiCamera using the method that Jeff Geerling describes on his blog.

That’s a wrap!

If you’re interested in reading additional barcode blog posts on my website, check out the posts with the “barcode” tag.

Summary

In today’s blog post you learned how to build an OpenCV barcode and QR code scanner.

To accomplish this, we used the ZBar library.

Once the ZBar library was installed on our system, we created two Python scripts:

  1. The first one to scan barcodes and QR codes in a single image
  2. And a second script to read barcodes and QR codes in real-time

In both cases, we used OpenCV to facilitate the process of building our barcode/QR code scanner.

Finally, we wrapped up today’s blog post by deploying our barcode reader to the Raspberry Pi.

The barcode scanner is fast enough to run in real-time on the Raspberry Pi without an issue.

Feel free to use this barcode and QR code scanner functionality in your own projects!

And if you build something fun and interesting with it, be sure to share your project in the comments.

I hope you enjoyed today’s post. I’ll see you next week.

To be notified when future blog posts are published here on PyImageSearch, be sure to enter your email address in the form below!

Downloads:

If you would like to download the code and images used in this post, please enter your email address in the form below. Not only will you get a .zip of the code, I’ll also send you a FREE 17-page Resource Guide on Computer Vision, OpenCV, and Deep Learning. Inside you'll find my hand-picked tutorials, books, courses, and libraries to help you master CV and DL! Sound good? If so, enter your email address and I’ll send you the code immediately!

, , , ,

42 Responses to An OpenCV barcode and QR code scanner with ZBar

  1. Nachiket May 21, 2018 at 12:01 pm #

    Hey Adrian, you have written this following note:
    ” If you already have OpenCV installed on your system you can skip the OpenCV compile process and simply sym-link your cv2.so bindings into the site-packages directory of your new Python virtual environment”.
    Here you have mentioned to symlink the cv2.so file with site packages …How to do it? Can you please elaborate on that?
    Thank you

    • Adrian Rosebrock May 21, 2018 at 2:27 pm #

      Hey Nachiket — are you using a Python virtual environment? And did you follow one of my tutorials here on PyImageSearch to install OpenCV? Provided you used one of my tutorials, select the relevant one, and then follow the sym-link steps.

      • Nachiket June 2, 2018 at 1:04 am #

        Yes sir I have always followed your tutorials on creating virtual environment but I couldn’t find the symlink steps. I have recently followed the rpi stretch OS installation on Rpi, are symlink steps are involved there?

        • Adrian Rosebrock June 5, 2018 at 8:08 am #

          If you are struggling with the process I would just recommend installing the ZBar into your current Python virtual environment.

  2. Gilad May 21, 2018 at 5:07 pm #

    Great post.
    I actually open the url with webbrowser

    Now – It will be great to port it to my phone (Android)

    • Adrian Rosebrock May 22, 2018 at 5:57 am #

      Thanks Gilad, I’m glad you enjoyed the post and found it helpful 🙂

  3. Suganya Robert May 21, 2018 at 10:05 pm #

    Hi Adrian,
    It is interesting. You are increasing my hope to continue my research in the field of IoT. Waiting for more posts.

    Thank you.
    Suganya

    • Adrian Rosebrock May 22, 2018 at 5:54 am #

      Thank you for the kind words, Suganya 🙂

  4. Godwine Papin May 23, 2018 at 6:12 am #

    Hello I am very interested in your work on opencv and python. Currently I am working on a project using opencv and python. I have questions of understanding to ask you. Can you send me your email address or directly reply to my email address: EMAIL ADDRESS REMOVED. Thank you Best regards

    • Adrian Rosebrock May 23, 2018 at 7:08 am #

      Please do not share your email address directly within a comment. You can contact me via the PyImageSearch contact form.

  5. TetsFR May 24, 2018 at 4:22 am #

    Hi, first thank you very much for your excellent tutorial, super clear and complete as usual.
    2 quick questions:
    1. How many fps do you achieve on the rpi3?
    2. Why do you always resize to 400px in your tutorial through opencv, especially for video stream? You could setup the frame size h x w at camera init stage at it would save a costly opencv resize at each frame no? (Assuming the sizing is done by the camera, not the video driver)

    Thanks
    Tets

    • Adrian Rosebrock May 24, 2018 at 6:35 am #

      1. I did not compute the raw FPS but the barcode detector was easily running in real-time on the Raspberry Pi 3.

      2. You could certainly do that as well; however, you should be careful when doing it. There are times were you might want to display the full size output frame but process a smaller version of the frame for efficiency/accuracy. A great example would be processing all frames in a video file and writing them back to disk. It’s a use case decision to be mindful of.

  6. K May 30, 2018 at 10:15 am #

    Hi Adrian, first of all thanks for this great tutorial!

    I have a couple questions on how I can further improve the system.
    I tried running the python file with some images of my own (of objects with QR codes on it) but it would not detect the QR codes at all.

    Is this because of noise? Or could it be that the size of the QR code in respect to the image overall is too small?

    I have also tried to run it with a resized and enlarged photo, but it does not seem to work and I’m not sure what next steps I should be taking and would like some advice.

    Thanks
    k

    • Adrian Rosebrock May 30, 2018 at 5:55 pm #

      This could happen for many reasons, including noise, the QR code being too small, or an issue with your camera sensor. Unfortunately I’m not sure what the issue would be without seeing an example image of what you’re working with.

  7. Markus June 4, 2018 at 4:03 am #

    Hi Adrian, thank you for your great tutorials! For a running project I tried to combine some of your blog posts. I would like to detect DataMatrix Codes in an Image or Video-Stream.
    Unfortunately ZBAR does not Support Datamatrix Codes. I tried pylibdmtx (https://github.com/NaturalHistoryMuseum/pylibdmtx ) and it works fine for perfect separated and aligned Codes but (of course) not for detection within an image or video.
    I gave OpenMV a chance but the result was very slow an not suitable for small Codes because of the low resolution of the camera.
    Based on your blog post („Detecting Barcodes in Images with Python and OpenCV“) I also tried to detect the DataMatrix Code with OpenCV. Its perfect for 1D-Barcodes but it failed in detecting 2D-Barcodes…
    Can you give me a hint, how to detect DataMatrix Codes in OpenCV?

    Thank you & Best regards

    • Adrian Rosebrock June 5, 2018 at 7:54 am #

      It sounds like you may want to continue working with the pylibdmtx library for the project. I don’t have any experience with that particular library but if you’re having issues detecting/reading the DataMatrix codes you may want to try cleaning up the image and extracting just the barcode before you try to pass it into the library.

  8. Saverio Pieri June 11, 2018 at 10:18 am #

    Hi Adrian,
    thanks for this tutorial. I did some experiments: the software recognizes correctly the bar code only if it’s very close to the camera (2/3 inches).
    It will be great to implement an object recognition for bar code images in a frame, crop the rect and pass it to pyzlib.

    Using a HiRes camera (FullHD) it would be possible to increase the results.

    What do you think about it?

  9. ashish June 20, 2018 at 5:36 pm #

    thanks for the tutorial. I was trying real time barcode scanning using a web cam, since the pi camera have fixed focus. can u give me a link or your own work on barcode scanning using web cam. It would be a great help

    • Adrian Rosebrock June 21, 2018 at 5:41 am #

      The code in this blog post already works with a webcam. See Lines 18 and 19 of the barcode_scanner_video.py script.

      • ashish June 21, 2018 at 9:11 am #

        oh yeah, thanks for the great tutorial

  10. peen June 28, 2018 at 2:51 am #

    hi adrian,
    ive followed each step you taught, but im getting :

    from pyzbar import pyzbar
    ImportError: No module name pyzbar

    • Adrian Rosebrock June 28, 2018 at 7:59 am #

      You need to install the pyzbar library as instructed in the post:

      $ pip install pyzbar

      If you are using a Python virtual environment make sure you access it when you install pyzbar and when you run the code.

  11. peen July 1, 2018 at 1:20 am #

    thanks..

  12. peen July 1, 2018 at 2:39 am #

    hi, im getting error:

    ImportError : no module named imutils.video

    but, ive already uninstalled and installing it again by using ‘sudo pip3 install –upgrade imutis’

    ive also imported imutils.video in python and not getting error

    so why did the importError happened?

    • Adrian Rosebrock July 3, 2018 at 8:35 am #

      Are you using a Python virtual environment? If so, you can’t use “sudo” as sudo will install into your system Python. Instead, use “workon” to access your Python virtual environment and then “pip install imutils”. If you’re not using a Python virtual environment let me know.

  13. Raymond July 2, 2018 at 8:13 am #

    Hi Adrian,

    I’ve combined this tutorial with your other tutorial to control an LED when an green surface is detected. I need to scan barcodes and check them with a preferred barcode, when the barcode differs from the preferred one it will give a signal to an LED. To ensure a good scan I made a flashlight which will lighten the barcode. Unfortunately the camera change automatically the parameters of the brightness and contrast. How can I set the contrast to an preferred number? I use this for the stream:

    vs = VideoStream(src=0).start()

    frame = vs.read()
    frame = imutils.resize(frame, width=400)

    I’ve tried to put some set parameters but every time I get aan AttributeError.
    I placed “vs.__init.set(CV_CAP_PROP_BRIGHTNESS, 0.1) after the vs = VideoStream(src=0).start()

    Can you help me with this, please?

    Kind regard,

    Raymond

    • Adrian Rosebrock July 3, 2018 at 7:28 am #

      The VideoStream class is not intended to have the camera properties set that way. I have an idea to update the “src” attribute with a pre-initialized cv2.VideoCapture object in the future but haven’t been able to.

      Instead, I would suggest you use the WebcamVideoStream form imutils instead. Before you run the .start() method of the object you can set any camera properties:

      vs.stream.set(cv2.PROPERY_NAME_HERE, value)

  14. Tenda Support Number July 9, 2018 at 1:52 pm #

    I was thought about to make a mini project on the technology-based something and after visit this blog I have got the idea to try this in my project using zbar for scanning barcode and QR code. keep posting this kind of interesting technology-based experiments.

  15. Po-Hsun July 16, 2018 at 6:22 am #

    Hi Adrian,

    I have followed your instructions, and it works great. However, when I have a small QR code; approximately 0.7 inch x 0.7 inch, the QR code can’t be detected. Is there anyway to improve it? Thank you

    • Adrian Rosebrock July 17, 2018 at 7:19 am #

      You may want to consider resizing your image and increasing the resolution of your input image prior to passing it through the detector.

  16. Felipe Cesar July 18, 2018 at 7:27 am #

    Dear Adrian, firstval i congratulate you for the great qork you’ve done. I have a question, i’m just a beginner, can i get the streaming from an IP Camera with VideoStream Class? Thanks.

    • Adrian Rosebrock July 20, 2018 at 6:45 am #

      Provided you use vs = VideoStream(src="your IP path") it should work.

      • Felipe Cesar August 3, 2018 at 9:20 am #

        Thanks! It worked. The Ip camera detects only QRcode but when i try to detect barcode, it dosn’t work. Dou you have any idea? Thanks

        • Adrian Rosebrock August 7, 2018 at 7:05 am #

          I had some issues with ZBar detecting some barcodes. It always worked really well for QRcodes but not always barcodes. I think it may be a limitation of the ZBar library itself.

        • Robert Reed August 13, 2018 at 6:43 pm #

          My PiCamera was initially unable to detect barcodes because the image itself was too blurry, so it wasn’t able to detect the barcode. Manually adjusting the focus (rotating it counter clockwise) of the camera fixed this for me. Keep in mind, the camera is now essentially set up to only focus on close-up images, and is blurry further away.

          I found the idea here: https://www.jeffgeerling.com/blog/2017/fixing-blurry-focus-on-some-raspberry-pi-camera-v2-models

          I was able to rotate the focus using my fingernails while supporting the base of the camera. Hope this helps get a more crisp image!

  17. ashish August 1, 2018 at 8:38 am #

    what if i have an entry of almost 50k barcode values in the csv file and i have to cross check one value that i read using a code that goes through the main csv file. How much time in average will it take if the particular barcode value is almost at the last of csv file.

    • Adrian Rosebrock August 2, 2018 at 9:28 am #

      I would suggest you run an experiment and see as it will depend on which hardware you are using. Generate 50K barcode entries and then run your matching algorithm.

  18. john August 1, 2018 at 11:52 am #

    hello,
    the frame rate for real time barcode snanning is low. any method that can increase the frame rate for video using a webcam.

    • Adrian Rosebrock August 2, 2018 at 9:26 am #

      Hey John, did you try the code on your system? The frame rate is actually much higher than it appears on the GIF animations. To create the GIFs I just wrote a Python script to take a screenshot every two seconds.

  19. Liam Scobey August 9, 2018 at 1:22 pm #

    Hey Adrian, great tutorial, I got this up and running in no time. I was hoping to use this through ssh, and I was able to do that by commenting out the cv2.imshow line, and that allows it to work well enough for me. However, it would be a great bonus if I could stream the actual video via ssh in addition to the data. Is that doable or would that be a whole additional project?

    Thanks for any advice. By the way I’m running this on a Raspberry Pi 3

    • Adrian Rosebrock August 9, 2018 at 2:35 pm #

      You can use “cv2.imshow” and have the result streamed to your machine connected to the Pi by enabling X11 forwarding:

      $ ssh -X pi@your_ip_address

Leave a Reply