PyQT5开发之线程协作实现流水线式图像分析

PyQT5开发之线程协作实现流水线式图像分析 张显显 2023-09-20 19:02:57 256

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

应用程序概述

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

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

易百纳社区

代码实现

这样实现了三个类


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

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

class 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

处理图像线程

class 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

界面线程

class 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)
演示部分

演示部分

易百纳社区

易百纳社区

易百纳社区

文章来源公众号:OpenCV开发者联盟

声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
红包 1 收藏 评论 打赏
评论
0个
内容存在敏感词
手气红包
    易百纳技术社区暂无数据
相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
张显显
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区