Computer Vision

[Image Descriptor] Histogram of Oriented Gradients

Xin chào, trong bài viết này chúng ta sẽ tìm hiểu về bộ mô tả hình ảnh Histogram of Oriented Gradients (HOG) và các ứng dụng của nó.


HIỂU VỀ HISTOGRAM OF ORIENTED GRADIENTS

Histogram of Oriented Gradients được sử dụng chủ yếu cho bài toán Object Detection, ngoài ra HOG cũng là bộ mô tả kết cấu, hình dạng của đối tượng khá mạnh mẽ.

Bản chất của HOG là mô hình hóa đối tượng bằng sự phân bố cường độ gradient trong từng vùng điểm ảnh của bức ảnh. HOG descriptor có 3 tham số quan trọng, gồm: orientations, pixel_per_cellcells_per_block, chúng ta sẽ nói đến các tham số này sau.

Để tính toán HOG cần thực hiện 4 bước sau:

Bước 1: Chuẩn hóa hình ảnh (normalizing the image)

Bước này sẽ giúp cải thiện hiệu suất của HOG descriptor.

Bước 2: Tính toán gradients

Đầu tiên chúng ta sẽ tính toán gradient của bức ảnh theo cả 2 trục X, Y. Sau đó tính Gradient Magnitude và Gradient Orientation (tham khảo bài viết này).

Bước 3: Tính toán histogram cho từng vùng của bức ảnh

Chúng ta sẽ chia bức ảnh thành các vùng chữ nhật bằng nhau, được gọi là cells. Mỗi cell sẽ là một vùng hình ảnh với số lượng pixel được quy định trước. Ví dụ, chúng ta có một bức ảnh kích thước 640×480 pixels, nếu giá trị pixel_per_cell là 80×80 thì chúng ta sẽ có tổng cộng (640/80)x(480/80) = 8×6 = 48 cells, nhưng nếu giá trị pixel_per_cell là 40×40 thì số cell tổng cộng sẽ là (640/40)x(480/40) = 16×12 = 192 cells, xem Figure 1.

Figure 1 – Phân chia bức ảnh thành các cells

Sau khi đã chia bức ảnh thành các cells, chúng ta tính HOG cho từng cell sử dụng các giá trị Gradient Magnitude và Gradient Orientation đã có ở bước 2.

Tham số orientations sẽ quy định số bins trong histogram. Góc quay của Gradient trong không gian sẽ có giá trị trong khoảng [0, 180] (unsigned integer) hoặc [0, 360] (signed integer). Thông thường chúng ta sẽ chọn orientations trong khoảng [9, 12] để tính toán histogram.

Mỗi pixel trong cell sẽ sử dụng giá trị Gradient Magnitude của mình như một trọng số khi tính toán histogram.

hist

Figure 2 – Histogram of Oriented Gradients

Bước 4: Chuẩn hóa theo khối

Việc chuẩn hóa theo từng khối sẽ cải thiện bộ descriptor trước sự thay đổi ánh sáng và độ tương phản. Để làm được việc này chúng ta sẽ nhóm các cells lại thành từng block và các block này có thể overlap (chồng lấn nhau). Tham số cells_per_block sẽ quy định số cell có trong một block. Thông thường cells_per_block sẽ có giá trị là 2×2 hoặc 3×3 để cho hiệu năng tốt nhất, xem Figure 3.

Quay lại ví dụ phía trên, giả sử chúng ta có một bức ảnh kích thước 640×480, giá trị pixels_per_cell là 80×80 và chúng ta sẽ có 8×6 = 48 cells. Nếu giá trị cells_per_block là 2×2 thì chúng ta sẽ có tổng cộng (8-1)x(6-1) = 7×5 = 35 blocks. Nếu giá trị cells_per_block là 3×3 thì chúng ta sẽ có tổng cộng (8-2)x(6-2) = 6×4 = 24 blocks.

hog_contrast_normalization

Figure 3 – Nhóm các cells thành block

Sau khi đã nhóm các cells vào từng block, chúng ta sẽ ghép (concatenate) histogram của từng cell trong block với nhau và thực hiện L1 hoặc L2 normalizing. Lặp lại tương tự với tất cả các block. Sau cùng histogram của tất cả các block sẽ được ghép lại với nhau tạo thành Feature Vector.

Quan trọng: khi tính toán HOG, các bức ảnh sẽ được chia thành các blocks và cells, do đó cần đảm bảo rằng tất cả các bức ảnh được dùng để train và test phải được chuyển về một kích thước duy nhất. Khi sử dụng cùng các tham số orientations, pixel_per_cell và cells_per_block cho các bức ảnh có kích thước khác nhau, chúng sẽ sinh ra số lượng cell và block khác nhau, điều này dẫn đến các HOG Feature Vectors sẽ có kích thước khác nhau và kết quả sẽ không như chúng ta mong muốn.


ÁP DỤNG HISTOGRAM OF ORIENTED GRADIENT

Trong bài viết này chúng ta sẽ sử dụng thư viện scikit-image để tính toán HOG.

Install:

pip3 install -U scikit-image

Function:

hog, hogImage = skimage.feature.hog(image, orientations=9, pixels_per_cell=(8, 8), cells_per_block=(3, 3), transform_sqrt=False, block_norm=None,visualize=False)

Trong đó:

  • hog – Histogram of Oriented Gradient tính toán được, 1-D array;
  • hogImage – 2-D array biểu diễn HOG image;
  • image – bức ảnh cần tính toán HOG;
  • orientations – số bins của histogram, mặc định là 9;
  • pixels_per_cell –  số pixel có trong một cell, mặc định là 8×8;
  • cells_per_block –  số cell có trong một block, mặc định là 3×3;
  • transform_sqrt – thực hiện chuẩn hóa hình ảnh trước khi tính toán HOG, mặc định là False;
  • block_norm – thực hiện chuẩn hóa theo khối, gồm các method sau: ‘L1’, ‘L1-sqrt’, ‘L2’, ‘L2-Hys’; mặc định là None;
  • visualize – (>= v0.16) nếu visualize=True thì kết quả trả về của function sẽ là hog và hogImage; mặc định là False; (< v0.16) sử dụng visualise thay cho visualize;

Bây giờ chúng ta sẽ áp dụng HOG và Machine Learning để nhận biết logo của một số hãng ô tô nổi tiếng nhé.

Training set của chúng ta sẽ nằm trong folder có tên training, gồm các bức ảnh sau:

Các bức ảnh này được đặt tên theo cú pháp sau: carbrand_number.png, ví dụ: ford_01.png, honda_03.png, hyundai_02.png.

Testing set sẽ nằm trong folder có tên test, gồm các bức ảnh sau (không cần đặt tên theo cú pháp nào cả):

Source code: hog.py

from sklearn.svm import LinearSVC
from skimage import exposure
from skimage import feature
import argparse
import cv2
import imutils
from imutils import paths

ap = argparse.ArgumentParser()
ap.add_argument("-d", "--training", required=True)
ap.add_argument("-t", "--test", required=True)

args = vars(ap.parse_args())

print("Extracting features...")
data = []
labels = []

for imagePath in paths.list_images(args["training"]):
    brand = imagePath[imagePath.rfind("\\") + 1:].split("_")[0]

    image = cv2.imread(imagePath)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    edged = imutils.auto_canny(gray)

    cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1]

    c = max(cnts, key=cv2.contourArea)

    (x,y,w,h) = cv2.boundingRect(c)
    logo = gray[y:y+h,x:x+w]
    logo = cv2.resize(logo, (200,100))

    H = feature.hog(logo, orientations=9, pixels_per_cell=(10,10), cells_per_block=(2,2), transform_sqrt=True, block_norm="L1")

    data.append(H)
    labels.append(brand)

print("Training classifier...")

model = LinearSVC()
model.fit(data, labels)
print("Evaluting...")

for (i, imagePath) in enumerate(paths.list_images(args["test"])):
    image = cv2.imread(imagePath)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    logo = cv2.resize(gray, (200,100))

    H, hogImage = feature.hog(logo, orientations=9, pixels_per_cell=(10,10), cells_per_block=(2,2), transform_sqrt=True, block_norm="L1", visualize=True)

    pred = model.predict(H.reshape(1,-1))[0]

    hogImage = exposure.rescale_intensity(hogImage, out_range=(0,255))
    hogImage = hogImage.astype("uint8")
    cv2.imshow("HOG image #{}".format(i+1), hogImage)
    cv2.putText(image, pred.title(), (10,35), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0,255,0), 3)
    cv2.imshow("Test image #{}".format(i+1), image)
    cv2.waitKey(0)

Giải thích:
– Dòng 19-31: xử lý các bức ảnh trong training set, tìm ra vị trí của logo trong bức ảnh và tách vùng hình ảnh đó;
– Dòng 32: chuyển các vùng hình ảnh chứa logo về một kích thước đồng nhất, ở ví dụ trên là 200×100;
– Dòng 34-37: tính toán HOG với orientations là 9, pixels_per_cell là 10×10, cells_per_block là 2×2, áp dụng normalize L1. Sau đó các Feature Vector và Labels sẽ được đưa vào các tập hợp tương ứng;
– Dòng 41,42: tạo một instance Linear Support Vector Classification và “dạy” cho máy biết cách phân biệt từng category với method fit();
– Dòng 45-48: xử lý các bức ảnh trong testing set;
– Dòng 50-52: tính toán HOG cho các bức ảnh này với các tham số tương tự như đối với training set. Feature Vector nhận được sẽ được dùng để dự đoán category với method model.predict().

Execution:
$ python hog.py -d training -t test

Output:

Như vậy với HOG và Machine Learning, chúng ta đã có thể phân loại được các logo này. Tuy nhiên, chương trình của chúng ta có thể hoạt động tốt là do các bức ảnh trong testing set đã được chụp cận cảnh và tập trung vào phần logo. Nếu chúng ta sử dụng một bức ảnh chụp toàn bộ chiếc xe thì chưa chắc kết quả sẽ như ý muốn.


SUGGESTIONS

Tham số orientations thường được chọn từ 9, tham số pixels_per_cell bắt đầu từ 4×4 và cells_per_block bắt đầu từ 2×2.

Kích thước bức ảnh input cũng là một điều cần chú ý. Giả sử chúng ta có orientations=9, pixels_per_cell=(10,10), cells_per_block=(2,2), nếu bức ảnh được resize thành 200×200 thì chúng ta sẽ nhận được Feature Vectors có dạng 12996-d, nếu bức ảnh được resize thành 150×150 thì Feature Vectors có dạng 7056-d. Tất nhiên, kích thước của Feature Vector càng lớn thì thời gian tính toán càng cao.

Ưu điểm của HOG:

  • Rất mạnh mẽ;
  • Rất hữu ích để biểu diễn các đối tượng có cấu trúc không thay đổi đáng kể;
  • Rất chính xác trong Object Classification.

Khuyết điểm:

  • Feature Vector có thể trở nên rất lớn nếu không chú ý các tham số như orientations, pixels_per_cell, cells_per_block và kích thước input image;
  • Khá chậm;
  • Không hoạt động tốt nếu cấu trúc của đối tượng cần được mô tả bị thay đổi lớn (ví dụ: đối tượng bị xoay).

SUMMARY

Như vậy chúng ta đã tìm hiểu cách hoạt động của HOG cũng như áp dụng HOG để giải quyết một bài toán đơn giản. Cảm ơn các bạn đã theo dõi bài viết.

Thân ái và quyết thắng.

Reference:
[1] sklearn.svm.LinearSVC.
[2] scikit-image.feature.hog method.

2 thoughts on “[Image Descriptor] Histogram of Oriented Gradients

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