Title: OpenCV Thresholding ( cv2.threshold ) - PyImageSearch

URL Source: https://pyimagesearch.com/2021/04/28/opencv-thresholding-cv2-threshold/

Published Time: 2021-04-28T14:00:00+00:00

Markdown Content:
In this tutorial, you will learn how to use OpenCV and the `cv2.threshold` function to apply basic thresholding and Otsu thresholding.

![Image 1](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20700%20450'%3E%3C/svg%3E)

A dataset for this topic enables us to understand the effect of different thresholding techniques on different types of images. It helps us to select the appropriate thresholding method based on the image characteristics.

[Roboflow](https://roboflow.com/?ref=pyimagesearch) has free tools for each stage of the computer vision pipeline that will streamline your workflows and supercharge your productivity.

Sign up or Log in to your [Roboflow account](https://roboflow.com/?ref=pyimagesearch) to access state of the art dataset libaries and revolutionize your computer vision pipeline.

You can start by choosing your own datasets or using our [PyimageSearch’s assorted library of useful datasets](https://universe.roboflow.com/pyimagesearch?ref=pyimagesearch).

Bring data in any of 40+ formats to [Roboflow](https://roboflow.com/?ref=pyimagesearch), train using any state-of-the-art model architectures, deploy across multiple platforms (API, NVIDIA, browser, iOS, etc), and connect to applications or 3rd party tools.

Thresholding is one of the most common (and basic) segmentation techniques in [computer vision](https://pyimagesearch.com/) and it allows us to separate the _foreground_ (i.e., the objects that we are interested in) from the _background_ of the image.

Thresholding comes in three forms:

1.  We have **simple thresholding** where we manually supply parameters to segment the image — this works extremely well in controlled lighting conditions where we can ensure high contrast between the foreground and background of the image.
2.  We also have methods such as **Otsu’s thresholding** that attempt to be more dynamic and _automatically_ compute the optimal threshold value based on the input image.
3.  And finally we have **adaptive thresholding** which, instead of trying to threshold an image _globally_ using a single value, instead breaks the image down into smaller pieces, and thresholds each of these pieces _separately_ and _individually_.

We’ll be discussing simple thresholding and Otsu’s thresholding here today. Our next tutorial will cover adaptive thresholding in detail.

**To learn how to apply basic thresholding and Otsu thresholding with [OpenCV](https://opencv.org/) and the `cv2.threshold` function, _just keep reading._**

![Image 2](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20128%20128'%3E%3C/svg%3E)

#### Looking for the source code to this post?

[Jump Right To The Downloads Section](#download-the-code)

In the first part of this tutorial, we’ll discuss the concept of thresholding and how thresholding can help us segment images using OpenCV.

From there we’ll configure our development environment and review our project directory structure.

I’ll then show you two methods to threshold an image using OpenCV:

1.  **Basic thresholding** where you have to manually supply a threshold value, _T_
2.  **Otsu’s thresholding,** which _automatically_ determines the threshold value

As a computer vision practitioner it’s _critical_ that you understand how these methods work.

Let’s get started.

### **What is thresholding?**

Thresholding is the binarization of an image. In general, we seek to convert a grayscale image to a binary image, where the pixels are either _0_ or _255_.

A simple thresholding example would be selecting a threshold value _T_, and then setting all pixel intensities less than _T_ to _0_, and all pixel values greater than _T_ to _255._ In this way, we are able to create a binary representation of the image.

For example, take a look at the (grayscale) PyImageSearch logo below and its thresholded counterpart:

![Image 3](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20700%20122'%3E%3C/svg%3E)

**Figure 1:** Thresholding the PyImageSearch logo. On the _left,_ we have the original image, and on the _right,_ we have its thresholded output.

On the _left_, we have the original PyImageSearch logo that has been converted to grayscale. And on the _right_, we have the thresholded, binary representation of the PyImageSearch logo.

To construct this thresholded image I simply set my threshold value _T=225._ That way, all pixels _p_ in the logo where _p < T_ are set to _255_, and all pixels _p >= T_ are set to _0_.

By performing this thresholding I have been able to segment the PyImageSearch logo from the background.

Normally, we use thresholding to focus on objects or areas of particular interest in an image. In the examples in the lesson below, we will be using thresholding to detect coins in images, segment the pieces of the OpenCV logo, and separate license plate letters and characters from the license plate itself.

### **Configuring your development environment**

To follow 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**](https://pyimagesearch.com/2018/09/19/pip-install-opencv/) — it will have you up and running in a matter of minutes.

### **Having problems configuring your development environment?**

[![Image 4](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20650%20434'%3E%3C/svg%3E)](https://pyimagesearch.com/pyimagesearch-university/)

**Figure 2:** Having trouble configuring your dev environment? Want access to pre-configured Jupyter Notebooks running on Google Colab? Be sure to join [PyImageSearch University](https://pyimagesearch.com/pyimagesearch-university/) — you’ll be up and running with this tutorial in a matter of minutes.

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 University](https://pyimagesearch.com/pyimagesearch-university/) 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!

### **Project structure**

Before we can apply thresholding using OpenCV and the `cv2.threshold` function, we first need to review our project directory structure.

Start by accessing the **_“Downloads”_** section of this tutorial to retrieve the source code and example images.

You’ll then be presented with the following directory structure:

$ tree . --dirsfirst
.
├── images
│   ├── coins01.png
│   ├── coins02.png
│   └── opencv\_logo.png
├── otsu\_thresholding.py
└── simple\_thresholding.py

1 directory, 5 files

We have two Python scripts to review today:

1.  `simple_thresholding.py`: Demonstrates how to apply thresholding with OpenCV. Here we _manually_ set thresholds such that we can segment our foreground from the background.
2.  `otsu_thresholding.py`: Applies Otsu’s thresholding method such that the threshold parameter is set _automatically._

The benefit of Otsu’s thresholding technique is that we don’t have to fiddle with manually setting the threshold cutoff — Otsu’s method will do that automatically for us.

Inside the `images` directory are a number of demo images that we’ll apply these thresholding scripts to.

### **Implementing simple thresholding with OpenCV**

Applying simple thresholding methods requires human intervention. We must specify a threshold value _T_. All pixel intensities below _T_ are set to _255_. And all pixel intensities greater than _T_ are set to _0_.

We could also apply the inverse of this binarization by setting all pixels greater than _T_ to _255_ and all pixel intensities below _T_ to _0_.

Let’s explore some code to apply simple thresholding methods. Open the `simple_thresholding.py` file in your project directory structure and insert the following 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, required=True,
	help="path to input image")
args = vars(ap.parse\_args())

We start on **Lines 2 and 3** by importing our required Python packages. We then parse our command line arguments on **Lines 6-9**.

Only a single command line argument is needed, `--image`, which is the path to the input image we wish to apply thresholding to.

With our imports and command line arguments taken care of, let’s move on to loading our image from disk and preprocessing it:

\# load the image and display it
image = cv2.imread(args\["image"\])
cv2.imshow("Image", image)

# convert the image to grayscale and blur it slightly
gray = cv2.cvtColor(image, cv2.COLOR\_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (7, 7), 0)

**Lines 12 and 13** load our input `image` from disk and display it on our screen.

We then preprocess the image by both:

1.  Converting it to grayscale
2.  Applying a _7×7_ Gaussian blur

Applying Gaussian blurring helps remove some of the high frequency edges in the image that we are not concerned with and allow us to obtain a more “clean” segmentation.

Now, let’s go ahead and apply the actual thresholding:

\# apply basic thresholding -- the first parameter is the image
# we want to threshold, the second value is is our threshold
# check; if a pixel value is greater than our threshold (in this
# case, 200), we set it to be \*black, otherwise it is \*white\*
(T, threshInv) = cv2.threshold(blurred, 200, 255,
	cv2.THRESH\_BINARY\_INV)
cv2.imshow("Threshold Binary Inverse", threshInv)

After the image is blurred, we compute the thresholded image on **Lines 23 and 24** using the `cv2.threshold` function. This method requires four arguments.

The first is the grayscale image that we wish to threshold. We supply our `blurred` image as the first.

Then, we _manually_ supply our _T_ threshold value. We use a value of _T=200_.

Our third argument is the output value applied during thresholding. Any pixel intensity _p_ that is greater than _T_ is set to zero and any _p_ that is less than _T_ is set to the output value:

![Image 5](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20600%2076'%3E%3C/svg%3E)

**Figure 3:** The ‘if’ statement test for “cv2.THRESH\_BINARY\_INV.”

In our example, any pixel value that is greater than _200_ is set to _0_. Any value that is less than _200_ is set to _255_.

Finally, we must provide a thresholding method. We use the `cv2.THRESH_BINARY_INV` method, which indicates that pixel values _p_ less than _T_ are set to the output value (the third argument).

The `cv2.threshold` function then returns a tuple of 2 values: the first, _T_, is the threshold value. In the case of simple thresholding, this value is trivial since we manually supplied the value of _T_ in the first place. But in the case of Otsu’s thresholding where _T_ is dynamically computed for us, it’s nice to have that value. The second returned value is the thresholded image itself.

But what if we wanted to perform the _reverse_ operation, like this:

![Image 6](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20600%2076'%3E%3C/svg%3E)

**Figure 4:** The ‘if’ statement test for “cv2.THRESH\_BINARY.”

What if wanted to set all pixels _p_ greater than _T_ to the output value? Is that possible?

Of course! And there are two ways to do it. The first method is to simply take the bitwise NOT of the output threshold image. But that adds an extra line of code.

Instead, we can just supply a different flag to the `cv2.threshold` function:

\# using normal thresholding (rather than inverse thresholding)
(T, thresh) = cv2.threshold(blurred, 200, 255, cv2.THRESH\_BINARY)
cv2.imshow("Threshold Binary", thresh)

On **Line 28** we apply a different thresholding method by supplying `cv2.THRESH_BINARY`.

In most cases you are normally seeking your segmented objects to appear as _white_ on a _black_ background, hence using `cv2.THRESH_BINARY_INV`. But in the case that you want your objects to appear as _black_ on a _white_ background, be sure to supply the `cv2.THRESH_BINARY` flag.

The last task we are going to perform is to reveal the foreground objects in the image and hide everything else. Remember when we discussed [image masking](https://pyimagesearch.com/2021/01/19/image-masking-with-opencv/)? That will come in handy here:

\# visualize only the masked regions in the image
masked = cv2.bitwise\_and(image, image, mask=threshInv)
cv2.imshow("Output", masked)
cv2.waitKey(0)

On **Line 32,** we perform masking by using the `cv2.bitwise_and` function. We supply our original input image as the first two arguments, and then our inverted thresholded image as our mask. Remember, a mask only considers pixels in the original image where the mask is greater than zero.

### **Simple thresholding results**

Ready to see the results of applying basic thresholding with OpenCV?

Start by accessing the **_“Downloads”_** section of this tutorial to retrieve the source code and example images.

From there, you can execute the following command:

$ python simple\_thresholding.py --image images/coins01.png

![Image 7](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20610%20500'%3E%3C/svg%3E)

**Figure 5:** Using simple thresholding to segment coins (foreground) from the background.

On the _top-left,_ we have our original input image. And on the _top-right,_ we have the segmented image using inverse thresholding, where the coins appear as _white_ on a _black_ background.

Similarly, on the _bottom-left_ we flip the thresholding method and now the coins appear as _black_ on a _white_ background.

Finally, the _bottom-right_ applies our bitwise AND with the threshold mask and we are left with just the coins in the image (no background).

Let’s try a second image of coins:

$ python simple\_thresholding.py --image images/coins02.png

![Image 8](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20700%20442'%3E%3C/svg%3E)

**Figure 6:** Applying simple thresholding to a second image.

Once again we are able to successfully segment the foreground of the image from the background.

But take a close look and compare the output of **Figure 5** and **Figure 6**. You’ll notice that in **Figure 5** there are some coins that appear to have “holes” in them. This is because the thresholding test failed to pass — and thus we could not include that region of the coin in the output thresholded image.

However, in **Figure 6** you’ll notice that there are no holes — indicating that the segmentation is (essentially) perfect.

**_Note:_** _Realistically, this isn’t a problem. All that really matters is that we are able to obtain the contour or outline of the coins. These small gaps inside the thresholded coin mask can be filled in using_ [_morphological operations_](https://pyimagesearch.com/2021/04/28/opencv-morphological-operations/) _or contour methods._

So given that the thresholding worked perfectly in **Figure 6**, why did it not work just as perfectly in **Figure 5**?

The answer is simple: _lighting conditions._

While subtle, these two images were indeed captured under different lighting conditions. And since we have manually supplied a thresholding value, there is no guarantee that this threshold value _T_ is going to work from one image to the next in the presence of lighting changes.

One method to combat this is to simply provide a threshold value _T_ for each image you want to threshold. But that’s a serious problem, especially if we want our system to be _dynamic_ and work under various lighting conditions.

**The solution is to use methods such as Otsu’s method and adaptive thresholding to aid us in obtaining better results.**

But for the time being, let’s look at one more example where we segment the pieces of the OpenCV logo:

$ python simple\_thresholding.py --image images/opencv\_logo.png

![Image 9](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20543%20500'%3E%3C/svg%3E)

**Figure 7:** Segmenting the OpenCV logo using simple thresholding.

Notice how we have been able to segment the semicircles of the OpenCV logo along with the “OpenCV” text itself from the input image. While this may not look very interesting, being able to segment an image into pieces is an extremely valuable skill to have. This will become more apparent when we dive into contours and use them to quantify and identify different objects in an image.

But for the time being, let’s move on to some more advanced thresholding techniques where we do not have to manually supply a value of _T_.

### **Implementing Otsu thresholding with OpenCV**

In the previous section on simple thresholding we needed to _manually_ supply a threshold value of _T_. For simple images in controlled lighting conditions, it might be feasible for us to hardcode this value.

But in real-world conditions where we do not have any _a priori_ knowledge of the lighting conditions, we actually automatically compute an optimal value of _T_ using Otsu’s method.

Otsu’s method assumes that our image contains two classes of pixels: the _background_ and the _foreground_.

**Furthermore, Otsu’s method makes the assumption that the grayscale histogram of our pixel intensities of our image is** **_bi-modal_****, which simply means that the histogram is** **_two peaks._**

For example, take a look at the following image of a prescription pill and its associated grayscale histogram:

![Image 10](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20700%20375'%3E%3C/svg%3E)

**Figure 8:** A pill image and it’s associated grayscale histogram. Notice how the histogram clearly has two peaks, and is thus bi-modal.

Notice how the histogram clearly has two peaks — the first sharp peak corresponds to the uniform background color of the image, while the second peak corresponds to the pill region itself.

If the concept of histograms is a bit confusing to you right now, don’t worry — we’ll be covering them in more detail in our [image histograms blog post](https://pyimagesearch.com/2021/04/28/opencv-image-histograms-cv2-calchist/). But for the time being just understand that a histogram is simply a tabulation or a “counter” on the number of times a pixel value appears in the image.

Based on the grayscale histogram, Otsu’s method then computes an optimal threshold value _T_ such that the variance between the background and foreground peaks is minimal.

However, Otsu’s method has no _a priori_ knowledge of what pixels belong to the foreground and which pixels belong to the background — it’s simply trying to optimally separate the peaks of the histogram.

Let’s go ahead and take a look at some code to perform Otsu’s thresholding. Open the `otsu_thresholding.py` file in your project directory structure and insert the following 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, required=True,
	help="path to 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 switch here, `--image`, which is the path to our input image that we wish to apply Otsu thresholding to.

We can now load and preprocess our image:

\# load the image and display it
image = cv2.imread(args\["image"\])
cv2.imshow("Image", image)

# convert the image to grayscale and blur it slightly
gray = cv2.cvtColor(image, cv2.COLOR\_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (7, 7), 0)

**Lines 12 and 13** load our image from disk and display it on our screen.

We then apply preprocessing by converting the image to grayscale and blurring it to reduce high frequency noise.

Let’s now apply Otsu’s thresholding algorithm:

\# apply Otsu's automatic thresholding which automatically determines
# the best threshold value
(T, threshInv) = cv2.threshold(blurred, 0, 255,
	cv2.THRESH\_BINARY\_INV | cv2.THRESH\_OTSU)
cv2.imshow("Threshold", threshInv)
print("\[INFO\] otsu's thresholding value: {}".format(T))

# visualize only the masked regions in the image
masked = cv2.bitwise\_and(image, image, mask=threshInv)
cv2.imshow("Output", masked)
cv2.waitKey(0)

Applying Otsu’s method is handled on **Line 21 and 22**, again using the `cv2.threshold` function of OpenCV.

We start by passing in the (blurred) image that we want to threshold. But take a look at the second parameter — this is supposed to be our threshold value _T_.

So why are we setting it to zero?

**Remember that Otsu’s method is going to** **_automatically_** **compute the optimal value of** **_T_** **for us!** We could technically specify any value we wanted for this argument; however, I like to supply a value of `0` as a type of “don’t care” parameter.

The third argument is the output value of the threshold, provided the given pixel passes the threshold test.

The last argument is one we need to pay _extra special attention_ to. Previously, we had supplied values of `cv2.THRESH_BINARY` or `cv2.THRESH_BINARY_INV` depending on what type of thresholding we wanted to perform.

But now we are passing in a second flag that is logically OR’d with the previous method. Notice that this method is `cv2.THRESH_OTSU`, which obviously corresponds to Otsu’s thresholding method.

The `cv2.threshold` function will again return a tuple of 2 values for us: the threshold value _T_ and the thresholded image itself.

In the previous section, the value of _T_ returned was redundant and irrelevant — we already knew this value of _T_ since we had to manually supply it.

But now that we are using Otsu’s method for automatic thresholding, this value of _T_ becomes interesting — we do not know what the optimal value of _T_ is ahead of time, hence why we are using Otsu’s method to compute it for us. **Line 24** prints out the value of _T_ as determined by Otsu’s method.

Finally, we display the output thresholded image to our screen on **Lines 28 and 29.**

### **Otsu thresholding results**

To see Otsu’s method running, be sure to access the **_“Downloads”_** section of this tutorial to retrieve the source code and example images.

From there you can execute the following command:

$ python otsu\_thresholding.py --image images/coins01.png
\[INFO\] otsu's thresholding value: 191.0

![Image 11](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20700%20204'%3E%3C/svg%3E)

**Figure 9:** Using Otsu’s method to automatically determine the optimal threshold.

Pretty nice, right? We didn’t even have to supply our value of _T_ — Otsu’s method automatically took care of this for us. And we still got a nice threshold image as an output. And if we inspect our terminal we’ll see that Otsu’s method computed a value of _T=191_:

So based on our input image, the optimal value of _T_ is `191`; therefore, any pixel _p_ that is greater than `191` is set to `0`, and any pixel less than `191` is set to `255` (since we supplied the `cv2.THRESH_BINARY_INV` flag detailed in section _“Implementing simple thresholding with OpenCV”_ above).

Before we move on to the next example, let’s take a second and discuss what is meant by the term “optimal.” The value of _T_ returned by Otsu’s method **may not** be optimal in visual investigation of our image — we can clearly see some gaps and holes in the coins of the thresholded image. But this value _is optimal_ in the sense that it does the best possible job to split the foreground and the background **assuming a bi-modal distribution of grayscale pixel values.**

If the grayscale image does not follow a bi-modal distribution, then Otsu’s method will still run, but it may not give us our intended results. In that case, we will have to try adaptive thresholding, which we’ll cover in our next tutorial.

Anyway, let’s try a second image:

$ python otsu\_thresholding.py --image images/coins02.png 
\[INFO\] otsu's thresholding value: 180.0

![Image 12](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20700%20168'%3E%3C/svg%3E)

**Figure 10:** Otsu’s automatic thresholding method applied to a second coin image.

Again, notice that Otsu’s method has done a good job separating the foreground from the background for us. And this time, Otsu’s method has determined the optimal value of _T_ to be `180`. Any pixel value greater than to `180` is set to `0`, and any pixel less than `180` is set to `255` (again, assuming inverse thresholding).

As you can see, Otsu’s method can save us a lot of time guessing and checking the best value of _T_. However, there are some major drawbacks.

The first is that Otsu’s method assumes a bi-modal distribution of the grayscale pixel intensities of our input image. If this is not the case, then Otsu’s method can return sub-par results.

Secondly, Otsu’s method is a global thresholding method. In situations where lighting conditions are semi-stable and the objects we want to segment have sufficient contrast from the background, we might be able to get away with Otsu’s method.

But when the lighting conditions are non-uniform — such as when different parts of the image are illuminated more than others, we can run into some serious problem. And when that’s the case, we’ll need to rely on **adaptive thresholding** (which we’ll discuss next week)

CV2.threshold applications
--------------------------

There are several applications you can build with the help of cv2.threshold.

*   Image segmentation applications where you separate objects based on pixel wise comparisons.
*   Background removal where you separate and remove foreground and background objects.
*   [Optical Character Recognition](https://pyimagesearch.com/2021/08/09/what-is-optical-character-recognition-ocr/) (OCR) where you threshold to improve contrast and OCR accuracy.
*   Create a binary image where you threshold and convert and image into a binary image where each pixel is either 0 (black) or 255 (white).

Updates:  
December 30th, 2022 updated tutorial content and links.

### What's next? We recommend [PyImageSearch University](https://pyimagesearch.com/pyimagesearch-university/?utm_source=blogPost&utm_medium=bottomBanner&utm_campaign=What%27s%20next%3F%20I%20recommend).

**Course information:**  
84 total classes • 114+ hours of on-demand code walkthrough videos • Last updated: February 2024  
★★★★★ 4.84 (128 Ratings) • 16,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:**

*   ✓ **84 courses** on essential computer vision, deep learning, and OpenCV topics
*   ✓ **84 Certificates** of Completion
*   ✓ **114+ hours** of on-demand video
*   ✓ **Brand new courses released _regularly_**, 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_ 536+ tutorials** on PyImageSearch
*   ✓ **Easy one-click downloads** for code, datasets, pre-trained models, etc.
*   ✓ **Access** on mobile, laptop, desktop, etc.

[Click here to join PyImageSearch University](https://pyimagesearch.com/pyimagesearch-university/?utm_source=blogPost&utm_medium=bottomBanner&utm_campaign=What%27s%20next%3F%20I%20recommend)

**Summary**
-----------

In this lesson, we learned all about thresholding: what thresholding is, why we use thresholding, and how to perform thresholding using OpenCV and the `cv2.threshold` function.

We started by performing **simple thresholding**, which requires us to manually supply a value of _T_ to perform the threshold. However, we quickly realized that manually supplying a value of _T_ is very tedious and requires us to hardcode this value, implying that this method will not work in all situations.

We then moved on to **Otsu’s thresholding method,** which _automatically_ computes the optimal value of _T_ for us, assuming a bi-modal distribution of the grayscale representation of our input image.

The problem here is that (1) our input images need to be bi-modal for Otsu’s method to correctly segment the image, and (2) Otsu’s method is a global thresholding method, which implies that we need to have at least some decent control over our lighting conditions.

In situations where our lighting conditions are less than ideal, or we simply cannot control them, we need **adaptive thresholding** (which is also known as local thresholding). We’ll be covering adaptive thresholding in our next tutorial.

**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!_**

![Image 13](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20410%20567'%3E%3C/svg%3E)

#### 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!