Chapter 6 - Thresholding

Threshold processing refers to removing pixels whose pixel values ​​in the image are higher than or lower than a certain value. For example, set the threshold to 127, then:

  • Set the values ​​of all pixels in the image with pixel values ​​greater than 127 to 255.
  • Set the values ​​of all pixels in the image with pixel values ​​less than or equal to 127 to 0.

A binary image can be obtained through the above method. As shown in the figure, a grayscale image is processed into a binary image according to the above threshold processing method, which effectively realizes the separation of foreground and background.

image-20211013111330415

OpenCV provides the function cv2.threshold() and function cv2.adaptiveThreshold() for thresholding.

one.threshold function:

OpenCV3.0 uses the cv2.threshold() function for thresholding. The syntax of this function is:

  • retval, dst = cv2.threshold(src, thresh, maxval, type)

    • retval: Represents the returned threshold.
    • dst: represents the threshold segmentation result image, which has the same size and type as the original image.
    • src: Represents the image to be thresholded, which can be multi-channel, 8-bit or 32-bit floating-point value.
    • thresh: represents the threshold to be set.
    • maxval: Represents the maximum value that needs to be set when the type parameter is THRESH_BINARY or THRESH_BINARY_INV type.
    • type represents the type of threshold segmentation, and the specific type values ​​are shown in the table:

    image-20211013103741015

The above formula is relatively abstract and can be visualized as:

image-20211013103854712

1. Binarization threshold processing (cv2.THRESH_BINARY):

Binarization thresholding processes the original image into a binary image with only two values, as shown. Its processing method for pixels is:

  • For pixels whose gray value is greater than the threshold thresh, set their gray value to the maximum value.
  • For pixels whose gray value is less than or equal to the threshold thresh, their gray value is set to 0.

image-20211013104222845

Expressed as an expression:

dst ( x , y ) = { maxval , src ( x , y ) > thresh 0 , in other cases dst(x, y) = \begin{cases} maxval, \quad src(x, y) >thresh \\ 0, \quad other cases\end{cases}dst(x,y)={ maxval,src(x,y)>thresh0,other situations, thresh represents a specific threshold.

In 8-bit images, the maximum value is 255. Therefore, when binarizing an 8-bit grayscale image, if the threshold is set to 127, then:

  • All pixels greater than 127 will be processed as 255.
  • Other values ​​are treated as 0.

For convenience, in the subsequent descriptions, we will take an 8-bit image as an example, that is, the maximum value of the pixel value is 255.

For example: use the cv2.threshold() function to perform binarization threshold processing on the array.

import cv2
import numpy as np

img = np.random.randint(0, 256, size=[4, 5], dtype=np.uint8)
t, rst = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
print('img=\n', img)
print('t', t)
print('rst=\n', rst)

# 输出结果
img=
 [[138 241 156  43   1]
 [205 128 223 143 236]
 [ 61  34 106 233 196]
 [180  81 192 210 107]]
t 127.0
rst=
 [[255 255 255   0   0]
 [255 255 255 255 255]
 [  0   0   0 255 255]
 [255   0 255 255   0]]

2. Anti-binarization threshold processing (cv2.THRESH_BINARY_INV)

The result of anti-binarization threshold processing is also a binary image with only two values. The difference from binarization threshold processing is that the two process pixel values ​​in different ways. The anti-binarization threshold processing method for pixel points is:

  • For pixels whose gray value is greater than the threshold, set its value to 0.
  • For pixels whose gray value is less than or equal to the threshold, set its value to 255.

image-20211013111532792

Expressed as an expression:

dst ( x , y ) = { 0 , src ( x , y ) > threshmaxval , in other cases dst(x, y) = \begin{cases} 0, \quad src(x, y) >thresh \\ maxval, \ quad other cases\end{cases}dst(x,y)={ 0,src(x,y)>threshmaxval,other situations, thresh represents a specific threshold.

For example: use the cv2.threshold() function to perform debinarization threshold processing on the array.

import cv2
import numpy as np

img = np.random.randint(0, 256, size=[4, 5], dtype=np.uint8)
t, rst = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
print('img=\n', img)
print('t', t)
print('rst=\n', rst)

# 输出结果
img=
 [[ 22 209 106 193  56]
 [218 252 109 166  47]
 [141 247  76 247 102]
 [ 42   4 217 110 199]]
t 127.0
rst=
 [[255   0 255   0 255]
 [  0   0 255   0 255]
 [  0   0 255   0 255]
 [255 255   0 255   0]]

3. Truncation thresholding (cv2.THRESH_TRUNC)

Truncation thresholding will process the value of the pixel points greater than the threshold value in the image to the set threshold value, and the value of the pixel points less than or equal to the threshold value will remain unchanged. As shown in the diagram:

image-20211013112247076

For example, if the threshold value is selected as 127, then when truncating the thresholding process:

  • For a pixel with a pixel value greater than 127, its pixel value will be set to 127.
  • For pixels whose pixel value is less than or equal to 127, the pixel value will keep changing.

If it is represented by an expression, then the generation rule of its target value is:

dst ( x , y ) = { thresh , src ( x , y ) > thresh remains unchanged, otherwise dst(x, y) = \begin{cases} thresh, \quad src(x, y) >thresh \\ remain the same, \quad other cases\end{cases}dst(x,y)={ thresh,src(x,y)>threshstay the same ,other situations, thresh represents a specific threshold.

import cv2
import numpy as np

img = np.random.randint(0, 256, size=[4, 5], dtype=np.uint8)
t, rst = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
print('img=\n', img)
print('t=\n', t)
print('rst=\n', rst)


# 输出结果
img=
 [[ 75 169  58 247 235]
 [ 95  95 139 129 102]
 [ 93  86  97 140 200]
 [108  64 250  33 171]]
t=
 127.0
rst=
 [[ 75 127  58 127 127]
 [ 95  95 127 127 102]
 [ 93  86  97 127 127]
 [108  64 127  33 127]]

4. Super-threshold zero processing (cv2.THRESH_TOZERO_INV)

The super-threshold zero processing will treat the value of the pixel points greater than the threshold value in the image as 0, and the value of the pixel points less than or equal to the threshold value will remain unchanged. That is, first select a threshold, and then process the image as follows:

  • For a pixel whose pixel value is greater than the threshold, its pixel value will be processed as 0.
  • For pixels whose pixel values ​​are less than or equal to the threshold, their pixel values ​​will remain unchanged.

The working principle of suprathreshold zero processing is shown in the figure.

image-20211013113055544

For example, if the threshold is selected as 127, then:

  • For pixel values ​​greater than 127, its value will be set to 0.
  • For pixels whose value is less than or equal to 127, the value will keep changing.

If it is represented by an expression, the generation rule of its target value is:

dst ( x , y ) = { 0 , src ( x , y ) > threshsrc ( x , y ) , in other cases dst(x, y) = \begin{cases} 0, \quad src(x, y) >thresh \\ src(x, y), \quad other cases\end{cases}dst(x,y)={ 0,src(x,y)>threshsrc(x,y),other situations, thresh represents a specific threshold.

import cv2
import numpy as np

img = np.random.randint(0, 256, size=[4, 5], dtype=np.uint8)
t, rst = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)
print('img=\n', img)
print('t=\n', t)
print('rst=\n', rst)


# 输出结果
img=
 [[101  38  87  65 182]
 [126 143  54  27 236]
 [233 150 144  30 206]
 [182 174 185 212 115]]
t=
 127.0
rst=
 [[101  38  87  65   0]
 [126   0  54  27   0]
 [  0   0   0  30   0]
 [  0   0   0   0 115]]

5. Low threshold zero processing (cv2.THRESH_TOZERO)

Low threshold zero processing will treat the value of the pixel points less than or equal to the threshold value in the image as 0, and the value of the pixel points greater than the threshold value will remain unchanged. That is, first select a threshold, and then process the image as follows:

  • For pixels with pixel values ​​greater than the threshold, their values ​​will remain unchanged.
  • For pixels whose pixel value is less than or equal to the threshold, its value will be processed as 0.

image-20211013114019979

For example, if the threshold is selected as 127, then:

  • For pixels with pixel values ​​greater than 127, their pixel values ​​will keep changing.
  • For pixels whose pixel values ​​are less than or equal to 127, their pixel values ​​will be set to 0.

If it is represented by an expression, the generation rule of its target value is:

dst ( x , y ) = { src ( x , y ) , src ( x , y ) > thresh 0 , other cases dst(x, y) = \begin{cases} src(x, y), \quad src( x, y) >thresh \\ 0, \quad other cases\end{cases}dst(x,y)={ src(x,y),src(x,y)>thresh0,other situations, thresh represents a specific threshold.

import cv2
import numpy as np

img = np.random.randint(0, 256, size=[4, 5], dtype=np.uint8)
t, rst = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
print('img=\n', img)
print('t=\n', t)
print('rst=\n', rst)


# 输出结果
img=
 [[  2 202  44   8  95]
 [130 187  52 214 168]
 [153 169 199 224 155]
 [  4 232  38 244  97]]
t=
 127.0
rst=
 [[  0 202   0   0   0]
 [130 187   0 214 168]
 [153 169 199 224 155]
 [  0 232   0 244   0]]

two. Adaptive Thresholding:

​ For a color-balanced image, thresholding the image can be done directly using a threshold. However, sometimes the color of the image is unbalanced. At this time, if only one threshold is used, a clear and effective threshold segmentation result image cannot be obtained.

There is an improved thresholding technique, which uses a dynamically changing threshold to complete the thresholding of the image. This technique is called adaptive thresholding . When performing threshold processing, the adaptive threshold processing method obtains a threshold by calculating the weighted average value of the adjacent area around each pixel , and uses the threshold to process the current pixel. Compared with ordinary thresholding methods, adaptive thresholding can better deal with images with large differences between light and dark.

OpenCV provides the function cv2.adaptiveThreshold() to implement adaptive threshold processing. The syntax of this function is:

  • dst=cv.adaptiveThreshold(src,maxValue,adaptiveMethod,thresholdType,blockSize,C)
    • dst: represents the adaptive threshold processing result.
    • src: represents the original image to be processed. It should be noted that the image must be an 8-bit single-channel image.
    • maxValue: represents the maximum value.
    • adaptiveMethod: Represents an adaptive method.
    • thresholdType: Represents the threshold processing method, the value must be one of cv2.THRESH_BINARY or cv2.THRESH_BINARY_INV.
    • blockSize: represents the block size. Indicates the neighborhood size used by a pixel when calculating its threshold, usually 3, 5, 7, etc.
    • C: is a constant.

​ The function cv2.adaptiveThreshold() determines the calculation method of the adaptive threshold according to the parameter adaptiveMethod. The function includes two different methods: cv2.ADAPTIVE_THRESH_MEAN_C and cv2.ADAPTIVE_THRESH_GAUSSIAN_C. Both methods calculate the adaptive threshold pixel by pixel, and the adaptive threshold is equal to the weighted average of each pixel's neighborhood specified by the parameter blockSize minus the constant C. Two different methods differ in the way they compute the weighted average of neighborhoods:

  • cv2.ADAPTIVE_THRESH_MEAN_C: The weight values ​​of all pixels in the neighborhood are consistent.
  • cv2.ADAPTIVE_THRESH_GAUSSIAN_C: It is related to the distance from each pixel point in the neighborhood to the center point, and the weight value of each point is obtained through the Gaussian equation.
import cv2

img = cv2.imread('../sugar.tiff', 0)
print(img)
t1, thd = cv2.threshold(img, 200, 255, cv2.THRESH_BINARY)
athdMEAN = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 3, 3)
athdGAUS = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 3, 3)
cv2.imshow('img', img)
cv2.imshow('thd', thd)
cv2.imshow('athdMEAN', athdMEAN)
cv2.imshow('athdGAUS', athdGAUS)
cv2.waitKey()
cv2.destroyAllWindows()

image-20211013153325121

By comparing ordinary threshold processing with adaptive threshold processing, it can be found that adaptive threshold processing retains more detailed information. In some extreme cases, ordinary thresholding will lose a lot of information, but adaptive thresholding can get better binary images.

three. Otsu processing:

​ When using the function cv2.threshold() for thresholding, you need to customize a threshold and use this threshold as the basis for image thresholding. Usually the processed images are all color balanced, and it is more appropriate to directly set the threshold to 127. However, sometimes the gray level of the image is unevenly distributed. If the threshold is set to 127 at this time, the result of threshold processing will fail. For example, there is an image img, the pixel value inside is:

image-20211013155455573

At this point, if 127 is used as the threshold, the threshold processing result is:

image-20211013155549062

Obviously, this is not the result we want. We can observe that for img, if the threshold is 125 for segmentation, better results can be obtained:

image-20211013155632125

​ However, the actual processed images are often very complex, and it is impossible to observe the most suitable threshold at a glance like the above img. If you try one by one, the workload will undoubtedly be huge.

The Otsu method can give the best inter-class segmentation threshold according to the current image . In short, Otsu's method iterates through all possible thresholds to find the best one.

​In OpenCV, by passing an additional parameter "cv2.THRESH_OTSU" to the type of the parameter type in the function cv2.threshold(), the threshold segmentation of the Otsu method can be realized.

​ It should be noted that when using the Otsu method, the threshold should be set to 0 . The function cv2.threshold() at this time will automatically find the optimal threshold and return the threshold. For example, the following statement makes the function cv2.threshold() use Otsu's method for threshold segmentation:

  • t, otsu=cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

The difference from ordinary threshold segmentation is that:

  • The parameter type adds a parameter value "cv2.THRESH_OTSU".
  • The set threshold is 0.
  • The return value t is the optimal threshold calculated and used by the Otsu method.

Example 1:

import cv2
import numpy as np

img = np.zeros((5, 5), dtype=np.uint8)
img[0:6, 0:6] = 123
img[2:6, 2:6] = 126
t1, thd = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
t2, otsu = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)

print('img=\n', img)
print('thd=\n', thd)
print('otsu=\n', otsu)

# 输出结果
img=
 [[123 123 123 123 123]
 [123 123 123 123 123]
 [123 123 126 126 126]
 [123 123 126 126 126]
 [123 123 126 126 126]]
thd=
 [[0 0 0 0 0]
 [0 0 0 0 0]
 [0 0 0 0 0]
 [0 0 0 0 0]
 [0 0 0 0 0]]
otsu=
 [[  0   0   0   0   0]
 [  0   0   0   0   0]
 [  0   0 255 255 255]
 [  0   0 255 255 255]
 [  0   0 255 255 255]]

Example 2:

import cv2

img = cv2.imread('../lena.bmp', 0)
t1, thd = cv2.threshold(img, 50, 255, cv2.THRESH_BINARY)
t2, otsu = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
cv2.imshow('img', img)
cv2.imshow('thd', thd)
cv2.imshow('otus', otsu)
cv2.waitKey()
cv2.destroyAllWindows()

image-20211013161802362

Guess you like

Origin blog.csdn.net/weixin_57440207/article/details/122646992