Giải thuật phân vùng Region Growing trên ảnh màu
OpenCV - tut 14: Region Growing
Môi trường "hành sự"
- Linux (bài viết sử dụng Ubuntu 16.04)
- OpenCV (bài viết sử dụng OpenCV 3.4.1)
- Python (bài viết sử dụng Python 3.5.5)
- Ảnh mẫu để xử lý: jung_yun.jpg
Bạn có thể download ảnh mẫu về:
jung_yun.jpg (Nguồn: Hình ảnh nghệ sĩ Jung Yun)
Giải thuật Region Growing
Giải thuật Region Growing (tạm dịch: lan vùng) là giải thuật phân vùng dựa trên mức sáng. Từ các điểm xác định trước trên ảnh (seed), vùng phân đoạn ảnh sẽ được lan dần rộng ra các pixel liền kề xung quanh, nếu các pixel liền kề có mức sáng gần tương tự mức sáng của điểm xác định trước (seed) (dựa theo ngưỡng) thì chúng là các điểm ta cần phân đoạn. Tiếp tục lặp lại quá trình lan rộng ra xung để thu thập các pixel hợp lệ theo mức sáng thì ta được kết quả phân đoạn ảnh.
Diễn giải trên có thể hơi khó hình dùng, các bạn có thể tham khảo thêm trên Wikipedia: https://en.wikipedia.org/wiki/Region_growing
Hiện thực giải thuật Region Growing trên ảnh màu
Prototype về ý tưởng hiện thực giải thuật region growing trên ảnh màu:
- Vì ý tưởng là ta sẽ bắt đầu lan màu từ các điểm, do đó ta sẽ dùng 1 stack để chứa các điểm tọa độ 2D sẽ duyệt. Biến đó chính là loop_points trong code hiện thực bên dưới.
- Ta tiến hành lặp trên stack để xem xét từng tọa độ, nếu mức sáng tại đó thỏa yêu cầu (hiệu mức sáng trên 3 kênh đều nhỏ hơn ngưỡng delta), ta sẽ đánh dấu chúng lên mặt nạ mask để cho biết rằng pixel ở tọa độ này đã duyệt rồi (nhằm tránh duyệt trùng í mà :P). Đường nhiên mask có cùng kích thước so với ảnh.
- Thêm 8 điểm hàng xóm xung quanh pixel hợp lệ đang xét vào stack! Lưu ý rằng phải kiểm tra rằng các "bà hàng xóm" này đã được duyệt chưa, cũng như tọa độ có hợp lệ không rồi mới đưa vào stack cho lần lặp tới.
Vậy là xong giải thuật Region Growing rồi đó các bạn. Một số nhận xét về giải thuật region growing này:
- Lan trên vùng cùng màu với điểm khởi tạo.
- Nhạy với ánh sáng, màu sắc.
- Nhạy với nhiễu (do đó nên áp dụng làm mờ để giảm nhiễu), nếu để ý, bạn sẽ thấy code bên dưới mình lọc Gauss trước khi chạy giải thuật region growing.
- Chi phí tính toán lớn với ảnh có độ phân giải cao. Ý tưởng optimize cho trường hợp này: bạn có thể resize về ảnh bé để chạy region growing, sau đó resize mask về kích thước gốc. Dĩ nhiên sẽ hy sinh một chút độ chính xác.
- Do lan dần ra các pixel xung quanh nên ta phải lặp tuần tự, khó tối ưu hóa bằng xử lý song song.
- Ngưỡng delta cho mức sáng / màu sắc là quan trọng! Nó là siêu tham số cần điều chỉnh phù hợp.
- Kết quả phân đoạn phụ thuộc vào điểm gốc ban đầu (seed) và ngưỡng delta thiết lập.
import os
import cv2
import numpy as np
def region_growing(img, x, y, delta=15):
"""
x: relative coord (0-1)
y: relative coord (0-1)
"""
def is_valid_px(x, y, img):
if x>=0 and x<img.shape[1] and y>=0 and y<img.shape[0]:
return True
return False
mask = np.zeros(img.shape[:2])
y_abs = int(y * img.shape[0])
x_abs = int(x * img.shape[1])
loop_points = []
loop_points.append((x_abs, y_abs))
px_intensity = img[y_abs, x_abs, :]
blue = px_intensity[0]
green = px_intensity[1]
red = px_intensity[2]
while len(loop_points) != 0:
(cx, cy) = loop_points.pop()
current_intensity = img[cy, cx, :]
b = int(current_intensity[0])
g = int(current_intensity[1])
r = int(current_intensity[2])
if abs(r-red)<delta and abs(g-green)<delta and abs(b-blue)<delta:
mask[cy, cx] = 255
neighbors = [
(cx-1, cy),
(cx+1, cy),
(cx-1, cy-1),
(cx, cy-1),
(cx+1, cy-1),
(cx-1, cy+1),
(cx, cy+1),
(cx+1, cy+1)
]
for (nx, ny) in neighbors:
if is_valid_px(nx, ny, img) and mask[ny, nx] == 0:
loop_points.append((nx, ny))
else:
pass
return mask
def main(img_path):
img = cv2.imread(img_path, cv2.IMREAD_COLOR)
KERNEL_WIDTH = KERNEL_HEIGHT = 5
SIGMA_X = SIGMA_Y = 2
img[:,:,0] = cv2.GaussianBlur(img[:,:,0], ksize=(KERNEL_WIDTH, KERNEL_HEIGHT), sigmaX=SIGMA_X, sigmaY=SIGMA_Y)
img[:,:,1] = cv2.GaussianBlur(img[:,:,1], ksize=(KERNEL_WIDTH, KERNEL_HEIGHT), sigmaX=SIGMA_X, sigmaY=SIGMA_Y)
img[:,:,2] = cv2.GaussianBlur(img[:,:,2], ksize=(KERNEL_WIDTH, KERNEL_HEIGHT), sigmaX=SIGMA_X, sigmaY=SIGMA_Y)
cv2.imwrite('out_blur_'+os.path.basename(img_path), img)
x = 0.5
y = 0.6
delta = 20
mask = region_growing(img=img, x=x, y=y, delta=delta)
cv2.imwrite('out_mask.jpg', mask)
img_color = cv2.imread(img_path, cv2.IMREAD_COLOR)
img_color[mask>0,:] = (0, 255, 255)
cv2.circle(img_color, (int(x*img.shape[1]), int(y*img.shape[0])), radius=5, color=(0, 0, 255), thickness=2)
cv2.imwrite('out_seg_'+os.path.basename(img_path), img_color)
if __name__ == "__main__":
print('Running...')
main('jung_yun.jpg')
print('* Follow me @ ' + "\x1b[1;%dm" % (34) + ' https://www.facebook.com/minhng.info/' + "\x1b[0m")
print('* Join GVGroup for discussion @ ' + "\x1b[1;%dm" % (34) + 'https://www.facebook.com/groups/ip.gvgroup/' + "\x1b[0m")
print('* Thank you ^^~')
Ảnh kết quả sau khi chạy phân vùng nè. Chấm đó chính là điểm seed, vùng màu vàng là kết quả phân vùng:
Cảm ơn bạn đã theo dõi bài viết. Hãy kết nối với tớ nhé!
- Minh: https://www.facebook.com/minhng.info
- Khám phá xử lý ảnh - GVGroup: https://www.facebook.com/groups/ip.gvgroup
Khám phá xử lý ảnh - GVGroup
Danh sách bài viết series OpenCV:
- Hashtag #OpenCV
- Tut 1: Xử lý ảnh - OpenCV đọc ghi hình ảnh (code Python và C++)
- Tut 1.1: Xử lý ảnh - Cấu trúc dữ liệu ảnh trong OpenCV. Pixel là gì?
- Tut 1.2: Xử lý ảnh - Chuyển đổi ảnh OpenCV sang Pillow và ngược lại
- Tut 2: Xử lý ảnh - OpenCV resize, crop và padding hình ảnh (code Python và C++)
- Tut 3: Xử lý ảnh - OpenCV biến đổi mức sáng hình ảnh (code Python)
- Tut 4: Xử lý ảnh - OpenCV vùng quan tâm (ROI) là gì? (code Python)
- Tut 4.1: Xử lý ảnh - OpenCV: vẽ văn bản, đường thẳng, mũi tên, hình chữ nhật, hình tròn, ellipse, đa giác
- Tut 4.2: Xử lý ảnh - Pha trộn ảnh trong OpenCV (blending)
- Tut 5: Xử lý ảnh - OpenCV ảnh nhị phân
- Tut 6: Xử lý ảnh - OpenCV cân bằng sáng (histogram equalization)
- Tut 7: Xử lý ảnh - OpenCV kỹ thuật cửa sổ trượt (sliding window)
- Tut 8: Xử lý ảnh - Convolution là gì?
- Tut 9: Xử lý ảnh - Làm mờ ảnh (blur)
- Tut 10: Xử lý ảnh - Gradient của ảnh là gì?
- Tut 11: Xử lý ảnh - Phát hiện cạnh Canny (Canny Edge Detection)
- Tut 12: Xử lý ảnh - Phát hiện đường thẳng bằng Hough Transform (Hough Line)
- Tut 13: Xử lý ảnh - Hiện thực phát hiện đoạn thẳng dùng Hough Transform (Hough Line)
- Tut 14: Xử lý ảnh - Giải thuật phân vùng Region Growing trên ảnh màu
- Tut 15: Xử lý ảnh - Giải thuật Background Subtraction trên ảnh màu
- Tut 16: Xử lý ảnh - Frame Subtraction để phát hiện chuyển động trong video
- Tut 17: Xử lý ảnh - HOG - Histograms of Oriented Gradients
- Tut 18: Xử lý ảnh - HOG - Huấn luyện mô hình phân loại người
- Tut 19: Xử lý ảnh - HOG - Phát hiện người
- Tut 20: Xử lý ảnh - Tổng hợp kinh nghiệm xử lý ảnh (End)
- Tut 21: Xử lý ảnh - Hiện thực trích đặc trưng Local Binary Patterns (LBP)
- Tut 22: Xử lý ảnh - Trích đặc trưng Gabor filters