Seamless Cloning using Poisson Image Blending in Python

Beberapa waktu lalu ketika saya sedang mempersiapkan tulisan sebelumnya tentang face tracking, Saya terdampar di sebuah situs berbahasa Jepang yang cukup membuat penasaran (sampai sekarang saya belum ketemu lagi dengan situs tsb). Judul yang saya ingat waktu itu adalah “Poisson Image Editing”. Kalau dari judulnya saja saya tidak terlalu tertarik, tapi saya justru tertarik karena melihat gambar yang ditampilkan.

image cloning

pemindahan citra kepala beruang ke citra bukit dengan panduan mask. (gambar bukan milik saya)

Singkat kata, “Poisson Image Editing” ini merupakan judul makalah yang ditulis oleh Perez et al. Isinya kurang lebih sebagai memaparkan cara menggabungkan dua buah citra (sumber + mask + target = hasil). mask merupakan citra biner yang menandakan bagian citra sumber yang boleh dipindahkan ke citra hasil. Contohnya terlihat pada gambar di atas. Pada gambar tersebut terdapat 3 citra yaitu citra sumber, mask, dan citra hasil. citra target adalah citra hasil yang sebelumnya tidak ada gambar kepala beruang yang merupakan bagian dari citra sumber yang dipindahkan menggunakan panduan dari citra mask. citra mask dapat dianggap sebagai cetakan atau penyaring bagian yang dipindahkan dan yang tidak.

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

Mudah Mengimplementasikan Fasilitas Pencarian dengan Python Whoosh

Beberapa waktu lalu saya sempat membutuhkan mekanisme pencarian terhadap kumpulan dokumen teks. Beberapa alternatif yang populer diantaranya Apache Lucene (Solr) dan Sphinx search. Lucene berlisensi Apache License, Sphinx GPL. Keduanya ditulis dalam bahasa JAVA (OK, BIG NO). Setelah kesana-kemari mencari akhirnya ketemu alternatif yang cukup menarik yaitu Whoosh. library ini ditulis dalam pure python, berlisensi BSD, cepat, dan mudah digunakan.

Whoosh is a fast, featureful full-text indexing and searching library
implemented in pure Python. Programmers can use it to easily add search
functionality to their applications and websites. Every part of how Whoosh
works can be extended or replaced to meet your needs exactly.

Read More

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