Local Binary Pattern with NumPy

Dulu saya pernah menulis tentang menghitung fitur LBP (Local Binary Pattern) menggunakan OpenCV di python. Waktu itu interface opencv yang digunakan masih OpenCV 2.1 . Saya juga sempat membahas bagaimana mempercepat perhitungan LBP tersebut dengan kode inline C++ menggunakan scipy.weave. Kemarin saya penasaran, bagaimana caranya mempercepat operasi tersebut tanpa menggunakan inline code (yang membuat ada penundaan di awal karena harus melakukan kompilasi dan import native extension) dan hanya menggunakan apa yang tersedia di numpy (di versi-versi terakhir, python OpenCV menggunakan numpy array sebagai representasi citra).

Setelah mencari dokumentasi dari numpy, Akhirnya ketemu dua cara. Cara yang pertama masih lebih lambat (200-300ms) sedangkan cara kedua ternyata cukup cepat (100-150ms). Pada dasarnya adalah melakukan pengurangan menggunakan operator broadcast terhadap seluruh elemen array dengan pengurangnya adalah array tersebut yang digeser sesuai dengan posisi 8 tetangga tiap piksel sehingga total array yang perlu dibuat adalah 8 array. Dalam praktiknya, array tersebut dibuat dengan cara melakukan slicing.


menggunakan numpy.ix_
Cara pertama adalah memanfaatkan numpy.ix_ untuk membangkitkan index 2 dimensi yang indeksnya sudah digeser.

import cv2
import cv2.cv as cv
import numpy as np

def lbp_numpy_ix(img):
    """
    uses ix_ for indexing array using another array of index
    """
    tmp = img.astype(np.int16)
    #yn: normal, yt:shifted top, yb:shifted bottom
    yn = np.array(range(img.shape[0]))
    yt = np.array(range(1,img.shape[0]))-1
    yb = np.array(range(img.shape[0]-1))+1

    xn = np.array(range(img.shape[1]))
    xl = np.array(range(1,img.shape[1]))-1
    xr = np.array(range(img.shape[1]-1))+1

    lbp = np.zeros(img.shape,dtype=np.uint8)
    ti = np.zeros(img.shape,dtype=np.int16)

    ti = tmp[np.ix_(yt,xn)]-tmp
    lbp[ti>0] |= 1

    #topright
    ti = tmp[np.ix_(yt,xr)]-tmp
    lbp[ti>0] |= 2

    #right
    ti = tmp[np.ix_(yn,xr)]-tmp
    lbp[ti>0] |= 4

    #bottomright
    ti = tmp[np.ix_(yb,xr)]-tmp
    lbp[ti>0] |= 8

    #bottom
    ti = tmp[np.ix_(yb,xn)]-tmp
    lbp[ti>0] |= 16

    #bottomleft
    ti = tmp[np.ix_(yb,xl)]-tmp
    lbp[ti>0] |= 32

    #left
    ti = tmp[np.ix_(yn,xl)]-tmp
    lbp[ti>0] |= 64

    #topleft
    ti = tmp[np.ix_(yt,xl)]-tmp
    lbp[ti>0] |= 128
    
    return lbp

terlihat dari kode di atas, saya masih perlu membuat array khusus untuk menyimpan indeks. proses ini dan pengaksesan indeks menggunakan ix_ masih bisa dipercepat lagi.

menggunakan numpy.roll

Cara kedua adalah menggunakan numpy.roll. kalau cara pertama yang diatur adalah proses slicing/indexing, cara ini melakukan pergeseran/rotasi dari elemen array untuk menghasilkan array baru yang ukurannya sama tetapi elemennya bergeser.

def lbp_numpy_roll(img):
    """
    uses roll for shifting array values
    """
    tmp = img.astype(np.int16)
    lbp = np.zeros(img.shape,dtype=np.uint8)
    ti = np.zeros(img.shape,dtype=np.int16)

    #top
    ti = np.roll(tmp, -1, axis=0) - tmp
    lbp[ti>0] |= 1

    #topright
    ti = np.roll(np.roll(tmp, 1, axis=1), -1, axis=0) - tmp
    lbp[ti>0] |= 2

    #right
    ti = np.roll(tmp, 1, axis=1) - tmp
    lbp[ti>0] |= 4

    #bottomright
    ti = np.roll(np.roll(tmp, 1, axis=1), 1, axis=0) - tmp
    lbp[ti>0] |= 8

    #bottom
    ti = np.roll(tmp, 1, axis=0) - tmp
    lbp[ti>0] |= 16

    #bottomleft
    ti = np.roll(np.roll(tmp, -1, axis=1), 1, axis=0) - tmp
    lbp[ti>0] |= 32

    #left
    ti = np.roll(tmp, -1, axis=1) - tmp
    lbp[ti>0] |= 64

    #topleft
    ti = np.roll(np.roll(tmp, -1, axis=1), -1, axis=0) - tmp
    lbp[ti>0] |= 128
    #cv2.imshow("topleft", (ti+128).astype(np.uint8))
    return lbp

Sebetulnya kode ini masih bisa diperpendek dengan mengorbankan ‘sedikit’ penurunan kinerja (kadang lebih cepat tapi seringkali lebih lambat 10-50ms). Kode di atas dipersingkat dengan memanfaatkan list of lambda expression seperti terlihat di kode berikut :

def lbp_numpy_roll_lambda(img):
    """
    uses roll+lambda expression for shifting array values
    """
    tmp = img.astype(np.int16)
    lbp = np.zeros(img.shape,dtype=np.uint8)
    ti = np.zeros(img.shape,dtype=np.int16)

    fns = [
        lambda x: np.roll(x, -1, axis=0)-x, 
        lambda x: np.roll(np.roll(x, 1, axis=1), -1, axis=0) - x,
        lambda x: np.roll(x, 1, axis=1) - x,
        lambda x: np.roll(np.roll(x, 1, axis=1), 1, axis=0) - x,
        lambda x: np.roll(x, 1, axis=0) - x,
        lambda x: np.roll(np.roll(x, -1, axis=1), 1, axis=0) - x,
        lambda x: np.roll(x, -1, axis=1) - x,
        lambda x: np.roll(np.roll(x, -1, axis=1), -1, axis=0) - x
        ]
    for i in xrange(len(fns)):
        lbp[fns[i](tmp)>0] |= 1 << i
    
    return lbp

Catatan terakhir, parameter citra masukan diasumsi berupa greyscale (1 channel) dan sebelum dioperasikan perlu dikonversi menjadi signed integer (dari unsigned byte).

One comment

  1. abox · Januari 10, 2015

    Kalo menghitung titik sudut dari gambar menggunakan LBP ada gak mas tutorialnya?

Tinggalkan Balasan

Isikan data di bawah atau klik salah satu ikon untuk log in:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s