OpenCV Tutorial: A Guide to Learn OpenCV

Whether you’re interested in learning how to apply facial recognition to video streams, building a complete deep learning pipeline for image classification, or simply want to tinker with your Raspberry Pi and add image recognition to a hobby project, you’ll need to learn OpenCV somewhere along the way.

The truth is that learning OpenCV used to be quite challenging. The documentation was hard to navigate. The tutorials were hard to follow and incomplete. And even some of the books were a bit tedious to work through.

The good news is learning OpenCV isn’t as hard as it used to be. And in fact, I’ll go as far as to say studying OpenCV has become significantly easier.

And to prove it to you (and help you learn OpenCV), I’ve put together this complete guide to learning the fundamentals of the OpenCV library using the Python programming language.

Let’s go ahead and get started learning the basics of OpenCV and image processing. By the end of today’s blog post, you’ll understand the fundamentals of OpenCV.

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

OpenCV Tutorial: A Guide to Learn OpenCV

This OpenCV tutorial is for beginners just getting started learning the basics. Inside this guide, you’ll learn basic image processing operations using the OpenCV library using Python.

And by the end of the tutorial you’ll be putting together a complete project to count basic objects in images using contours.

While this tutorial is aimed at beginners just getting started with image processing and the OpenCV library, I encourage you to give it a read even if you have a bit of experience.

A quick refresher in OpenCV basics will help you with your own projects as well.

Installing OpenCV and imutils on your system

The first step today is to install OpenCV on your system (if you haven’t already).

I maintain an OpenCV Install Tutorials page which contains links to previous OpenCV installation guides for Ubuntu, macOS, and Raspberry Pi.

You should visit that page and find + follow the appropriate guide for your system.

Once your fresh OpenCV development environment is set up, install the imutils package via pip. I have created and maintained imutils  (source on GitHub) for the image processing community and it is used heavily on my blog. You should install imutils  in the same environment you installed OpenCV into — you’ll need it to work through this blog post as it will facilitate basic image processing operations:

Note: If you are using Python virtual environments don’t forget to use the workon  command to enter your environment before installing imutils !

OpenCV Project Structure

Before going too far down the rabbit hole, be sure to grab the code + images from the “Downloads” section of today’s blog post.

From there, navigate to where you downloaded the .zip in your terminal ( cd ). And then we can unzip  the archive, change working directories ( cd ) into the project folder, and analyze the project structure via tree :

In this tutorial we’ll be creating two Python scripts to help you learn OpenCV basics:

  1. Our first script,   will cover basic image processing operations using an image from the movie, Jurassic Parkjp.png ).
  2. From there,  will show you how to use these image processing building blocks to create an OpenCV application to count the number of objects in a Tetris image ( tetris_blocks.png ).

Loading and displaying an image

Figure 1: Learning OpenCV basics with Python begins with loading and displaying an image — a simple process that requires only a few lines of code.

Let’s begin by opening up  in your favorite text editor or IDE:

On Lines 2 and 3 we import both imutils  and cv2 . The cv2  package is OpenCV and despite the 2 embedded, it can actually be OpenCV 3 (or possibly OpenCV 4 which may be released later in 2018). The imutils  package is my series of convenience functions.

Now that we have the required software at our fingertips via imports, let’s load an image from disk into memory.

To load our Jurassic Park image (from one of my favorite movies), we call cv2.imread("jp.png") . As you can see on Line 8, we assign the result to image . Our image  is actually just a NumPy array.

Later in this script, we’ll need the height and width. So on Line 9, I call image.shape  to extract the height, width, and depth.

It may seem confusing that the height comes before the width, but think of it this way:

  • We describe matrices by # of rows x # of columns
  • The number of rows is our height
  • And the number of columns is our width

Therefore, the dimensions of an image represented as a NumPy array are actually represented as (height, width, depth).

Depth is the number of channels — in our case this is three since we’re working with 3 color channels: Blue, Green, and Red.

The print command shown on Line 10 will output the values to the terminal:

To display the image on the screen using OpenCV we employ cv2.imshow("Image", image)  on Line 14. The subsequent line waits for a keypress (Line 15). This is important otherwise our image would display and disappear faster than we’d even see the image.

Note: You need to actually click the active window opened by OpenCV and press a key on your keyboard to advance the script. OpenCV cannot monitor your terminal for input so if you a press a key in the terminal OpenCV will not notice. Again, you will need to click the active OpenCV window on your screen and press a key on your keyboard.

Accessing individual pixels

Figure 2: Top: grayscale gradient where brighter pixels are closer to 255 and darker pixels are closer to 0. Bottom: RGB venn diagram where brighter pixels are closer to the center.

First, you may ask:

What is a pixel?

All images consist of pixels which are the raw building blocks of images. Images are made of pixels in a grid. A 640 x 480 image has 640 columns (the width) and 480 rows (the height). There are 640 * 480 = 307200  pixels in an image with those dimensions.

Each pixel in a grayscale image has a value representing the shade of gray. In OpenCV, there are 256 shades of gray — from 0 to 255. So a grayscale image would have a grayscale value associated with each pixel.

Pixels in a color image have additional information. There are several color spaces that you’ll soon become familiar with as you learn about image processing. For simplicity let’s only consider the RGB color space.

In OpenCV color images in the RGB (Red, Green, Blue) color space have a 3-tuple associated with each pixel: (B, G, R) .

Notice the ordering is BGR rather than RGB. This is because when OpenCV was first being developed many years ago the standard was BGR ordering. Over the years, the standard has now become RGB but OpenCV still maintains this “legacy” BGR ordering to ensure no existing code breaks.

Each value in the BGR 3-tuple has a range of [0, 255] . How many color possibilities are there for each pixel in an RGB image in OpenCV? That’s easy: 256 * 256 * 256 = 16777216 .

Now that we know exactly what a pixel is, let’s see how to retrieve the value of an individual pixel in the image:

As shown previously, our image dimensions are width=600, height=322, depth=3 . We can access individual pixel values in the array by specifying the coordinates so long as they are within the max width and height.

The code, image[100, 50] , yields a 3-tuple of BGR values from the pixel located at x=50  and y=100 (again, keep in mind that the height is the number of rows and the width is the number of columns — take a second now to convince yourself this is true). As stated above, OpenCV stores images in BGR ordering (unlike Matplotlib, for example). Check out how simple it is to extract the color channel values for the pixel on Line 19.

The resulting pixel value is shown on the terminal here:

Array slicing and cropping

Extracting “regions of interest” (ROIs) is an important skill for image processing.

Say, for example, you’re working on recognizing faces in a movie. First, you’d run a face detection algorithm to find the coordinates of faces in all the frames you’re working with. Then you’d want to extract the face ROIs and either save them or process them. Locating all frames containing Dr. Ian Malcolm in Jurassic Park would be a great face recognition mini-project to work on.

For now, let’s just manually extract an ROI. This can be accomplished with array slicing.

Figure 3: Array slicing with OpenCV allows us to extract a region of interest (ROI) easily.

Array slicing is shown on Line 24 with the format:  image[startY:endY, startX:endX] . This code grabs an roi  which we then display on Line 25. Just like last time, we display until a key is pressed (Line 26).

As you can see in Figure 3, we’ve extracted the face of Dr. Ian Malcolm. I actually predetermined the (x, y)-coordinates using Photoshop for this example, but if you stick with me on the blog you could detect and extract face ROI’s automatically.

Resizing images

Resizing images is important for a number of reasons. First, you might want to resize a large image to fit on your screen. Image processing is also faster on smaller images because there are fewer pixels to process. In the case of deep learning, we often resize images, ignoring aspect ratio, so that the volume fits into a network which requires that an image be square and of a certain dimension.

Let’s resize our original image to 200 x 200 pixels:

On Line 29, we have resized an image ignoring aspect ratio. Figure 4 (right) shows that the image is resized but is now distorted because we didn’t take into account the aspect ratio.

Figure 4: Resizing an image with OpenCV and Python can be conducted with cv2.resize however aspect ratio is not preserved automatically.

Let’s calculate the aspect ratio of the original image and use it to resize an image so that it doesn’t appear squished and distorted:

Recall back to Line 9 of this script where we extracted the width and height of the image.

Let’s say that we want to take our 600-pixel wide image and resize it to 300 pixels wide while maintaining aspect ratio.

On Line 35 we calculate the ratio of the new width to the old width (which happens to be 0.5).

From there, we specify our dimensions of the new image, dim . We know that we want a 300-pixel wide image, but we must calculate the height using the ratio by multiplying h  by r  (the original height and our ratio respectively).

Feeding dim  (our dimensions) into the cv2.resize  function, we’ve now obtained a new image named resized  which is not distorted (Line 37).

To check our work, we display the image using the code on Line 38:

Figure 5: Resizing images while maintaining aspect ratio with OpenCV is a three-step process: (1) extract the image dimensions, (2) compute the aspect ratio, and (3) resize the image (cv2.resize) along one dimension and multiply the other dimension by the aspect ratio. See Figure 6 for an even easier method.

But can we make this process of preserving aspect ratio during resizing even easier?


Computing the aspect ratio each time we want to resize an image is a bit tedious, so I wrapped the code in a function within imutils .

Here is how you may use  imutils.resize :

In a single line of code, we’ve preserved aspect ratio and resized the image.

Simple right?

All you need to provide is your target width  or target height  as a keyword argument (Line 43).

Here’s the result:

Figure 6: If you’d like to maintain aspect ratio while resizing images with OpenCV and Python, simply use imutils.resize. Now your image won’t risk being “squished” as in Figure 4.

Rotating an image

Let’s rotate our Jurassic Park image for our next example:

Rotating an image about the center point requires that we first calculate the center (x, y)-coordinates of the image (Line 50).

Note: We use //  to perform integer math (i.e., no floating point values).

From there we calculate a rotation matrix, M  (Line 51). The -45  means that we’ll rotate the image 45 degrees clockwise. Recall from your middle/high school geometry class about the unit circle and you’ll be able to remind yourself that positive angles are counterclockwise and negative angles are clockwise.

From there we warp the image using the matrix (effectively rotating it) on Line 52.

The rotated image is displayed to the screen on Line 52 and is shown in Figure 7:

Figure 7: Rotating an image with OpenCV about the center point requires three steps: (1) compute the center point using the image width and height, (2) compute a rotation matrix with cv2.getRotationMatrix2D, and (3) use the rotation matrix to warp the image with cv2.warpAffine.

Now let’s perform the same operation in just a single line of code using imutils :

Since I don’t have to rotate image as much as resizing them (comparatively) I find the rotation process harder to remember. Therefore, I created a function in imutils  to handle it for us. In a single line of code, I can accomplish rotating the image 45 degrees clockwise (Line 57) as in Figure 8:

Figure 8: With imutils.rotate, we can rotate an image with OpenCV and Python conveniently with a single line of code.

At this point you have to be thinking:

Why in the world is the image clipped?

The thing is, OpenCV doesn’t care if our image is clipped and out of view after the rotation. I find this to be quite bothersome, so here’s my imutils  version which will keep the entire image in view. I call it rotate_bound :

There’s a lot going on behind the scenes of rotate_bound . If you’re interested in how the method on Line 64 works, be sure to check out this blog post.

The result is shown in Figure 9:

Figure 9: The rotate_bound function of imutils will prevent OpenCV from clipping the image during a rotation. See this blog post to learn how it works!

Perfect! The entire image is in the frame and it is correctly rotated 45 degrees clockwise.

Smoothing an image

In many image processing pipelines, we must blur an image to reduce high-frequency noise, making it easier for our algorithms to detect and understand the actual contents of the image rather than just noise that will “confuse” our algorithms. Blurring an image is very easy in OpenCV and there are a number of ways to accomplish it.

Figure 10: This image has undergone a Gaussian blur with an 11 x 11 kernel using OpenCV. Blurring is an important step of many image processing pipelines to reduce high-frequency noise.

I often use the GaussianBlur  function:

On Line 70 we perform a Gaussian Blur with an 11 x 11 kernel the result of which is shown in Figure 10.

Larger kernels would yield a more blurry image. Smaller kernels will create less blurry images. To read more about kernels, refer to this blog post or the PyImageSearch Gurus course.

Drawing on an image

In this section, we’re going to draw a rectangle, circle, and line on an input image. We’ll also overlay text on an image as well.

Before we move on with drawing on an image with OpenCV, take note that drawing operations on images are performed in-place. Therefore at the beginning of each code block, we make a copy of the original image storing the copy as output . We then proceed to draw on the image called output in-place so we do not destroy our original image.

Let’s draw a rectangle around Ian Malcolm’s face:

First, we make a copy of the image on Line 75 for reasons just explained.

Then we proceed to draw the rectangle.

Drawing rectangles in OpenCV couldn’t be any easier. Using pre-calculated coordinates, I’ve supplied the following parameters to the cv2.rectangle  function on Line 76:

  • img : The destination image to draw upon. We’re drawing on output .
  • pt1 : Our starting pixel coordinate which is the top-left. In our case, the top-left is (320, 60) .
  • pt2 : The ending pixel — bottom-right. The bottom-right pixel is located at  (420, 160) .
  • color : BGR tuple. To represent red, I’ve supplied (0 , 0, 255) .
  • thickness : Line thickness (a negative value will make a solid rectangle). I’ve supplied a thickness of 2 .

Since we are using OpenCV’s functions rather than NumPy operations we can supply our coordinates in (x, y) order rather than (y, x) since we are not manipulating or accessing the NumPy array directly — OpenCV is taking care of that for us.

Here’s our result in Figure 11:

Figure 11: Drawing shapes with OpenCV and Python is an easy skill to pick up. In this image, I’ve drawn a red box using cv2.rectangle. I pre-determined the coordinates around the face for this example, but you could use a face detection method to automatically find the face coordinates.

And now let’s place a solid blue circle in front of Dr. Ellie Sattler’s face:

To draw a circle, you need to supply following parameters to :

  • img : The output image.
  • center : Our circle’s center coordinate. I supplied (300, 150)  which is right in front of Ellie’s eyes.
  • radius : The circle radius in pixels. I provided a value of 20  pixels.
  • color : Circle color. This time I went with blue as is denoted by 255 in the B and 0s in the G + R components of the BGR tuple, (255, 0, 0) .
  • thickness : The line thickness. Since I supplied a negative value ( -1 ), the circle is solid/filled in.

Here’s the result in Figure 12:

Figure 12: OpenCV’s method allows you to draw circles anywhere on an image. I’ve drawn a solid circle for this example as is denoted by the -1 line thickness parameter (positive values will make a circular outline with variable line thickness).

It looks like Ellie is more interested in the dinosaurs than my big blue dot, so let’s move on!

Next, we’ll draw a red line. This line goes through Ellie’s head, past her eye, and to Ian’s hand.

If you look carefully at the method parameters and compare them to that of the rectangle, you’ll notice that they are identical:

Just as in a rectangle, we supply two points, a color, and a line thickness. OpenCV’s backend does the rest.

Figure 13 shows the result of Line 89 from the code block:

Figure 13: Similar to drawing rectangles and circles, drawing a line in OpenCV using cv2.line only requires a starting point, ending point, color, and thickness.

Oftentimes you’ll find that you want to overlay text on an image for display purposes. If you’re working on face recognition you’ll likely want to draw the person’s name above their face. Or if you advance in your computer vision career you may build an image classifier or object detector. In these cases, you’ll find that you want to draw text containing the class name and probability.

Let’s see how OpenCV’s putText function works:

The putText  function of OpenCV is responsible for drawing text on an image. Let’s take a look at the required parameters:

  • img : The output image.
  • text : The string of text we’d like to write/draw on the image.
  • pt : The starting point for the text.
  • font : I often use the cv2.FONT_HERSHEY_SIMPLEX . The available fonts are listed here.
  • scale : Font size multiplier.
  • color : Text color.
  • thickness : The thickness of the stroke in pixels.

The code on Lines 95 and 96 will draw the text, “OpenCV + Jurassic Park!!!” in green on our output  image in Figure 14:

Figure 14: Oftentimes, you’ll find that you want to display text on an image for visualization purposes. Using the cv2.putText code shown above you can practice overlaying text on an image with different colors, fonts, sizes, and/or locations.

Running the first OpenCV tutorial Python script

In my blog posts, I generally provide a section detailing how you can run the code on your computer. At this point in the blog post, I make the following assumptions:

  1. You have downloaded the code from the “Downloads” section of this blog post.
  2. You have unzipped the files.
  3. You have installed OpenCV and the imutils library on your system.

To execute our first script, open a terminal or command window and navigate to the files or extract them if necessary.

From there, enter the following command:

The command is everything after the bash prompt $  character. Just type python  in your terminal and then the first image will appear.

To cycle through each step that we just learned, make sure an image window is active, and press any key.

Our first couple code blocks above told Python to print information in the terminal. If your terminal is visible, you’ll see the terminal output (Lines 2 and 3) shown.

I’ve also included a GIF animation demonstrating all the image processing steps we took sequentially, one right after the other:

Figure 15: Output animation displaying the OpenCV fundamentals we learned from this first example Python script.

Counting objects

Now we’re going to shift gears and work on the second script included in the “Downloads” associated with this blog post.

In the next few sections we’ll learn how to use create a simple Python + OpenCV script to count the number of Tetris blocks in the following image:

Figure 16: If you’ve ever played Tetris (who hasn’t?), you’ll recognize these familiar shapes. In the 2nd half of this OpenCV fundamentals tutorial, we’re going to find and count the shape contours.

Along the way we’ll be:

  • Learning how to convert images to grayscale with OpenCV
  • Performing edge detection
  • Thresholding a grayscale image
  • Finding, counting, and drawing contours
  • Conducting erosion and dilation
  • Masking an image

Go ahead and close the first script you downloaded and open up  to get started with the second example:

On Lines 2-4 we import our packages. This is necessary at the start of each Python script. For this second script, I’ve imported argparse  — a command line arguments parsing package which comes with all installations of Python.

Take a quick glance at Lines 7-10. These lines allow us to provide additional information to our program at runtime from within the terminal. Command line arguments are used heavily on the PyImageSearch blog and in all other computer science fields as well.

I encourage you to read about them on this post: Python, argparse, and command line arguments.

We have one required command line argument --image  , as is defined on Lines 8 and 9.

We’ll learn how to run the script with the required command line argument down below. For now, just know that wherever you encounter   args["image"]  in the script, we’re referring to the path to the input image.

Converting an image to grayscale

We load the image into memory on Line 14. The parameter to the cv2.imread  function is our path contained in the args  dictionary referenced with the "image"  key,  args["image"] .

From there, we display the image until we encounter our first keypress (Lines 15 and 16).

We’re going to be thresholding and detecting edges in the image shortly. Therefore we convert the image to grayscale on Line 19 by calling cv2.cvtColor  and providing the image  and cv2.COLOR_BGR2GRAY  flag.

Again we display the image and wait for a keypress (Lines 20 and 21).

The result of our conversion to grayscale is shown in Figure 17 (bottom).

Figure 17: (top) Our Tetris image. (bottom) We’ve converted the image to grayscale — a step that comes before thresholding.

Edge detection

Edge detection is useful for finding boundaries of objects in an image — it is effective for segmentation purposes.

Let’s perform edge detection to see how the process works:

Using the popular Canny algorithm (developed by John F. Canny in 1986), we can find the edges in the image.

We provide three parameters to the cv2.Canny  function:

  • img : The gray  image.
  • minVal : A minimum threshold, in our case 30 .
  • maxVal : The maximum threshold which is 150  in our example.
  • aperture_size : The Sobel kernel size. By default this value is 3  and hence is not shown on Line 25.

Different values for the minimum and maximum thresholds will return different edge maps.

In Figure 18 below, notice how edges of Tetris blocks themselves are revealed along with sub-blocks that make up the Tetris block:

Figure 18: To conduct edge detection with OpenCV, we make use of the Canny algorithm.


Image thresholding is an important intermediary step for image processing pipelines. Thresholding can help us to remove lighter or darker regions and contours of images.

I highly encourage you to experiment with thresholding. I tuned the following code to work for our example by trial and error (as well as experience):

In a single line (Line 32) we are:

  • Grabbing all pixels in the gray  image greater than 225 and setting them to 0 (black) which corresponds to the background of the image
  • Setting pixel vales less than 225 to 255 (white) which corresponds to the foreground of the image (i.e., the Tetris blocks themselves).

For more information on the cv2.threshold function, including how the thresholding flags work, be sure to refer to official OpenCV documentation.

Segmenting foreground from background with a binary image is critical to finding contours (our next step).

Figure 19: Prior to finding contours, we threshold the grayscale image. We performed a binary inverse threshold so that the foreground shapes become white while the background becomes black.

Notice in Figure 19 that the foreground objects are white and the background is black.

Detecting and drawing contours

Figure 20: We’re working towards finding contour shapes with OpenCV and Python in this OpenCV Basics tutorial.

Pictured in the Figure 20 animation, we have 6 shape contours. Let’s find and draw their outlines via code:

On Lines 38 and 39, we use cv2.findContours  to detect the contours in the image. Take note of the parameter flags but for now let’s keep things simple — our algorithm is finding all foreground (white) pixels in the thresh.copy()  image.

Line 40 is very important accounting for the fact that cv2.findContours  implementation changed between OpenCV 2.4, OpenCV 3, and OpenCV 4. This compatibility line is present on the blog wherever contours are involved.

We make a copy of the original image on Line 41 so that we can draw contours on subsequent Lines 44-49.

On Line 47 we draw each c  from the cnts  list on the image using the appropriately named cv2.drawContours . I chose purple which is represented by the tuple (240, 0, 159) .

Using what we learned earlier in this blog post, let’s overlay some text on the image:

Line 52 builds a text  string containing the number of shape contours. Counting the total number of objects in this image is as simple as checking the length of the contours list —  len(cnts) .

The result is shown in Figure 21:

Figure 21: Counting contours with OpenCV is as easy as finding them and then calling len(cnts).

Erosions and dilations

Erosions and dilations are typically used to reduce noise in binary images (a side effect of thresholding).

To reduce the size of foreground objects we can erode away pixels given a number of iterations:

On Line 59 we copy the thresh  image while naming it mask .

Then, utilizing cv2.erode , we proceed to reduce the contour sizes with 5 iterations  (Line 60).

Demonstrated in Figure 22, the masks generated from the Tetris contours are slightly smaller:

Figure 22: Using OpenCV we can erode contours, effectively making them smaller or causing them to disappear completely with sufficient iterations. This is typically useful for removing small blobs in mask image.

Similarly, we can foreground regions in the mask. To enlarge the regions, simply use cv2.dilate :

Figure 23: In an image processing pipeline if you ever have the need to connect nearby contours, you can apply dilation to the image. Shown in the figure is the result of dilating contours with five iterations, but not to the point of two contours becoming one.

Masking and bitwise operations

Masks allow us to “mask out” regions of an image we are uninterested in. We call them “masks” because they will hide regions of images we do not care about.

If we use the thresh image from Figure 18 and mask it with the original image, we’re presented with Figure 23:

Figure 24: When using the thresholded image as the mask in comparison to our original image, the colored regions reappear as the rest of the image is “masked out”. This is, of course, a simple example, but as you can imagine, masks are very powerful.

In Figure 24, the background is black now and our foreground consists of colored pixels — any pixels masked by our mask  image.

Let’s learn how to accomplish this:

The mask  is generated by copying the binary thresh  image (Line 73).

From there we bitwise AND the pixels from both images together using cv2.bitwise_and .

The result is Figure 24 above where now we’re only showing/highlighting the Tetris blocks.

Running the second OpenCV tutorial Python script

To run the second script, be sure you’re in the folder containing your downloaded source code and Python scripts. From there, we’ll open up a terminal provide the script name + command line argument:

The argument flag is --image  and the image argument itself is tetris_blocks.png  — a path to the relevant file in the directory.

There is no terminal output for this script. Again, to cycle through the images, be sure you click on an image window to make it active, from there you can press a key and it will be captured to move forward to the next waitKey(0)  in the script. When the program is finished running, your script will exit gracefully and you’ll be presented with a new bash prompt line in your terminal.

Below I have included a GIF animation of the basic OpenCV image processing steps in our example script:

Figure 25: Learning OpenCV and the basics of computer vision by counting objects via contours.

Where can I learn more?

If you’re looking to continue learning OpenCV and computer vision, be sure to take a look at my book, Practical Python and OpenCV.

Inside the book we’ll explore the OpenCV fundamentals we discussed here today in more detail.

You’ll also learn how to use these fundamentals to build actual computer vision + OpenCV applications, including:

  • Face detection in images and video
  • Handwriting recognition
  • Feature extraction and machine learning
  • Basic object tracking
  • …and more!

To learn more about Practical Python and OpenCV, and how it can help you learn OpenCV (in less than a single weekend), just click here.


In today’s blog post you learned the fundamentals of image processing and OpenCV using the Python programming language.

You are now prepared to start using these image processing operations as “building blocks” you can chain together to build an actual computer vision application — a great example of such a project is the basic object counter we created by counting contours.

I hope this tutorial helped you learn OpenCV!

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


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!

, , , , , , , ,

66 Responses to OpenCV Tutorial: A Guide to Learn OpenCV

  1. Hacklavya July 19, 2018 at 4:13 pm #

    Why don’t you switch to conda environment, that is much easy to deal with than this virtualenv and virtualenvwrapper.

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

      Yes and no. Conda has positives but it can also be restrictive. Conda has the benefit of having OpenCV ready to go, but there’s no guarantee it’s been optimized for your particular build either. I also have zero intention on forcing people to use one environment over another — use whatever you are comfortable with, but I do recommend Python virtual environments in some form, regardless if you’re using Conda or virtualenv/virtualenvwrapper.

      • Hacklavya July 20, 2018 at 11:05 am #

        Thanks Adrian

  2. masefc11 July 20, 2018 at 7:50 am #

    How to install latest pakages of opencv python like 3.4.2 which isn’t available yet with pip command.

    • Adrian Rosebrock July 21, 2018 at 7:38 am #

      You’ll want to follow one of my tutorials and compile from source.

    • Vijay Balkrishna Konduskar April 1, 2019 at 3:51 pm #

      just open command prompt with admin rights and type following command

      python -m pip install opencv-python

      • Adrian Rosebrock April 2, 2019 at 5:47 am #

        I would recommend using “opencv-contrib-python” instead. Anyone trying to install OpenCV via pip should read this post first.

  3. h3a July 20, 2018 at 12:09 pm #

    Hi Adrian!

    At the start of the post you say : “OpenCV Project Structure
    Before going too far down the rabbit hole, be sure to grab the code + images from the “Downloads” section of today’s blog post.”

    In the downloads section there is only your Resource Guide…

    Thanks for everything,

    • Adrian Rosebrock July 21, 2018 at 7:38 am #

      Directly underneath the “Downloads” header there is a form with a button that says “Download the Code!”. I think you may have missed that form.

  4. Luis M August 22, 2018 at 7:37 pm #

    This is an excellent course of OpenCV, just what I needed! Thanks!

    • Adrian Rosebrock August 24, 2018 at 8:47 am #

      Thanks Luis, I’m glad it helped you! 🙂

  5. Parth Agarwal September 13, 2018 at 4:35 pm #

    Great tutorial Adrian, this got me started with OpenCV. I think there’s a mistake in the angle mentioned for bounded rotation in the Jurassic Park:

    # OpenCV doesn’t “care” if our rotated image is clipped after rotation
    # so we can instead use another imutils convenience function to help
    # us out
    rotated = imutils.rotate_bound(image, 45)
    cv2.imshow(“Imutils Bound Rotation”, rotated)

    Here the angle should be -45 instead of +45 if I am correct.

    • Adrian Rosebrock September 14, 2018 at 9:26 am #

      The rotate_bound function actually takes the negative of the angle so the code is correct.

      • Parth Agarwal September 15, 2018 at 1:15 am #

        OK, got it, thanks.

  6. shiva kumar October 6, 2018 at 7:25 pm #

    When i ran i got the below error. Pl help.

    usage: [-h] -i IMAGE error: the following arguments are required: -i/–image
    An exception has occurred, use %tb to see the full traceback.

    • Adrian Rosebrock October 8, 2018 at 9:40 am #

      It sounds like you are trying to execute the script within IPython. Instead, execute it via command line argument and supply the command line arguments.

  7. shincheng October 18, 2018 at 4:28 am #

    A 640 x 480 image has 640 rows and 480 columns.
    Should it be 480 rows and 640 columns???

    • Adrian Rosebrock October 20, 2018 at 7:43 am #

      Yes, thank you for catching this typo! I have updated the blog post 🙂 Thanks again!

  8. Vishal October 21, 2018 at 2:14 pm #

    In the Masking and bitwise operations sections why do we use image twice in

    cv2.bitwise_and(image,image,mask) ?

    What is the differece between using the same image twice and suing two diff images?

    • Adrian Rosebrock October 22, 2018 at 7:56 am #

      We need two inputs to perform bitwise ANDs and ORs. Since our goal is applying the mask to the image we supply the input image twice as both inputs so the output will always be TRUE expect for where the mask is applied.

  9. Phong October 25, 2018 at 1:49 pm #

    I can’t execute your code to the your images. I coppied all the code you write but the images stay the same before processed. Is there any problems like outdated OpenCV lib version or something else?

    • Adrian Rosebrock October 29, 2018 at 1:51 pm #

      I don’t suggest copying and pasting. Make sure you use the “Downloads” section of the blog post to download the code and example images. From there you can run the script. My guess is that you accidentally introduced an error in the code or logic during the copy and paste.

  10. Joel November 7, 2018 at 1:32 am #

    cnts = cnts[0] if imutils.is_cv2() else cnts[1]

    How the array stores more than one element? Like cnts[0], cnts[1], cnts[2]…..

  11. Agribot December 14, 2018 at 12:03 pm #

    Thanks for posting this helpful article, code and details of how-to.

    I’m a newbe to the area of python + computer vision + opencv, so I bought one of your bundles listed at;-

    and I found the information and examples very helpful as i struggled to get my head around some topics. It was very useful to have the bundle available,

    Better than searching around to pull together pieces of information and / or python examples
    The bundle gave it all together – at a good level of clarity.

    keep up the good work.

    • Adrian Rosebrock December 18, 2018 at 9:22 am #

      Thank you so much for picking up a copy, Agribot! I’m so happy to hear you found the book helpful. Enjoy it!

  12. Priyanka Saggu January 21, 2019 at 12:36 pm #

    Hi Adrian,

    So, grateful for all your useful and rich content. I want to know:
    1. what changes are meant to be made if the background is any color other than white?
    2. How to get rid of the coinciding shadows of different objects. As 2 objects with intersecting shadows are counted as 1?

    • Adrian Rosebrock January 22, 2019 at 9:17 am #

      1. Which image are you referring to?

      2. Use the Watershed algorithm.

      • Priyanka Saggu January 22, 2019 at 12:26 pm #

        I meant to ask, that the images which are taken as an example while counting the no. of objects, has a white background. So, what if an image has a background color other than white. What changes should be made in that case?

        • Adrian Rosebrock January 25, 2019 at 7:47 am #

          You could either:

          1. Invert the image via cv2.bitwise_not
          2. Swap the flag to cv2.THRESH_BINARY when thresholding

          For more information on the basics of computer vision and image processing I would definitely suggest referring to Practical Python and OpenCV which covers them in detail.

  13. Tehreem Qasim February 14, 2019 at 8:08 am #

    Dear Adrian,
    I get the following error,

    module ‘imutils’ has no attribute ‘grabcontours’.

    Some one else has raised the same concern here:

    • Adrian Rosebrock February 14, 2019 at 12:44 pm #

      You simply need to upgrade your imutils install:

      $ pip install --upgrade imutils

  14. Mohammed Patel February 17, 2019 at 7:03 pm #

    Hi! I have learnt a lot from this tutorial! I was wondering how I could count objects for images with slightly shaded backgrounds with shadows and without knowing the highest shadow value to threshold from. Please can you help me with this?

  15. Aishvarya Kumar Jain March 6, 2019 at 4:42 am #

    Hi…thanks for building up this wonderful course. I am bit curious about the function rotate_bounded() and rotate().

    Why there is non uniformity between the function when passing the angle of rotation?
    rotate() consider negative angles as clockwise but rotate_bounded() consider positive angles as clockwise.

    • Adrian Rosebrock March 8, 2019 at 5:46 am #

      It was simply how I implemented the “rotate_bounded” function, that’s all.

  16. Devansh Shah March 23, 2019 at 2:23 am #

    Hey! I have downloaded the tutorial file and when I run the code “”, everything seems to be working fine, i.e the size of the image outputted is right, the pixel value for the specific pixel chosen is right according to the blog, however: all the images in my display window just appear white. I am using Mac OSx btw, thanks

    • Adrian Rosebrock March 27, 2019 at 9:23 am #

      That’s definitely very strange. How did you install OpenCV on your machine? Did you follow one of my tutorials?

      • Ramisha April 20, 2019 at 9:38 am #

        Thank you for your tutorial.

        What is the math behind converting a grayscale image to color image?

        • Adrian Rosebrock April 25, 2019 at 9:19 am #

          Hey Ramisha — I noticed you posted a few comments across various posts on the PyImageSearch blog. It’s great that you’re enjoying the tutorials but I would recommend you read through Practical Python and OpenCV — that book will teach you the basics.

  17. Ettienne Gerwel June 3, 2019 at 10:00 am #

    Great tutorial!!!!! Well done!

    • Adrian Rosebrock June 6, 2019 at 7:58 am #

      Thanks so much, I’m glad you liked it!

  18. sonny osunkwo June 18, 2019 at 11:12 am #

    why is this course not designed for windows OS?

    • Adrian Rosebrock June 19, 2019 at 1:46 pm #

      The code will work on Windows, I just do not support Windows on the PyImageSearch blog. See this link.

  19. Zlatko June 22, 2019 at 12:45 pm #

    Hello again!

    I asked for help yesterday, but I need not it any more. I have found mistake: in code I received there was statement:

    image = cv2.imread(“tetris_blocks”)

    instead of:

    image = cv2.imread(“tetris_blocks.png”)

    Regardless, thank you very much for your efforts and good will in helping me to learn basics of Computer vision and image processing!

    • Adrian Rosebrock June 26, 2019 at 1:37 pm #

      Yes, always make sure to double and triple-check those files paths 🙂

  20. Henar Domínguez Elvira July 3, 2019 at 6:44 am #

    Hi! First of all thank you very much for all the tutorials, I am learning a lot!

    While doing the last part of this tutorial a found a problem that I do not know how to solve 🙁
    trying the code line “cnts = imutils.grab_contours(cnts)” it gives me an error saying “AttributeError: module ‘imutils’ has no attribute ‘grab_contours’ ”

    Do you know what could be the cause of the error?

    Thank you very much again!

    • Adrian Rosebrock July 4, 2019 at 10:16 am #

      You need to upgrade your imutils install (you’re using an old version):

      $ pip install --upgrade imutils

  21. Etienne Bauscher July 29, 2019 at 9:49 am #

    Thank you Adrian for investing in people. This is valuable information you are sharing here…

    • Adrian Rosebrock August 7, 2019 at 12:59 pm #

      Thanks Etienne 🙂

  22. Abhinav visen July 30, 2019 at 7:27 am #

    Hey Adrian,
    Awesome blog by the way.
    A small doubt.
    Theoretically according to this threshold command :-
    cv2.threshold(gray, 225, 255, cv2.THRESH_BINARY_INV)[1]
    The essential output should be a region in figure 17 (below) with darker regions of the Tetris and lighter region in the white space.But essential our code gives the output in figure 19.Also can you explain the value [1] representing the function and what other image in the list does it returns.

    • Adrian Rosebrock August 7, 2019 at 12:53 pm #

      Hey Abhinav — if you need further help studying OpenCV I would recommend you read Practical Python and OpenCV where both your questions are answered.

  23. dibora August 12, 2019 at 7:31 am #

    Hi, i am just starting with computer vision and i wanted to get to the handy staff .Do you think i need to go deep in understanding the algorithms and mathematical aspects first?

    • Adrian Rosebrock August 16, 2019 at 5:43 am #

      No, at least not yet. Start with a practical education first. 1000s of PyImageSearch readers have used Practical Python and OpenCV to get their start in computer vision and OpenCV. I would definitely suggest starting there.

  24. gabbyyy October 21, 2019 at 12:02 pm #

    Hi newbie here i have a problem in finding the contours and it’s so hard when i use different images for example i use image of cars via satellite and i want to count how many cars in there.

    • Adrian Rosebrock October 25, 2019 at 10:22 am #

      Contours will not work well for detecting robust objects, especially if you cannot easily segment them. Try utilizing object detection algorithms.

      • gabbyyy October 26, 2019 at 7:02 am #

        Okay thanks 🙂

        Can i buy the practical python and opencv + case studies hardcopy without bundle?

        • Adrian Rosebrock November 7, 2019 at 10:41 am #

          The hardcopy edition of Practical Python and OpenCV + Case Studies is only offered in the Hardcopy Bundle. As a self-published author, it’s not cheap to have copies of the books printed — I also manually fulfill all orders myself. In order to make the hardcopies feasible, I need to charge a little extra and provide a ton of added value through the virtual machine and video tutorials.

  25. Nikesh Prasad October 24, 2019 at 4:02 am #

    Hi Adrian,

    Thanks for this starter on OpenCV, both the tutorials were great.

    I have 2 questions:

    1. Why do we make a copy of image for erosion and dilation when the function does not erodes or dilates the image inplace?

    2. How the bitwise_and is done in OpenCV?

    Thank you.

    • Adrian Rosebrock October 25, 2019 at 10:17 am #

      Thanks Nikesh, I’m glad you enjoyed the tutorial. If you need additional help I would recommend reading Practical Python and OpenCV to help you learn the OpenCV library.

      • Sanjay October 25, 2019 at 11:05 am #

        A nice tutorial Adrian. You did not answer Nikesh. Even I have the same question. Why do you make a copy of the image before erode or dilate ? Does the original image change.

        • Adrian Rosebrock November 7, 2019 at 10:44 am #

          Hey Sanjay — I do my best to get to every question but I receive 200+ questions per day. If I’ve already answered a reader’s specific question in one of my existing blog posts, books, or courses, I link them to it (I cannot re-explain the same answers over and over as that would not be a good use of my time or yours). In this case, both your and Nikesh’s question is answered inside Practical Python and OpenCV.

  26. cherif November 3, 2019 at 11:32 am #

    Excellent course
    This tutorial push people to like OpenCv

    Thank you very much!

    • Adrian Rosebrock November 7, 2019 at 10:24 am #

      You are welcome, I’m glad you enjoyed it! 🙂

  27. Anthony The Koala December 26, 2019 at 10:48 am #

    Under the heading “rotating an image”, I wanted code that you would actually see an image rotate.

    I wanted to animate the ‘image’ over a series of angles.

    That is I wanted to show and watch the image move. I was unable to achieve that even with a delay between successive angles.

    The result was a grey screen with ONLY the final angled image showing.

    Can you suggest a way to show and watch an ‘image’ being rotated.

    Thank you,
    Anthony of Sydney

    • Anthony The Koala January 2, 2020 at 7:47 am #

      Dear Dr Adrian,
      The very question may well be answered in your blog post titled . In that tutorial you used the contours to extract the image of the pill leaving the background black.

      I will be applying a version of the question I want answer myself by rotating the whole image without any contours. Then later I will experiment with the contours of which you have a few posts about that.

      Thank you,
      Anthony of Sydney

      • Adrian Rosebrock January 2, 2020 at 8:46 am #

        Hey Anthony, it sounds like you may have addressed your own question. Give the rotation tutorial a try.

Before you leave a comment...

Hey, Adrian here, author of the PyImageSearch blog. I'd love to hear from you, but before you submit a comment, please follow these guidelines:

  1. If you have a question, read the comments first. You should also search this page (i.e., ctrl + f) for keywords related to your question. It's likely that I have already addressed your question in the comments.
  2. If you are copying and pasting code/terminal output, please don't. Reviewing another programmers’ code is a very time consuming and tedious task, and due to the volume of emails and contact requests I receive, I simply cannot do it.
  3. Be respectful of the space. I put a lot of my own personal time into creating these free weekly tutorials. On average, each tutorial takes me 15-20 hours to put together. I love offering these guides to you and I take pride in the content I create. Therefore, I will not approve comments that include large code blocks/terminal output as it destroys the formatting of the page. Kindly be respectful of this space.
  4. Be patient. I receive 200+ comments and emails per day. Due to spam, and my desire to personally answer as many questions as I can, I hand moderate all new comments (typically once per week). I try to answer as many questions as I can, but I'm only one person. Please don't be offended if I cannot get to your question
  5. Do you need priority support? Consider purchasing one of my books and courses. I place customer questions and emails in a separate, special priority queue and answer them first. If you are a customer of mine you will receive a guaranteed response from me. If there's any time left over, I focus on the community at large and attempt to answer as many of those questions as I possibly can.

Thank you for keeping these guidelines in mind before submitting your comment.

Leave a Reply