Histogram of Oriented Gradient in Numpy

Di tulisan terdahulu saya pernah mencoba menulis tentang menghitung fitur HOG lalu tulisan tentang mempercepat perhitungan hog dengan menggunakan inlince c++ dari scipy. Tulisan kali ini sebetulnya hampir sama dengan tulisan terdahulu, yaitu menghitung fitur yang sama, yang berbeda adalah tulisan yang dulu dibuat dengan menggunakan interface opencv versi 1 (import cv) sedangkan tulisan kali ini dibuat menggunakan interface versi kedua (cv2). Pada opencv versi pertama, objek citra disimpan menggunakan struktur berbasis C (IplImage) sedangkan di versi 2, objek citra sudah terintegrasi dengan Numpy array (ndarray) dan opencv versi 2 sudah ditulis ulang dengan menggunakan C++. Dengan terintegrasinya struktur penyimpanan citra menjadi array numpy, maka operasi2 pengolahan citra jadi seperti yang dilakukan dengan MATLAB. operasi-operasi tertentu juga menjadi makin mudah melalui operasi array slicing yang ada pada Numpy. Oleh sebab itu, saya coba membuat algoritma penghitungan fitur HOG yang mengeksploitasi fasiltas yang ada di Numpy yang lebih cepat karena menghindari loop yang eksplisit dilakukan pada kode python tetapi mendelegasikan operasi yang bersifat element-wise ke numpy.

modul yang diimpor
kalau di versi opencv pertama modul yang digunakan adalah cv, maka sekarang adalah cv2. modul cv masih dapat diakses untuk menjaga kompatibilitas ke versi sebelumnya menjadi submodul di dalam cv2.

# jika ingin menggunakan antarmuka opencv versi 1
import cv2.cv as cv 
# interface opencv versi 2
import numpy as np
import cv2

Menghitung HOG secara umum
fitur HOG yang dibahas pada tulisan ini adalah varian penyederhanaan dari varian-varian utama yang digunakan saat ini :

  • varian HOG Dalal-Triggs untuk pedestrian detection
  • varian HOG Felzenswalb untuk deformable part model di PASCAL VOC (Visual Object Categories/Classes) Challenge

Read More

Iklan

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.

Read More

Tracking Using Local Binary Pattern

Oke, terakhir kali saya membuat tulisan di blog ini adalah akhir april. Jeda yang cukup panjang untuk memulai membuat tulisan. Ada banyak hal yang terjadi pada saya selama masa jeda tersebut. Mulai dari kegiatan diklat prajabatan CPNS pada bulan mei, panggilan wawancara beasiswa dikti di bulan juni, hingga akhirnya per tanggal 1 Oktober ini saya resmi menjadi abdi negara yang bertugas di kampus.

#devfestjkt

Pada penghujung bulan september kemarin, saya mendapat kesempatan berbicara pada acara Google DevFest di Jakarta. Awalnya memang saya berniat mengisi acara Google DevFest yang di Bandung akhir minggu ini. Pada acara di jakarta saya diminta membahas tentang Computer Vision. Saya sendiri memang suka ngoprek citra untuk diutak-atik, tapi untuk menjadi pembicara tentang computer vision sepertinya kompetensi saya masih jauh dari seorang pakar. Oleh sebab itu dan karena yang meminta adalah rekan satu almamater, saya menyetujui dan mencoba tampil sebagai seorang enthusiast bukan sebagai expert.

Awalnya saya agak grogi karena tidak terbiasa berbicara membawakan materi teknik di hadapan orang yang banyak. Mengajar di kampus pun paling banyak mungkin hanya 100-an, itupun kuliah wajib. Kalau menjadi dosen pengganti di kuliah “Interpretasi dan Pengolahan Citra”, pesertanya jauh lebih sedikit lagi. Mungkin bisa dihitung dengan jari. OK, jari-jari di kedua tangan. 🙂 Di luar perkiraan, ternyata responnya cukup memberi semangat.

Nah, ceritanya di acara itu saya menampilkan beberapa video yang sudah diproses dengan menggunakan OpenCV sebagai demonstrasi hal-hal yang bisa dilakukan dengan menggunakan teknik-teknik computer vision. Nah, salah satu video yang saya tampilkan sebetulnya adalah pengembangan dari apa yang pernah saya tuliskan di sini. Karena keterbatasan waktu dan tema presentasi, di acara itu saya hanya sempat menampilkan videonya saja. Oleh sebab itu di tulisan ini saya akan bedah bagaimana hal tersebut dilakukan.

frame pertama

frame pertama

frame terakhir

frame terakhir


Read More

Simple Face Tracking with OpenCV (Python)

Beberapa waktu yang lalu ada yang menanyakan perihal lambatnya operasi openCV yang diintegrasikan dengan wxPython. Setelah melihat kode yang dipakai, ternyata penyebabnya adalah operasi deteksi objek yang menggunakan CascadeClassifier yang dilakukan setiap frame. Walaupun konon operasi pendeteksian objek menggunakan detektor CascadeClasifier merupakan state-of-the-art mendeteksi tercepat (baik menggunakan fitur Haar, ataupun yang lebih cepat lagi dengan menggunakan Local Binary Pattern), Operasi ini sangat tidak dianjurkan dijalankan di setiap frame karena tidak efisien.

Penjejakan (tracking) objek dengan cara mendeteksi objek di tiap frame merupakan cara yang paling naif karena setiap piksel pada beberapa tingkatan skala akan diperiksa. Cara yang lebih cerdas adalah memanfaatkan informasi yang sebelumnya sudah diketahui (deteksi pada frame sebelumnya), dan memanfaatkan asumsi bahwa objek yang diikuti tidak akan bergerak jauh dalam rentang dua buah gambar yang berurutan. Pada umumnya asumsi ini dapat dimanfaatkan, dengan pengecualian jika terjadi penutupan objek selama beberapa frame ataupun jika ada lebih dari satu objek yang diikuti dalam area yang berdekatan atau bersinggungan.

Berikut ini akan dijabarkan contoh penjejakan objek secara sederhana dengan memanfaatkan informasi dari hasil deteksi dan template matching. Teknik ini sangat sederhana karena informasi yang diestimasi hanyalah posisi dua dimensi (tidak menangani perubahan skala atau rotasi). Sederhananya proses penjejakan dilakukan dalam dua tahap yaitu deteksi dan estimasi. Jika belum ada objek yang terdeteksi maka proses deteksi akan dijalankan hingga ada yang terdeteksi. Jika sudah ada objek yang terdeteksi maka sudah ada informasi sebelumnya yang dapat dimanfaatkan yaitu posisi, area, dan isi area yang mendeskripsikan objek yang diikuti. Dengan demikian posisi objek pada gambar berikutnya dapat dilakukan dengan mencari area yang paling mirip di sekitar posisi awal (posisi hasil deteksi atau estimasi di gambar sebelumnya). Ukuran kemiripan dihitung dengan menggunakan beberapa cara. Cara yang paling umum adalah menggunakan metrik euclidean yaitu selisih dua buah vektor yang kemudian tiap elemennya dikuadratkan dan dijumlahkan sehingga menghasilkan konotasi jarak.

Kode berikut dapat dicoba dan dipelajari lebih lanjut agar konsep penjejakan dapat dipahami.

import numpy as np
import cv2
import cv
 
#video_src = 0 #webcam
video_src = r"angklung\angklung.avi"
cascade_fn = "lbpcascade_frontalface.xml"
#cascade_fn = "haarcascade_frontalface_alt.xml"
cascade = cv2.CascadeClassifier(cascade_fn)
cam = cv2.VideoCapture(video_src)
gotface = False
 
while True:
    ret, img = cam.read()
    if not ret: break
    gray = cv2.cvtColor(img, cv.CV_BGR2GRAY)
    
    if not gotface: #detect a face
        rects = cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors=2, minSize=(20, 20))
        if len(rects)>0: 
            gotface = True
            x,y,width,height = rects[0]
            #create the first template for tracking from detected area
            face = np.array([0]*width*height, dtype=np.uint8).reshape((width,height))
            face[:,:] = gray[y:y+height,x:x+width]
    else: #track that face
        #window enlargement value to be used as search area
        wnd = min(width, height)/4
        #track using squared difference measurement
        result = cv2.matchTemplate(gray[y-wnd:y+height+wnd,x-wnd:x+width+wnd], face, cv.CV_TM_SQDIFF)
        
        #alternative measurement to track object, but more prone to drifting (COEFF > CCORR) than previous approach
        #result = cv2.matchTemplate(gray[y-wnd:y+height+wnd,x-wnd:x+width+wnd], face, cv.CV_TM_CCORR)
        #result = result.max()-result #inverse the value if CCOEFF is used
        
        yy,xx = np.unravel_index(result.argmin(), result.shape)
        x,y = (x-wnd) + xx, (y-wnd) + yy
        alpha = 0.5 #blending factor for template updating
        face[:,:] = face*alpha + (1.0-alpha) * gray[y:y+height, x:x+width]
    
    if gotface: #display tracked face
        cv2.rectangle(img, (x, y), (x+width, y+height), (255,0,0), 2)
        cv2.imshow('faceregion',face)
    cv2.imshow('facedetect',img)
    
    if cv2.waitKey(20) == 27: break

Pada kode di atas, kedua tahapan diimplementasi dengan menggunakan analisa kasus terhadap variabel gotface yang menyatakan ada atau tidaknya objek yang sudah terdeteksi. Contoh di atas juga ada beberapa bagian yang dikomentari dengan tujuan sebagai percobaan mandiri misalnya pada bagian ukuran alternatif yang secara prinsip menggunakan operasi perkalian bukan pengurangan seperti pada metode SQDIFF.

Gambar berikut menunjukkan kinerja beberapa metode yang digunakan dalam Template Matching. Kotak berwarna biru adalah metode CV_TM_SQDIFF, kotak berwarna hijau adalah metode CV_TM_CCORR, dan kotak berwarna merah adalah metode CV_TM_CCOEFF. Gambar diambil dari frame terakhir yang diproses. Pada awal deteksi setiap metode berangkat dari tempat yang sama. Pada gambar tersebut terlihat metode CCOEFF paling melenceng dari objek wajah sedangkan kinerja yang hampir sama ditunjukkan oleh metode CCORR dan SQDIFF.

perbandingan beberapa metode template matching untuk tracking objek

perbandingan beberapa metode template matching untuk tracking objek. Video diambil dari youtube tentang saung angklung Udjo.

NB:Oya, saya lupa mencantumkan informasi kode tersebut dijalankan di OpenCV versi 2.3.1

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

Read More

Local Binary Pattern in OpenCV (Python)

Masih melanjutkan tulisan sebelumnya, sengaja disambung karena isinya sangat sederhana (gatal kalau tidak ditulis). LBP (Local Binary Pattern) atau Pola Biner Lokal merupakan salah satu informasi yang dapat dianalisis dari citra. Informasi LBP biasanya digunakan sebagai deksriptor dari tekstur. Salah satu kelebihan dari LBP adalah sifatnya yang invarian terhadap perubahan fotometri dari objek yang sama karena sifatnya yang merupakan ukuran intensitas relatif suatu piksel dengan intensitas piksel di sekitarnya.

Read More

Computing HOG Features in OpenCV (Python)

Sudah lama tidak menulis di blog *hiyaaa* karena masih beradaptasi dengan aktivitas sebagai dosen (yang tidak cuma mengajar dan meneliti, “maklumlah dosen muda, kalau kata dosen-dosen lain yang sudah lebih senior”). Padahal banyak sekali yang mau ditulis (dan dikerjakan tentunya).

Curhatnya saya hentikan sampai sini saja. Ceritera mengenai pengalaman saya dalam menjalani aktivitas sebagai dosen saya tuliskan di blog kampus. tulisan-tulisan di sini akan tetap saya fokuskan pada hasil utak-atik (terutama kode proof-of-concept) dalam mempelajari topik-topik dalam dunia informatika.

Kali ini saya sedang iseng membuat implementasi dari HOG (Histogram of Oriented Gradients). Fitur ini dikaji secara lebih dalam oleh Navneet Dalal dan Bill Triggs dari INRIA, Perancis untuk mendeteksi pejalan kaki (pedestrian) pada citra di tahun 2005. Sama seperti deskriptor yang digunakan pada SIFT (Scale Invariant Feature Transform *eh, saya belum membahas SIFT ya?*), informasi vektor gradien disimpan dalam koordinat polar (panjang dan arah).

Walaupun HOG *katanya* sudah ada di OpenCV tapi di dokumentasi python sepertinya belum ditambahkan. Python ini sedikit dianaktirikan di OpenCV, saya baru bisa menikmati fasilitas SVM di python di versi 2.3 (dengan python 2.6), karena OpenCV versi 2.2 untuk python hanya berisi modul untuk python versi 2.7. Akhirnya saya terpaksa membuat sendiri. Sebetulnya pembuatan HOG di OpenCV from scratch sudah pernah ditulis oleh Saurabh Goyal di sini. Apa yang saya buat mengadopsi dari yang sudah ditulis di sana (dengan modifikasi sesuka saya tentunya) terutama bagian penghitungan dengan memanfaatkan citra integral. Kode yang ditulis dengan python menurut saya jadi lebih sederhana dan (semoga) lebih mudah dibaca dan dipahami oleh pembaca (setia?) blog ini. Selamat menikmati 🙂

Read More