mirror of
https://github.com/opencv/opencv.git
synced 2024-11-24 03:00:14 +08:00
Merge pull request #14245 from mehlukas:3.4-fixtutorial
* improve thresholding tutorial, fix grammar issues and incorrections * keep full list of simple thresholding types
This commit is contained in:
parent
aa167434e6
commit
ab21dc6d47
@ -4,20 +4,21 @@ Image Thresholding {#tutorial_py_thresholding}
|
||||
Goal
|
||||
----
|
||||
|
||||
- In this tutorial, you will learn Simple thresholding, Adaptive thresholding, Otsu's thresholding
|
||||
etc.
|
||||
- You will learn these functions : **cv.threshold**, **cv.adaptiveThreshold** etc.
|
||||
- In this tutorial, you will learn Simple thresholding, Adaptive thresholding and Otsu's thresholding.
|
||||
- You will learn the functions **cv.threshold** and **cv.adaptiveThreshold**.
|
||||
|
||||
Simple Thresholding
|
||||
-------------------
|
||||
|
||||
Here, the matter is straight forward. If pixel value is greater than a threshold value, it is
|
||||
assigned one value (may be white), else it is assigned another value (may be black). The function
|
||||
used is **cv.threshold**. First argument is the source image, which **should be a grayscale
|
||||
image**. Second argument is the threshold value which is used to classify the pixel values. Third
|
||||
argument is the maxVal which represents the value to be given if pixel value is more than (sometimes
|
||||
less than) the threshold value. OpenCV provides different styles of thresholding and it is decided
|
||||
by the fourth parameter of the function. Different types are:
|
||||
Here, the matter is straight forward. For every pixel, the same threshold value is applied.
|
||||
If the pixel value is smaller than the threshold, it is set to 0, otherwise it is set to a maximum value.
|
||||
The function **cv.threshold** is used to apply the thresholding.
|
||||
The first argument is the source image, which **should be a grayscale image**.
|
||||
The second argument is the threshold value which is used to classify the pixel values.
|
||||
The third argument is the maximum value which is assigned to pixel values exceeding the threshold.
|
||||
OpenCV provides different types of thresholding which is given by the fourth parameter of the function.
|
||||
Basic thresholding as described above is done by using the type cv.THRESH_BINARY.
|
||||
All simple thresholding types are:
|
||||
|
||||
- cv.THRESH_BINARY
|
||||
- cv.THRESH_BINARY_INV
|
||||
@ -25,12 +26,12 @@ by the fourth parameter of the function. Different types are:
|
||||
- cv.THRESH_TOZERO
|
||||
- cv.THRESH_TOZERO_INV
|
||||
|
||||
Documentation clearly explain what each type is meant for. Please check out the documentation.
|
||||
See the documentation of the types for the differences.
|
||||
|
||||
Two outputs are obtained. First one is a **retval** which will be explained later. Second output is
|
||||
our **thresholded image**.
|
||||
The method returns two outputs.
|
||||
The first is the threshold that was used and the second output is the **thresholded image**.
|
||||
|
||||
Code :
|
||||
This code compares the different simple thresholding types:
|
||||
@code{.py}
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
@ -53,34 +54,31 @@ for i in xrange(6):
|
||||
|
||||
plt.show()
|
||||
@endcode
|
||||
@note To plot multiple images, we have used plt.subplot() function. Please checkout Matplotlib docs
|
||||
for more details.
|
||||
@note To plot multiple images, we have used the plt.subplot() function. Please checkout the matplotlib docs for more details.
|
||||
|
||||
Result is given below :
|
||||
The code yields this result:
|
||||
|
||||
![image](images/threshold.jpg)
|
||||
|
||||
Adaptive Thresholding
|
||||
---------------------
|
||||
|
||||
In the previous section, we used a global value as threshold value. But it may not be good in all
|
||||
the conditions where image has different lighting conditions in different areas. In that case, we go
|
||||
for adaptive thresholding. In this, the algorithm calculate the threshold for a small regions of the
|
||||
image. So we get different thresholds for different regions of the same image and it gives us better
|
||||
results for images with varying illumination.
|
||||
In the previous section, we used one global value as a threshold.
|
||||
But this might not be good in all cases, e.g. if an image has different lighting conditions in different areas.
|
||||
In that case, adaptive thresholding thresholding can help.
|
||||
Here, the algorithm determines the threshold for a pixel based on a small region around it.
|
||||
So we get different thresholds for different regions of the same image which gives better results for images with varying illumination.
|
||||
|
||||
It has three ‘special’ input params and only one output argument.
|
||||
Additionally to the parameters described above, the method cv.adaptiveThreshold three input parameters:
|
||||
|
||||
**Adaptive Method** - It decides how thresholding value is calculated.
|
||||
- cv.ADAPTIVE_THRESH_MEAN_C : threshold value is the mean of neighbourhood area.
|
||||
- cv.ADAPTIVE_THRESH_GAUSSIAN_C : threshold value is the weighted sum of neighbourhood
|
||||
values where weights are a gaussian window.
|
||||
The **adaptiveMethod** decides how the threshold value is calculated:
|
||||
- cv.ADAPTIVE_THRESH_MEAN_C: The threshold value is the mean of the neighbourhood area minus the constant **C**.
|
||||
- cv.ADAPTIVE_THRESH_GAUSSIAN_C: The threshold value is a gaussian-weighted sum of the neighbourhood
|
||||
values minus the constant **C**.
|
||||
|
||||
**Block Size** - It decides the size of neighbourhood area.
|
||||
The **blockSize** determines the size of the neighbourhood area and **C** is a constant that is subtracted from the mean or weighted sum of the neighbourhood pixels.
|
||||
|
||||
**C** - It is just a constant which is subtracted from the mean or weighted mean calculated.
|
||||
|
||||
Below piece of code compares global thresholding and adaptive thresholding for an image with varying
|
||||
The code below compares global thresholding and adaptive thresholding for an image with varying
|
||||
illumination:
|
||||
@code{.py}
|
||||
import cv2 as cv
|
||||
@ -106,33 +104,30 @@ for i in xrange(4):
|
||||
plt.xticks([]),plt.yticks([])
|
||||
plt.show()
|
||||
@endcode
|
||||
Result :
|
||||
Result:
|
||||
|
||||
![image](images/ada_threshold.jpg)
|
||||
|
||||
Otsu’s Binarization
|
||||
Otsu's Binarization
|
||||
-------------------
|
||||
|
||||
In the first section, I told you there is a second parameter **retVal**. Its use comes when we go
|
||||
for Otsu’s Binarization. So what is it?
|
||||
In global thresholding, we used an arbitrary chosen value as a threshold.
|
||||
In contrast, Otsu's method avoids having to choose a value and determines it automatically.
|
||||
|
||||
In global thresholding, we used an arbitrary value for threshold value, right? So, how can we know a
|
||||
value we selected is good or not? Answer is, trial and error method. But consider a **bimodal
|
||||
image** (*In simple words, bimodal image is an image whose histogram has two peaks*). For that
|
||||
image, we can approximately take a value in the middle of those peaks as threshold value, right ?
|
||||
That is what Otsu binarization does. So in simple words, it automatically calculates a threshold
|
||||
value from image histogram for a bimodal image. (For images which are not bimodal, binarization
|
||||
won’t be accurate.)
|
||||
Consider an image with only two distinct image values (*bimodal image*), where the histogram would only consist of two peaks.
|
||||
A good threshold would be in the middle of those two values.
|
||||
Similarly, Otsu's method determines an optimal global threshold value from the image histogram.
|
||||
|
||||
For this, our cv.threshold() function is used, but pass an extra flag, cv.THRESH_OTSU. **For
|
||||
threshold value, simply pass zero**. Then the algorithm finds the optimal threshold value and
|
||||
returns you as the second output, retVal. If Otsu thresholding is not used, retVal is same as the
|
||||
threshold value you used.
|
||||
In order to do so, the cv.threshold() function is used, where cv.THRESH_OTSU is passed as an extra flag.
|
||||
The threshold value can be chosen arbitrary.
|
||||
The algorithm then finds the optimal threshold value which is returned as the first output.
|
||||
|
||||
Check out below example. Input image is a noisy image. In first case, I applied global thresholding
|
||||
for a value of 127. In second case, I applied Otsu’s thresholding directly. In third case, I
|
||||
filtered image with a 5x5 gaussian kernel to remove the noise, then applied Otsu thresholding. See
|
||||
how noise filtering improves the result.
|
||||
Check out the example below.
|
||||
The input image is a noisy image.
|
||||
In the first case, global thresholding with a value of 127 is applied.
|
||||
In the second case, Otsu's thresholding is applied directly.
|
||||
In the third case, the image is first filtered with a 5x5 gaussian kernel to remove the noise, then Otsu thresholding is applied.
|
||||
See how noise filtering improves the result.
|
||||
@code{.py}
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
@ -167,17 +162,17 @@ for i in xrange(3):
|
||||
plt.title(titles[i*3+2]), plt.xticks([]), plt.yticks([])
|
||||
plt.show()
|
||||
@endcode
|
||||
Result :
|
||||
Result:
|
||||
|
||||
![image](images/otsu.jpg)
|
||||
|
||||
### How Otsu's Binarization Works?
|
||||
### How does Otsu's Binarization work?
|
||||
|
||||
This section demonstrates a Python implementation of Otsu's binarization to show how it works
|
||||
actually. If you are not interested, you can skip this.
|
||||
|
||||
Since we are working with bimodal images, Otsu's algorithm tries to find a threshold value (t) which
|
||||
minimizes the **weighted within-class variance** given by the relation :
|
||||
minimizes the **weighted within-class variance** given by the relation:
|
||||
|
||||
\f[\sigma_w^2(t) = q_1(t)\sigma_1^2(t)+q_2(t)\sigma_2^2(t)\f]
|
||||
|
||||
@ -186,7 +181,7 @@ where
|
||||
\f[q_1(t) = \sum_{i=1}^{t} P(i) \quad \& \quad q_2(t) = \sum_{i=t+1}^{I} P(i)\f]\f[\mu_1(t) = \sum_{i=1}^{t} \frac{iP(i)}{q_1(t)} \quad \& \quad \mu_2(t) = \sum_{i=t+1}^{I} \frac{iP(i)}{q_2(t)}\f]\f[\sigma_1^2(t) = \sum_{i=1}^{t} [i-\mu_1(t)]^2 \frac{P(i)}{q_1(t)} \quad \& \quad \sigma_2^2(t) = \sum_{i=t+1}^{I} [i-\mu_2(t)]^2 \frac{P(i)}{q_2(t)}\f]
|
||||
|
||||
It actually finds a value of t which lies in between two peaks such that variances to both classes
|
||||
are minimum. It can be simply implemented in Python as follows:
|
||||
are minimal. It can be simply implemented in Python as follows:
|
||||
@code{.py}
|
||||
img = cv.imread('noisy2.png',0)
|
||||
blur = cv.GaussianBlur(img,(5,5),0)
|
||||
@ -220,7 +215,6 @@ for i in xrange(1,256):
|
||||
ret, otsu = cv.threshold(blur,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
|
||||
print( "{} {}".format(thresh,ret) )
|
||||
@endcode
|
||||
*(Some of the functions may be new here, but we will cover them in coming chapters)*
|
||||
|
||||
Additional Resources
|
||||
--------------------
|
||||
|
Loading…
Reference in New Issue
Block a user