【YOLOv8实时产品缺陷检测】

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

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

项目概况

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

项目实现

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

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

YOLOv8安装及模型训练

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

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

关键代码展示

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

import timeimport cv2
from ultralytics import YOLO
import numpy as np
from PyQt5 import QtGui
from PyQt5.QtCore import pyqtSignal, QThread
from MindVisionSdk import mvsdk
import warningswarnings.filterwarnings('ignore')class MyThread(QThread):camera_signal = pyqtSignal(object)  # 输出相机数据流info_signal = pyqtSignal(str)  # 输出信息状态def __init__(self, parent=None):super().__init__(parent)self.drawLine = Falseself.monoCamera = Noneself.FrameBufferSize = Noneself.isPause = Falseself.mCamera = Noneself.pFrameBuffer = Noneself.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 timeimport 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 myYamlToolclass 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 = Falseself.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 = Trueself.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 = Trueself.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 = Falseself.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.shapebytes_per_line = 3 * widthqimage = QtGui.QImage(annotated_frame.data, width, height, bytes_per_line,QtGui.QImage.Format_RGB888)# 将QImage转换为QPixmappixmap = 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 = Trueself.pushButton_addline.setText("去除网格线")else:self.camera_thread.drawLine = Falseself.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.rhkb.cn/news/441102.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

手机误删照片?试试这5款免费数据恢复神器!

大家好&#xff01;今天咱们来聊聊一个大家都关心的话题——免费数据恢复工具。不论是误删照片、视频&#xff0c;还是丢失重要文件&#xff0c;数据恢复都是个让人头疼的问题。但好消息是&#xff0c;现在有众多免费的数据恢复工具能帮助我们找回失去的数据。今天我就来为大家…

力扣16~20题

题16&#xff08;中等&#xff09;&#xff1a; 思路&#xff1a; 双指针法&#xff0c;和15题差不多&#xff0c;就是要排除了&#xff0c;如果total<target则排除了更小的&#xff08;left右移&#xff09;&#xff0c;如果total>target则排除了更大的&#xff08;rig…

pycharm 远程ssh时,mujuco提示mujoco.FatalError: gladLoadGL error

在ubuntu系统运行时完全没问题&#xff0c;但是使用pycharm远程ssh登录时就会提示这个。 解决方法&#xff1a; 1. 可以修改环境变量 2. export LD_PRELOAD/usr/lib/x86_64-linux-gnu/libstdc.so.6 参考【Mujuco】WSL2安装Mujoco用于python,遇到FatalError,以及图形驱动架构…

【Git原理与使用】远程操作标签管理

远程操作&&标签管理 1.理解分布式版本控制系统2.新建远程仓库3.克隆远程仓库4.向远程仓库推送5.拉取远程仓库6.配置 Git7.配置命令别名8.标签管理8.1创建标签8.2操作标签 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496;…

RTOS系统移植

一、完成系统移植 系统移植上官网寻找合适的系统包&#xff0c;下载后将文件移植入工程文件 二、创建任务句柄、内核对象句柄&#xff08;信号量&#xff0c;消息队列&#xff0c;事件标志组&#xff0c;软件定时器&#xff09;、声明全局变量、声明函数 三、创建主函数&#…

Vue2电商项目(七)、订单与支付

文章目录 一、交易业务Trade1. 获取用户地址2. 获取订单信息 二、提交订单三、支付1. 获取支付信息2. 支付页面--ElementUI(1) 引入Element UI(2) 弹框支付的业务逻辑(这个逻辑其实没那么全)(3) 支付逻辑知识点小总结 四、个人中心1. 搭建二级路由2. 展示动态数据(1). 接口(2).…

【计算机网络 - 基础问题】每日 3 题(二十九)

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?typeblog &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞…

【Docker】03-自制镜像

1. 自制镜像 2. Dockerfile # 基础镜像 FROM openjdk:11.0-jre-buster # 设定时区 ENV TZAsia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone # 拷贝jar包 COPY docker-demo.jar /app.jar # 入口 ENTRYPOINT ["ja…

Redis:通用命令 数据类型

Redis&#xff1a;通用命令 & 数据类型 通用命令SETGETKEYSEXISTSDELEXPIRETTLTYPEFLUSHALL 数据类型 Redis的客户端提供了很多命令用于操控Redis&#xff0c;在Redis中&#xff0c;key的类型都是字符串&#xff0c;而value有多种类型&#xff0c;每种类型都有自己的操作命…

Redis篇(最佳实践)(持续更新迭代)

介绍一&#xff1a;键值设计 一、优雅的key结构 Redis 的 Key 虽然可以自定义&#xff0c;但最好遵循下面的几个最佳实践约定&#xff1a; 遵循基本格式&#xff1a;[业务名称]:[数据名]:[id]长度不超过 44 字节不包含特殊字符 例如&#xff1a; 我们的登录业务&#xff0…

Leetcode—76. 最小覆盖子串【困难】

2024每日刷题&#xff08;167&#xff09; Leetcode—76. 最小覆盖子串 C实现代码 class Solution { public:string minWindow(string s, string t) {int bestL -1;int l 0, r 0;vector<int> cnt(128);for(const char c: t) {cnt[c];}int require t.length();int m…

【实战教程】SpringBoot全面指南:快速上手到项目实战(SpringBoot)

文章目录 【实战教程】SpringBoot全面指南&#xff1a;快速上手到项目实战(SpringBoot)1. SpringBoot介绍1.1 SpringBoot简介1.2系统要求1.3 SpringBoot和SpringMVC区别1.4 SpringBoot和SpringCloud区别 2.快速入门3. Web开发3.1 静态资源访问3.2 渲染Web页面3.3 YML与Properti…

[SpringBoot] 苍穹外卖--面试题总结--上

前言 1--苍穹外卖-SpringBoot项目介绍及环境搭建 详解-CSDN博客 2--苍穹外卖-SpringBoot项目中员工管理 详解&#xff08;一&#xff09;-CSDN博客 3--苍穹外卖-SpringBoot项目中员工管理 详解&#xff08;二&#xff09;-CSDN博客 4--苍穹外码-SpringBoot项目中分类管理 详…

pytest(六)——allure-pytest的基础使用

前言 一、allure-pytest的基础使用 二、需要掌握的allure特性 2.1 Allure报告结构 2.2 Environment 2.3 Categories 2.4 Flaky test 三、allure的特性&#xff0c;allure.step()、allure.attach的详细使用 3.1 allure.step 3.2 allure.attach&#xff08;挺有用的&a…

Redis入门第四步:Redis发布与订阅

欢迎继续跟随《Redis新手指南&#xff1a;从入门到精通》专栏的步伐&#xff01;在本文中&#xff0c;我们将深入探讨Redis的发布与订阅&#xff08;Pub/Sub&#xff09;模式。这是一种强大的消息传递机制&#xff0c;适用于各种实时通信场景&#xff0c;如聊天应用、实时通知和…

3、Redis Stack扩展功能

文章目录 一、了解Redis产品二、申请RedisCloud实例三、Redis Stack体验1、RedisStack有哪些扩展&#xff1f;2、Redis JSON1、Redis JSON是什么2、Redis JSON有什么用3、Redis JSON的优势 3、Search And Query1、传统Scan搜索2、Search And Query搜索 4、Bloom Filter1、布隆过…

LabVIEW提高开发效率技巧----阻塞时钟

在LabVIEW开发中&#xff0c;阻塞时钟&#xff08;Blocking Timed Loops&#xff09;是一种常见且强大的技术&#xff0c;尤其适用于时间关键的应用。在这些应用中&#xff0c;精确控制循环的执行频率是关键任务。阻塞时钟通过等待循环的执行完成后再进入下一次迭代&#xff0c…

如何设置LTE端到端系统

LTE Setup Guide Baseline Hardware Requirements 基础硬件要求 需要2个RF前端和2个装有基于Linux的操作系统的PC。系统架构如下&#xff1a; srsUE&#xff1a;需要1个RF前端和1个PC。srsENB&#xff1a;需要1个RF前端和1个PC。srsEPC&#xff1a;需要1个PC。 系统硬件要…

python实现RC4加解密算法

RC4算法 一、算法介绍1.1 背景1.2 密钥调度算法(KSA)1.3 伪随机生成算法(PRGA) 二、代码实现三、演示效果 一、算法介绍 1.1 背景 RC4算法是由Ron Rivest在1987年为RSA数据安全公司设计的一种流密码算法&#xff0c;其安全性主要依赖于其密钥流的随机性和不可预测性。该算法因…

碰撞检测 | 图解视线生成Bresenham算法(附ROS C++/Python/Matlab实现)

目录 0 专栏介绍1 Bresenham算法介绍2 图解Bresenham算法3 算法流程4 仿真实现4.1 ROS C实现4.2 Python实现4.3 Matlab实现 0 专栏介绍 &#x1f525;课设、毕设、创新竞赛必备&#xff01;&#x1f525;本专栏涉及更高阶的运动规划算法轨迹优化实战&#xff0c;包括&#xff…