OpenCV #007 Thresholding
Digital Image Processing using OpenCV (Python & C++)
Highlights: In this post we will learn about Thresholding. This method is essential in many computer vision applications. The purpose of this post is to show and benchmark performance of several different approaches, and also to provide you with the necessary knowledge to implement it.
Tutorial Overview:
1. Thresholding
In the last post we explained why edges are important for better understanding of the image. Here we will present one new method, which can help us to find them.
This method is known as Thresholding.
Thresholding is both simple and effective method for image segmentation. Generally, what we do when we look for thresholds is we take a histogram of the intensities. In the \(x\) direction, it can go from 0 to 255 (pixel values), and in \(y\) direction we have the number of pixels that have those intensity values. Then, we will create a new binary image depending where we have set a threshold value.
There are three main types of
Types of Thresholding:
- Simple thresholding
- Adaptive Thresholding
- Otsu’s Binarization
2. Simple thresholding
This process is pretty easy. If a pixel value is greater than a threshold value, it is set to one value. Maybe it will become white pixel. On the other hand, if it is lower than a threshold value it may be set to a black value. The function used for this is called threshold, both in Python and C++.
Different types of a thresholding algorithm are supported:
- THRESH_BINARY
- THRESH_BINARY_INV
- THRESH_TRUNC
- THRESH_TOZERO
- THRESH_TOZERO_INV
Python
C++
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
// Loading the actual image
Mat image, gray;
Mat thresh1, thresh2, thresh3, thresh4, thresh5;
image=imread("car.jpg", IMREAD_COLOR);
cv::imshow("Original image", image);
cv::waitKey();
// Edge cases
if(image.empty()){
cout << "Error loading image" << endl;
return -1;
}
// converting the color image into grayscale
cvtColor(image, gray, COLOR_BGR2GRAY);
// First type of Simple Thresholding is Binary Thresholding
// After thresholding the image with this type of operator, we will
// have image with only two values, 0 and 255.
threshold( gray, thresh1, 127, 255, THRESH_BINARY );
// Inverse binary thresholding is just the opposite of binary thresholding.
threshold( gray, thresh2, 127, 255, THRESH_BINARY_INV );
// Truncate Thresholding is type of thresholding where pixel
// is set to the threshold value if it exceeds that value.
// Othervise, it stays the same.
threshold( gray, thresh3, 127, 255, THRESH_TRUNC );
// Threshold to Zero is type of thresholding where pixel value stays the same
// if it is greater than the threshold. Otherwise it is set to zero.
threshold( gray, thresh4, 127, 255, THRESH_TOZERO );
// Inverted Threshold to Zero is the opposite of the last one.
// Pixel value is set to zero if it is greater than the threshold.
// Otherwise it stays the same.
threshold( gray, thresh5, 127, 255, THRESH_TOZERO_INV );
// Displaying the result
cv::imshow("THRESH_BINARY", thresh1);
cv::imshow("THRESH_BINARY_INV", thresh2);
cv::imshow("THRESH_TRUNC", thresh3);
cv::imshow("THRESH_TOZERO", thresh4);
cv::imshow("THRESH_TOZERO_INV", thresh5);
cv::waitKey(0);
return 0;
}
Output:
3. Adaptive Thresholding
The algorithm calculates the threshold for small/local regions of the image. This will give us better results for images with varying illumination. Subsequently, we get different thresholds for different regions of the same image.
In contrast to the Simple Thresholding, here, we have one additional parameter. This parameter controls how a threshold value is calculated. We can use ADAPTIVE_THRESH_MEAN_C if we want the threshold value to be the mean of pixel intensities in a neighbourhood area. In addition, ADAPTIVE_THRESH_GAUSSIAN_C can be used if we want that the neighbourhood pixel values are multiplied with weights obtained from a Gaussian kernel.
In addition to that, we must define the block size (neighbourhood area), and the constant that will be subtracted from the mean/weighted mean.
Python
C++
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
// Loading the actual image
Mat image, gray;
Mat thresh1, thresh2;
image=imread("car.jpg", IMREAD_COLOR);
cv::imshow("Original image", image);
cv::waitKey();
// Edge cases
if(image.empty()){
cout << "Error loading image" << endl;
return -1;
}
// converting the color image into grayscale
cvtColor(image, gray, COLOR_BGR2GRAY);
// ADAPTIVE_THRESH_MEAN_C - Threshold value is the mean of neighbourhood area.
// ADAPTIVE_THRESH_GAUSSIAN_C - Threshold value is the weighted sum of neighbourhood
// values where weights are a Gaussian window.
// Block Size - It decides the size of neighbourhood area.
// C - It is just a constant which is subtracted from the mean or weighted mean calculated.
adaptiveThreshold(gray, thresh1, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 11, 2);
adaptiveThreshold(gray, thresh2, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 11, 2);
// Displaying the result
cv::imshow("Adaptive Mean Thresholding", thresh1);
cv::imshow("Adaptive Gaussian Thresholding", thresh2);
cv::waitKey(0);
return 0;
}
Output:
4. Otsu’s Binarization
This method is developed for a special case. Those are images that have two dominant gray intensity levels. This is called a bimodal image. For such images, we can use Otsu’s method, because it automatically calculates a threshold value from the image histogram. For bimodal images, we can approximately take a value in the middle of those peaks as a threshold value.
It basically assumes that our system is bimodal, and finds the
Python
C++
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
// Loading the actual image
Mat image, gray, blurred;
Mat thresh1, thresh2;
image=imread("car.jpg", IMREAD_COLOR);
cv::imshow("Original image", image);
cv::waitKey();
// Edge cases
if(image.empty()){
cout << "Error loading image" << endl;
return -1;
}
// converting the color image into grayscale
cvtColor(image, gray, COLOR_BGR2GRAY);
// Otsu's thresholding
threshold(gray, thresh1, 0, 255, THRESH_BINARY+THRESH_OTSU);
// Otsu's thresholding with Gaussian filtering
GaussianBlur(gray, blurred, Size(5,5),0);
threshold(blurred, thresh2, 0, 255, THRESH_BINARY+THRESH_OTSU);
// Displaying the result
cv::imshow("Otsu's thresholding", thresh1);
cv::imshow("Otsu's thresholding with Gaussian filtering", thresh2);
cv::waitKey(0);
return 0;
}
Output:
Summary
To summarize, we have learned how to use Thresholding to segment and image, which are important for us to better understand the image. In the next post, we will talk more about Canny Edge Detector.