In this tutorial, you will learn how to use OpenCV’s basic drawing functions. You will learn how to use OpenCV to draw:
You will also learn how to use OpenCV to draw on images and blank/empty arrays initialized with NumPy.
To learn how to use OpenCV’s basic drawing functions, just keep reading.
Drawing with OpenCV
In the first part of this tutorial, we will briefly review OpenCV’s drawing functions.
We will then configure our development environment and review our project directory structure.
With the review taken care of, we will move on to implement two Python scripts:
These scripts will help you understand how to perform basic drawing functions with OpenCV.
By the end of this guide, you will understand how to use OpenCV to draw lines, circles, and rectangles.
Drawing functions in OpenCV
OpenCV has a number of drawing functions you can use to draw various shapes, including polygons of irregular shapes, but the three most common OpenCV drawing functions you will see are:
cv2.line: Draws a line on image, starting at a specified (x, y)-coordinate and ending at another (x, y)-coordinate
cv2.circle: Draws a circle on an image specified by the center (x, y)-coordinate and a supplied radius
cv2.rectangle: Draws a rectangle on an image specified by the top-left corner and bottom-right corner (x, y)-coordinates
We will cover these three drawing functions today.
However, it’s worth noting that more advanced OpenCV drawing functions exist, including:
cv2.ellipse: Draws an ellipse on an image
cv2.polylines: Draws the outline of a polygon specified by a set of (x, y)-coordinates
cv2.fillPoly: Draws a polygon, but instead of drawing the outline, instead fills in the polygon
cv2.arrowedLine: Draws an arrow pointing from a starting (x, y)-coordinate to an ending (x, y)-coordinate
These OpenCV drawing functions are used less often but are still worth noting. We use them occasionally on the PyImageSearch blog.
Configuring your development environment
To follow along with this guide, you need to have the OpenCV library installed on your system.
Luckily, OpenCV is pip-installable:
$ pip install opencv-contrib-python
If you need help configuring your development environment for OpenCV, I highly recommend that you read my pip install OpenCV guide — it will have your system up and running in a matter of minutes.
Having problems configuring your development environment?
All that said, are you:
- Short on time?
- Learning on your employer’s administratively locked system?
- Wanting to skip the hassle of fighting with the command line, package managers, and virtual environments?
- Ready to run the code right now on your Windows, macOS, or Linux systems?
Then join PyImageSearch Plus today!
Gain access to Jupyter Notebooks for this tutorial and other PyImageSearch guides that are pre-configured to run on Google Colab’s ecosystem right in your web browser! No installation required.
And best of all, these Jupyter Notebooks will run on Windows, macOS, and Linux!
Let’s start by reviewing our project directory structure for our OpenCV drawing project:
$ tree . --dirsfirst . ├── adrian.png ├── basic_drawing.py └── image_drawing.py 0 directories, 3 files
We have two Python scripts to review today:
basic_drawing.py: Initializes an empty NumPy array and utilizes OpenCV to draw lines, circles, and rectangles
adrian.pngfrom disk and then draws on the image (rather than an empty/blank NumPy array canvas).
We are now ready to get started!
Implementing basic drawing functions with OpenCV
Before we draw on actual images, let’s first learn how to initialize an empty NumPy array/image and draw on it.
basic_drawing.py file in your project directory structure, and let’s get to work.
# import the necessary packages import numpy as np import cv2 # initialize our canvas as a 300x300 pixel image with 3 channels # (Red, Green, and Blue) with a black background canvas = np.zeros((300, 300, 3), dtype="uint8")
Lines 2 and 3 import the packages we will be using.
As a shortcut, we will create an alias for
np. You will see this convention utilized in all PyImageSearch tutorials that leverage NumPy (and in fact, you will commonly see this convention in the Python community as well!)
We will also import
cv2, so we can have access to the OpenCV library.
Initializing our image is handled on Line 7. We construct a NumPy array using the
np.zeros method with 300 rows and 300 columns, yielding a 300 x 300 pixel image. We also allocate space for 3 channels — one each for Red, Green, and Blue. As the name suggests, the
np.zeros method fills every element in the array with an initial value of zero.
Secondly, it’s important to draw your attention to the second argument of the
np.zeros method: the data type,
Since we represent our image as an RGB image with pixels in the range [0, 255], we must use an 8-bit unsigned integer, or
uint8. There are many other data types that we can use (common ones include 32-bit integers and 32- and 64-bit floats), but we will mainly use
uint8 for the majority of the examples in this lesson.
Now that we have our canvas initialized, we can do some drawing:
# draw a green line from the top-left corner of our canvas to the # bottom-right green = (0, 255, 0) cv2.line(canvas, (0, 0), (300, 300), green) cv2.imshow("Canvas", canvas) cv2.waitKey(0) # draw a 3 pixel thick red line from the top-right corner to the # bottom-left red = (0, 0, 255) cv2.line(canvas, (300, 0), (0, 300), red, 3) cv2.imshow("Canvas", canvas) cv2.waitKey(0)
The first thing we do on Line 11 is to define a tuple used to represent the color “green.” Then, we draw a green line from point (0, 0), the top-left corner of the image, to point (300, 300), the bottom-right corner of the image, on Line 12.
In order to draw the line, we make use of the
- The first argument to this method is the image upon which we are going to draw. In this case, it’s our
- The second argument is the starting point of the line. We choose to start our line from the top-left corner of the image, at point (0, 0) — again, remember that the Python language is zero-index.
- We also need to supply an ending point for the line (the third argument). We define our ending point to be (300, 300), the bottom-right corner of the image.
- The last argument is the color of our line (in this case, green). Lines 13 and 14 show our image and then wait for a keypress (see Figure 2).
As you can see, using the
cv2.line function is quite simple! But there is one other important argument to consider in the
cv2.line method: the thickness.
On Lines 18-21, we define the color red as a tuple (again, in BGR rather than RGB format). We then draw a red line from the top-right corner of the image to the bottom-left. The last parameter to the method controls the thickness of the line — we decide to make the thickness 3 pixels. Again, we show our image and wait for a keypress:
Drawing a line was simple enough. Now we can move on to drawing rectangles. Check out the code below for more details:
# draw a green 50x50 pixel square, starting at 10x10 and ending at 60x60 cv2.rectangle(canvas, (10, 10), (60, 60), green) cv2.imshow("Canvas", canvas) cv2.waitKey(0) # draw another rectangle, this one red with 5 pixel thickness cv2.rectangle(canvas, (50, 200), (200, 225), red, 5) cv2.imshow("Canvas", canvas) cv2.waitKey(0) # draw a final rectangle (blue and filled in ) blue = (255, 0, 0) cv2.rectangle(canvas, (200, 50), (225, 125), blue, -1) cv2.imshow("Canvas", canvas) cv2.waitKey(0)
On Line 24, we make use of the
cv2.rectangle method. The signature of this method is identical to the
cv2.line method above, but let’s explore each argument anyway:
- The first argument is the image upon which we want to draw our rectangle. We want to draw on our
canvas, so we pass it into the method.
- The second argument is the starting (x, y) position of our rectangle — here, we start our rectangle at point (10, 10).
- Then, we must provide an ending (x, y) point for the rectangle. We decide to end our rectangle at (60, 60), defining a region of 50 x 50 pixels (take a second to convince yourself that the resulting rectangle is 50 x 50).
- Finally, the last argument is the color of the rectangle we want to draw. Here, we are drawing a green rectangle.
Just as we can control a line’s thickness, we can also control the rectangle’s thickness. Line 29 provides that thickness argument. Here, we draw a red rectangle that is 5 pixels thick, starting at point (50, 200) and ending at (200, 225).
At this point, we have only drawn the outline of a rectangle. How do we draw a rectangle that is “completely filled”?
Simple. We just pass a negative value for the thickness argument.
Line 35 demonstrates how to draw a rectangle of a solid color. We draw a blue rectangle, starting at (200, 50) and ending at (225, 125). By specifying
-1 (or use the
cv2.FILLED keyword) as the thickness, our rectangle is drawn as a solid blue.
Figure 4 displays the full output of drawing our lines and rectangles:
As you can see, the output matches our code. We could draw a green line from the top-left corner to the bottom-right corner, followed by a thicker red line from the top-right corner to the bottom-left corner.
We were also able to draw a green rectangle, a slightly thicker red rectangle, and a completely filled blue rectangle.
That’s great and all — but what about circles?
How can we use OpenCV to draw circles?
Drawing circles is just as simple as drawing rectangles, but the function arguments are a little different:
# re-initialize our canvas as an empty array, then compute the # center (x, y)-coordinates of the canvas canvas = np.zeros((300, 300, 3), dtype="uint8") (centerX, centerY) = (canvas.shape // 2, canvas.shape // 2) white = (255, 255, 255) # loop over increasing radii, from 25 pixels to 150 pixels in 25 # pixel increments for r in range(0, 175, 25): # draw a white circle with the current radius size cv2.circle(canvas, (centerX, centerY), r, white) # show our work of art cv2.imshow("Canvas", canvas) cv2.waitKey(0)
On Line 41, we re-initialize our canvas to blank:
Line 42 calculates two variables:
centerY. These two variables represent the (x, y)-coordinates of the image’s center.
We calculate the center by examining the shape of our NumPy array and then dividing by two:
- The height of the image can be found in
canvas.shape(number of rows)
- The width is found in
canvas.shape(number of columns)
Finally, Line 43 defines a white pixel (i.e., the buckets for each of the Red, Green, and Blue components are “full”).
Now, let’s draw some circles!
On Line 45, we loop over several radius values, starting at
0 and ending at
150, incrementing by
25 at each step. The
range function is exclusive; therefore, we specify a stopping value of
175 rather than
To demonstrate this for yourself, open a Python shell, and execute the following code:
$ python >>> list(range(0, 175, 25)) [0, 25, 50, 75, 100, 125, 150]
Notice how the output of
range stops at
150 and does not include
Line 49 handles the actual drawing of the circle:
- The first parameter is our
canvas, the image upon which we want to draw the circle.
- We then need to supply the point around which we will draw our circle. We pass in a tuple of
(centerX, centerY)so that our circles will be centered at the center of the image.
- The third argument is the radius,
r, of the circle we wish to draw.
- Finally, we pass in the color of our circle: in this case, white.
Lines 52 and 53 then show our image and wait for a keypress:
Check out Figure 6, and you will see that we have drawn a simple bullseye! The “dot” in the very center of the image is drawn with a radius of 0. The larger circles are drawn with ever-increasing radii sizes from our
Not too bad. But what else can we do?
Let’s do some abstract drawing:
# re-initialize our canvas once again canvas = np.zeros((300, 300, 3), dtype="uint8") # let's draw 25 random circles for i in range(0, 25): # randomly generate a radius size between 5 and 200, generate a # random color, and then pick a random point on our canvas where # the circle will be drawn radius = np.random.randint(5, high=200) color = np.random.randint(0, high=256, size=(3,)).tolist() pt = np.random.randint(0, high=300, size=(2,)) # draw our random circle on the canvas cv2.circle(canvas, tuple(pt), radius, color, -1) # display our masterpiece to our screen cv2.imshow("Canvas", canvas) cv2.waitKey(0)
Our code starts on Line 59 with more looping. This time we aren’t looping over our radii’s size — we are instead going to draw
25 random circles, making use of NumPy’s random number capabilities through the
To draw a random circle, we need to generate three values: the
radius of the circle, the
color of the circle, and the
pt — the (x, y)-coordinate of where the circle will be drawn.
We generate a
radius value in the range [5, 200] on Line 63. This value controls how large our circle will be.
On Line 64, we randomly generate a
color. As we know, the color of an RGB pixel consists of three values in the range [0, 255]. To get three random integers rather than only one integer, we pass the keyword argument
size=(3,), instructing NumPy to return a list of three numbers.
Lastly, we need an (x, y)-center point to draw our circle. We will generate a point in the range [0, 300), again using NumPy’s
The drawing of our circle then takes place on Line 68, using the
pt that we randomly generated. Notice how we use a thickness of
-1, so our circles are drawn as a solid color and not just an outline.
Lines 71 and 72 shown our masterpiece, which you can see in Figure 7:
Notice how each circle has a different size, color, and placement on our canvas.
OpenCV basic drawing results
To execute our basic drawing script, be sure to access the “Downloads” section to retrieve the source code and example image.
From there, you can execute the following command:
$ python basic_drawing.py
Your output should be identical to that of the previous section.
Drawing on images with OpenCV
Up until this point, we have only explored drawing shapes on a blank canvas. But what if we want to draw shapes on an existing image?
It turns out that the code to draw shapes on an existing image is exactly the same as if we were drawing on a blank canvas generated from NumPy.
To demonstrate this, let’s look at some code:
# import the necessary packages import argparse import cv2 # construct the argument parser and parse the arguments ap = argparse.ArgumentParser() ap.add_argument("-i", "--image", type=str, default="adrian.png", help="path to the input image") args = vars(ap.parse_args())
Lines 2 and 3 import our required Python packages, while Lines 6-9 parse our command line arguments.
We only need a single argument,
--image, which is the path to our input image on disk. By default, we set the
--image command line argument to point to the
adrian.png image in our project directory structure.
# load the input image from disk image = cv2.imread(args["image"]) # draw a circle around my face, two filled in circles covering my # eyes, and a rectangle over top of my mouth cv2.circle(image, (168, 188), 90, (0, 0, 255), 2) cv2.circle(image, (150, 164), 10, (0, 0, 255), -1) cv2.circle(image, (192, 174), 10, (0, 0, 255), -1) cv2.rectangle(image, (134, 200), (186, 218), (0, 0, 255), -1) # show the output image cv2.imshow("Output", image) cv2.waitKey(0)
Line 12 loads our
--image from disk. From there, we proceed to:
- Draw an empty circle (not filled in) surrounding my head (Line 16)
- Draw two filled in circles covering my eyes (Lines 17 and 18)
- Draw a rectangle over my mouth (Line 19)
Our final output,
image, is then displayed on our screen.
OpenCV image drawing results
Let’s see how we can use OpenCV to draw on an image versus a “blank canvas” generated by NumPy.
Start by accessing the “Downloads” section of this guide to retrieve the source code and example image.
You can then execute the following command:
$ python image_drawing.py
Here, you can see that we have drawn an outlined circle surrounding my face, two filled circles over my eyes, and a filled rectangle over my mouth.
In fact, there is no difference between drawing shapes on an image loaded from disk versus a blank NumPy array. As long as our image/canvas can be represented as a NumPy array, OpenCV will draw on it just the same.
Would you like to learn how to successfully and confidently apply OpenCV to your projects?
Are you worried that configuring your development environment for Computer Vision, Deep Learning, and OpenCV will be too challenging, resulting in confusing, hard to debug error messages?
Concerned that you’ll get lost sifting through endless tutorials and video guides as you struggle to master Computer Vision?
No problem, because I’ve got you covered. PyImageSearch University is your chance to learn from me at your own pace.
You’ll find everything you need to master the basics (like we did together in this tutorial) and move on to advanced concepts.
Don’t worry about your operating system or development environment. I’ve got you covered with pre-configured Jupyter Notebooks in Google Colab for every tutorial on PyImageSearch, including Jupyter Notebooks for our new weekly tutorials as well!
Best of all, these Jupyter Notebooks will run on your machine, regardless of whether you are using Windows, macOS, or Linux! Irrespective of the operating system used, you will still be able to follow along and run the code in every lesson (all inside the convenience of your web browser).
Additionally, you can massively accelerate your progress by watching our video lessons accompanying each post. Every lesson at PyImageSearch University includes a detailed, step-by-step video guide.
You may feel that learning Computer Vision, Deep Learning, and OpenCV is too hard. Don’t worry; I’ll guide you gradually through each lecture and topic, so we build a solid foundation, and you grasp all the content.
When you think about it, PyImageSearch University is almost an unfair advantage compared to self-guided learning. You’ll learn more efficiently and master Computer Vision faster.
Oh, and did I mention you’ll also receive Certificates of Completion as you progress through each course at PyImageSearch University I’m sure PyImageSearch University will help you master OpenCV drawing and all the other computer vision skills you will need. Why not join today?
In this tutorial, you learned how to draw with OpenCV. Specifically, you learned how to use OpenCV to draw:
Lines were drawn using the
cv2.line function. We used the
cv2.circle function to draw circles and the
cv2.rectangle method to draw rectangles with OpenCV.
Other drawing functions exist in OpenCV. However, these are the functions you will use most often.
To download the source code to this post (and be notified when future tutorials are published here on PyImageSearch), simply 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!