PyQT5+OpenCV多线程协作演示

描述

 

引言

    学习多线程最典型的问题就是如何在多个线程之间传递消息与写作,PyQT5的线程支持在不同线程之间传递信号触发事件,实现多个线程之间的协助,完成诸如生产者-消费者这样经典的多线程协作。本文将通过QThread与信号槽机制构建一个生产者-消费者模型,演示多个线程之间的协作。

应用程序概述

这里演示了一个从图像采集(用本地图像数据集替代)到图像分析处理(简单二值化+形态学处理)、到主界面更新的应用程序。主界面是UI线程、图像采集跟图像分析分别在两个不同的工作线程中,通过信号与槽机制协作工作,相互配合实现图像采集到分析到结果更新到界面线程。

多线程协作信号触发示意图

应用程序

代码实现

这样实现了三个类

 

ImageFetchThread // 图像采集
ImageAnalysisThread // 图像分析
ContentPanel // 界面显示与更新

 

这三个类的代码分别,模拟图像采集线程

 

 1class ImageFetchThread(QtCore.QThread):
 2    fire_stats_signal = QtCore.pyqtSignal(dict)
 3
 4    def __init__(self, images_dir):
 5        super(ImageFetchThread, self).__init__()
 6        self.images_dir = images_dir
 7        self.read_next = True
 8
 9    def request_image(self):
10        self.read_next = True
11
12    def run(self):
13        if len(self.images_dir) == 0:
14            return
15        files = os.listdir(self.images_dir)
16        idx = 0
17        while True:
18            if idx == len(files):
19                break
20            if self.read_next is True:
21                print("grab one image...")
22                image = cv.imread(os.path.join(self.images_dir, files[idx]))
23                gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
24                idx += 1
25                self.read_next = False
26                self.fire_stats_signal.emit({"im_data": gray})
27        self.fire_stats_signal.emit({"done": "done"})
28        return

 

处理图像线程

 

 1class ImageAnalysisThread(QtCore.QThread):
 2    request_image_signal = QtCore.pyqtSignal()
 3    update_result_signal = QtCore.pyqtSignal(dict)
 4
 5    def __init__(self):
 6        super(ImageAnalysisThread, self).__init__()
 7        self.image_data = None
 8        self.stop = False
 9
10    def process_im(self, results):
11        self.image_data = results.get("im_data")
12        if results.get("done") is not None:
13            self.stop = True
14
15    def run(self):
16        while True:
17            if self.stop is True:
18                break
19            if self.image_data is None:
20                continue
21            print("started to process one image...")
22            # ret, binary = cv.threshold(self.image_data, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
23            binary = cv.adaptiveThreshold(self.image_data, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C,
24                                          cv.THRESH_BINARY_INV, 25, 10)
25            se = cv.getStructuringElement(cv.MORPH_RECT, (7, 7))
26            resutl = cv.morphologyEx(binary, cv.MORPH_DILATE, se)
27            self.request_image_signal.emit()
28            self.update_result_signal.emit({"im_data": resutl})
29            self.image_data = None
30        self.update_result_signal.emit({"done": "done"})
31        return

 

界面线程

 

 1class ContentPanel(QtWidgets.QWidget):
 2    def __init__(self, parent=None):
 3        super().__init__(parent)
 4        fileBtn = QtWidgets.QPushButton("目录...")
 5        self.image_files_dir= QtWidgets.QLineEdit()
 6        self.image_files_dir.setMinimumWidth(100)
 7        self.image_files_dir.setEnabled(False)
 8        self.processBtn = QtWidgets.QPushButton("开始处理")
 9        hbox_layout = QtWidgets.QHBoxLayout()
10        hbox_layout.addWidget(fileBtn)
11        hbox_layout.addWidget(self.image_files_dir)
12        hbox_layout.addWidget(self.processBtn)
13        panel1 = QtWidgets.QGroupBox("目录选择")
14        panel1.setLayout(hbox_layout)
15
16        # 图像标签
17        self.imgLabel = QtWidgets.QLabel()
18        self.imgLabel.setMinimumSize(800, 600)
19        self.imgLabel.setStyleSheet("background-color:black; color: deeppink")
20        self.imgLabel.setAlignment(QtCore.Qt.AlignCenter)
21
22        # 添加到布局管理器中
23        vbox_layout = QtWidgets.QVBoxLayout()
24        vbox_layout.addWidget(panel1)
25        vbox_layout.addWidget(self.imgLabel)
26        vbox_layout.addStretch(1)
27
28        # 面板容器
29        self.setLayout(vbox_layout)
30
31        # setup listener
32        fileBtn.clicked.connect(self.on_select_image_dir)
33        self.processBtn.clicked.connect(self.on_process)
34
35        self.fetch_thread = None
36        self.analysis_thread = None
37
38    def on_select_image_dir(self):
39        img_dir = QtWidgets.QFileDialog.getExistingDirectory(self, "图像文件夹", ".")
40        self.image_files_dir.setText(img_dir)

 

演示部分

应用程序

应用程序

应用程序

  审核编辑:汤梓红
 
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分