Mengintegrasikan openCV 2.x dengan wxPython

10Feb11

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.



7 Responses to “Mengintegrasikan openCV 2.x dengan wxPython”

  1. 1 Mamik

    Thank’s Mas..
    btw, Python+OpenCv ini menurut saya kok agak lemot ya….
    terutama setelah penambahan button dan menu… jadi agak lama Prosesnya

  2. 4 pebbie

    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.


  1. 1 Simple Face Tracking with OpenCV (Python) « GAIBlog

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 271 pengikut lainnya.