Computer Vision

Thresholding

Thresholding chính là nhị phân hóa hình ảnh, chuyển đổi grayscale image thành binary image. Thông thường chúng ta sử dụng thresholding để tập trung vào các đối tượng hoặc vùng hình ảnh cần quan tâm.


SIMPLE THRESHOLDING

Simple thresholding cần sự can thiệp của người dùng. Chúng ta cần phải chọn một giá trị threshold T, tất cả các pixel có cường độ nhỏ hơn T sẽ được set bằng 0, cường độ lớn hơn T sẽ được set bằng 255.

Function:

(thresholdValue, thresholdedImage) = cv2.threshold(grayscaleImage, thresholdValue, maxValue, thresholdingMethod)

Trong đó:

  • thresholdValue – giá trị threshold được áp dụng.
  • maxValue –  các pixel có cường độ lớn hơn thresholdValue sẽ được set bằng giá trị này.
  • thresholdingMethod – cv2.THRESH_BINARY, cv2.THRESH_BINARY_INV, cv2.THRESH_TRUNC, cv2.THRESH_TOZERO, cv2.THRESH_TOZERO_INV.

Example 1: Simple thresholding

import numpy as np
import cv2

image = cv2.imread("image_path")
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(image, (5, 5), 0)
cv2.imshow("Image", image)
cv2.imshow("Blurred", blurred)

(T, thresh) = cv2.threshold(blurred, 155, 255, cv2.THRESH_BINARY)
cv2.imshow("Threshold Binary", thresh)

(T, threshInv) = cv2.threshold(blurred, 155, 255, cv2.THRESH_BINARY_INV)
cv2.imshow("Threshold Binary Inverse", threshInv)

cv2.imshow("Coins", cv2.bitwise_and(image, image, mask = threshInv))
cv2.waitKey(0)

Output:
simplethresh

Figure 1 – Simple thresholding

Trước khi áp dụng thresholding, chúng ta cần thực hiện blurring để giảm nhiễu cho hình ảnh. Ở example 1 tôi đã sử dụng Gaussian Blurring.

Ở function cv2.threshold(), hình ảnh sau khi được blurring sẽ được áp dụng threshold=155, các pixel có cường độ lớn hơn 155 đều được set bằng 255, ngược lại thì được set bằng 0 (hình Threshold Binary).

Ngoài ra chúng ta có thể thực hiện inverse thresholding với method cv2.THRESH_BINARY_INV (hình Threshold Binary Inverse).


ADAPTIVE THRESHOLDING

Một điểm trừ của Simple Thresholding là chúng ta phải cung cấp một giá trị threshold, điều này không hữu ích khi bức ảnh có nhiều khoảng cường độ khác nhau (do điều kiện ánh sáng không đồng nhất), chưa kể đến việc xác định được giá trị threshold phù hợp là không đơn giản và yêu cầu nhiều kinh nghiệm.

Để giải quyết vấn đề này chúng ta sử dụng Adaptive Thresholding, phương pháp này xem xét từng block điểm ảnh (tương tự các phương pháp Blurring) và tìm ra giá trị threshold tối ưu cho các block này.

Function:

thresholdedImage = cv2.adaptiveThreshold(grayscaleImage, maxValue, adaptiveMethod, thresholdingMethod, blockSize, const)

Trong đó:

  • adaptiveMethod – phương pháp để tính toán giá trị threshold cho từng block điểm ảnh. Bao gồm: cv2.ADAPTIVE_THRESH_MEAN_C, cv2.ADAPTIVE_THRESH_GAUSSIAN_C.
  • threshodingMethod – cv2.THRESH_BINARY, cv2.THRESH_BINARY_INV.
  • blockSize – kích thước của block điểm ảnh, bắt buộc là một số lẻ (tương tự như Kernel).
  • const – It is just a constant which is subtracted from the mean or weighted mean calculated, allow us to fine-tune our thresholding.

Example 2: Adaptive thresholding

import numpy as np
import cv2

image = cv2.imread("image_path")
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(image, (5, 5), 0)
cv2.imshow("Image", image)

thresh = cv2.adaptiveThreshold(blurred, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 5, 3)
cv2.imshow("Mean thresh", thresh)

thresh = cv2.adaptiveThreshold(blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 5, 3)
cv2.imshow("Gaussian Thresh", thresh)

cv2.waitKey(0)

Output:

adaptivethresh

Figure 2 – Adaptive thresholding

Để lựa chọn giữa Mean Adaptive Thresholding và Gaussian Adaptive Thresholding cần có một vài kinh nghiệm.

Hai tham số quan trọng để điều chỉnh là blockSize và const. Khi đã có kinh nghiệm, chúng ta có thể tạo ra thay đổi lớn trong kết quả thresholding.


OTSU THRESHOLDING

Đây là một phương pháp khác để tự động tính toán giá trị threshold.

Phương pháp Otsu giả sử có hai đỉnh trong Grayscale Histogram của image (của foreground và background) và nó cố gắng tìm ra giá trị threshold tối ưu để tách biệt hai đỉnh này.

Function:

(thresholdValue, thresholdedImage) = cv2.threshold(grayscaleImage, thresholdValue, maxValue, thresholdingMethod | cv2.THRESH_OTSU)

Chúng ta sẽ sử dụng function cv2.threshold() với giá trị threshold tùy ý và flag cv2.THRESH_OTSU.

Example 4: Otsu thresholding with cv2.threshold()

import numpy as np
import cv2

image = cv2.imread("image")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5,5), 0)
cv2.imshow("Image", image)

T, thresh = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
cv2.imshow("thresholded", thresh)
print("Otsu's thresholding value: {}".format(T))
cv2.waitKey(0)

Output:
Otsu’s thresholding value: 137.0

otsu

Giá trị thresholding được tính toán bởi Otsu method bằng 137.

Otsu thresholding còn được gọi là “Global thresholding”, tức là một giá trị threshold sẽ được áp dụng cho toàn bộ bức ảnh (khác với Adaptive Thresholding). Như đã giới thiệu ở trước, Otsu thresholding sẽ giả sử trong Grayscale Histogram của bức ảnh có 2 đỉnh nhằm phân biệt foreground và background, nếu bức ảnh không đáp ứng được yêu cầu này (do điều kiện ánh sáng không đồng nhất) thì kết quả của Otsu thresholding sẽ không như mong đợi, lúc này chúng ta sẽ phải sử dụng Adaptive Thresholding.


FLOODFILL

Với kết quả từ các phương pháp thresholding ở phía trên, có thể thấy rằng nếu sử dụng ngay thresholdedImage để làm mask (nhằm tách các đồng xu ra khỏi background) thì sẽ không ổn lắm. Để loại bỏ các mảng đen trong từng đồng xu, chúng ta có thể áp dụng function cv2.floodFill() hoặc kỹ thuật Contour Detection (sẽ được giới thiệu ở bài viết khác).

Function:

cv2.floodFill(image, mask, seedPoint, newValue)

Trong đó:

  • image – input/output image. Vì input image sẽ bị biến đổi, không nên truyền trực tiếp Original Image vào function, thay vào đó hãy copy nó;
  • mask – operation mask, được khởi tạo là zeros, có kích thước lớn hơn 2 pixels (cả width và height) so với image;
  • seedPoint – điểm bắt đầu;
  • newValue – giá trị mới được áp dụng cho các pixel bị “ngập”.

Example:

import numpy as np
import cv2

image = cv2.imread("coins.png")
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(image, (5, 5), 0)
cv2.imshow("Image", image)
cv2.imshow("Blurred", blurred)

(T, threshInv) = cv2.threshold(blurred, 155, 255, cv2.THRESH_BINARY_INV)
cv2.imshow("Threshold Binary Inverse", threshInv)
cv2.imshow("Coins 1", cv2.bitwise_and(image, image, mask = threshInv))

threshFilled = threshInv.copy()
mask = np.zeros((threshInv.shape[0]+2, threshInv.shape[1]+2), dtype="uint8")
cv2.floodFill(threshFilled, mask, (0,0), 255)
threshFilled = cv2.bitwise_not(threshFilled)
threshFilled = cv2.bitwise_or(threshFilled, threshInv)

cv2.imshow("Threshold Binary Filled", threshFilled)
cv2.imshow("Coins 2", cv2.bitwise_and(image, image, mask = threshFilled))
cv2.waitKey(0)

Output:

Capture


SUMMARY

Cảm ơn các bạn đã theo dõi bài viết. Thân ái và quyết thắng.

Reference:
[1] OpenCV: Image thresholding.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s