Thị giác máy tính với OpenCV-Python Bài 4, Phần 9: Biểu đồ hình ảnh
10:03 - 06/01/2022
Trong phần này chúng ta sẽ cùng tìm hiểu cách vẽ và sử dụng biểu đồ (histogram) thông qua các hàm cv2.calcHist() và np.histogram().
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
A. Khái niệm
Biểu đồ của hình ảnh là gì? Có thể coi biểu đồ là một biểu đồ cung cấp ý tưởng tổng thể về sự phân bố cường độ của một hình ảnh. Đó là một biểu đồ có các giá trị pixel (không phải lúc nào cũng nằm trong khoảng từ 0 đến 255) theo trục X và số pixel tương ứng trong hình ảnh trên trục Y.
Nó chỉ là một cách hiểu khác của hình ảnh. Bằng cách nhìn vào biểu đồ của một hình ảnh, chúng ta có được trực giác về độ tương phản, độ sáng, phân bố cường độ,... của hình ảnh đó. Hầu hết tất cả các công cụ xử lý ảnh hiện nay, đều cung cấp các tính năng trên biểu đồ.
B. Tìm biểu đồ
Bây giờ chúng ta có một ý tưởng về biểu đồ là gì, chúng ta có thể xem xét cách tìm ra nó. Cả OpenCV và Numpy đều có sẵn hàm thực hiện cho việc này. Trước khi sử dụng các hàm đó, chúng ta cần hiểu một số thuật ngữ liên quan đến biểu đồ.
- BINS : Biểu đồ hiển thị số lượng pixel cho mọi giá trị pixel, tức là từ 0 đến 255. Tức là bạn cần 256 giá trị để hiển thị biểu đồ. Nhưng hãy xem xét, điều gì sẽ xảy ra nếu bạn không cần tìm số lượng pixel cho tất cả các giá trị pixel một cách riêng biệt, mà là số lượng pixel trong một khoảng giá trị pixel? Ví dụ: bạn cần tìm số pixel nằm trong khoảng từ 0 đến 15, sau đó là 16 đến 31, ..., 240 đến 255. Bạn sẽ chỉ cần 16 giá trị để biểu diễn biểu đồ. Lúc này, những gì bạn làm chỉ đơn giản là chia toàn bộ biểu đồ thành 16 phần con và giá trị của mỗi phần con là tổng của tất cả số lượng pixel trong đó. Mỗi phần phụ này được gọi là “BIN”. BINS được biểu thị bằng thuật ngữ histSize trong tài liệu OpenCV.
- DIMS : Là số tham số mà chúng tôi thu thập dữ liệu. Trong trường hợp này, chúng tôi thu thập dữ liệu chỉ liên quan đến một thứ - giá trị cường độ. Vì vậy, nó bằng 1.
- RANGE : Là phạm vi giá trị cường độ bạn muốn đo. Thông thường, nó là [0, 256], tức là tất cả các giá trị cường độ.
1. Tính toán biểu đồ trong OpenCV
Bây giờ chúng ta sẽ sử dụng hàm cv2.calcHist() để tìm biểu đồ. Hãy cùng làm quen với hàm và các tham số của nó:
cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]])
Trong đó:
- images: là hình ảnh nguồn dạng uint8 hoặc float32, phải được đặt trong dấu ngoặc vuông, tức là “[img]”.
- channels: Đây là chỉ số của kênh mà chúng ta tính toán biểu đồ và cũng được để trong ngoặc vuông. Lấy ví dụ: nếu đầu vào là hình ảnh thang độ xám, giá trị của nó là [0]. Đối với hình ảnh màu, có thể truyền [0], [1] hoặc [2] để tính toán biểu đồ của kênh màu xanh lam, xanh lục hoặc đỏ tương ứng.
- mask: mặt nạ hình ảnh. Để tìm biểu đồ của hình ảnh đầy đủ, mask để ở chế độ “không có”. Nhưng nếu bạn muốn tìm biểu đồ của vùng ảnh cụ thể, bạn phải tạo một mặt nạ hình ảnh cho vùng đó.
- histSize: thể hiện số lượng BIN, cần được cho trong ngoặc vuông. Đối với quy mô tối đa, chúng ta truyền [256].
- ranges: là phạm vi như đã nói, giá trị nằm trong khoảng [0, 256].
Vì vậy, hãy bắt đầu với một hình ảnh mẫu. Chỉ cần tải một hình ảnh ở chế độ thang độ xám và tìm biểu đồ đầy đủ của nó.
- img = cv2.imread('test.jpg', 0)
- hist = cv2.calcHist([img], [0], None, [256], [0, 256])
hist là một mảng 256x1, mỗi giá trị tương ứng với số pixel trong hình ảnh đó với giá trị pixel tương ứng của nó.
2. Tính toán biểu đồ trong Numpy
Numpy cũng cung cấp cho bạn một hàm, np.histogram(). Vì vậy, thay vì hàm calcHist(), bạn có thể thử dòng dưới đây:
- hist, bins = np.histogram(img.ravel(), 256, [0,256])
hist giống như chúng ta đã tính toán trước đây. Nhưng bins sẽ có 257 phần tử, vì Numpy tính toán các bins là 0-0,99; 1-1,99; 2-2,99;... và phạm vi cuối cùng sẽ là 255-255,99.
Ngoài ra, Numpy có một hàm khác, đó là np.bincount(), nhanh hơn nhiều (khoảng 10 lần) so với np.histogram (). Đừng quên đặt minlength = 256 trong np.bincount. Lấy ví dụ: hist = np.bincount(img.ravel(), minlength = 256).
Ghi chú: Hàm OpenCV nhanh hơn (khoảng 40 lần) so với np.histogram(). Vì vậy, hãy ưu tiên sử dụng hàm của OpenCV.
C. Lập biểu đồ
Như vậy, có hai cách để vẽ biểu đồ:
- Cách nhanh: sử dụng các hàm vẽ đồ thị Matplotlib;
- Cách dài: sử dụng các chức năng vẽ OpenCV.
1. Sử dụng Matplotlib
Matplotlib cung cấp hàm vẽ biểu đồ: matplotlib.pyplot.hist(). Xem đoạn code bên dưới:
- import cv2
- import numpy as np
- from matplotlib import pyplot as plt
- img = cv2.imread('lampard.jpg', 0)
- plt.hist(img.ravel(), 256, [0,256]); plt.show()
Kết quả:
Hoặc bạn có thể sử dụng cách vẽ đồ thị bình thường của matplotlib, đặc biệt phù hợp với đồ thị BGR. Xem đoạn code minh họa dưới đây:
- import cv2
- import numpy as np
- from matplotlib import pyplot as plt
- img = cv2.imread('lampard.jpg')
- color = ('b', 'g', 'r')
- for i,col in enumerate(color):
- histr = cv2.calcHist([img], [i], None, [256], [0,256])
- plt.plot(histr, color = col)
- plt.xlim([0, 256])
- plt.show()
Kết quả:
2. Sử dụng OpenCV
Ở đây, chúng ta điều chỉnh giá trị của biểu đồ cùng với giá trị bin của nó để trông giống như tọa độ x, y, từ đó chúng ta có thể vẽ nó bằng cách sử dụng hàm cv2.line() hoặc cv2.polyline() để tạo ra hình ảnh tương tự như trên.
Ứng dụng của mặt nạ
Chúng ta đã sử dụng cv2.calcHist() để tìm biểu đồ của hình ảnh đầy đủ. Điều gì xảy ra nếu bạn muốn tìm biểu đồ của một số vùng trên hình ảnh? Chỉ cần tạo một mặt nạ hình ảnh với màu trắng trên vùng bạn muốn tìm biểu đồ và màu đen ở vùng không cần:
- img = cv2.imread('lampard.jpg', 0)
- # create a mask
- mask = np.zeros(img.shape[:2], np.uint8)
- mask[0:300, 150:400] = 255
- masked_img = cv2.bitwise_and(img, img, mask = mask)
- # Calculate histogram with mask and without mask
- # Check third argument for mask
- hist_full = cv2.calcHist([img], [0], None, [256], [0,256])
- hist_mask = cv2.calcHist([img], [0], mask, [256], [0,256])
- plt.subplot(221), plt.imshow(img, 'gray')
- plt.subplot(222), plt.imshow(mask,'gray')
- plt.subplot(223), plt.imshow(masked_img, 'gray')
- plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask)
- plt.xlim([0, 256])
- plt.show()
Xem kết quả ở hình dưới đây. Đường màu xanh lam hiển thị biểu đồ của hình ảnh đầy đủ trong khi đường màu xanh lục hiển thị biểu đồ của vùng bị che:
Ở phần tiếp theo chúng ta sẽ cùng tìm hiểu cách cân bằng hình ảnh (làm tăng độ tương phản) thông qua histogram.
(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