Thị giác máy tính với OpenCV-Python Bài 3, Phần 1: Các thao tác cơ bản trên hình ảnh
09:37 - 14/12/2021
Trong phần này chúng ta sẽ tìm hiểu cách: truy cập các giá trị pixel và sửa đổi chúng; truy cập thuộc tính hình ảnh; đặt vùng ảnh (ROI); tách và hợp nhất hình ảnh.
Thị giác máy tính với OpenCV-Python Bài 7 Phần 2: Phát hiện người đi bộ trong video
Thị giác máy tính với OpenCV-Python Bài 7 Phần 1: Phát hiện người đi bộ trong hình ảnh
Thị giác máy tính với OpenCV-Python Bài 6 Phần 2: Phép trừ nền
Thị giác máy tính với OpenCV-Python Bài 6 Phần 1: Bắt bám đối tượng với Meanshift và Camshift
Hầu như tất cả các thao tác trong phần này chủ yếu liên quan đến Numpy hơn là OpenCV.
Truy cập và sửa đổi giá trị pixel
Trước tiên, hãy tải một hình ảnh màu:
- import cv2
- import numpy as np
- img = cv2.imread('messi.jpg')
Có thể truy cập một giá trị pixel theo tọa độ hàng và cột của nó. Đối với hình ảnh BGR, nó trả về một mảng các giá trị Xanh lam, Xanh lục, Đỏ. Đối với hình ảnh thang độ xám, chỉ trả về cường độ tương ứng. Xem ví dụ minh họa dưới đây:
- px = img[100, 100]
- print (px) //[8 5 1] (kết quả tùy vào mỗi bức ảnh)
- # accessing only blue pixel
- blue = img[100, 100, 0]
- print (blue) //8
Bạn có thể sửa đổi các giá trị pixel theo cách tương tự.
- img[100, 100] = [255, 255, 255]
- print (img[100, 100]) //[255 255 255]
Ghi chú:
Numpy là một thư viện được tối ưu hóa để tính toán mảng nhanh. Vì vậy, thao tác truy cập từng giá trị pixel và sửa đổi nó sẽ rất chậm và không được khuyến khích.
Phương thức được đề cập ở trên thường được sử dụng để chọn một vùng của mảng, chẳng hạn như 5 hàng đầu tiên và 3 cột cuối cùng. Đối với quyền truy cập pixel riêng lẻ, các phương thức mảng numpy như array.item() và array.itemset() được coi là tốt hơn cả. Nhưng nó luôn trả về một đại lượng vô hướng. Vì vậy, nếu muốn truy cập tất cả các giá trị B, G, R, chúng ta cần gọi array.item() riêng cho từng loại.
Phương pháp chỉnh sửa và truy cập pixel tốt hơn:
- # accessing RED value
- img.item(10, 10, 2) //3
- # modifying RED value
- img.itemset((10, 10, 2), 100)
- img.item(10, 10, 2) //100
Truy cập thuộc tính hình ảnh
Thuộc tính hình ảnh bao gồm số hàng, cột và kênh, loại dữ liệu hình ảnh, số lượng pixel, v.v.
Hình dạng của hình ảnh được truy cập bởi “img.shape”. Nó trả về một số lượng hàng, cột và kênh (nếu hình ảnh có màu):
- print (img.shape) //(1200, 1200, 3)
Ghi chú:
Nếu hình ảnh có thang độ xám, tuple trả về chỉ chứa số hàng và cột. Vì vậy, đây là một phương pháp tốt để kiểm tra xem hình ảnh được tải là hình ảnh xám hay hình ảnh màu.
Tổng số pixel được truy cập bởi img.size:
- print (img.size) //4320000
Kiểu dữ liệu hình ảnh được lấy bởi `img.dtype`:
- print (img.dtype) //uint8
Ghi chú:
img.dtype rất quan trọng vì một số lượng lớn lỗi trong các đoạn code OpenCV-Python là do kiểu dữ liệu không hợp lệ.
ROI của hình ảnh
Đôi khi, cần phải chọn ra một vùng hình ảnh nhất định. Để phát hiện mắt trong hình ảnh, đầu tiên phải khoanh vùng ra khuôn mặt, sau khi thu được khuôn mặt, chỉ cần tìm kiếm đôi mắt bên trong nó thay vì tìm kiếm toàn bộ hình ảnh. Thuật toán này giúp cải thiện độ chính xác (vì mắt luôn ở trên khuôn mặt) và hiệu suất (vì tìm kiếm một khu vực nhỏ).
ROI nhận được bằng cách sử dụng lập chỉ mục Numpy. Ở đây chúng ta cần chọn quả bóng và sao chép nó sang một vùng khác trong hình ảnh:
- img_resize = cv2.resize(img, (600, 600)) //resize image to (600, 600)
- ball = img_resize[25:150, 225:350]
- img[400:525, 400:525] = ball
Kết quả nhận được như hình bên dưới:
Tách và hợp nhất các kênh hình ảnh
Đôi khi cần phải làm việc riêng rẽ trên các kênh B, G, R của hình ảnh, khi đó, cần chia các hình ảnh BGR thành những mặt phẳng đơn lẻ. Cũng có trường hợp cần phải gộp các kênh riêng lẻ này vào hình ảnh BGR. Có thể làm điều đó đơn giản bằng cách:
- b,g,r = cv2.split(img)
- img = cv2.merge(b ,g, r)
Hoặc là
- b = img[:,:,0]
Giả sử, bạn muốn biến tất cả các pixel màu đỏ thành 0, bạn không cần chia rồi mới đặt nó bằng 0. Có thể đơn giản sử dụng lập chỉ mục Numpy như sau:
- img[:,:,2] = 0
Ghi chú:
cv2.split() là một thao tác tốn thời gian. Vì vậy, hãy thực hiện nó chỉ khi thực sự cần. Nếu không, tốt nhất là lập chỉ mục Numpy.
Tạo viền cho hình ảnh (Padding)
Nếu bạn muốn tạo một đường viền xung quanh hình ảnh, kiểu như khung ảnh, bạn có thể sử dụng hàm cv2.copyMakeBorder(). Hàm này nhận các đối số sau:
- src - hình ảnh đầu vào.
- top, bottom, left, right - chiều rộng đường viền theo số pixel theo các hướng tương ứng.
- borderType - Cờ xác định loại đường viền sẽ được thêm vào. Có thể là các loại sau:
- BORDER_CONSTANT - Thêm đường viền có màu không đổi. Giá trị sẽ được đưa ra làm đối số tiếp theo.
- BORDER_REFLECT - Đường viền sẽ là phản chiếu của các phần tử gần viền, theo kiểu như sau: fedcba | abcdefgh | hgfedcb.
- BORDER_REFLECT_101 hoặc cv2.BORDER_DEFAULT - Tương tự như trên, nhưng có một chút thay đổi: gfedcb | abcdefgh | gfedcba.
- BORDER_REPLICATE - Phần tử cuối cùng được sao chép, theo kiểu như sau: aaaaaa | abcdefgh | hhhhhhh.
- BORDER_WRAP – Phản chiếu một phần, theo kiểu như sau: cdefgh | abcdefgh | abcdefg.
- value - Màu của đường viền nếu loại đường viền là cv2.BORDER_CONSTANT.
Xem đoạn code dưới đây để hiểu rõ hơn:
- import cv2
- import numpy as np
- from matplotlib import pyplot as plt
- BLUE = [255, 0, 0]
- img1 = cv2.imread('opencv.png')
- replicate = cv2.copyMakeBorder(img1, 10, 10, 10, 10, cv2.BORDER_REPLICATE)
- reflect = cv2.copyMakeBorder(img1, 10, 10, 10, 10, cv2.BORDER_REFLECT)
- reflect101 = cv2.copyMakeBorder(img1, 10, 10, 10, 10, cv2.BORDER_REFLECT_101)
- wrap = cv2.copyMakeBorder(img1, 10, 10, 10, 10, cv2.BORDER_WRAP)
- constant= cv2.copyMakeBorder(img1, 10, 10, 10, 10, cv2.BORDER_CONSTANT,value=BLUE)
- plt.subplot(231), plt.imshow(img1, 'gray'), plt.title('ORIGINAL')
- plt.subplot(232), plt.imshow(replicate, 'gray'), plt.title('REPLICATE')
- plt.subplot(233), plt.imshow(reflect, 'gray'), plt.title('REFLECT')
- plt.subplot(234), plt.imshow(reflect101, 'gray'), plt.title('REFLECT_101')
- plt.subplot(235), plt.imshow(wrap, 'gray'), plt.title('WRAP')
- plt.subplot(236), plt.imshow(constant, 'gray'), plt.title('CONSTANT')
- plt.show()
Xem kết quả bên dưới. (Hình ảnh được hiển thị bằng matplotlib. Vì vậy, các mặt phẳng ĐỎ và XANH sẽ được hoán đổi cho nhau):
Bài tiếp theo chúng ta sẽ tìm hiểu một số phép toán cơ bản trên hình ảnh.
(Sưu tầm)
VIỆN IMC
Tòa nhà IMC Tower, Số 176 Trường Chinh, Phường Khương
Thượng, Quận Đống Đa, Thành phố Hà Nội, Việt Nam
Tel/Fax : (+84) 24 3566 6232 / 24 3566 6234
Email: contact@imc.org.vn Website: https://imc.org.vn