Skip to content

Commit ab21dc6

Browse files
mehlukasalalek
authored andcommitted
Merge pull request opencv#14245 from mehlukas:3.4-fixtutorial
* improve thresholding tutorial, fix grammar issues and incorrections * keep full list of simple thresholding types
1 parent aa16743 commit ab21dc6

File tree

1 file changed

+52
-58
lines changed

1 file changed

+52
-58
lines changed

doc/py_tutorials/py_imgproc/py_thresholding/py_thresholding.markdown

Lines changed: 52 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,34 @@ Image Thresholding {#tutorial_py_thresholding}
44
Goal
55
----
66

7-
- In this tutorial, you will learn Simple thresholding, Adaptive thresholding, Otsu's thresholding
8-
etc.
9-
- You will learn these functions : **cv.threshold**, **cv.adaptiveThreshold** etc.
7+
- In this tutorial, you will learn Simple thresholding, Adaptive thresholding and Otsu's thresholding.
8+
- You will learn the functions **cv.threshold** and **cv.adaptiveThreshold**.
109

1110
Simple Thresholding
1211
-------------------
1312

14-
Here, the matter is straight forward. If pixel value is greater than a threshold value, it is
15-
assigned one value (may be white), else it is assigned another value (may be black). The function
16-
used is **cv.threshold**. First argument is the source image, which **should be a grayscale
17-
image**. Second argument is the threshold value which is used to classify the pixel values. Third
18-
argument is the maxVal which represents the value to be given if pixel value is more than (sometimes
19-
less than) the threshold value. OpenCV provides different styles of thresholding and it is decided
20-
by the fourth parameter of the function. Different types are:
13+
Here, the matter is straight forward. For every pixel, the same threshold value is applied.
14+
If the pixel value is smaller than the threshold, it is set to 0, otherwise it is set to a maximum value.
15+
The function **cv.threshold** is used to apply the thresholding.
16+
The first argument is the source image, which **should be a grayscale image**.
17+
The second argument is the threshold value which is used to classify the pixel values.
18+
The third argument is the maximum value which is assigned to pixel values exceeding the threshold.
19+
OpenCV provides different types of thresholding which is given by the fourth parameter of the function.
20+
Basic thresholding as described above is done by using the type cv.THRESH_BINARY.
21+
All simple thresholding types are:
2122

2223
- cv.THRESH_BINARY
2324
- cv.THRESH_BINARY_INV
2425
- cv.THRESH_TRUNC
2526
- cv.THRESH_TOZERO
2627
- cv.THRESH_TOZERO_INV
2728

28-
Documentation clearly explain what each type is meant for. Please check out the documentation.
29+
See the documentation of the types for the differences.
2930

30-
Two outputs are obtained. First one is a **retval** which will be explained later. Second output is
31-
our **thresholded image**.
31+
The method returns two outputs.
32+
The first is the threshold that was used and the second output is the **thresholded image**.
3233

33-
Code :
34+
This code compares the different simple thresholding types:
3435
@code{.py}
3536
import cv2 as cv
3637
import numpy as np
@@ -53,34 +54,31 @@ for i in xrange(6):
5354

5455
plt.show()
5556
@endcode
56-
@note To plot multiple images, we have used plt.subplot() function. Please checkout Matplotlib docs
57-
for more details.
57+
@note To plot multiple images, we have used the plt.subplot() function. Please checkout the matplotlib docs for more details.
5858

59-
Result is given below :
59+
The code yields this result:
6060

6161
![image](images/threshold.jpg)
6262

6363
Adaptive Thresholding
6464
---------------------
6565

66-
In the previous section, we used a global value as threshold value. But it may not be good in all
67-
the conditions where image has different lighting conditions in different areas. In that case, we go
68-
for adaptive thresholding. In this, the algorithm calculate the threshold for a small regions of the
69-
image. So we get different thresholds for different regions of the same image and it gives us better
70-
results for images with varying illumination.
66+
In the previous section, we used one global value as a threshold.
67+
But this might not be good in all cases, e.g. if an image has different lighting conditions in different areas.
68+
In that case, adaptive thresholding thresholding can help.
69+
Here, the algorithm determines the threshold for a pixel based on a small region around it.
70+
So we get different thresholds for different regions of the same image which gives better results for images with varying illumination.
7171

72-
It has three ‘special’ input params and only one output argument.
72+
Additionally to the parameters described above, the method cv.adaptiveThreshold three input parameters:
7373

74-
**Adaptive Method** - It decides how thresholding value is calculated.
75-
- cv.ADAPTIVE_THRESH_MEAN_C : threshold value is the mean of neighbourhood area.
76-
- cv.ADAPTIVE_THRESH_GAUSSIAN_C : threshold value is the weighted sum of neighbourhood
77-
values where weights are a gaussian window.
74+
The **adaptiveMethod** decides how the threshold value is calculated:
75+
- cv.ADAPTIVE_THRESH_MEAN_C: The threshold value is the mean of the neighbourhood area minus the constant **C**.
76+
- cv.ADAPTIVE_THRESH_GAUSSIAN_C: The threshold value is a gaussian-weighted sum of the neighbourhood
77+
values minus the constant **C**.
7878

79-
**Block Size** - It decides the size of neighbourhood area.
79+
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.
8080

81-
**C** - It is just a constant which is subtracted from the mean or weighted mean calculated.
82-
83-
Below piece of code compares global thresholding and adaptive thresholding for an image with varying
81+
The code below compares global thresholding and adaptive thresholding for an image with varying
8482
illumination:
8583
@code{.py}
8684
import cv2 as cv
@@ -106,33 +104,30 @@ for i in xrange(4):
106104
plt.xticks([]),plt.yticks([])
107105
plt.show()
108106
@endcode
109-
Result :
107+
Result:
110108

111109
![image](images/ada_threshold.jpg)
112110

113-
Otsus Binarization
111+
Otsu's Binarization
114112
-------------------
115113

116-
In the first section, I told you there is a second parameter **retVal**. Its use comes when we go
117-
for Otsu’s Binarization. So what is it?
118-
119-
In global thresholding, we used an arbitrary value for threshold value, right? So, how can we know a
120-
value we selected is good or not? Answer is, trial and error method. But consider a **bimodal
121-
image** (*In simple words, bimodal image is an image whose histogram has two peaks*). For that
122-
image, we can approximately take a value in the middle of those peaks as threshold value, right ?
123-
That is what Otsu binarization does. So in simple words, it automatically calculates a threshold
124-
value from image histogram for a bimodal image. (For images which are not bimodal, binarization
125-
won’t be accurate.)
126-
127-
For this, our cv.threshold() function is used, but pass an extra flag, cv.THRESH_OTSU. **For
128-
threshold value, simply pass zero**. Then the algorithm finds the optimal threshold value and
129-
returns you as the second output, retVal. If Otsu thresholding is not used, retVal is same as the
130-
threshold value you used.
131-
132-
Check out below example. Input image is a noisy image. In first case, I applied global thresholding
133-
for a value of 127. In second case, I applied Otsu’s thresholding directly. In third case, I
134-
filtered image with a 5x5 gaussian kernel to remove the noise, then applied Otsu thresholding. See
135-
how noise filtering improves the result.
114+
In global thresholding, we used an arbitrary chosen value as a threshold.
115+
In contrast, Otsu's method avoids having to choose a value and determines it automatically.
116+
117+
Consider an image with only two distinct image values (*bimodal image*), where the histogram would only consist of two peaks.
118+
A good threshold would be in the middle of those two values.
119+
Similarly, Otsu's method determines an optimal global threshold value from the image histogram.
120+
121+
In order to do so, the cv.threshold() function is used, where cv.THRESH_OTSU is passed as an extra flag.
122+
The threshold value can be chosen arbitrary.
123+
The algorithm then finds the optimal threshold value which is returned as the first output.
124+
125+
Check out the example below.
126+
The input image is a noisy image.
127+
In the first case, global thresholding with a value of 127 is applied.
128+
In the second case, Otsu's thresholding is applied directly.
129+
In the third case, the image is first filtered with a 5x5 gaussian kernel to remove the noise, then Otsu thresholding is applied.
130+
See how noise filtering improves the result.
136131
@code{.py}
137132
import cv2 as cv
138133
import numpy as np
@@ -167,17 +162,17 @@ for i in xrange(3):
167162
plt.title(titles[i*3+2]), plt.xticks([]), plt.yticks([])
168163
plt.show()
169164
@endcode
170-
Result :
165+
Result:
171166

172167
![image](images/otsu.jpg)
173168

174-
### How Otsu's Binarization Works?
169+
### How does Otsu's Binarization work?
175170

176171
This section demonstrates a Python implementation of Otsu's binarization to show how it works
177172
actually. If you are not interested, you can skip this.
178173

179174
Since we are working with bimodal images, Otsu's algorithm tries to find a threshold value (t) which
180-
minimizes the **weighted within-class variance** given by the relation :
175+
minimizes the **weighted within-class variance** given by the relation:
181176

182177
\f[\sigma_w^2(t) = q_1(t)\sigma_1^2(t)+q_2(t)\sigma_2^2(t)\f]
183178

@@ -186,7 +181,7 @@ where
186181
\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]
187182

188183
It actually finds a value of t which lies in between two peaks such that variances to both classes
189-
are minimum. It can be simply implemented in Python as follows:
184+
are minimal. It can be simply implemented in Python as follows:
190185
@code{.py}
191186
img = cv.imread('noisy2.png',0)
192187
blur = cv.GaussianBlur(img,(5,5),0)
@@ -220,7 +215,6 @@ for i in xrange(1,256):
220215
ret, otsu = cv.threshold(blur,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
221216
print( "{} {}".format(thresh,ret) )
222217
@endcode
223-
*(Some of the functions may be new here, but we will cover them in coming chapters)*
224218

225219
Additional Resources
226220
--------------------

0 commit comments

Comments
 (0)