Kivy集成OpenCV调用相机加载耗时长、程序无响应问题
问题表现
- 相机视频流加载耗时极长,加载期间程序始终处于*Not Responding(无响应)*状态,完成相机加载需要1-2分钟。
原始实现代码
class Attendance(Screen): def on_enter(self, *args): self.image = Image() self.capture = cv2.VideoCapture(0) Clock.schedule_interval(self.load_video, 1.0/30.0) self.add_widget(self.image) return super().on_enter(*args) def load_video(self, *args): frame = self.capture.read() self.iamge_frame = frame buffer = cv2.flip(frame, 0).tobytes() texture = Texture.create(size=(frame.shape[1], frame.shape[0]), colorfmt='bgr') texture.blit_buffer(buffer, colorfmt='bgr',bufferfmt = 'ubyte') self.image.texture = texture
问题根因
cv2.VideoCapture(0)是阻塞式IO操作,直接运行在Kivy UI主线程中,摄像头初始化的长耗时会直接阻塞UI事件循环,系统无法收到界面响应消息就会标记程序为无响应状态。- 代码存在逻辑错误:
cv2.VideoCapture.read()返回值为(读取状态标识, 帧数据)的元组,代码直接将元组赋值给frame变量,后续调用frame.shape等属性时实际存在类型不匹配问题;同时存在self.iamge_frame的拼写错误。 - 每帧渲染都重复创建
Texture对象,存在无意义的性能开销,会进一步拖慢视频流加载和渲染速度。 - 缺少页面退出时的资源释放逻辑,摄像头资源持续占用会导致后续进入页面时初始化耗时进一步增加。
修复后代码
import threading import cv2 from kivy.clock import Clock from kivy.uix.image import Image from kivy.graphics.texture import Texture from kivy.uix.screenmanager import Screen class Attendance(Screen): def on_enter(self, *args): self.image = Image() self.add_widget(self.image) self.capture = None self.render_texture = None # 将摄像头初始化操作放到守护子线程执行,避免阻塞UI主线程 threading.Thread(target=self._init_camera, daemon=True).start() return super().on_enter(*args) def _init_camera(self): self.capture = cv2.VideoCapture(0) # 可根据实际需求设置摄像头采集分辨率,降低帧处理开销 self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, 640) self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) # 摄像头初始化完成后,再在主线程启动帧更新定时任务 Clock.schedule_interval(self._load_video_frame, 1.0 / 30.0) def _load_video_frame(self, *args): # 摄像头未就绪时直接跳过 if not self.capture or not self.capture.isOpened(): return ret, frame = self.capture.read() # 帧读取失败直接跳过本次渲染 if not ret: return self.image_frame = frame frame_buffer = cv2.flip(frame, 0).tobytes() # 纹理对象仅在首次创建或分辨率变化时更新,后续复用减少开销 frame_size = (frame.shape[1], frame.shape[0]) if self.render_texture is None or self.render_texture.size != frame_size: self.render_texture = Texture.create(size=frame_size, colorfmt='bgr') self.render_texture.blit_buffer(frame_buffer, colorfmt='bgr', bufferfmt='ubyte') self.image.texture = self.render_texture def on_leave(self, *args): # 离开页面时释放摄像头资源、取消定时任务,避免资源泄漏 Clock.unschedule(self._load_video_frame) if self.capture and self.capture.isOpened(): self.capture.release() return super().on_leave(*args)
修复说明
- 阻塞式的摄像头初始化逻辑移到独立子线程执行,UI主线程不会被阻塞,不会再出现加载期无响应的问题。
- 补全了帧读取状态校验、变量拼写修正,避免运行时异常。
- 纹理对象做复用处理,减少每帧重复创建对象的性能开销。
- 增加页面退出时的资源回收逻辑,避免摄像头被持续占用导致的重复加载慢问题。
内容的提问来源于stack exchange,提问作者chicken
相关产品推荐
相关产品推荐

