[Part 2] Edge Detection với OpenCV
Bài đăng này đã không được cập nhật trong 4 năm
1. Lời mở đầu
Ở phần 1 về chủ đề Edge Detection, mình đã trình bày về tiêu chuẩn đánh giá một detector như thế nào là một detector tốt cũng như thuật toán Sobel Edge Detection và ví dụ cụ thể. Các bạn có thể xem lại ở đây. Ở phần 2 này, mình xin tiếp tục trình bày về thuật toán Canny Edge Detection. Đây là một thuật toán xác định cạnh được sử dụng phổ biến và hiệu quả trong các bài toán về thị giác máy tính.
2. Thuật toán Canny Edge Detection
Một cách tổng quát, thuật toán Canny Edge Detection gồm có 4 bước:
- Lấy đạo hàm của ảnh theo chiều ngang và dọc theo phân phối Gaussian
- Tính cường độ và hướng của gradient
- Non-maximum supression (phần này mình xin để tiếng anh vì không biết nói như nào )
- Sử dụng threshold để tạo loại bỏ cạnh gỉa, xác định cạnh thực sự (real edge)
Sau đây chúng ta lần lượt khám phá ý nghĩa từng bước một. Ở đây mình dùng một ảnh dưới đây là ảnh đầu vào cho tất cả các ví dụ bên dưới
2.1. Lấy đạo hàm theo phân phối Gaussian
Cũng tương tự như khi xử lý với thuật toán Sobel, ta lấy đạo hàm theo chiều ngang x (Gx) và chiều dọc y (Gy) . Do hướng của gradient luôn vuông góc với các cạnh nên ảnh lấy đạo hàm theo chiềng ngang x (Gx) sẽ thu được các nét dọc còn ảnh lấy đạo hàm theo chiều dọc (Gy) sẽ thu được các nét ngang của bức ảnh
2.2. Tính cường độ và hướng gradient
Từ kết quả tính đạo hàm theo hướng x và y trên ảnh đầu vào, ta tính cường độ gradient và hướng của mỗi pixel theo công thức :
- Cường độ gradient = sqrt( Gx^2 + Gy^2)
- Hướng của gradient: theta = atan2(gy, gx)
2.3. Non-maximum supression
Các bạn có thể nhìn thấy cô gái trong ảnh của phần kết quả 2.2 có nhiều đường biên bao quanh cơ thể dày hơn ảnh gốc. Đó là do có nhiều pixel cùng miêu tả một pixel trên cạnh của ảnh ban đầu. Vậy để làm những đường biên trở nên sắc nét và mảnh hơn giống ảnh gốc ta sử dụng Non-maximum supression để loại bỏ những pixel thừa.
Mình giải thích thuật toán Non-maximum supression như sau: Với một pixel A được xác định nằm trên một cạnh. Ta sẽ có vector gradient direction luôn vuông góc với cạnh edge. Trên vector gradient direction ta có thể có nhiều pixel ví dụ ở đây là B và C. Ba pixel A, B, C cùng miêu tả một pixel trên cạnh ban đầu nên ta phải so sánh giá trị giữa A, B và C xác định đâu là pixel nào có giá trị lớn nhất. Sau đó loại bỏ hai pixel còn lại bằng cách đặt chúng bằng 0.
Các bạn có nhìn thấy sự khác biệt rõ ràng ảnh bên trái chưa sử dụng Non-maximum supression thì các nét sẽ dày và đậm hơn còn sau khi áp dụng thuật toán ta thu được cạnh với những nét mảnh hơn do loại bỏ các pixel thừa.
2.4. 'Hysteresis' thresholding
Bước này sẽ quyết định một cạnh ta dự đoán ở các bước trên nó có phải là một cạnh thật sự hay không. Giá trị threshold ở đây có hai ngưỡng Vmax và Vmin . Ta triển khai thuật toán dựa vào hai giá trị này như sau:
- Nếu cường độ của gradient(Magnitude) > Vmax thì đó là chắc chắn là một cạnh (strong edge)
- Nếu Magnitude < low threshold < Vmin thì đó là noise
- Nếu Vmin < Magnitude < Vmax thì đó là một cạnh yếu chưa xác định được là cạnh hay nhiễu (weak edge)
- Đối với những weak edge, nếu cạnh nào có kết nối với một strong edge thì weak edge đó là cạnh, nếu không sẽ là noise
Ví dụ ở hình trên A nằm trên ngưỡng Vmax nên A chắc chắn là cạnh (strong edge). C nằm giữa ngường Vmax và Vmin nên C là cạnh yếu (weak edge) nhưng C kết nối với strong edge là A nên C cũng là một cạnh. Tuy nhiên B cũng nằm trong ngưỡng giống C nhưng không kết nối với cạnh nào chắc chắn là cạnh (strong edge) nên B là nhiễu
3. Ví dụ với Canny Edge Detection
def canny_edge_detection(image_path, blur_ksize=5, threshold1=100, threshold2=200):
"""
image_path: link to image
blur_ksize: Gaussian kernel size
threshold1: min threshold
threshold2: max threshold
"""
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_gaussian = cv2.GaussianBlur(gray,(blur_ksize,blur_ksize),0)
img_canny = cv2.Canny(img_gaussian,threshold1,threshold2)
return img_canny
image_path = 'example.png'
gray = cv2.imread(image_path, 0)
img_canny = canny_edge_detection(image_path, 25, 50, 100)
Ta có thể điều chỉnh tham số threshold1 và threshold2 để tùy chỉnh cho từng bức ảnh bạn áp dụng thuật toán.
Cảm ơn các bạn đã theo dõi bài viết của mình. Nhớ like và subrice để ủng hộ bài viết của mình nha
All rights reserved