问题描述
背景和问题
我正在尝试处理来自相机的流数据。 Python 不断因此消息而崩溃:
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
崩溃发生有时同时发出包含图像的信号。
我的代码,如下所示,遵循这个过程:
- 名为
QObject
的CameraThread
在 GUI 中实例化并由QThread
运行。 -
CameraThread
实例化一个类IngestManager
并将其提供给数据源。数据源会反复调用IngestManager
的{{1}}方法,提供数据。-
write()
的作用是将传入的数据重定向到等待线程。 This is the basis for this approach.
-
- 工作线程处理数据并通过回调方法
IngestManager
将其发送回IngestManager
-
frame_callback
发出信号。这是它有时崩溃的地方。
我的尝试/观察
我尝试了几种方法来修复它,包括将 IngestManager
传递给工作线程本身。我也认为有时线程会同时完成并发出,但我不知道如何解决这个问题。
我与 GUI 交互越多,崩溃发生得越快,例如快速按下虚拟按钮。如果我不与 UI 交互,它几乎不会发生(但它仍然会发生)。我想我可能需要某种锁。
我该如何开始解决这个问题?
这是连接到数据源、处理数据和发出信号的代码。
pyqtSignal
import io
import threading
from PIL import Image
from PIL.ImageQt import ImageQt
from PyQt5.QtCore import QObject,pyqtSignal,pyqtSlot
from PyQt5.QtGui import Qpixmap
class CameraThread(QObject):
signal_new_frame = pyqtSignal(Qpixmap)
def __init__(self,parent=None):
QObject.__init__(self,parent)
@pyqtSlot()
def run(self):
with DataSource() as source:
output = IngestManager(frame_signal=self.signal_new_frame)
# The source continuously calls IngestManager.write() with new data
source.set_ingest(output)
class IngestManager(object):
"""Manages incoming data stream from camera."""
def __init__(self,frame_signal):
self.frame_signal = frame_signal
# Construct a pool of 4 image processors along with a lock to control access between threads
self.lock = threading.Lock()
self.pool = [ImageProcessor(self) for _ in range(4)]
self.processor = None # First "frame" intentionally dropped,else thread would see garbled data at start
def write(self,buf):
if buf.startswith(b'\xff\xd8'): # Frame detected
if self.processor:
self.processor.event.set() # Let waiting processor thread kNow a frame is here
with self.lock:
if self.pool:
self.processor = self.pool.pop()
else:
# All threads popped and busy. No choice but to skip frame.
self.processor = None
if self.processor:
self.processor.stream.write(buf) # Feed frame data to current processor
def frame_callback(self,image):
print('Frame processed. Emitting.')
self.frame_signal.emit(image)
class ImageProcessor(threading.Thread):
def __init__(self,owner: IngestManager):
super(ImageProcessor,self).__init__()
# Data Stuff
self.stream = io.BytesIO()
# Thread stuff
self.event = threading.Event()
self.owner = owner
self.start()
def run(self):
while True:
if self.event.wait(1):
pil_image = Image.open(self.stream)
# Image is processed here,then sent back
# ...
# ...
q_image = ImageQt(pil_image)
q_pixmap = Qpixmap.fromImage(q_image)
self.owner.frame_callback(q_pixmap)
# Reset the stream and event
self.stream.seek(0)
self.stream.truncate()
self.event.clear()
# Return to available pool
with self.owner.lock:
self.owner.pool.append(self)
解决方法
首先,在主线程之外使用 QPixmap
是不安全的。所以你应该改用 QImage
。
其次,ImageQt
共享传递给它的 Image
的缓冲区。因此,如果在 Qt 图像仍然存在时删除缓冲区,则很可能会发生崩溃。如果您不能长时间保持 PIL 图像,您可能需要 copy the Qt image 来防止这种情况发生。