opencv 找出面板缺陷

通过使用opencv找出面板中的缺陷

原图是这样的:

很明显通过肉眼我们能看到有一条黑色的横线。

通过openc操作:


代码在https://github.com/Juary88/DL_Defect 欢迎star

<think>我们要求一个更完善的视觉检测软件,包括登录界面、模板匹配、缺陷小图展示等功能。我们将使用PyQt5来构建GUI界面,结合OpenCV进行图像处理。主要功能规划:1.登录界面:用户输入用户名和密码(简单的本地验证即可,实际生产需加密)2.主界面:-摄像头实时检测画面-模板图像展示区域(可导入模板)-检测结果区域(实时显示检测结果和缺陷小图)-控制按钮(开始、停止、设置等)3.图像处理功能:-模板匹配:使用模板图像与实时画面进行匹配,找出缺陷-缺陷小图展示:将检测到的缺陷区域单独显示在结果区域我们将分模块构建代码:1.登录窗口2.主窗口3.图像处理线程(避免阻塞UI)注意:由于代码较长,我们只实现核心功能,并尽量保持结构清晰。步骤:1.安装必要库:PyQt5,opencv-python,numpypipinstallPyQt5opencv-pythonnumpy2.代码结构:-LoginWindow:登录窗口-MainWindow:主窗口-CameraThread:摄像头处理线程3.功能说明:-登录成功后才进入主窗口-主窗口可以导入模板图像,开始/停止检测-实时检测画面,通过模板匹配找出缺陷,并显示缺陷小图4.模板匹配方法:使用OpenCV的模板匹配函数,然后设置一个阈值来判断是否匹配成功,超过阈值则认为存在缺陷(这里我们简化,实际可能更复杂)5.缺陷小图展示:在检测缺陷时,将缺陷区域从画面中截取出来,显示在右侧的缺陷列表中。注意:由于模板匹配对旋转和缩放比较敏感,本示例中假设待检测物体位置和角度基本固定。由于代码较长,我们将分块展示:先写登录窗口,然后主窗口,然后是图像处理部分。注意:实际应用中,模板匹配可能够鲁棒,这里仅作为示例,后续可以改进为其他算法。</think> 以下是一个功能更完善的视觉检测软件设计方案,采用PyQt5构建用户界面,集成OpenCV进行图像处理和缺陷检测,并加入登录系统、模板匹配和缺陷小图展示功能。 ### 完整的视觉检测系统实现 ```python import sys import cv2 import numpy as np import os from datetime import datetime from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, QPushButton, QVBoxLayout, QWidget, QLineEdit, QHBoxLayout, QMessageBox, QFileDialog, QListWidget, QListWidgetItem, QGraphicsScene, QGraphicsView, QGraphicsPixmapItem) from PyQt5.QtCore import QTimer, Qt from PyQt5.QtGui import QImage, QPixmap, QIcon class LoginWindow(QWidget): def __init__(self): super().__init__() self.setWindowTitle("视觉检测系统 - 登录") self.setGeometry(300, 300, 400, 250) self.setStyleSheet("background-color: #2c3e50; color: white;") layout = QVBoxLayout() # 标题 title = QLabel("视觉检测系统") title.setStyleSheet("font-size: 24px; font-weight: bold;") title.setAlignment(Qt.AlignCenter) # 登录表单 form_layout = QVBoxLayout() self.user_field = QLineEdit() self.user_field.setPlaceholderText("用户名") self.user_field.setStyleSheet("padding: 10px; font-size: 16px;") self.password_field = QLineEdit() self.password_field.setPlaceholderText("密码") self.password_field.setEchoMode(QLineEdit.Password) self.password_field.setStyleSheet("padding: 10px; font-size: 16px;") self.login_button = QPushButton("登录") self.login_button.setStyleSheet("background-color: #3498db; color: white; padding: 10px; font-size: 16px;") self.login_button.clicked.connect(self.attempt_login) form_layout.addWidget(title) form_layout.addWidget(QLabel("")) form_layout.addWidget(self.user_field) form_layout.addWidget(self.password_field) form_layout.addWidget(self.login_button) # 添加系统信息 info = QLabel("© 2023 视觉检测系统 | v1.0") info.setStyleSheet("font-size: 12px; opacity: 0.7;") info.setAlignment(Qt.AlignCenter) layout.addLayout(form_layout) layout.addWidget(info) self.setLayout(layout) def attempt_login(self): # 简单验证逻辑,实际应用中应使用数据库验证 username = self.user_field.text() password = self.password_field.text() if username == "admin" and password == "admin123": self.main_window = MainWindow() self.main_window.show() self.close() else: QMessageBox.warning(self, "登录失败", "用户名或密码错误") class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("视觉检测系统") self.setGeometry(100, 100, 1200, 700) self.setStyleSheet("background-color: #34495e; color: white;") # 初始化变量 self.cap = None self.template_img = None self.detection_active = False self.defects = [] self.threshold = 0.8 # 模板匹配阈值 self.save_path = "defects" os.makedirs(self.save_path, exist_ok=True) self.init_ui() self.setup_toolbar() def init_ui(self): central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QHBoxLayout() # 左侧面板 - 摄像头和模板 left_panel = QVBoxLayout() # 实时视频显示 self.video_label = QLabel("摄像头未启动") self.video_label.setAlignment(Qt.AlignCenter) self.video_label.setStyleSheet("background-color: #2c3e50; border: 2px solid #7f8c8d; min-height: 450px;") # 模板图像显示 self.template_label = QLabel("未加载模板图像") self.template_label.setAlignment(Qt.AlignCenter) self.template_label.setStyleSheet("background-color: #2c3e50; border: 2px solid #7f8c8d; min-height: 150px;") left_panel.addWidget(self.video_label) left_panel.addWidget(self.template_label) # 右侧面板 - 控制区 right_panel = QVBoxLayout() # 控制按钮 control_layout = QHBoxLayout() self.start_btn = QPushButton("启动检测") self.start_btn.setStyleSheet("background-color: #27ae60; padding: 10px; font-size: 14px;") self.start_btn.clicked.connect(self.toggle_detection) self.stop_btn = QPushButton("停止") self.stop_btn.setStyleSheet("background-color: #e74c3c; padding: 10px; font-size: 14px;") self.stop_btn.clicked.connect(self.stop_detection) self.stop_btn.setEnabled(False) self.load_btn = QPushButton("加载模板") self.load_btn.setStyleSheet("background-color: #3498db; padding: 10px; font-size: 14px;") self.load_btn.clicked.connect(self.load_template) control_layout.addWidget(self.start_btn) control_layout.addWidget(self.stop_btn) control_layout.addWidget(self.load_btn) # 阈值控制 threshold_layout = QHBoxLayout() threshold_layout.addWidget(QLabel("检测阈值:")) self.threshold_slider = QLabel(f"{self.threshold}") self.threshold_slider.setStyleSheet("font-size: 14px; min-width: 50px;") self.threshold_btn1 = QPushButton("高") self.threshold_btn1.setStyleSheet("background-color: #9b59b6; padding: 5px; font-size: 12px;") self.threshold_btn1.clicked.connect(lambda: self.set_threshold(0.9)) self.threshold_btn2 = QPushButton("中") self.threshold_btn2.setStyleSheet("background-color: #9b59b6; padding: 5px; font-size: 12px;") self.threshold_btn2.clicked.connect(lambda: self.set_threshold(0.8)) self.threshold_btn3 = QPushButton("低") self.threshold_btn3.setStyleSheet("background-color: #9b59b6; padding: 5px; font-size: 12px;") self.threshold_btn3.clicked.connect(lambda: self.set_threshold(0.7)) threshold_layout.addWidget(self.threshold_btn1) threshold_layout.addWidget(self.threshold_btn2) threshold_layout.addWidget(self.threshold_btn3) threshold_layout.addWidget(self.threshold_slider) threshold_layout.addStretch() # 缺陷列表 self.defect_list = QListWidget() self.defect_list.setStyleSheet("background-color: #2c3e50;") self.defect_list.itemDoubleClicked.connect(self.show_defect_details) # 信息显示 self.info_label = QLabel("就绪") self.info_label.setStyleSheet("font-size: 14px; background-color: #2c3e50; padding: 5px;") right_panel.addLayout(control_layout) right_panel.addLayout(threshold_layout) right_panel.addWidget(QLabel("检测到的缺陷:")) right_panel.addWidget(self.defect_list) right_panel.addWidget(self.info_label) main_layout.addLayout(left_panel, 70) main_layout.addLayout(right_panel, 30) central_widget.setLayout(main_layout) # 设置定时器 self.timer = QTimer(self) self.timer.timeout.connect(self.update_frame) def setup_toolbar(self): # 创建菜单栏 menu_bar = self.menuBar() menu_bar.setStyleSheet("background-color: #2c3e50; color: white;") # 文件菜单 file_menu = menu_bar.addMenu("文件") export_action = file_menu.addAction("导出缺陷报告") export_action.triggered.connect(self.export_report) exit_action = file_menu.addAction("退出") exit_action.triggered.connect(self.close) # 帮助菜单 help_menu = menu_bar.addMenu("帮助") about_action = help_menu.addAction("关于") about_action.triggered.connect(self.show_about) def toggle_detection(self): if not self.detection_active: # 启动摄像头 self.cap = cv2.VideoCapture(0) if not self.cap.isOpened(): self.show_message("错误", "无法打开摄像头") return self.detection_active = True self.start_btn.setText("检测中...") self.start_btn.setEnabled(False) self.stop_btn.setEnabled(True) self.timer.start(30) # 30ms更新一帧 self.info_label.setText("检测运行中...") else: self.stop_detection() def stop_detection(self): if self.cap and self.cap.isOpened(): self.cap.release() self.detection_active = False self.timer.stop() self.start_btn.setText("启动检测") self.start_btn.setEnabled(True) self.stop_btn.setEnabled(False) self.info_label.setText("检测已停止") def load_template(self): file_path, _ = QFileDialog.getOpenFileName( self, "选择模板图像", "", "图像文件 (*.png *.jpg *.bmp)" ) if file_path: self.template_img = cv2.imread(file_path, cv2.IMREAD_GRAYSCALE) if self.template_img is None: self.show_message("错误", "无法加载模板图像") return # 显示模板 h, w = self.template_img.shape template_display = cv2.resize(self.template_img, (w*2, h*2)) q_img = self.convert_cv_to_qt(template_display) self.template_label.setPixmap(QPixmap.fromImage(q_img)) self.info_label.setText(f"模板已加载: {os.path.basename(file_path)}") def set_threshold(self, value): self.threshold = value self.threshold_slider.setText(f"{value:.2f}") def update_frame(self): ret, frame = self.cap.read() if not ret: self.show_message("错误", "无法读取摄像头帧") self.stop_detection() return # 转换为灰度图进行检测 gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) processed_frame = frame.copy() # 如果有模板,执行模板匹配 if self.template_img is not None: # 模板匹配 result = cv2.matchTemplate(gray_frame, self.template_img, cv2.TM_CCOEFF_NORMED) _, max_val, _, max_loc = cv2.minMaxLoc(result) h, w = self.template_img.shape top_left = max_loc # 绘制匹配区域 cv2.rectangle(processed_frame, top_left, (top_left[0] + w, top_left[1] + h), (0, 255, 0), 2) # 标记匹配度 match_score = max_val cv2.putText(processed_frame, f"相似度: {match_score:.2f}", (top_left[0], top_left[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) # 检测缺陷 if match_score < self.threshold: # 保存缺陷图像 defect_img = frame[top_left[1]:top_left[1]+h, top_left[0]:top_left[0]+w] timestamp = datetime.now().strftime("%Y%m%d%H%M%S") defect_path = os.path.join(self.save_path, f"defect_{timestamp}.jpg") cv2.imwrite(defect_path, defect_img) # 添加到缺陷列表 self.defects.append({ "time": timestamp, "score": match_score, "image": defect_img, "path": defect_path }) self.update_defect_list() self.info_label.setText(f"检测缺陷! 相似度: {match_score:.2f}") # 在图像上标记缺陷 cv2.putText(processed_frame, "缺陷检测!", (top_left[0] + 20, top_left[1] + 40), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2) cv2.rectangle(processed_frame, top_left, (top_left[0] + w, top_left[1] + h), (0, 0, 255), 3) # 显示处理后的图像 q_img = self.convert_cv_to_qt(processed_frame) self.video_label.setPixmap(QPixmap.fromImage(q_img)) def convert_cv_to_qt(self, cv_img): h, w, ch = cv_img.shape bytes_per_line = ch * w q_img = QImage(cv_img.data, w, h, bytes_per_line, QImage.Format_BGR888) return q_img def update_defect_list(self): self.defect_list.clear() for defect in self.defects: time_str = datetime.strptime(defect["time"], "%Y%m%d%H%M%S").strftime("%H:%M:%S") item = QListWidgetItem(f"{time_str} | 相似度: {defect['score']:.2f}") item.setData(Qt.UserRole, defect) self.defect_list.addItem(item) def show_defect_details(self, item): defect = item.data(Qt.UserRole) defect_dialog = QDialog(self) defect_dialog.setWindowTitle("缺陷详情") defect_dialog.setGeometry(400, 300, 400, 300) layout = QVBoxLayout() # 显示缺陷图像 defect_img = defect["image"] h, w, _ = defect_img.shape scale_factor = min(300 / w, 300 / h) defect_img = cv2.resize(defect_img, (int(w * scale_factor), int(h * scale_factor))) q_img = self.convert_cv_to_qt(defect_img) image_label = QLabel() image_label.setPixmap(QPixmap.fromImage(q_img)) image_label.setAlignment(Qt.AlignCenter) # 缺陷信息 info_label = QLabel( f"检测时间: {datetime.strptime(defect['time'], '%Y%m%d%H%M%S').strftime('%Y-%m-%d %H:%M:%S')}\n" f"模板匹配度: {defect['score']:.4f}\n" f"文件路径: {defect['path']}" ) # 导出按钮 export_btn = QPushButton("导出缺陷图") export_btn.clicked.connect(lambda: self.export_defect(defect)) layout.addWidget(image_label) layout.addWidget(info_label) layout.addWidget(export_btn) defect_dialog.setLayout(layout) defect_dialog.exec_() def export_defect(self, defect): file_path, _ = QFileDialog.getSaveFileName( self, "保存缺陷图像", f"defect_{defect['time']}.jpg", "图像文件 (*.jpg *.png)" ) if file_path: cv2.imwrite(file_path, defect["image"]) self.show_message("成功", f"缺陷图像已保存到:\n{file_path}") def export_report(self): if not self.defects: self.show_message("信息", "没有检测到的缺陷") return file_path, _ = QFileDialog.getSaveFileName( self, "导出检测报告", "defect_report.txt", "文本文件 (*.txt)" ) if file_path: try: with open(file_path, "w") as f: f.write("视觉检测系统 - 缺陷报告\n") f.write("=" * 50 + "\n\n") f.write(f"报告生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") f.write(f"检测阈值: {self.threshold}\n") f.write(f"总缺陷数: {len(self.defects)}\n\n") for defect in self.defects: time_str = datetime.strptime(defect["time"], "%Y%m%d%H%M%S").strftime("%Y-%m-%d %H:%M:%S") f.write(f"- 时间: {time_str}\n") f.write(f" 匹配度: {defect['score']:.4f}\n") f.write(f" 图像路径: {defect['path']}\n\n") self.show_message("成功", f"报告已导出到:\n{file_path}") except
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值