Mempercepat Operasi OpenCV di Python dengan scipy.weave

Tadi pagi saya mencoba menerapkan kode tentang LBP (Local Binary Pattern) dari yang tadinya hanya memroses satu citra menjadi memroses tiap frame pada video. Saya mencoba LBP lebih dahulu dibanding HOG karena berdasarkan kode yang dibuat sebelumnya, waktu eksekusi HOG memang lebih lambat dibanding LBP. Namun ternyata waktu eksekusi perhitungan fitur LBP cukup berat yang membuat frekuensi penggambarannya turun hingga 1 frame per detik! Setelah diidentifikasi ternyata perulangan bersarang (nested loop) di python sangat lambat walaupun sudah menggunakan generator function xrange. Akhirnya teringat kode yang dibuat oleh Tom Haines yang memanfaatkan modul weave dalam paket scipy yang mempermudah membuat kode inline dalam bahasa C++ yang akan dikompilasi pada saat run-time sehingga yang dijalankan adalah kode native tanpa harus membuat kode dalam file terpisah.

Di awal-awal mencoba dengan hanya bermodalkan google, sempat seringkali gagal compile. kegagalan pertama, scipy.weave akan mencari compiler MS Visual C++ sehingga saya harus memaksa untuk menggunakan gcc. Kesalahan berikutnya adalah gagal compile. Perjuangan masih berlanjut ketika kode sudah berhasil dikompilasi, tetapi Image tidak berubah padahal di dalam kode inline nilainya sudah berubah. Hal lain yang perlu dicatat adalah saya mulai menggunakan interface opencv versi 2 (cv2) yang sudah terintegrasi dengan numpy karena untuk melakukan manipulasi piksel dengan menggunakan scipy.weave lebih memudahkan untuk menggunakan representasi numpy.array dibandingkan dengan IplImage (ya iyalah, scipy kan pake numpy).

OK, Pembahasan akan saya mulai dengan penggunaan fungsi inline dari scipy.weave. Perhatikan kode berikut:

import cv, cv2
import numpy as np
from scipy.weave import inline

MASK = np.array([[0,-1],[1,-1],[1,0],[1,1],[0,1],[-1,-1],[-1,0],[-1,1]])
def calc_lbp(src, dst):
    code = r"""
        for (int y=1; y<Nsrc[0]-1; ++y){
            for (int x=1; x<Nsrc[1]-1; ++x){
                unsigned char px = SRC2(y,x);
                unsigned char n = 0;
                for(int m=0; m<8; ++m) 
                    if(SRC2(y+MASK2(m,1),x+MASK2(m,0))>px) 
                        n |= 1 << m;
                DST2(y,x) = n;
            }
        }
    """
    inline(code, ['src','dst','MASK'], compiler='gcc')
    return dst

parameter src dan dst merupakan objek numpy.array. kode ditulis dalam string, lalu beberapa variabel ditransfer ke dalam kode C++ yang akan digenerate dan dikompilasi pada saat pertama kali dipanggil lalu disimpan dalam disk cache sehingga jika dilakukan pemanggilan berikutnya yang dijalankan adalah kode yang sudah terkompilasi (baca: jauh lebih cepat!). Pada kode tersebut saya tidak mentransfer dimensi dari array src dan dst karena pada saat pembangkitan kode C++, scipy.weave akan membuat variabel-variabel yang memudahkan mengakses data dari objek numpy.array diantaranya N{nama-variabel-array} yaitu array yang berisi banyaknya elemen tiap dimensi array, D{nama-variabel-array} yang berupa nilai integer yang menyatakan dimensi array tersebut. Pada contoh kode di atas, variabel Nsrc merupakan variabel yang disediakan untuk mengakses banyaknya elemen pada tiap dimensi array src (misal untuk array 2 dimensi row-wise, Nsrc[0] untuk banyaknya baris dan Nsrc[1] menunjukkan banyaknya kolom, Dsrc bernilai 2). Untuk mengakses elemen dari array (get/set) disediakan MACRO-MACRO tambahan dengan format {nama-variabel-array-uppercase}{dimensi}({indeks}). Pada kode di atas, array src dan dst berdimensi 2 sehingga untuk mengakses elemennya menggunakan SRC2(i,j) dan DST2(i,j). Kalau src adalah array berdimensi satu maka elemennya diakses menggunakan MACRO SRC1(i). kalau parameter compiler tidak diisi maka compiler yang digunakan dalam lingkungan windows adalah MSVC.

Kode sisanya adalah membaca berkas video, dan menampilkan hasil eksekusi fungsi lbp di atas ke dalam windownya sendiri.

#...lanjutan kode di atas

filename = r"tol2.avi"
cap = cv2.VideoCapture(filename)
if not cap.isOpened(): 
    print "capture failed"
    exit()
f, im = cap.read()
gr = cv2.cvtColor(im, cv.CV_BGR2GRAY)
lbp = cv2.cvtColor(im, cv.CV_BGR2GRAY)

calc_lbp(gr, lbp) #panggilan pertama agak lambat karena harus memanggil kompilator gcc

while True:
    f, im = cap.read()
    if not f:
        cap = cv2.VideoCapture(filename)
        f, im = cap.read()
    cv2.cvtColor(im, cv.CV_BGR2GRAY, gr)
    
    calc_lbp(gr, lbp) #pemanggilan berikutnya jauh lebih cepat (menjalankan ekstensi native)
  
    cv2.imshow("lbp", lbp)
    cv2.imshow("img", im)
    cv2.imshow("gr", gr)
    
    key = cv2.waitKey(1)
    if key == 27:
        break

5 comments

  1. fajran · November 27, 2011

    gw biasanya make cython sambil juga ngecek kode C buatan cython tuk memastikan python call cuma minimal (e.g. memastikan gak ada python call di dalam loop)

    • pebbie · November 28, 2011

      ho, kebetulan kmaren scipy udah terpasang, btw klo pake cython gimana? mesti dikompilasi terpisah atau sambil jalan? *ngubek2 docs*

  2. iang · November 28, 2011

    dibuat otomatis juga bisa. tinggal tambahin yg beginian di awal skrip pythonnya

    import pyximport
    pyximport.install()

    tuk ngecek hasil bahasa C nya, tinggal jalanin “cython file.pyx” dan nanti akan ada “file.c” yg bisa diintip.

    cython dan numpy juga bersahabat, bisa lempar2an array.

  3. Ping-balik: Local Binary Pattern with NumPy « GAIBlog
  4. Ping-balik: Histogram of Oriented Gradient in Numpy | GAIBlog

Tinggalkan Balasan ke iang Batalkan balasan

Situs ini menggunakan Akismet untuk mengurangi spam. Pelajari bagaimana data komentar Anda diproses.