Computer Vision

[Image Descriptor] Local Binary Patterns

Xin chào, trong bài viết này chúng ta sẽ tìm hiểu về bộ mô tả kết cấu (texture descriptor) Local Binary Patterns (LBP).


HIỂU VỀ LOCAL BINARY PATTERNS

Tương tự như Haralick Texture Features, LBP được dùng để mô tả kết cấu, hoa văn (texture, pattern) của một bức ảnh/đối tượng. Tuy nhiên, thay vì miêu tả kết cấu một cách tổng quát như Haralick Texture, LBP miêu tả theo từng khu vực, bằng cách so sánh giá trị của một pixel với các pixel khác bao quanh nó. Nhờ đó mà LBP mạnh mẽ hơn nhiều so với Haralick Texture Features, tuy nhiên kèm theo đó là yêu cầu về tính toán và kích thước Feature Vector sẽ lớn hơn. LBP đã được áp dụng rất thành công trong bài toán nhận diện khuôn mặt.

Bước đầu tiên để xây dựng một LBP descriptor là chuyển đổi input image về grayscale. Với mỗi pixel trong grayscale image, chúng ta chọn ra vùng lân cận (neighborhood) bao quanh pixel trung tâm đó, tất nhiên sẽ là 3×3. Một giá trị LBP sẽ được tính toán cho pixel trung tâm này và lưu vào một ma trận 2D có kích thước giống với input image.

Khi so sánh pixel trung tâm và các pixel khác trong vùng lân cận, nếu các pixel này có giá trị lớn hơn hoặc bằng pixel trung tâm thì sẽ được đánh dấu là “1”, ngược lại sẽ được đánh dấu là “0”, xem figure 1.

lbp

Figure 1 – Tính toán giá trị LBP với 3×3 neighborhood

Với 8 pixel xung quanh pixel trung tâm, LBP sẽ có giá trị nằm trong khoảng [0, 255]. Tiếp theo chúng ta tính toán histogram cho ma trận 2D chứa các giá trị LBP của tất cả các pixel có trong input image và tìm ra Feature Vector.

Ưu điểm của 3×3 neighborhood là tính toán rất nhanh và hoạt động rất hiệu quả ở small scale. Tuy nhiên, chúng ta sẽ không thể áp dụng nó khi cần thay đổi scale vì neighborhood cố định ở 3×3.

Để giải quyết vấn đề này, một phiên bản mở rộng của LBP được đề xuất cho phép xử lý các vùng neighborhood có kích thước có thể thay đổi. Phương pháp này sử dụng hai tham số quan trọng, gồm:

  1. Số lượng điểm p sẽ được xem xét trong một khu vực hình tròn;
  2. Bán kính r của đường tròn đó, cho phép chúng ta xử lý ở các quy mô khác nhau (different scales).

lbp

Figure 2 – Giá trị p và r khi tính toán LBP

Việc sử dụng các giá trị p và r có các ý nghĩa sau: giá trị p càng lớn thì càng nhiều điểm trong vùng lân cận được xem xét, càng nhiều pattern (cũng như thông tin), dẫn đến yêu cầu càng nhiều khả năng tính toán; giá trị r càng lớn thì càng nhiều chi tiết được capture, tuy nhiên nếu giá trị p không tăng cùng với r thì LBP descriptor sẽ mất đi khả năng phân biệt cục bộ.


ÁP DỤNG LOCAL BINARY PATTERNS

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

Install:

pip3 install -U scikit-image

Function:

lbp = skimage.feature.local_binary_pattern(image, numPoints, radius, method=”uniform”)

Trong đó:

  • lbp – ma trận 2D có kích thước tương ứng với image, chứa các giá trị LBP của từng pixel có trong image, các phần tử của ma trận có kiểu Float64;
  • image – bức ảnh grayscale được dùng để chứa texture cần mô tả;
  • numPoints – số lượng điểm sẽ được xem xét để tính toán LBP;
  • radius – bán kính đường tròn chứa các pixel lân cận;
  • method – dùng để xác định pattern, chúng ta sẽ sử dụng method=”uniform” để tính toán LBP ở dạng bất biến với sự quay (rotation invariance).

Ma trận lbp nhận được từ function local_binary_pattern() chứa các giá trị LBP nằm trong khoảng [0, numPoints+1]. Để chuyển ma trận lbp này thành Feature Vector, chúng ta cần phải tính histogram của các giá trị LBP có trong ma trận. Kết quả nhận được sẽ là một Feature Vector có [numPoints+2] chiều.

Bây giờ chúng ta sẽ áp dụng Local Binary Patterns để so sánh các mẫu áo sơ mi của An Phước. Dưới đây là dataset lấy từ website của An Phước.

Dataset: 

Query:

query-2

Source code: localbinarypatterns.py

from skimage import feature
import numpy as np
import argparse
import cv2
import glob

class LocalBinaryPatterns:
    def __init__(self, numPoints, radius):
        self.numPoints = numPoints
        self.radius = radius

    def describe(self, image, eps=1e-7):
        lbp = feature.local_binary_pattern(image, self.numPoints, self.radius, method="uniform").astype("uint8")

        hist = cv2.calcHist([lbp], [0], None, [self.numPoints+2], [0,self.numPoints+2]).flatten()
        #hist = np.histogram(lbp.ravel(), bins=range(0,self.numPoints+3), range=(0,self.numPoints+2))[0]

        hist = hist.astype("float")
        hist /= (hist.sum()+eps)

        return hist

ap = argparse.ArgumentParser()
ap.add_argument("-d", "--dataset")
ap.add_argument("-q", "--query")
args = vars(ap.parse_args())

desc = LocalBinaryPatterns(24,3)
index = {}

for imagePath in glob.glob(args["dataset"]+"\\*.jpg"):
    image = cv2.imread(imagePath)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    hist = desc.describe(gray)

    filename = imagePath[imagePath.rfind("\\")+1:]
    index[filename] = hist

query = cv2.imread(args["query"])
queryFeatures = desc.describe(cv2.cvtColor(query, cv2.COLOR_BGR2GRAY))

cv2.imshow("Query", query)
results = {}

for (k, features) in index.items():
    d = 0.5*np.sum(((features-queryFeatures)**2)/(features+queryFeatures+1e-10))
    results[k] = d

results = sorted([(v,k) for (k,v) in results.items()])[:3]

for (i, (score, filename)) in enumerate(results):
    print("#{}. {}: {:.4f}".format(i + 1, filename, score))
    image = cv2.imread(args["dataset"] + "\\" + filename)
    cv2.imshow("Result #{}".format(i + 1), image)
    cv2.waitKey(0)

Giải thích
– Ở dòng 13 và 15: chúng ta tính toán ra ma trận LBP và histogram của các giá trị có trong ma trận đó;
– Ở dòng 18, 19: normalize histogram vừa nhận được;
– Ở dòng 28: tạo một object LocalBinaryPatterns với numPoints = 24 và radius = 3, như vậy chúng ta sẽ nhận được 26-d Feature Vector;
– Ở dòng 45-47: sau khi nhận được Feature Vectors của các mẫu áo, chúng ta tính toán sự khác biệt giữa mẫu query và dataset với Chi-squared Distance. Chi-squared Distance là một lựa chọn tuyệt vời để so sánh các histogram;
– Ở dòng 49-55: chọn ra 3 mẫu có Feature Vector gần với Feature Vector của query nhất và hiển thị kết quả ra màn hình.

Execution

$ python localbinarypatterns.py -d data -q query.jpg

Output:

result

Như kết quả nhận được, các mẫu áo của Result #1, #2 và #3 tuy không có màu sắc trùng với màu của mẫu áo Query nhưng về texture thì khá tương đồng.


SUGGESTIONS

Số lượng điểm p và bán kính r của đường tròn chứa các điểm lân cận pixel trung tâm có ảnh hưởng lớn đến kích thước của Feature Vector và thời gian tính toán. Cần chú ý rằng, giá trị p và r cần được thay đổi tương xứng với nhau (cùng tăng hoặc cùng giảm).

Khi bắt đầu, giá trị p thường nằm trong khoảng [8, 24] và r nằm trong khoảng [1,3], tăng giá trị r nếu như độ chính xác được cải thiện.

Ưu điểm của LBP:

  • Bất biến với sự quay (Rotation invariance) khi sử dụng “uniform patterns”;
  • Mô tả kết cấu (texture) của bức ảnh rất tốt;
  • Hữu ích trong bài toán nhận diện khuôn mặt.

Nhược điểm:

  • Kích thước của Feature Vector và thời gian tính toán sẽ tăng mạnh khi tăng giá trị numPoints p và radius r.

SUMMARY

Qua bài viết này chúng ta đã tìm hiểu về LBP và cách áp dụng LBP với thư viện scikit-image. Cảm ơn các bạn đã theo dõi bài viết.

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

Reference:
[1] scikit-image LBP function.
[2] Local Binary Patterns with Python & OpenCV.
[3] codefish.de – Local Binary Patterns.

One thought on “[Image Descriptor] Local Binary Patterns

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