Mengintegrasikan openCV 2.x dengan wxPython
sebetulnya topik ini sudah lama ada, namun sepertinya tulisan-tulisan lainnya masih menampilkan kode dengan interface python versi lama. wxPython merupakan pustaka antarmuka grafis (GUI) yang berbasis wxWidget yang sudah dibungkus untuk digunakan dalam kode python. Alasan utama menggunakan pustaka GUI selain dari bawaan openCV adalah keterbatasan pustaka GUI bawaan openCV (tidak ada koleksi widget dan dialog). Selain wxPython bisa juga menggunakan PyQt4 (Qt4 dibungkus untuk python). Namun, saya sedang tidak tertarik menggunakan PyQt karena hambatan lisensi. Oke, kembali ke topik utama mengintegrasikan bungkusan baru openCV. Bungkusan (wrapper) lama openCV sudah mulai tidak digunakan lagi dan tata-cara pemanggilan fungsi openCV di bungkusan yang baru lebih nyaman karena tidak banyak redundansi (misal opencv.cvQueryFrame() menjadi cv.QueryFrame() ).
Kode berikut merupakan contoh integrasi dengan wxPython yang ada di halaman wiki opencv
import wx
import opencv.cv as cv
import opencv.highgui as gui
class CvMovieFrame(wx.Frame):
TIMER_PLAY_ID = 101
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1)
self.capture = gui.cvCreateCameraCapture(-1)
frame = gui.cvQueryFrame(self.capture)
self.SetSize((frame.width, frame.height))
self.displayPanel = wx.Panel(self, -1)
cv.cvCvtColor(frame, frame, cv.CV_BGR2RGB)
self.bmp = wx.BitmapFromBuffer(frame.width, frame.height, frame.imageData)
self.Bind(wx.EVT_PAINT, self.onPaint)
self.playTimer = wx.Timer(self, self.TIMER_PLAY_ID)
wx.EVT_TIMER(self, self.TIMER_PLAY_ID, self.onNextFrame)
fps = gui.cvGetCaptureProperty(self.capture, gui.CV_CAP_PROP_FPS)
self.Show(True)
if fps!=0: self.playTimer.Start(1000/fps)#every X ms
else: self.playTimer.Start(1000/15)#assuming 15 fps
def onPaint(self, evt):
if self.bmp:
dc=wx.BufferedPaintDC(self.displayPanel, self.bmp)
evt.Skip()
def onNextFrame(self, evt):
frame = gui.cvQueryFrame(self.capture)
if frame:
cv.cvCvtColor(frame, frame, cv.CV_BGR2RGB)
self.bmp.CopyFromBuffer(frame.imageData)
self.Refresh()
evt.Skip()
if __name__=="__main__":
app = wx.App()
app.RestoreStdio()
CvMovieFrame(None)
app.MainLoop()
kode ini kemudian disesuaikan sehingga menjadi
import wx
import cv #cukup 1 import
class CvMovieFrame(wx.Frame):
TIMER_PLAY_ID = 101
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1)
self.capture = cv.CreateCameraCapture(-1)
frame = cv.QueryFrame(self.capture)
self.SetSize((frame.width, frame.height))
self.displayPanel = wx.Panel(self, -1)
cv.CvtColor(frame, frame, cv.CV_BGR2RGB)
self.bmp = wx.BitmapFromBuffer(frame.width, frame.height, frame.tostring()) #imageData sudah tidak ada
self.Bind(wx.EVT_PAINT, self.onPaint)
self.playTimer = wx.Timer(self, self.TIMER_PLAY_ID)
wx.EVT_TIMER(self, self.TIMER_PLAY_ID, self.onNextFrame)
fps = cv.GetCaptureProperty(self.capture, cv.CV_CAP_PROP_FPS)
self.Show(True)
if fps!=0: self.playTimer.Start(1000/fps)#every X ms
else: self.playTimer.Start(1000/15)#assuming 15 fps
def onPaint(self, evt):
if self.bmp:
dc=wx.BufferedPaintDC(self.displayPanel, self.bmp)
evt.Skip()
def onNextFrame(self, evt):
frame = cv.QueryFrame(self.capture)
if frame:
cv.CvtColor(frame, frame, cv.CV_BGR2RGB)
self.bmp.CopyFromBuffer(frame.tostring())
self.Refresh()
evt.Skip()
if __name__=="__main__":
app = wx.App()
app.RestoreStdio()
CvMovieFrame(None)
app.MainLoop()
setelah dicoba dan berhasil timbul masalah baru (sepertinya hanya di windows) yaitu gambar dari webcam terlihat berkedip (sesekali). walaupun kode di atas sudah menggunakan doublebuffering (wx.BufferedPaintDC) seperti yang dianjurkan oleh wxPython, namun sepertinya tidak mempan kalau dijalankan di Windows (saya coba di Win7). Cara lain untuk menghilangkan kedipan ini ditunjukkan di mailing-list wxPython-users dengan catatan perlu menggunakan pustaka khusus windows yaitu pythonwin32 seperti di kode berikut.
import wx
import win32api
import win32con
import sys
import cv
sys.path.append('C:\OpenCV2.1\Python2.6\Lib\site-packages')
class captureTest(wx.Frame):
TIMER_PLAY_ID = 101
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1)
def SetCompositeMode(self, on=True):
exstyle = win32api.GetWindowLong(self.GetHandle(), win32con.GWL_EXSTYLE)
if on:
exstyle |= win32con.WS_EX_COMPOSITED
else:
exstyle &= ~win32con.WS_EX_COMPOSITED
win32api.SetWindowLong(self.GetHandle(), win32con.GWL_EXSTYLE, exstyle)
SetCompositeMode(self, True)
self.capture = cv.CaptureFromCAM(0)
capImg = cv.QueryFrame(self.capture)
self.SetSize((capImg.width, capImg.height))
self.displayPanel = wx.Panel(self, -1)
cv.CvtColor(capImg, capImg, cv.CV_BGR2RGB)
self.buildBmp = wx.BitmapFromBuffer(capImg.width, capImg.height, capImg.tostring())
self.Bind(wx.EVT_PAINT, self.onPaint)
self.playTimer = wx.Timer(self, self.TIMER_PLAY_ID)
wx.EVT_TIMER(self, self.TIMER_PLAY_ID, self.onNextFrame)
fps = cv.GetCaptureProperty(self.capture, cv.CV_CAP_PROP_FPS)
self.Show(True)
if fps!=0: self.playTimer.Start(1000/fps) #every X ms
else: self.playTimer.Start(1000/15) #assuming 15 fps
def onPaint(self, evt):
if self.buildBmp:
dc=wx.BufferedPaintDC(self.displayPanel, self.buildBmp)
evt.Skip()
def onNextFrame(self, evt):
capImg = cv.QueryFrame(self.capture)
if capImg:
cv.CvtColor(capImg, capImg, cv.CV_BGR2RGB)
self.buildBmp.CopyFromBuffer(capImg.tostring())
self.Refresh()
evt.Skip()
if __name__=="__main__":
app = wx.App()
app.RestoreStdio()
captureTest(None)
app.MainLoop()
Kode tersebut belum saya coba, karena di komputer yang saya gunakan belum terpasang pustaka pythonwin32. Setelah meneruskan membaca sambil mencoba ternyata di diskusi tersebut juga ada jawabannya yaitu tidak menggunakan Timer tetapi memanfaatkan event EVT_IDLE. kode akhirnya seperti ini (catatan tambahan, saya mengabaikan frame-per-second dari data video):
import wx
import cv
class CvMovieFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1)
self.capture = cv.CreateCameraCapture(-1)
frame = cv.QueryFrame(self.capture)
self.SetSize((frame.width, frame.height))
self.displayPanel = wx.Panel(self, -1)
cv.CvtColor(frame, frame, cv.CV_BGR2RGB)
self.bmp = wx.BitmapFromBuffer(frame.width, frame.height, frame.tostring())
self.Show(True)
self.Bind(wx.EVT_IDLE, self.onIdle)
def onIdle(self, event):
self.nextFrame()
event.RequestMore()
def nextFrame(self):
img = cv.QueryFrame(self.capture)
cv.CvtColor(img, img, cv.CV_BGR2RGB)
self.bmp.CopyFromBuffer(img.tostring())
dc = wx.ClientDC(self.displayPanel)
dc.DrawBitmap(self.bmp, 0, 0, False)
if __name__=="__main__":
app = wx.App()
app.RestoreStdio()
CvMovieFrame(None)
app.MainLoop()
Kode di atas ternyata cukup memuaskan. tidak ada lagi kedipan ketika menggambar frame baru.
Filed under: computer vision, programming | 7 Komentar
Tag:flicker, opencv, python, wxPython
Thank’s Mas..
btw, Python+OpenCv ini menurut saya kok agak lemot ya….
terutama setelah penambahan button dan menu… jadi agak lama Prosesnya
ada contoh kodenya?
saya kombinasikan denga face detection yang sy dapat dari internet.
kode nya tak taruh sini Mas https://gist.github.com/2203182
Mohon Bantuannya
haarcascade memang lambat. apalagi kalau ditambah integrasi dengan wx diproses di tiap frame.
kalau mau deteksi tiap frame agak susah kecuali klo multithread, tapi mengingat di python nggak ada true-multithread tampaknya sulit.
paling alternatifnya deteksinya cukup sekali, selanjutnya di-track pakai template matching ke daerah sekitar frame sebelumnya.
hadduduh.. saya ini masih newbie mas..
multithread dan template matching pun saya nggak paham
ada contohnya ??
mentok-mentok kalopun tidak bisa,, Ya sudahlah mas..
satu lagi Mas,,
mengenai materi deteksi wajah, (punya materi yang MUDAH dipelajari untuk pemula Mas) Mungkin materi kuliah mas pebbie atau tutorial yg bertahap lainnya.
Hatur Nuhun
saya juga sudah bertanya dimilis mas,,
jawabanya hampir sama…
disuruh bikin multithread :’(
https://groups.google.com/group/wxpython-users/browse_thread/thread/9e5c4f444e18f0e4/1a2b4e7e8859bc19?hl=id#1a2b4e7e8859bc19