OpenCV与wxPython的集成

7

我想将我的网络摄像头的opencv视频流集成到比highgui提供更复杂的GUI中,仅有几个按钮和其他功能,但实际上并不是那么简单。我找不到任何可以开始设计GUI的基础示例。

我尝试将此代码转换为新的opencv接口,结果非常糟糕。我对opencv、numpy和GUI设计都还很陌生。有时会流视频,但大多数情况下它只是卡在那里。我猜想我的一个错误可能在于wx.BitmapFromBuffer(col, row, img),因为在旧版本中它们使用了pil图像格式,现在它正在使用numpy数组,因此在原始代码中,他们使用了pil函数“imageData”,而不是像我一样直接传递numpy数组。

非常感谢任何帮助。

color_channels_pic

这是我转换的代码。

import wx
import cv2

class MyFrame(wx.Frame):
   def __init__(self, parent):
       wx.Frame.__init__(self, parent)
       self.displayPanel = wx.Panel(self)
       self.displayPanel.SetSize(wx.Size(800,640))

       self.cam = cv2.VideoCapture(1)
       self.cam.set(3, 640)
       self.cam.set(4, 480)
       ret, img = self.cam.read()

       cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
       row, col, x = img.shape
       self.SetSize((col,row))
       self.bmp = wx.BitmapFromBuffer(col, row, img)
       self.displayPanel.Bind(wx.EVT_PAINT, self.onPaint)

       self.playTimer = wx.Timer(self)
       self.Bind(wx.EVT_TIMER, self.onNextFrame)

       self.playTimer.Start(1000/15)

    def onPaint(self, evt):
        if self.bmp:
            dc = wx.BufferedPaintDC(self.displayPanel)
            self.PrepareDC(dc)
            dc.DrawBitmap(self.bmp, 0, 0, True)
        evt.Skip()

    def onNextFrame(self, evt):
        ret, img = self.cam.read()
        if ret == True:
            cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
            self.bmp.CopyFromBuffer(img)
            self.displayPanel.Refresh()
        evt.Skip()

if __name__=="__main__":
    app = wx.App()
    MyFrame(None).Show()
    app.MainLoop()

1
你的代码对我不起作用,但是经过微调之后就可以正常运行了。我看到的问题是,例如当我调整窗口大小时,流会中断,直到我停止调整大小。但如果我不触摸窗口,它就可以正常运行。你除了简单地显示帧外还做了其他事情吗? - mmgp
2
关于颜色,cvtColor 不是原地操作,所以您必须将其结果重新分配给 img - mmgp
@mmgp 目前我只是显示捕获的帧,尽管我计划执行其他操作。 - wind85
@mmgp,您能否详细解释一下,因为我认为我在每个下一帧都按照您告诉我的方式进行操作... - wind85
2
如果你有一个不在原地操作的函数f(x),你必须将结果分配给某个变量以保存结果。cvtColor就是这样一个函数。 - mmgp
显示剩余2条评论
3个回答

12

下面的示例代码在我使用OS X时效果良好,但在不同平台上使用wx时会有一些小意外。这几乎是相同的代码,不同之处在于从返回的结果被重新分配,并且添加了wx.Panel的子类(这是重要的部分)。

import wx
import cv, cv2

class ShowCapture(wx.Panel):
    def __init__(self, parent, capture, fps=15):
        wx.Panel.__init__(self, parent)

        self.capture = capture
        ret, frame = self.capture.read()

        height, width = frame.shape[:2]
        parent.SetSize((width, height))
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

        self.bmp = wx.BitmapFromBuffer(width, height, frame)

        self.timer = wx.Timer(self)
        self.timer.Start(1000./fps)

        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_TIMER, self.NextFrame)


    def OnPaint(self, evt):
        dc = wx.BufferedPaintDC(self)
        dc.DrawBitmap(self.bmp, 0, 0)

    def NextFrame(self, event):
        ret, frame = self.capture.read()
        if ret:
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            self.bmp.CopyFromBuffer(frame)
            self.Refresh()


capture = cv2.VideoCapture(0)
capture.set(cv.CV_CAP_PROP_FRAME_WIDTH, 320)
capture.set(cv.CV_CAP_PROP_FRAME_HEIGHT, 240)

app = wx.App()
frame = wx.Frame(None)
cap = ShowCapture(frame, capture)
frame.Show()
app.MainLoop()

为什么你要子类化Panel而不是Frame?在wx层级结构中,wx.Frame不应该是第一个“容器”吗?另外第二个导入(import)在OSX上也不会工作,因为要访问cv2中的旧cv,你需要导入cv2.cv。你的代码在我的机器上运行良好,顺便说一下,我的机器是基于Linux Debian的机器,但它没有将框架大小设置为640X480。 - wind85
1
我可以在OSX中很好地导入cv。框架肯定没有设置为640 x 480,因为在示例中它被设置为320 x 240,你尝试更改了吗?一个wx.Frame正在被创建,它是wx.Panel子类的父级。 - mmgp
1
“调整大小”是指调整运行程序的窗口吗?当我调整这个窗口时,就像之前评论的那样,我也停止收到更新。但它不会使程序崩溃。你所说的“崩溃”,是指程序退出吗?也许你得到的错误信息会有用。 - mmgp
抱歉,我没有表达清楚,我的意思是它变得无响应并停止更新。 - wind85
在OS X Yosemite中,我遇到了一个错误:Python意外退出。通过删除import cv(仅保留import cv2)并删除最后两行代码,我使脚本正常工作。因此,我认为这可能与我的OpenCV安装有关。 - sdk
显示剩余2条评论

0

您必须设置面板的大小以显示捕获的图像。我使用了您的代码并添加了"


self.SetSize(width,height)

没问题


0

你应该为下面的代码行添加注释

#self.PrepareDC(dc)

这对我有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接