Mempercepat Operasi OpenCV di Python dengan scipy.weave

27Nov11

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



3 Responses to “Mempercepat Operasi OpenCV di Python dengan scipy.weave”

  1. 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)

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

  2. 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.


Tinggalkan Balasan

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 / Ubah )

Twitter picture

You are commenting using your Twitter account. Log Out / Ubah )

Facebook photo

You are commenting using your Facebook account. Log Out / Ubah )

Connecting to %s


Ikuti

Get every new post delivered to your Inbox.

Bergabunglah dengan 213 pengikut lainnya.