Before we dive into this post, let’s take a second and talk about Oscar, a dedicated PyImageSearch reader.
He is just getting started in computer vision — and he’s taken the best possible route to mastering the subject: creating your own projects and solving them.
Oscar picked up a copy of my book, Practical Python and OpenCV, read through it in a single weekend, and then decided to create a project for himself and solve it.
He is absolute, 100% proof that you can start with very limited (or no) computer vision knowledge and in single weekend learn the skills necessary to build and solve computer vision projects.
Today we are going to discuss Oscar’s first project. He emailed me the image at the top of this post and asked for a little guidance on how to:
- Recognize only figures with the black background
- If 2 or more figures overlap they all should be treated as one object
- Detect and draw contours around each of the black shapes
- Count the number of black shapes
Honestly, this is a great first project.
I replied to Oscar and gave him a few tips on how to solve the problem. I told him what chapters in Practical Python and OpenCV to read, and I suggested a high-level approach to solve the problem.
A day later I found that I had a reply in my inbox — Oscar had solved the problem!
We continued to tweak his code a little more and improve the results.
And today I am going to show the final product.
So let’s give a big hand to Oscar. He’s done a great job. And he’s proof that you can learn computer vision in just a single weekend using Practical Python and OpenCV.
Looking for the source code to this post?
Jump right to the downloads section.
OpenCV and Python versions:
This example will run on Python 2.7 and OpenCV 2.4.X.
Finding Shapes in Images using Python and OpenCV
Let’s go ahead and get started.
Open up a new file, name it find_shapes.py , and we’ll get to work.
# import the necessary packages
import numpy as np
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", help = "path to the image file")
args = vars(ap.parse_args())
# load the image
image = cv2.imread(args["image"])
The first thing we’ll do is import the Python packages we’ll need. We’ll use NumPy for numerical processing, argparse to parse our command line arguments, and cv2 for our OpenCV bindings.
Lines 7-9 handle parsing the command line arguments. We need just a single switch, --image , which is the path to our image on disk.
Finally, we’ll load our image off disk on Line 12.
Now that we have loaded our image off disk, we can move on to detecting the black shapes in the image.
Our Goal: Detect the black shapes in the image.
Detecting these black shapes is actually very easy using the cv2.inRange function:
# find all the 'black' shapes in the image
lower = np.array([0, 0, 0])
upper = np.array([15, 15, 15])
shapeMask = cv2.inRange(image, lower, upper)
On Lines 15-16 we define a lower and an upper boundary points in the BGR color space. Remember, OpenCV stores images in BGR order rather than RGB.
Our lower boundary consists of pure black, specifying zeros for each of the Blue, Green, and Red channels, respectively.
And our upper boundary consists of a very dark shade of gray, this time specifying 15 for each of the channels.
We then find all pixels within the lower and upper range on Line 17.
Our shapeMask now looks something like this:
As you can see, all the black shapes in the original image are now white on a black background.
The next step is to detect the contours in the shapeMask . This is also very straightforward:
# find the contours in the mask
(cnts, _) = cv2.findContours(shapeMask.copy(), cv2.RETR_EXTERNAL,
print "I found %d black shapes" % (len(cnts))
# loop over the contours
for c in cnts:
# draw the contour and show it
cv2.drawContours(image, [c], -1, (0, 255, 0), 2)
We make a call to cv2.findContours on Line 20, instructing it to find all the external contours of the shapes (i.e. the boundaries).
From there, we print to the console the number of contours we found.
Then, we start looping over each of the individual contours on Line 26 and draw the outline of the shapes onto the original image on Line 28.
And that’s all there is to it!
To execute the script, fire up a shell, and issue the following command:
$ python find_shapes.py --image shapes.png
The output on your console should look like this:
(cv)annalee:finding-shapes-in-images adrianrosebrock$ python find_shapes.py --image shapes.png
I found 6 black shapes
If all goes well, you can now cycle through the black shapes, drawing a green outline around each of them:
As you can see, we have clearly found the black shapes in the image!
And all of this was under 30 lines of code, most of which is imports, comments, and parsing command line arguments.
In this post we discussed how to find shapes in images using the cv2.inRange and cv2.findContours functions.
This post was largely inspired by Oscar, a dedicated PyImageSearch reader who wrote in and asked how to solve this problem.
You see, Oscar picked up a copy of Practical Python and OpenCV, read through it, and then decided to develop projects on his own to sharpen and hone his skills.
This is by far the best method to learn — and I highly recommend it to anyone else who is interested in computer vision.
Anyway, if you want to be like Oscar and learn the secrets of computer vision (in a single weekend), just click here and pickup a copy of Practical Python and OpenCV. It worked for Oscar and I know it will work for you too!
Thanks again Oscar! And congrats on your first successful OpenCV project!