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 SectionAn 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:
- Use Python 3 (the official
zbar
Python bindings only support Python 2.7) - 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:
$ sudo apt-get install libzbar0
Installing ZBar for macOS
Installing ZBar for macOS using brew is equally as easy (assuming you have Homebrew installed):
$ brew install zbar
Step 2 (Optional): Create a virtual environment and install OpenCV
You have two options here:
- Use an existing virtual environment that has OpenCV ready to go (skip this step and head to Step 3).
- 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
:
$ mkvirtualenv barcode -p python3
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
:
$ workon barcode $ pip install pyzbar
If you are not using a Python virtual environment you can just do:
$ pip install pyzbar
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
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:
# import the necessary packages from pyzbar import pyzbar import argparse import cv2 # construct the argument parser and parse the arguments ap = argparse.ArgumentParser() ap.add_argument("-i", "--image", required=True, help="path to input image") args = vars(ap.parse_args())
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:
# load the input image image = cv2.imread(args["image"]) # find the barcodes in the image and decode each of the barcodes barcodes = pyzbar.decode(image)
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:
# loop over the detected barcodes for barcode in barcodes: # extract the bounding box location of the barcode and draw the # bounding box surrounding the barcode on the image (x, y, w, h) = barcode.rect cv2.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2) # the barcode data is a bytes object so if we want to draw it on # our output image we need to convert it to a string first barcodeData = barcode.data.decode("utf-8") barcodeType = barcode.type # draw the barcode data and barcode type on the image text = "{} ({})".format(barcodeData, barcodeType) cv2.putText(image, text, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) # print the barcode type and data to the terminal print("[INFO] Found {} barcode: {}".format(barcodeType, barcodeData)) # show the output image cv2.imshow("Image", image) cv2.waitKey(0)
Beginning on Line 19, we loop over the detected barcodes
.
In this loop, we proceed to:
- 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. - Draw draw a bounding box rectangle on the
image
around the detectedbarcode
(Line 23). - 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. - Format and draw the
barcodeData
andbarcodeType
on the image (Lines 31-33). - 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:
$ python barcode_scanner_image.py --image barcode_example.png [INFO] Found QRCODE barcode: {"author": "Adrian", "site": "PyImageSearch"} [INFO] Found QRCODE barcode: https://hcl.pyimagesearch.com/ [INFO] Found QRCODE barcode: PyImageSearch [INFO] Found CODE128 barcode: AdrianRosebrock
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:
# import the necessary packages from imutils.video import VideoStream from pyzbar import pyzbar import argparse import datetime import imutils import time import cv2 # construct the argument parser and parse the arguments ap = argparse.ArgumentParser() ap.add_argument("-o", "--output", type=str, default="barcodes.csv", help="path to output CSV file containing barcodes") args = vars(ap.parse_args())
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:
$ pip install imutils
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:
# initialize the video stream and allow the camera sensor to warm up print("[INFO] starting video stream...") # vs = VideoStream(src=0).start() vs = VideoStream(usePiCamera=True).start() time.sleep(2.0) # open the output CSV file for writing and initialize the set of # barcodes found thus far csv = open(args["output"], "w") found = set()
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:
# loop over the frames from the video stream while True: # grab the frame from the threaded video stream and resize it to # have a maximum width of 400 pixels frame = vs.read() frame = imutils.resize(frame, width=400) # find the barcodes in the frame and decode each of the barcodes barcodes = pyzbar.decode(frame)
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
:
# loop over the detected barcodes for barcode in barcodes: # extract the bounding box location of the barcode and draw # the bounding box surrounding the barcode on the image (x, y, w, h) = barcode.rect cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2) # the barcode data is a bytes object so if we want to draw it # on our output image we need to convert it to a string first barcodeData = barcode.data.decode("utf-8") barcodeType = barcode.type # draw the barcode data and barcode type on the image text = "{} ({})".format(barcodeData, barcodeType) cv2.putText(frame, text, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) # if the barcode text is currently not in our CSV file, write # the timestamp + barcode to disk and update the set if barcodeData not in found: csv.write("{},{}\n".format(datetime.datetime.now(), barcodeData)) csv.flush() found.add(barcodeData)
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:
# show the output frame cv2.imshow("Barcode Scanner", frame) key = cv2.waitKey(1) & 0xFF # if the `q` key was pressed, break from the loop if key == ord("q"): break # close the output CSV file do a bit of cleanup print("[INFO] cleaning up...") csv.close() cv2.destroyAllWindows() vs.stop()
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
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:
- Raspberry Pi 3 (or you could use the latest 3 B+)
- Raspberry Pi camera module
- Pi Foundation 7″ touchscreen display
- Portable Charger RAVPower 22000mAh Power Bank
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):
$ python barcode_scanner_video.py [INFO] starting video stream...
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:
Then I headed to my kitchen, with Pi, screen, and battery pack in hand, and found another QR code:
Success! It even works at many angles.
Now let’s try a QR code that is contains a JSON-blob of data:
No match for my OpenCV + ZBar + Python barcode scanner project!
And finally, I tried a traditional 1-D 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.
What's next? I recommend PyImageSearch University.
30+ total classes • 39h 44m video • Last updated: 12/2021
★★★★★ 4.84 (128 Ratings) • 3,000+ Students Enrolled
I strongly believe that if you had the right teacher you could master computer vision and deep learning.
Do you think learning computer vision and deep learning has to be time-consuming, overwhelming, and complicated? Or has to involve complex mathematics and equations? Or requires a degree in computer science?
That’s not the case.
All you need to master computer vision and deep learning is for someone to explain things to you in simple, intuitive terms. And that’s exactly what I do. My mission is to change education and how complex Artificial Intelligence topics are taught.
If you're serious about learning computer vision, your next stop should be PyImageSearch University, the most comprehensive computer vision, deep learning, and OpenCV course online today. Here you’ll learn how to successfully and confidently apply computer vision to your work, research, and projects. Join me in computer vision mastery.
Inside PyImageSearch University you'll find:
- ✓ 30+ courses on essential computer vision, deep learning, and OpenCV topics
- ✓ 30+ Certificates of Completion
- ✓ 39h 44m on-demand video
- ✓ Brand new courses released every month, ensuring you can keep up with state-of-the-art techniques
- ✓ Pre-configured Jupyter Notebooks in Google Colab
- ✓ Run all code examples in your web browser — works on Windows, macOS, and Linux (no dev environment configuration required!)
- ✓ Access to centralized code repos for all 500+ tutorials on PyImageSearch
- ✓ Easy one-click downloads for code, datasets, pre-trained models, etc.
- ✓ Access on mobile, laptop, desktop, etc.
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:
- The first one to scan barcodes and QR codes in a single image
- 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!
Download the Source Code and FREE 17-page Resource Guide
Enter your email address below to get a .zip of the code and 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!