前言

 目标检测代码对视频进行拆帧检测耗时较长,在界面加入该功能后,界面经常崩溃卡死,下面记录解决的过程。

设计思路

 在参考了一些资料后,尝试了两种解决方法。
①新建线程去进行视频检测,与界面互不干扰。需要解决的问题是如何知道线程中的检测是否结束。
②逐帧检测强制更新GUI将结果显示到相应控件。该方法对电脑的性能有要求。如果检测速度较快,可以选择该方法。我的垃圾a卡深度学习都跑的很呛,电脑性能不错的话可以尝试。

方案一

使用pyqtSignal()-高级自定义信号与槽获取检测是否结束
参考资料

1.定义回传数据方法

1
2
3
4
5
6
7
#接受线程回传数据
def callbacklog(self, msg):
# self.text = self.text + time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime()) + msg + "\n"
self.text = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime()) + msg + "\n"
print(self.text)
# 回调数据输出到文本框textEdit
self.textEdit.setText(self.text)

2.启动线程,连接自定义的信号

1
2
3
4
5
#新建线程检测视频
def start_detect(self):
th1 = Thread_detect(self)
th1.start()
th1._signal.connect(self.callbacklog) # 连接信号

3.线程中发送信号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

#检测线程中发送信号
class Thread_detect(QThread):
#声明带str类型的信号
_signal = pyqtSignal(str)
#检测函数部分
def run(self):
start = time.perf_counter() # 起始时间
#初始化检测模型
...
# 读取视频
cap = cv2.VideoCapture(videoName)
FrameNumber = cap.get(7) # 视频文件的帧数
n = 1
#发送信号
self._signal.emit("正在检测中")
# 间隔帧数
timeF = 5
# 指定解码格式
fourcc = cv2.VideoWriter_fourcc(*'XVID')
# 指定保存参数 保持原视频长宽
out = cv2.VideoWriter(r'路径', fourcc, 18.0, (int(cap.get(3)), int(cap.get(4))))
while (True):
# 读取某一帧
ref, frame = cap.read()
#发送信号
self._signal.emit("当前进度:"+str(n)+'/'+str(FrameNumber))
if (n % timeF == 0): # 每隔timeF帧进行存储操作if (c%timeF == 0): #每隔timeF帧进行存储操作

#检测部分
...


n = n + 1
if n == FrameNumber:
#检测结束
...
end = time.perf_counter() # 结束时间
#print('%.2f分钟' % ((end - start) / 60.0)) # 输出运行时间,精确到小数点后十位
self._signal.emit('视频检测完毕,共%.2f分钟' % ((end - start) / 60.0))
cap.release()
out.release()
cv2.destroyAllWindows()
break

 由于电脑性能限制,放弃了实时检测,当检测结束后保存视频,之后在选择播放检测结果。

方案二

逐帧检测,将结果更新到label中。
解决卡顿的关键就是QApplication.processEvents()
直接上代码吧,基本都注释了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#实时检测测试   显示起来一顿一顿(垃圾PC)
def show_img(self, img):
showImg = QImage(img.data, img.shape[1], img.shape[0],
img.shape[1] * 3, # 每行数据个数,3通道 所以width*3
QImage.Format_RGB888)
self.label_image.setPixmap(QPixmap.fromImage(showImg)) # 展示图片


def on_pushButton_start_clicked(self):
# 获取图像数据流
self.cap = cv2.VideoCapture(videoName)
FrameNumber = self.cap.get(7) # 视频文件的帧数
# 指定解码格式
fourcc = cv2.VideoWriter_fourcc(*'XVID')
# 指定保存参数 保持原视频
out = cv2.VideoWriter(r'路径', fourcc, 18.0, (int(self.cap.get(3)), int(self.cap.get(4))))
n = 1
# 判断数据流是否打开
if self.cap.isOpened():
# ‘开始’按钮设置为不可用
# 以免二次误点造成错误
self.pushButton_detect.setEnabled(False)

# 开始帧循环
self.video_flg = True
while self.video_flg:
# 按帧读取图像
ret, self.img_scr = self.cap.read()
# opencv中图像为BGR,这里转为RGB
self.img_scr = cv2.cvtColor(self.img_scr, cv2.COLOR_BGR2RGB)
print("当前帧数",n)
if( n % 10 == 0):
#图片检测部分
...

# 控件更新显示图像
self.show_img(img)
frame = cv2.cvtColor(self.img_scr, cv2.COLOR_RGB2BGR)
out.write(frame.astype('uint8'))
n = n+1
if n == FrameNumber:
print('视频检测完毕')
self.pushButton_detect.setEnabled(True)
self.cap.release()
out.release()
cv2.destroyAllWindows()
break
# 强制更新UI
# 如果没有,界面就‘假死’了,因为一直处于循环里
QApplication.processEvents()
else:
self.textEdit.setText('路径错误!!!\n请检查')

总结

 之前实在没有接触过,本来用强制更新GUI的方法解决,但是垃圾电脑它速度不行,眨眼补帧都不行。后来选择线程去检测视频,没有查到python中判断线程状态的内置函数,于是就用textEdit控件接收到检测完毕信号,然后在播放保存的检测结果。