【YOLOv8实时产品缺陷检测】

news2024/11/24 8:54:14

YOLOv8应用于产品缺陷检测实例

  • 项目概况
    • 项目实现
    • YOLOv8安装及模型训练
    • 关键代码展示
    • 动态效果展示

项目概况

本项目是应用YOLOv8框架实现训练自定义模型实现单一零件的缺陷检测,软件界面由PyQt5实现。
功能已正式使用,识别效果达到预期。

项目实现

项目使用了以下几个要素:

  1. 全新的界面设计 ,PyQt5结合QtDesigner自定义界面设计,快速构建想要的UI;
  2. 相机选型和光源 ,项目使用了迈德威视工业相机GYD-GE130M-T,对照官方给出的文档很容易就可以实现相机的使用,光源方面使用12V常规灯带打光,去除其他光源的干扰;
  3. IO触发 功能,实现串口控制IO,以快速输出检测识别结果,便于对接工业传送装置;

YOLOv8安装及模型训练

可参考该大佬文章: 传送门

项目展示图: 商业用途请勿随意使用

关键代码展示

迈德威视相机使用线程代码片.

import time

import cv2
from ultralytics import YOLO
import numpy as np
from PyQt5 import QtGui
from PyQt5.QtCore import pyqtSignal, QThread
from MindVisionSdk import mvsdk
import warnings

warnings.filterwarnings('ignore')


class MyThread(QThread):
    camera_signal = pyqtSignal(object)  # 输出相机数据流
    info_signal = pyqtSignal(str)  # 输出信息状态

    def __init__(self, parent=None):
        super().__init__(parent)
        self.drawLine = False
        self.monoCamera = None
        self.FrameBufferSize = None
        self.isPause = False
        self.mCamera = None
        self.pFrameBuffer = None
        self.model = YOLO(model="./YoloModel/best.pt")

    def __del__(self):
        mvsdk.CameraUnInit(self.mCamera)
        mvsdk.CameraAlignFree(self.pFrameBuffer)

    def run(self):
        # 枚举相机设备列表
        DevList = mvsdk.CameraEnumerateDevice()
        if len(DevList) >= 1:
            DevInfo = DevList[0]  # 选取设备列表第一个相机
            try:
                self.mCamera = mvsdk.CameraInit(DevInfo, -1, -1)
                self.info_signal.emit("【{}】 初始化相机成功".format(time.strftime("%Y-%m-%d %H:%M:%S")))
            except mvsdk.CameraException as e:
                self.info_signal.emit("【{}】 初始化相机异常:{}".format(time.strftime("%Y-%m-%d %H:%M:%S"), e))

        cap = mvsdk.CameraGetCapability(self.mCamera)
        self.monoCamera = (cap.sIspCapacity.bMonoSensor != 0)
        if self.monoCamera:
            mvsdk.CameraSetIspOutFormat(self.mCamera, mvsdk.CAMERA_MEDIA_TYPE_MONO8)
        else:
            mvsdk.CameraSetIspOutFormat(self.mCamera, mvsdk.CAMERA_MEDIA_TYPE_BGR8)
        mvsdk.CameraSetTriggerMode(self.mCamera, 0)
        mvsdk.CameraSetAeState(self.mCamera,True)  # True为自动曝光 False为手动曝光
        mvsdk.CameraSetRotate(self.mCamera, 0)
        mvsdk.CameraSetMirror(self.mCamera, 0, True)
        mvsdk.CameraPlay(self.mCamera)
        # 计算RGB buffer所需的大小,这里直接按照相机的最大分辨率来分配
        self.FrameBufferSize = cap.sResolutionRange.iWidthMax * cap.sResolutionRange.iHeightMax * (
            1 if self.monoCamera else 3)

        # 分配RGB buffer,用来存放ISP输出的图像
        # 备注:从相机传输到PC端的是RAW数据,在PC端通过软件ISP转为RGB数据(如果是黑白相机就不需要转换格式,但是ISP还有其它处理,所以也需要分配这个buffer)
        self.pFrameBuffer = mvsdk.CameraAlignMalloc(self.FrameBufferSize, 16)
        while True:
            try:
                if not self.isPause:

                    pRawData, FrameHead = mvsdk.CameraGetImageBuffer(self.mCamera, 10)
                    mvsdk.CameraImageProcess(self.mCamera, pRawData, self.pFrameBuffer, FrameHead)
                    if self.drawLine:
                        mvsdk.CameraImageOverlay(self.mCamera, self.pFrameBuffer, FrameHead)
                    mvsdk.CameraReleaseImageBuffer(self.mCamera, pRawData)
                    # 此时图片已经存储在pFrameBuffer中,对于彩色相机pFrameBuffer=RGB数据,黑白相机pFrameBuffer=8位灰度数据

                    # 把pFrameBuffer转换成opencv的图像格式以进行后续算法处理
                    frame_data = (mvsdk.c_ubyte * FrameHead.uBytes).from_address(self.pFrameBuffer)
                    frame = np.frombuffer(frame_data, dtype=np.uint8)
                    frame = frame.reshape((FrameHead.iHeight, FrameHead.iWidth,
                                           1 if FrameHead.uiMediaType == mvsdk.CAMERA_MEDIA_TYPE_MONO8 else 3))
                    frame = cv2.resize(frame,(960,540), interpolation=cv2.INTER_LINEAR)
                    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                    results = self.model.predict(source=frame,verbose=False)
                    # 提取检测结果
                    # for result in results:
                    #     boxes = result.boxes.xyxy  # 边界框坐标
                    #     scores = result.boxes.conf  # 置信度分数
                    #     classes = result.boxes.cls  # 类别索引
                    #     # 如果有类别名称,可以通过类别索引获取
                    #     class_names = [self.model.names[int(cls)] for cls in classes]
                    #     # 打印检测结果
                    #     for box, score, class_name in zip(boxes, scores, class_names):
                    #         print(f"Class: {class_name}, Score: {score:.2f}, Box: {box}")
                    annotated_frame = results[0].plot()
                    showImage = QtGui.QImage(annotated_frame.data, frame.shape[1], frame.shape[0], QtGui.QImage.Format_RGB888)
                    self.camera_signal.emit(showImage)
                    self.info_signal.emit("【{}】 实时显示中......".format(time.strftime("%Y-%m-%d %H:%M:%S")))
            except mvsdk.CameraException as e:
                if e.error_code != mvsdk.CAMERA_STATUS_TIME_OUT:
                    self.info_signal.emit("【{}】 实时显示线程报错:{}".format(time.strftime("%Y-%m-%d %H:%M:%S"), e))
            finally:
                self.msleep(20)

主界面逻辑代码片

import os.path
import sys
import time

import cv2
from PyQt5 import QtWidgets
from PyQt5 import QtGui
from PyQt5.QtWidgets import QMessageBox
from WindowUI import Ui_MainWindow
from CameraThread import MyThread
from MindVisionSdk import mvsdk
from Yaml_Tool import myYamlTool


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):

    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.camera_thread = MyThread()
        self.camera_thread.camera_signal.connect(self.showVideo)
        self.camera_thread.info_signal.connect(self.showStatus)
        self.pushButton_open_close.clicked.connect(self.camera_start_stop)
        self.pushButton_play.clicked.connect(self.camera_play_pause)
        self.pushButton_play.setEnabled(False)
        self.pushButton_catch.clicked.connect(self.catchPicture)
        self.pushButton_addline.clicked.connect(self.drawLine)
        self.radioButton_run.setStyleSheet("QRadioButton::indicator:!checked {border: 2px solid #8f8f91;}"
                                           "QRadioButton::indicator:checked {background-color: #55ff7f;}"
                                           "QRadioButton::indicator:checked:hover {border: 2px solid #8f8f91;}")
        self.radioButton_mark.setStyleSheet("QRadioButton::indicator:!checked {border: 2px solid #8f8f91;}"
                                            "QRadioButton::indicator:checked {background-color: #55ff7f;}"
                                            "QRadioButton::indicator:checked:hover {border: 2px solid #8f8f91;}")
        self.yamlTool = myYamlTool()
        self.yamlData = self.yamlTool.read_yaml('config.yaml')
        if self.yamlData['runMode'] == 'mark':
            self.radioButton_mark.setChecked(True)
        else:
            self.radioButton_run.setChecked(True)

        self.radioButton_mark.clicked.connect(lambda: self.selectMode(0))
        self.radioButton_run.clicked.connect(lambda: self.selectMode(1))
        self.doubleSpinBox_threshold.setValue(self.yamlData['threshold'])
        self.doubleSpinBox_threshold.valueChanged.connect(self.thresholdChange)

    def closeEvent(self, event):
        """
        重写关闭按钮事件
        :param event:
        :return:
        """
        reply = QMessageBox.question(self, '警告', '<font color=red><b>确定退出工具?</b></font>',
                                     QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

        if reply == QMessageBox.Yes:
            if self.camera_thread.isRunning():
                self.camera_thread.__del__()
                self.camera_thread.terminate()
            event.accept()
        else:
            event.ignore()

    def camera_start_stop(self):
        """
        打开关闭相机按钮事件
        :return:
        """
        if self.pushButton_open_close.text() == "打开相机":
            self.camera_thread.start()
            self.camera_thread.isPause = False
            self.pushButton_open_close.setText("关闭相机")
            self.statusbar.showMessage("【{}】 已打开相机".format(time.strftime("%Y-%m-%d %H:%M:%S")))
            self.pushButton_play.setEnabled(True)
            self.pushButton_play.setText("暂停")
            self.pushButton_catch.setEnabled(True)
        else:
            self.camera_thread.__del__()
            self.camera_thread.terminate()
            self.camera_thread.isPause = True
            self.pushButton_open_close.setText("打开相机")
            self.statusbar.showMessage("【{}】 已关闭相机".format(time.strftime("%Y-%m-%d %H:%M:%S")))
            self.label_display.setText("相机已关闭")
            self.pushButton_play.setEnabled(False)
            self.pushButton_play.setText("播放")
            self.pushButton_catch.setEnabled(False)

    def camera_play_pause(self):
        """
        相机暂停和继续
        :return:
        """
        if self.camera_thread.isRunning():
            if self.pushButton_play.text() == "暂停":
                mvsdk.CameraPause(self.camera_thread.mCamera)
                self.camera_thread.isPause = True
                self.statusbar.showMessage("【{}】 实时显示已暂停".format(time.strftime("%Y-%m-%d %H:%M:%S")))
                self.pushButton_play.setText("播放")
            else:
                mvsdk.CameraPlay(self.camera_thread.mCamera)
                self.camera_thread.isPause = False
                self.statusbar.showMessage("【{}】 实时显示已恢复".format(time.strftime("%Y-%m-%d %H:%M:%S")))
                self.pushButton_play.setText("暂停")

    def catchPicture(self):
        """
        抓取图片
        :return:
        """
        if self.camera_thread.isRunning():
            try:
                mvsdk.CameraSetMirror(self.camera_thread.mCamera, 1, True)
                pFrameBuffer = mvsdk.CameraAlignMalloc(self.camera_thread.FrameBufferSize, 16)
                pRawData, FrameHead = mvsdk.CameraGetImageBuffer(self.camera_thread.mCamera, 1000)
                mvsdk.CameraImageProcess(self.camera_thread.mCamera, pRawData, pFrameBuffer, FrameHead)
                mvsdk.CameraReleaseImageBuffer(self.camera_thread.mCamera, pRawData)
                mvsdk.CameraSetMirror(self.camera_thread.mCamera, 1, False)
                # 此时图片已经存储在pFrameBuffer中,对于彩色相机pFrameBuffer=RGB数据,黑白相机pFrameBuffer=8位灰度数据
                # 该示例中我们只是把图片保存到硬盘文件中
                if self.yamlData['runMode'] == 'mark':
                    if not os.path.exists("CatchImage"):
                        os.mkdir("CatchImage")
                    dirName = 'CatchImage'
                else:
                    if not os.path.exists("RunImage"):
                        os.mkdir("RunImage")
                    dirName = 'RunImage'
                catchTime = time.strftime("%Y%m%d%H%M%S")
                status = mvsdk.CameraSaveImage(self.camera_thread.mCamera,
                                               "./{}/grab_{}.jpg".format(dirName, catchTime),
                                               pFrameBuffer,
                                               FrameHead, mvsdk.FILE_JPG, 100)
                if os.path.exists("./{}/grab_{}.jpg".format(dirName, catchTime)):
                    # 进行目标检测
                    results = self.camera_thread.model.predict('./{}/grab_{}.jpg'.format(dirName, catchTime))
                    annotated_frame = results[0].plot()
                    # 保存处理后带标签的图片
                    cv2.imwrite("./{}/process_{}.jpg".format(dirName, catchTime), annotated_frame)
                    # 将图像数据转换为QImage格式
                    height, width, channel = annotated_frame.shape
                    bytes_per_line = 3 * width
                    qimage = QtGui.QImage(annotated_frame.data, width, height, bytes_per_line,
                                          QtGui.QImage.Format_RGB888)
                    # 将QImage转换为QPixmap
                    pixmap = QtGui.QPixmap.fromImage(qimage)

                    self.label_show.setPixmap(pixmap)
                    self.label_show.setScaledContents(True)
                    # 提取检测结果
                    self.textBrowser.append(
                        "*********************************{}*************************************".format(
                            time.strftime("%Y-%m-%d %H:%M:%S")))
                    find_labels = []
                    for result in results:
                        boxes = result.boxes.xyxy  # 边界框坐标
                        scores = result.boxes.conf  # 置信度分数
                        classes = result.boxes.cls  # 类别索引
                        # 如果有类别名称,可以通过类别索引获取
                        class_names = [self.camera_thread.model.names[int(cls)] for cls in classes]
                        # 打印检测结果
                        for box, score, class_name in zip(boxes, scores, class_names):
                            self.textBrowser.append(f"检测标签: {class_name}, 置信度: {score:.2f}")
                            if score >= self.yamlData['threshold']:
                                find_labels.append(class_name)
                    if len(find_labels) == 2 and 'pin-left-pass' in find_labels and 'pin-right-pass' in find_labels:
                        self.label_result.setText("PASS")
                        self.label_result.setStyleSheet("background-color: #55ff7f")
                        # TODO 给出PASS信号
                    else:
                        self.label_result.setText("FAIL")
                        self.label_result.setStyleSheet("background-color: #d80000")
                        # TODO 给出FAIL信号
                    self.textBrowser.append(
                        "*****************************************************************************************")
            except mvsdk.CameraException as e:
                QMessageBox.critical(self, "错误", "抓图失败({}): {}".format(e.error_code, e.message), QMessageBox.Yes,
                                     QMessageBox.Yes)

    def drawLine(self):
        """
        添加网格线
        :return:
        """
        if self.pushButton_addline.text() == "添加网格线":
            self.camera_thread.drawLine = True
            self.pushButton_addline.setText("去除网格线")
        else:
            self.camera_thread.drawLine = False
            self.pushButton_addline.setText("添加网格线")

    def showVideo(self, showImage):
        """
        显示视频
        :param showImage:
        :return:
        """
        self.label_display.setPixmap(QtGui.QPixmap.fromImage(showImage))

    def showStatus(self, info):
        """
        显示状态
        :param info:
        :return:
        """
        self.statusbar.showMessage(info)

    def selectMode(self, mode):
        """
        选择抓图模式
        :param mode:
        :return:
        """
        if mode == 0:
            self.yamlTool.update_yaml('config.yaml', 'runMode', 'mark')
        else:
            self.yamlTool.update_yaml('config.yaml', 'runMode', 'run')
        self.yamlData = self.yamlTool.read_yaml('config.yaml')

    def thresholdChange(self, value):
        """
        置信阈值改变事件
        :param value:
        :return:
        """
        self.yamlTool.update_yaml('config.yaml', 'threshold', value)
        self.yamlData = self.yamlTool.read_yaml('config.yaml')


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())

动态效果展示

yolov8视觉框架动态检测产品缺陷

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2196471.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

刷题 二分查找

二分查找 二分查找的本质就是 缩小有效范围 需要注意&#xff1a; int mid (left right) / 2; int mid left (right - left) / 2; 防止溢出 hot100 - 二分查找 ⭐️35. 搜索插入位置 找到第一个大于等于 target 的值 class Solution { public:// 目标: 找到第一个大于…

PD取电诱骗协议芯片支持PD3.1 支持大电流、大功率(28V5A 140W)快速充电。

PD取电快充协议芯片XSP16是受电端的一种PD取电快充协议芯片&#xff0c;它支持PD2.0/3.0&#xff0c;PD3.1、QC2.0/3.0、华为SCP/FCP、三星AFC等快充协议。支持UART串口发送电压/电流消息&#xff0c;供外部MCU读取&#xff0c;以便适应不同的负载。支持从充电器、车充、充电宝…

计算机基础知识:计算机中丢失 msvcr110.dll怎么修复?

1. msvcp110.dll 介绍 1.1 定义&#xff1a;Microsoft Visual C 2012的一部分 msvcp110.dll是Microsoft Visual C 2012 Redistributable Package的一部分&#xff0c;这是一个运行时库文件&#xff0c;包含了Microsoft Visual C 2012编译器所构建程序所需的函数和资源。 1.2…

使用Markdown Here插件生成邮件样式

使用Markdown Here插件生成邮件样式 通常大学生们都有给老师、助教使用邮箱发送作业的情景&#xff0c;怎样让自己发送的邮件美观呢&#xff0c;我们可以使用Markdown Here插件美化 以下为结果展示 Markdown Here 插件 官网地址 html代码 <font size"7", face…

大数据ETL数据提取转换和加载处理

什么是 ETL&#xff1f; 提取转换加载&#xff08;英语&#xff1a;Extract, transform, load&#xff0c;简称ETL&#xff09;&#xff0c;用来描述将资料从来源端经过抽取、转置、加载至目的端的过程。ETL一词较常用在数据仓库&#xff0c;但其对象并不限于数据仓库。 ETL&…

迎接国庆旅游热潮,火山引擎数据飞轮助力景区数智化升级

随着人们生活水平的提高和旅游消费观念的转变&#xff0c;国庆假期成为人们出行旅游的黄金时段。同程旅行发布的报告显示&#xff0c;北京、杭州、重庆、上海、南京、成都等城市仍是 “十一” 假期国内热门的目的地&#xff0c;而一些新兴的宝藏旅游目的地如新疆阿勒泰、云南迪…

《向量数据库指南》——Fivetran+Mlivus Cloud:打造AI搜索神器

哈哈,各位向量数据库和 AI 应用的同仁们,今天咱们来聊聊一个超级实用的话题——如何借助 Fivetran 和 Mlivus Cloud 构建 AI 驱动的搜索工具,从非结构化数据中挖掘出无尽的宝藏! 在这个信息爆炸的时代,非结构化数据已经成为了企业最重要的资产之一。它包含了大量的文本、…

进入猛增模式后,小米股价还剩下多少上涨空间?

猛兽财经核心观点&#xff1a; &#xff08;1&#xff09;小米集团的股价已经上涨到了2022年以来的最高点。 &#xff08;2&#xff09;股价从2023年的最低点上涨了185%以上。 &#xff08;3&#xff09;随着智能手机的需求反弹和电动汽车利润率的增长&#xff0c;猛兽财经认为…

YOLOv10改进策略【注意力机制篇】| NAM 即插即用模块,重新优化通道和空间注意力(含二次创新)

一、本文介绍 本文记录的是基于NAM模块的YOLOv10目标检测改进方法研究。 许多先前的研究专注于通过注意力操作捕获显著特征&#xff0c;但缺乏对权重贡献因素的考虑&#xff0c;而这些因素能够进一步抑制不重要的通道或像素。而本文利用NAM改进YOLOv10&#xff0c;通过权重的贡…

数字人直播违规被“封”,一文助你彻底解决!

随着数字人直播的日渐兴起&#xff0c;与之相关的各类消息逐渐进入到人们的视野之中&#xff0c;并开始成为众多企业、创业者以及技术爱好者所重点关注的对象。就目前的讨论情况来看&#xff0c;热度最高且讨论次数最多的便是数字人直播违规吗这一话题。 的确&#xff0c;从数字…

一个three三维 文字 粒子 着色器的作品用来感谢大家对github点星

一个three三维 文字 粒子 着色器的作品用来感谢大家对github点星 源链接&#xff1a;https://z2586300277.github.io/three-cesium-examples/#/codeMirror?navigationThreeJS&classifyshader&idtextStarShader 国内站点预览&#xff1a;http://threehub.cn github地…

CVE-2024-9014 pgAdmin4 OAuth2 client ID与secret敏感信息泄漏漏洞

文章目录 免责声明漏洞描述搜索语法漏洞复现nuclei修复建议 免责声明 本文章仅供学习与交流&#xff0c;请勿用于非法用途&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任 漏洞描述 pgAdmin4 是开源数据库 PostgreSQL 的图形管理工具攻击者可构造恶意…

向量数据库!AI 时代的变革者还是泡沫?

向量数据库&#xff01;AI 时代的变革者还是泡沫&#xff1f; 前言一、向量数据库的基本概念和原理二、向量数据库在AI中的应用场景三、向量数据库的优势和挑战四、向量数据库的发展现状和未来趋势五、向量数据库对AI发展的影响 前言 数据是 AI 的核心&#xff0c;而向量则是数…

一个设备不知道ip地址怎么办?应对策略来袭

在数字化时代&#xff0c;设备连接网络已成常态&#xff0c;IP地址作为设备的网络身份证&#xff0c;其重要性不言而喻。然而&#xff0c;面对设备IP地址遗失的困境&#xff0c;我们往往感到束手无策。 那么&#xff0c;一个设备不知道IP地址怎么办&#xff1f;本文将为你提供一…

中国通信技术革命史

文章目录 引言I 中国通信技术革命史电报中国卫星通信的历史固定电话寻呼机(BP机)大哥大(手机)制定自己的移动通信网络技术体系5G未来科技发展的总趋势:用更少的能量,传输、处理和存储更多的信息II 知识扩展通信史(单位能量的信息传输率越来越高,网络地不断融合。)超级智能…

秒杀系统的原则和注意项

做任何技术方案都需要结合当时的业务场景、资金情况、用户体量等维度综合考虑&#xff0c;没有最好的技术方案&#xff0c;只有最合适的技术方案。 做秒杀方案亦是如此&#xff0c;秒杀活动经常会引发高并发、系统宕机和库存超卖的棘手问题&#xff0c;作为开发者&#xff0c;我…

火情监测摄像机:守护生命与财产安全的“眼睛”

随着城市化进程的加快&#xff0c;火灾隐患日益增多。为了有效预防和及时应对火灾事故&#xff0c;火情监测摄像机应运而生&#xff0c;成为现代消防安全的重要组成部分。这种高科技设备不仅能够实时监控火灾发生&#xff0c;还能为救援提供宝贵的信息支持。火情监测摄像机主要…

vulnhub-THE PLANETS-EARTH靶机

下载并导入靶机至VMWare&#xff0c;设置网络模式为NAT&#xff0c;开机 开启攻击机&#xff08;kali&#xff09;&#xff0c;也设置为Nat模式&#xff0c;与靶机处于同一网段 扫描靶机ip Nmap 192.168.114.0/24 扫描网段内活跃的主机 可以推断靶机ip为192.168.114.129 扫描…

什么是源代码加密?十种方法教你软件开发源代码加密

什么是源代码加密 源代码加密是一种安全措施&#xff0c;它通过加密技术对软件的源代码进行保护&#xff0c;以防止未授权的访问、泄露、篡改或逆向工程。源代码是软件程序的原始代码&#xff0c;通常由程序员编写&#xff0c;然后编译成可执行程序。由于源代码包含了软件的设…

攻防世界---->工业协议分析2

前言&#xff1a;做题笔记。 下载 PCAPNG 说明是一个网络数据包文件。 那么直接用Wireshark查看分析。 调整一下长度显示&#xff1a; 可以看到 ARP协议&#xff1a; UDP 进行通信。 长度都是58&#xff0c;我们去找变动点。 转&#xff1a; flag{7FoM2StkhePz} 题外话&…