Multithreading PyQt4视频播放器在移动窗口时崩溃

Multithreading PyQt4视频播放器在移动窗口时崩溃,multithreading,python-2.7,opencv,numpy,pyqt4,Multithreading,Python 2.7,Opencv,Numpy,Pyqt4,我已经编写了一个简单的PyQt4 GUI,它可以播放OpenCVVideoCapture。这需要将帧从numpy数组转换为QImages。我正在使用OpenCV,以便使用findCircles方法检测圆 但是,当我将帧传递给findCircles时,当窗口移动时,程序崩溃。如果不搜索圆,则不会出现此问题。我不明白为什么会发生这种情况,因为我的印象是工作是在不同于GUI的线程上完成的,因为我从QThread的run方法调用findCircles 请注意,我在控制台中没有收到正常的错误消息;Pyth

我已经编写了一个简单的PyQt4 GUI,它可以播放OpenCV
VideoCapture
。这需要将帧从numpy数组转换为
QImages
。我正在使用OpenCV,以便使用
findCircles
方法检测圆

但是,当我将帧传递给
findCircles
时,当窗口移动时,程序崩溃。如果不搜索圆,则不会出现此问题。我不明白为什么会发生这种情况,因为我的印象是工作是在不同于GUI的线程上完成的,因为我从
QThread
run
方法调用
findCircles

请注意,我在控制台中没有收到正常的错误消息;Python崩溃如下:

是我用来测试播放器的视频文件。我正在Windows8.1上运行Python 2.7.6

import sys
import cv2.cv as cv, cv2
from PyQt4.Qt import *
import time

def numpyArrayToQImage(array):
    if array != None:
        height, width, bytesPerComponent = array.shape
        bytesPerLine = bytesPerComponent * width;
        cv2.cvtColor(array, cv.CV_BGR2RGB, array)
        return QImage(array.data, width, height, bytesPerLine, QImage.Format_RGB888)
    return None

def findCircles(frame):
    grayFrame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    blurredFrame = cv2.medianBlur(grayFrame, 3)
    circles = cv2.HoughCircles(blurredFrame, cv.CV_HOUGH_GRADIENT, 1, 30, param1=50, param2=30, minRadius=30, maxRadius=35)

    if circles is not None: 
        for i in circles[0]:
            cv2.circle(frame, (i[0], i[1]), i[2], (255, 0, 0), 1) # Perimeter
            cv2.circle(frame, (i[0], i[1]), 3, (0, 255, 0), -1) # Center

class VideoThread(QThread):
    frameProcessed = pyqtSignal(QImage)

    def __init__(self, video, videoLabel):
        QThread.__init__(self)
        self.video = video
        self.fps = self.video.get(cv.CV_CAP_PROP_FPS)
        self.frameCount = self.video.get(cv.CV_CAP_PROP_FRAME_COUNT)
        self.startingSecond = 0
        self.videoLabel = videoLabel

    def run(self):
        clockAtStart = time.clock()

        while True:
            runtime = self.startingSecond + (time.clock() - clockAtStart)
            currentFrame = int(runtime * self.fps)

            if currentFrame < self.frameCount - 1:
                self.video.set(cv.CV_CAP_PROP_POS_FRAMES, currentFrame)
                frame = self.video.read()[1]
                findCircles(frame) # Removing this line removes the issue
                self.frameProcessed.emit(numpyArrayToQImage(frame))
                time.sleep(.02)
            else:
                break

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.initUI()

    @pyqtSlot(QImage)
    def updateVideoLabel (self, image):
        self.videoLabel.setPixmap(QPixmap.fromImage(image))
        self.videoLabel.update()

    def initUI(self):
        self.setGeometry(300, 300, 500, 375)
        self.setMinimumHeight(250)
        self.createWidgets()
        self.addWidgets()

    def startNewVideo(self):
        self.video = cv2.VideoCapture(unicode(QFileDialog.getOpenFileName(self, "Open video").toUtf8(), encoding="UTF-8"))
        self.videoThread = VideoThread(self.video, self.videoLabel)
        self.videoThread.frameProcessed.connect(self.updateVideoLabel)
        self.playVideoFrom(0)

    def playVideoFrom(self, frame):
        self.videoThread.startingSecond = frame / self.videoThread.fps
        self.videoThread.start()

    def createWidgets(self):
        self.populateMenuBar()
        self.videoLabel = QLabel()
        self.videoLabel.setStyleSheet('background-color : black;');

    def populateMenuBar(self):
        self.menuBar = self.menuBar()
        fileMenu = QMenu('File', self)
        openAction = QAction('Open video...', self)
        openAction.triggered.connect(self.startNewVideo)
        fileMenu.addAction(openAction)
        self.menuBar.addMenu(fileMenu)

    def addWidgets(self):
        mainLayout = QVBoxLayout()
        mainLayout.addWidget(self.videoLabel, 1)
        centralWidget = QWidget()
        self.setCentralWidget(centralWidget)
        centralWidget.setLayout(mainLayout)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    player = MainWindow()
    player.show()
    sys.exit(app.exec_())
导入系统 将cv2.cv导入为cv,cv2 从PyQt4.Qt导入* 导入时间 def numpyArrayToQImage(阵列): 如果数组!=无: 高度、宽度、bytesPerComponent=array.shape bytesPerLine=bytesPerComponent*宽度; cv2.CVT颜色(数组,cv.cv_BGR2RGB,数组) 返回QImage(array.data、宽度、高度、bytesPerLine、QImage.Format_RGB888) 一无所获 def findCircles(帧): grayFrame=cv2.CVT颜色(frame,cv2.COLOR\u BGR2GRAY) 模糊帧=cv2.medianBlur(灰色帧,3) 圆=cv2。霍夫圆(模糊帧,cv.cv_霍夫_渐变,1,30,参数1=50,参数2=30,最小半径=30,最大半径=35) 如果圆不是无: 对于圆[0]中的i: cv2.圆(帧,(i[0],i[1]),i[2],(255,0,0),1)#周长 cv2.圆(帧,(i[0],i[1]),3,(0,255,0),-1)#中心 类VideoThread(QThread): frameProcessed=pyqtSignal(QImage) 定义初始化(自我、视频、视频标签): QThread.\uuuu init\uuuu(self) self.video=视频 self.fps=self.video.get(cv.cv\u CAP\u PROP\u fps) self.frameCount=self.video.get(cv.cv\u CAP\u PROP\u FRAME\u COUNT) self.startingSecond=0 self.videoLabel=videoLabel def运行(自): clockAtStart=time.clock() 尽管如此: 运行时=self.startingSecond+(time.clock()-clockAtStart) currentFrame=int(运行时*self.fps) 如果currentFrame我已经测试了您的程序,当它没有发现错误消息中所示的圆圈时,程序崩溃:

Traceback (most recent call last):
  File "test_opencv_tkinter.py", line 53, in run
    findCircles(frame) # Removing this line removes the issue
  File "test_opencv_tkinter.py", line 26, in findCircles
    if len(circles) > 0:
TypeError: object of type 'NoneType' has no len()
我在
findCircles(frame)
函数中做了如下更改,即使在屏幕上移动窗口时,它也不会出错

def findCircles(frame):
    grayFrame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    blurredFrame = cv2.medianBlur(grayFrame, 3)
    circles = cv2.HoughCircles(grayFrame,cv.CV_HOUGH_GRADIENT,1,20,
                            param1=50,param2=30,minRadius=0,maxRadius=0)
    if circles == None: 
        print "no circles found"
        return
    if len(circles) > 0:
        print "found circles ", len(circles[0])
        for i in circles[0]:
            cv2.circle(frame, (i[0], i[1]), i[2], (255, 0, 0), 1) # Perimeter
            cv2.circle(frame, (i[0], i[1]), 3, (0, 255, 0), -1) # Center

是否尝试从函数findCircles返回帧?
findCircles
方法在帧顶部绘制找到的圆,从而改变帧对象。没有必要返回任何内容。你能提供一个示例视频来测试你的程序吗?我无法用当前的示例脚本重现这个问题。代码似乎能够在显示一些硬币的简单视频中找到圆圈,尽管这样做非常不一致。但是,在显示视频时不断移动窗口没有效果。这是在Linux上,使用Python2.7.9、qt 4.8.6和pyqt 4.11.3。尝试使用Python2.7.9可以发现,在创建问题的一个简短示例时,我引入了一个最初不在代码中的错误,这就是您发现的错误。我稍微编辑了我的代码。现在没有发现圆圈时没有错误,但是移动窗口仍然是一个问题。你能提供崩溃消息吗?你的跑步环境是什么?