Check failed: status == CUBLAS_STATUS_SUCCESS (11 vs. 0) CUBLAS_STATUS_MAPPING_ERROR

本文记录了一次在使用CUBLAS进行深度学习模型训练时遇到的错误,包括LearningRatePolicy设置问题及非法内存访问错误。通过检查发现训练标签列表与模型类别不匹配导致了上述问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

I0930 21:23:15.115576 30918 solver.cpp:281] Learning Rate Policy: multistep
F0930 21:23:17.263314 31011 math_functions.cu:121] Check failed: status == CUBLAS_STATUS_SUCCESS (11 vs. 0) CUBLAS_STATUS_MAPPING_ERROR
*** Check failure stack trace: ***
F0930 21:23:17.264580 31013 math_functions.hpp:176] Check failed: error == cudaSuccess (77 vs. 0) Fn illegal memory access was encountered0930 21:23:17.264598 31012 math_functions.hpp:176] Check failed: error == cudaSuccess (77 vs. 0) an illegal memory access was encountered
*** Check failure stack trace: ***
F0930 21:23:17.264580 31013 math_functions.hpp:176] Check failed: error == cudaSuccess (77 vs. 0) Fn illegal memory access was encountered0930 21:23:17.264598 31012 math_functions.hpp:176] Check failed: error == cudaSuccess (77 vs. 0) an illegal memory access was encountered
*** Check failure stack trace: ***
Aborted (core dumped)

原因:训练的标签list不对,如 用18类的标签list训练2类的模型

转载于:https://www.cnblogs.com/haiyang21/p/7614669.html

# -*- coding: utf-8 -*- import sys import os import cv2 import numpy as np from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton, QWidget, QVBoxLayout, QHBoxLayout, QMessageBox, QLabel, QFileDialog, QToolBar, QComboBox, QStatusBar) from PyQt5.QtCore import QRect, Qt from CamOperation_class import CameraOperation sys.path.append("D:\\海康\\MVS\\Development\\Samples\\Python\\BasicDemo") from MvCameraControl_class import * from MvErrorDefine_const import * from CameraParams_header import * from PyUICBasicDemo import Ui_MainWindow import ctypes from datetime import datetime import logging # 配置日志系统 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler("cloth_inspection.log"), logging.StreamHandler() ] ) logging.info("布料印花检测系统启动") # 全局变量 current_sample_path = "" # 当前使用的样本路径 detection_history = [] # 检测历史记录 # 布料印花检测函数 def check_print_quality(sample_image_path, test_image_path, threshold=0.05): """ 检测布料印花是否合格,并在合格样本上标出错误位置 :param sample_image_path: 合格样本图像路径 :param test_image_path: 待检测图像路径 :param threshold: 差异阈值,超过该值则认为印花不合格 :return: 是否合格,差异值,带有错误标记的合格样本图像 """ # 读取图像 sample_image = cv2.imread(sample_image_path, cv2.IMREAD_GRAYSCALE) test_image = cv2.imread(test_image_path, cv2.IMREAD_GRAYSCALE) if sample_image is None or test_image is None: logging.error("无法加载图像,请检查路径是否正确!") return None, None, None # 确保两个图像大小一致 test_image = cv2.resize(test_image, (sample_image.shape[1], sample_image.shape[0])) # 计算两个图像之间的差异 diff = cv2.absdiff(sample_image, test_image) # 将差异图像二值化 ret, diff_binary = cv2.threshold(diff, 50, 255, cv2.THRESH_BINARY) # 计算差异的占比 diff_ratio = np.sum(diff_binary) / (diff_binary.shape[0] * diff_binary.shape[1] * 255) # 修正计算方式 # 判断是否合格 is_qualified = diff_ratio < threshold # 在合格样本上标出错误位置 if is_qualified: marked_image = cv2.cvtColor(sample_image, cv2.COLOR_GRAY2BGR) else: marked_image = cv2.cvtColor(sample_image, cv2.COLOR_GRAY2BGR) contours, _ = cv2.findContours(diff_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cv2.drawContours(marked_image, contours, -1, (0, 0, 255), 2) return is_qualified, diff_ratio, marked_image # 布料印花检测功能 def check_print(): global isGrabbing, obj_cam_operation, current_sample_path, detection_history if not isGrabbing: QMessageBox.warning(mainWindow, "错误", "请先开始取流并捕获图像!", QMessageBox.Ok) return # 检查样本路径是否有效 if not current_sample_path or not os.path.exists(current_sample_path): QMessageBox.warning(mainWindow, "错误", "请先设置有效的标准样本图像!", QMessageBox.Ok) return # 保存当前帧作为测试图像 test_path = "temp_test_image.bmp" ret = obj_cam_operation.save_bmp() if ret != MV_OK: QMessageBox.warning(mainWindow, "错误", "保存测试图像失败!", QMessageBox.Ok) return # 执行印花检测 is_qualified, diff_ratio, marked_image = check_print_quality(current_sample_path, test_path) if marked_image is not None: # 显示结果 result_text = f"印花是否合格: {'合格' if is_qualified else '不合格'}\n差异占比: {diff_ratio:.4f}" QMessageBox.information(mainWindow, "检测结果", result_text, QMessageBox.Ok) # 显示标记图像 cv2.imshow("缺陷标记结果", marked_image) cv2.waitKey(0) cv2.destroyAllWindows() # 记录检测结果 detection_result = { 'timestamp': datetime.now(), 'qualified': is_qualified, 'diff_ratio': diff_ratio, 'sample_path': current_sample_path, 'test_path': test_path } detection_history.append(detection_result) update_history_display() # 保存标准样本函数 def save_sample_image(): global isGrabbing, obj_cam_operation, current_sample_path if not isGrabbing: QMessageBox.warning(mainWindow, "错误", "请先开始取流并捕获图像!", QMessageBox.Ok) return # 弹出文件保存对话框 file_path, _ = QFileDialog.getSaveFileName( mainWindow, "保存标准样本图像", "", "BMP Files (*.bmp);;PNG Files (*.png);;JPEG Files (*.jpg)" ) if not file_path: return # 用户取消保存 # 获取文件扩展名 file_extension = os.path.splitext(file_path)[1].lower() # 根据扩展名设置保存格式 if file_extension == ".bmp": save_format = "bmp" elif file_extension == ".png": save_format = "png" elif file_extension == ".jpg" or file_extension == ".jpeg": save_format = "jpeg" else: QMessageBox.warning(mainWindow, "错误", "不支持的文件格式!", QMessageBox.Ok) return # 保存当前帧作为标准样本 ret = obj_cam_operation.save_image(file_path, save_format) if ret != MV_OK: strError = "保存样本图像失败: " + ToHexStr(ret) QMessageBox.warning(mainWindow, "错误", strError, QMessageBox.Ok) else: QMessageBox.information(mainWindow, "成功", f"标准样本已保存至:\n{file_path}", QMessageBox.Ok) # 更新当前样本路径 current_sample_path = file_path update_sample_display() # 预览当前样本 def preview_sample(): global current_sample_path if not current_sample_path or not os.path.exists(current_sample_path): QMessageBox.warning(mainWindow, "错误", "请先设置有效的标准样本图像!", QMessageBox.Ok) return try: sample_img = cv2.imread(current_sample_path) if sample_img is None: raise Exception("无法加载图像") cv2.imshow("标准样本预览", sample_img) except Exception as e: QMessageBox.warning(mainWindow, "错误", f"预览样本失败: {str(e)}", QMessageBox.Ok) # 更新样本路径显示 def update_sample_display(): global current_sample_path if current_sample_path: ui.lblSamplePath.setText(f"当前样本: {os.path.basename(current_sample_path)}") ui.lblSamplePath.setToolTip(current_sample_path) else: ui.lblSamplePath.setText("当前样本: 未设置样本") # 更新历史记录显示 def update_history_display(): global detection_history ui.cbHistory.clear() for i, result in enumerate(detection_history[-10:]): # 显示最近10条记录 timestamp = result['timestamp'].strftime("%H:%M:%S") status = "合格" if result['qualified'] else "不合格" ratio = f"{result['diff_ratio']:.4f}" ui.cbHistory.addItem(f"[{timestamp}] {status} - 差异: {ratio}") # 获取选取设备信息的索引,通过[]之间的字符去解析 def TxtWrapBy(start_str, end, all): start = all.find(start_str) if start >= 0: start += len(start_str) end = all.find(end, start) if end >= 0: return all[start:end].strip() # 将返回的错误码转换为十六进制显示 def ToHexStr(num): """将错误码转换为十六进制字符串""" # 处理非整数输入 if not isinstance(num, int): try: # 尝试转换为整数 num = int(num) except: # 无法转换时返回类型信息 return f"<非整数:{type(num)}>" chaDic = {10: 'a', 11: 'b', 12: 'c', 13: 'd', 14: 'e', 15: 'f'} hexStr = "" # 处理负数 if num < 0: num = num + 2 ** 32 # 转换为十六进制 while num >= 16: digit = num % 16 hexStr = chaDic.get(digit, str(digit)) + hexStr num //= 16 hexStr = chaDic.get(num, str(num)) + hexStr return "0x" + hexStr # ch:初始化SDK | en: initialize SDK MvCamera.MV_CC_Initialize() global deviceList deviceList = MV_CC_DEVICE_INFO_LIST() global cam cam = MvCamera() global nSelCamIndex nSelCamIndex = 0 global obj_cam_operation obj_cam_operation = 0 global isOpen isOpen = False global isGrabbing isGrabbing = False global isCalibMode # 是否是标定模式(获取原始图像) isCalibMode = True # 绑定下拉列表至设备信息索引 def xFunc(event): global nSelCamIndex nSelCamIndex = TxtWrapBy("[", "]", ui.ComboDevices.get()) # Decoding Characters def decoding_char(c_ubyte_value): c_char_p_value = ctypes.cast(c_ubyte_value, ctypes.c_char_p) try: decode_str = c_char_p_value.value.decode('gbk') # Chinese characters except UnicodeDecodeError: decode_str = str(c_char_p_value.value) return decode_str # ch:枚举相机 | en:enum devices def enum_devices(): global deviceList global obj_cam_operation deviceList = MV_CC_DEVICE_INFO_LIST() n_layer_type = (MV_GIGE_DEVICE | MV_USB_DEVICE | MV_GENTL_CAMERALINK_DEVICE | MV_GENTL_CXP_DEVICE | MV_GENTL_XOF_DEVICE) ret = MvCamera.MV_CC_EnumDevices(n_layer_type, deviceList) if ret != 0: strError = "Enum devices fail! ret = :" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) return ret if deviceList.nDeviceNum == 0: QMessageBox.warning(mainWindow, "Info", "Find no device", QMessageBox.Ok) return ret print("Find %d devices!" % deviceList.nDeviceNum) devList = [] for i in range(0, deviceList.nDeviceNum): mvcc_dev_info = cast(deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE or mvcc_dev_info.nTLayerType == MV_GENTL_GIGE_DEVICE: print("\ngige device: [%d]" % i) user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chUserDefinedName) model_name = decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName) print("device user define name: " + user_defined_name) print("device model name: " + model_name) nip1 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24) nip2 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16) nip3 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8) nip4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff) print("current ip: %d.%d.%d.%d " % (nip1, nip2, nip3, nip4)) devList.append( "[" + str(i) + "]GigE: " + user_defined_name + " " + model_name + "(" + str(nip1) + "." + str( nip2) + "." + str(nip3) + "." + str(nip4) + ")") elif mvcc_dev_info.nTLayerType == MV_USB_DEVICE: print("\nu3v device: [%d]" % i) user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stUsb3VInfo.chUserDefinedName) model_name = decoding_char(mvcc_dev_info.SpecialInfo.stUsb3VInfo.chModelName) print("device user define name: " + user_defined_name) print("device model name: " + model_name) strSerialNumber = "" for per in mvcc_dev_info.S极Info.stUsb3VInfo.chSerialNumber: if per == 0: break strSerialNumber = strSerialNumber + chr(per) print("user serial number: " + strSerialNumber) devList.append("[" + str(i) + "]USB: " + user_defined_name + " " + model_name + "(" + str(strSerialNumber) + ")") elif mvcc_dev_info.nTLayerType == MV_GENTL_CAMERALINK_DEVICE: print("\nCML device: [极d]" % i) user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stCMLInfo.chUserDefinedName) model_name = decoding_char(mvcc_dev_info.SpecialInfo.stCMLInfo.chModelName) print("device user define name: " + user_defined_name) print("device model name: " + model_name) strSerialNumber = "" for per in mvcc_dev_info.SpecialInfo.stCMLInfo.chSerialNumber: if per == 0: break strSerialNumber = strSerialNumber + chr(per) print("user serial number: " + strSerialNumber) devList.append("[" + str(i) + "]CML: " + user_defined_name + " " + model_name + "(" + str(strSerialNumber) + ")") elif mvcc_dev_info.nTLayerType == MV_GENTL_CXP_DEVICE: print("\nCXP device: [%d]" % i) user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stCXPInfo.chUserDefinedName) model_name = decoding_char(mvcc_dev_info.SpecialInfo.stCXPInfo.chModelName) print("device user define name: " + user_defined_name) print("device model name: " + model_name) strSerialNumber = "" for per in mvcc_dev_info.SpecialInfo.stCXPInfo.chSerialNumber: if per == 0: break strSerialNumber = strSerialNumber + chr(per) print("user serial number: " + strSerialNumber) devList.append("[" + str(i) + "]CXP: " + user_defined_name + " " + model_name + "(" + str(strSerialNumber) + ")") elif mvcc_dev_info.nTLayerType == MV_GENTL_XOF_DEVICE: print("\nXoF device: [%d]" % i) user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stXoFInfo.chUserDefinedName) model_name = decoding_char(mvcc_dev_info.SpecialInfo.stXoFInfo.chModelName) print("device user define name: " + user_defined_name) print("device model name: " + model_name) strSerialNumber = "" for per in mvcc_dev_info.SpecialInfo.stXoFInfo.chSerialNumber: if per == 0: break strSerialNumber = strSerialNumber + chr(per) print("user serial number: " + strSerialNumber) devList.append("[" + str(i) + "]XoF: " + user_defined_name + " " + model_name + "(" + str(strSerialNumber) + ")") ui.ComboDevices.clear() ui.ComboDevices.addItems(devList) ui.ComboDevices.setCurrentIndex(0) # ch:打开相机 | en:open device def open_device(): global deviceList global nSelCamIndex global obj_cam_operation global isOpen if isOpen: QMessageBox.warning(mainWindow, "Error", 'Camera is Running!', QMessageBox.Ok) return MV_E_CALLORDER nSelCamIndex = ui.ComboDevices.currentIndex() if nSelCamIndex < 0: QMessageBox.warning(mainWindow, "Error", 'Please select a camera!', QMessageBox.Ok) return MV_E_CALLORDER obj_cam_operation = CameraOperation(cam, deviceList, nSelCamIndex) ret = obj_cam_operation.open_device() if 0 != ret: strError = "Open device failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) isOpen = False else: set_continue_mode() get_param() isOpen = True enable_controls() # ch:开始取流 | en:Start grab image def start_grabbing(): global obj_cam_operation global isGrabbing ret = obj_cam_operation.start_grabbing(ui.widgetDisplay.winId()) if ret != 0: strError = "Start grabbing failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) else: isGrabbing = True enable_controls() # ch:停止取流 | en:Stop grab image def stop_grabbing(): global obj_cam_operation global isGrabbing ret = obj_cam_operation.Stop_grabbing() if ret != 0: strError = "Stop grabbing failed ret:" + ToHexStr QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) else: isGrabbing = False enable_controls() # ch:关闭设备 | Close device def close_device(): global isOpen global isGrabbing global obj_cam_operation if isOpen: obj_cam_operation.close_devicelose_device() isOpen = False isGrabbing = False enable_controls() # ch:设置触发模式 | en:set trigger mode def set_continue_mode(): ret = obj_cam_operation.set_trigger_mode(False) if ret != 0: strError = "Set continue mode failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) else: ui.radioContinueMode.setChecked(True) ui.radioTriggerMode.setChecked(False) ui.bnSoftwareTrigger.setEnabled(False) # ch:设置软触发模式 | en:set software trigger mode def set_software_trigger_mode(): ret = obj_cam_operation.set_trigger_mode(True) if ret != 0: strError = "Set trigger mode failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) else: ui.radioContinueMode.setChecked(False) ui.radioTriggerMode.setChecked(True) ui.bnSoftwareTrigger.setEnabled(isGrabbing) # ch:设置触发命令 | en:set trigger software def trigger_once(): ret = obj_cam_operation.trigger_once() if ret != 0: strError = "TriggerSoftware failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) # ch:存图 | en:save image def save_bmp(): ret = obj_cam_operation.save_bmp() if ret != MV_OK: strError = "Save BMP failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) else: print("Save image success") def is_float(str): try: float(str) return True except ValueError: return False # ch: 获取参数 | en:get param def get_param(): try: # 调用方法获取参数 ret = obj_cam_operation.get_param() # 记录调用结果(调试用) logging.debug(f"get_param() 返回: {ret} (类型: {type(ret)})") # 处理错误码 if ret != MV_OK: strError = "获取参数失败,错误码: " + ToHexStr(ret) QMessageBox.warning(mainWindow, "错误", strError, QMessageBox.Ok) else: # 成功获取参数后更新UI ui.edtExposureTime.setText("{0:.2f}".format(obj_cam_operation.exposure_time)) ui.edtGain.setText("{0:.2f}".format(obj_cam_operation.gain)) ui.edtFrameRate.setText("{0:.2f}".format(obj_cam_operation.frame_rate)) # 记录成功信息 logging.info("成功获取相机参数") except Exception as e: # 处理所有异常 error_msg = f"获取参数时发生错误: {str(e)}" logging.error(error_msg) QMessageBox.critical(mainWindow, "严重错误", error_msg, QMessageBox.Ok) # ch: 设置参数 | en:set param def set_param(): frame_rate = ui.edtFrameRate.text() exposure = ui.edtExposureTime.text() gain = ui.edtGain.text() if not (is_float(frame_rate) and is_float(exposure) and is_float(gain)): strError = "设置参数失败: 参数必须是有效的浮点数" QMessageBox.warning(mainWindow, "错误", strError, QMessageBox.Ok) return MV_E_PARAMETER try: # 使用正确的参数顺序和关键字 ret = obj_cam_operation.set_param( frame_rate=float(frame_rate), exposure_time=float(exposure), gain=float(gain) ) if ret != MV_OK: strError = "设置参数失败,错误码: " + ToHexStr(ret) QMessageBox.warning(mainWindow, "错误", strError, QMessageBox.Ok) else: logging.info("参数设置成功") return MV_OK except Exception as e: error_msg = f"设置参数时发生错误: {str(e)}" logging.error(error_msg) QMessageBox.critical(mainWindow, "严重错误", error_msg, QMessageBox.Ok) return MV_E_STATE # ch: 设置控件状态 | en:set enable status def enable_controls(): global isGrabbing global isOpen # 先设置group的状态,再单独设置各控件状态 ui.groupGrab.setEnabled(isOpen) ui.groupParam.setEnabled(isOpen) ui.bnOpen.setEnabled(not isOpen) ui.bnClose.setEnabled(isOpen) ui.bnStart.setEnabled(isOpen and (not isGrabbing)) ui.bnStop.setEnabled(isOpen and isGrabbing) ui.bnSoftwareTrigger.setEnabled(isGrabbing and ui.radioTriggerMode.isChecked()) ui.bnSaveImage.setEnabled(isOpen and isGrabbing) # 添加检测按钮控制 ui.bnCheckPrint.setEnabled(isOpen and isGrabbing) ui.bnSaveSample.setEnabled(isOpen and isGrabbing) ui.bnPreviewSample.setEnabled(bool(current_sample_path)) if __name__ == "__main__": # ch:初始化SDK | en: initialize SDK MvCamera.MV_CC_Initialize() deviceList = MV_CC_DEVICE_INFO_LIST() cam = MvCamera() nSelCamIndex = 0 obj_cam_operation = 0 isOpen = False isGrabbing = False isCalibMode = True # 是否是标定模式(获取原始图像) # 初始化UI app = QApplication(sys.argv) mainWindow = QMainWindow() ui = Ui_MainWindow() ui.setupUi(mainWindow) # 扩大主窗口尺寸 mainWindow.resize(1200, 800) # 宽度1200,高度800 # 创建工具栏 toolbar = mainWindow.addToolBar("检测工具") # 添加检测按钮 ui.bnCheckPrint = QPushButton("检测印花质量") toolbar.addWidget(ui.bnCheckPrint) # 添加保存样本按钮 ui.bnSaveSample = QPushButton("保存标准样本") toolbar.addWidget(ui.bnSaveSample) # 添加预览样本按钮 ui.bnPreviewSample = QPushButton("预览样本") toolbar.addWidget(ui.bnPreviewSample) # 添加历史记录下拉框 ui.cbHistory = QComboBox() ui.cbHistory.setMinimumWidth(300) toolbar.addWidget(QLabel("历史记录:")) toolbar.addWidget(ui.cbHistory) # 添加当前样本显示标签 ui.lblSamplePath = QLabel("当前样本: 未设置样本") status_bar = mainWindow.statusBar() status_bar.addPermanentWidget(ui.lblSamplePath) # 绑定按钮事件 ui.bnCheckPrint.clicked.connect(check_print) ui.bnSaveSample.clicked.connect(save_sample_image) ui.bnPreviewSample.clicked.connect(preview_sample) # 绑定其他按钮事件 ui.bnEnum.clicked.connect(enum_devices) ui.bnOpen.clicked.connect(open_device) ui.bnClose.clicked.connect(close_device) ui.bnStart.clicked.connect(start_grabbing) ui.bnStop.clicked.connect(stop_grabbing) ui.bnSoftwareTrigger.clicked.connect(trigger_once) ui.radioTriggerMode.clicked.connect(set_software_trigger_mode) ui.radioContinueMode.clicked.connect(set_continue_mode) ui.bnGetParam.clicked.connect(get_param) ui.bnSetParam.clicked.connect(set_param) ui.bnSaveImage.clicked.connect(save_bmp) # 显示主窗口 mainWindow.show() # 执行应用 app.exec_() # 关闭设备 close_device() # ch:反初始化SDK | en: finalize SDK MvCamera.MV_CC_Finalize() sys.exit() 这是one.py的程序按照你 刚刚的解答展示出完整代码
07-08
# -*- coding: utf-8 -*- import sys import os import cv2 import numpy as np from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton, QWidget, QVBoxLayout, QHBoxLayout, QMessageBox, QLabel, QFileDialog, QToolBar, QComboBox, QStatusBar) from PyQt5.QtCore import QRect, Qt, QSettings, QThread, pyqtSignal from PyQt5.QtGui import QImage, QPixmap from CamOperation_class import CameraOperation sys.path.append("D:\\海康\\MVS\\Development\\Samples\\Python\\BasicDemo") from MvCameraControl_class import * from MvErrorDefine_const import * from CameraParams_header import * from PyUICBasicDemo import Ui_MainWindow import ctypes from datetime import datetime import logging import platform # 配置日志系统 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler("cloth_inspection.log"), logging.StreamHandler() ] ) logging.info("布料印花检测系统启动") # 全局变量 current_sample_path = "" # 当前使用的样本路径 detection_history = [] # 检测历史记录 # 帧监控线程 class FrameMonitorThread(QThread): frame_status = pyqtSignal(str) def __init__(self, cam_operation): super().__init__() self.cam_operation = cam_operation self.running = True def run(self): while self.running: if self.cam_operation: status = self.cam_operation.get_frame_status() frame_text = "有帧" if status.get('current_frame', False) else "无帧" self.frame_status.emit(f"帧状态: {frame_text}") QThread.msleep(500) def stop(self): self.running = False # 布料印花检测函数(修复版) def check_print_quality(sample_image_path, test_image, threshold=0.05): """ 检测布料印花是否合格,直接使用内存中的测试图像 :param sample_image_path: 合格样本图像路径 :param test_image: 内存中的测试图像 (numpy数组) :param threshold: 差异阈值 :return: 是否合格,差异值,标记图像 """ # 读取样本图像 try: sample_img_data = np.fromfile(sample_image_path, dtype=np.uint8) sample_image = cv2.imdecode(sample_img_data, cv2.IMREAD_GRAYSCALE) if sample_image is None: logging.error(f"无法解码样本图像: {sample_image_path}") return None, None, None except Exception as e: logging.exception(f"样本图像读取异常: {str(e)}") return None, None, None # 确保测试图像是灰度图 if len(test_image.shape) == 3: # 如果是彩色图像 test_image = cv2.cvtColor(test_image, cv2.COLOR_BGR2GRAY) # 确保两个图像大小一致 try: test_image = cv2.resize(test_image, (sample_image.shape[1], sample_image.shape[0])) except Exception as e: logging.error(f"图像调整大小失败: {str(e)}") return None, None, None # 计算差异 diff = cv2.absdiff(sample_image, test_image) _, diff_binary = cv2.threshold(diff, 50, 255, cv2.THRESH_BINARY) diff_ratio = np.sum(diff_binary) / (diff_binary.shape[0] * diff_binary.shape[1] * 255) is_qualified = diff_ratio < threshold # 创建标记图像 marked_image = cv2.cvtColor(sample_image, cv2.COLOR_GRAY2BGR) if not is_qualified: contours, _ = cv2.findContours(diff_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cv2.drawContours(marked_image, contours, -1, (0, 0, 255), 2) return is_qualified, diff_ratio, marked_image # 布料印花检测功能 def check_print(): global isGrabbing, obj_cam_operation, current_sample_path, detection_history if not isGrabbing: QMessageBox.warning(mainWindow, "错误", "请先开始取流并捕获图像!", QMessageBox.Ok) return # 检查样本路径是否有效 if not current_sample_path or not os.path.exists(current_sample_path): QMessageBox.warning(mainWindow, "错误", "请先设置有效的标准样本图像!", QMessageBox.Ok) return # 直接从相机获取当前帧(避免文件保存) try: test_image = obj_cam_operation.get_current_frame() if test_image is None: QMessageBox.warning(mainWindow, "错误", "无法获取当前帧图像!", QMessageBox.Ok) return except Exception as e: QMessageBox.critical(mainWindow, "错误", f"获取图像帧失败: {str(e)}", QMessageBox.Ok) logging.exception("获取图像帧失败") return # 执行印花检测(直接使用内存中的图像) try: is_qualified, diff_ratio, marked_image = check_print_quality(current_sample_path, test_image) except Exception as e: QMessageBox.critical(mainWindow, "检测错误", f"检测过程中发生错误: {str(e)}", QMessageBox.Ok) logging.exception("印花检测失败") return # 显示结果 if marked_image is not None: # 显示结果 result_text = f"印花是否合格: {'合格' if is_qualified else '不合格'}\n差异占比: {diff_ratio:.4f}" QMessageBox.information(mainWindow, "检测结果", result_text, QMessageBox.Ok) # 显示标记图像 cv2.imshow("缺陷标记结果", marked_image) cv2.waitKey(0) cv2.destroyAllWindows() # 记录检测结果 detection_result = { 'timestamp': datetime.now(), 'qualified': is_qualified, 'diff_ratio': diff_ratio, 'sample_path': current_sample_path } detection_history.append(detection_result) update_history_display() # 保存标准样本函数 def save_sample_image(): global isGrabbing, obj_cam_operation, current_sample_path if not isGrabbing: QMessageBox.warning(mainWindow, "错误", "请先开始取流并捕获图像!", QMessageBox.Ok) return # 检查是否有有效图像 if not obj_cam_operation.is_frame_available(): QMessageBox.warning(mainWindow, "无有效图像", "未捕获到有效图像,请检查相机状态!", QMessageBox.Ok) return # 读取上次使用的路径 settings = QSettings("ClothInspection", "CameraApp") last_dir = settings.value("last_save_dir", os.path.join(os.getcwd(), "captures")) # 创建默认文件名 timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") default_filename = f"sample_{timestamp}" # 弹出文件保存对话框 file_path, selected_filter = QFileDialog.getSaveFileName( mainWindow, "保存标准样本图像", os.path.join(last_dir, default_filename), "BMP Files (*.bmp);;PNG Files (*.png);;JPEG Files (*.jpg);;所有文件 (*)", options=QFileDialog.DontUseNativeDialog ) if not file_path: logging.info("用户取消了图像保存操作") return # 用户取消保存 # 处理文件扩展名 file_extension = os.path.splitext(file_path)[1].lower() if not file_extension: # 根据选择的过滤器添加扩展名 if "BMP" in selected_filter: file_path += ".bmp" elif "PNG" in selected_filter: file_path += ".png" elif "JPEG" in selected_filter or "JPG" in selected_filter: file_path += ".jpg" else: # 默认使用BMP格式 file_path += ".bmp" file_extension = os.path.splitext(file_path)[1].lower() # 根据扩展名设置保存格式 format_mapping = { ".bmp": "bmp", ".png": "png", ".jpg": "jpg", ".jpeg": "jpg" } save_format = format_mapping.get(file_extension) if not save_format: QMessageBox.warning(mainWindow, "错误", "不支持的文件格式!", QMessageBox.Ok) return # 确保目录存在 directory = os.path.dirname(file_path) if directory and not os.path.exists(directory): try: os.makedirs(directory, exist_ok=True) logging.info(f"创建目录: {directory}") except OSError as e: error_msg = f"无法创建目录 {directory}: {str(e)}" QMessageBox.critical(mainWindow, "目录创建错误", error_msg, QMessageBox.Ok) return # 保存当前帧作为标准样本 try: ret = obj_cam_operation.save_image(file_path, save_format) if ret != MV_OK: strError = f"保存样本图像失败: {hex(ret)}" QMessageBox.warning(mainWindow, "错误", strError, QMessageBox.Ok) else: success_msg = f"标准样本已保存至:\n{file_path}" QMessageBox.information(mainWindow, "成功", success_msg, QMessageBox.Ok) # 更新当前样本路径 current_sample_path = file_path update_sample_display() # 保存当前目录 settings.setValue("last_save_dir", os.path.dirname(file_path)) except Exception as e: error_msg = f"保存图像时发生错误: {str(e)}" QMessageBox.critical(mainWindow, "异常错误", error_msg, QMessageBox.Ok) logging.exception("保存样本图像时发生异常") # 预览当前样本 def preview_sample(): global current_sample_path if not current_sample_path or not os.path.exists(current_sample_path): QMessageBox.warning(mainWindow, "错误", "请先设置有效的标准样本图像!", QMessageBox.Ok) return try: # 使用安全方法读取图像 img_data = np.fromfile(current_sample_path, dtype=np.uint8) sample_img = cv2.imdecode(img_data, cv2.IMREAD_COLOR) if sample_img is None: raise Exception("无法加载图像") cv2.imshow("标准样本预览", sample_img) cv2.waitKey(0) cv2.destroyAllWindows() except Exception as e: QMessageBox.warning(mainWindow, "错误", f"预览样本失败: {str(e)}", QMessageBox.Ok) # 更新样本路径显示 def update_sample_display(): global current_sample_path if current_sample_path: ui.lblSamplePath.setText(f"当前样本: {os.path.basename(current_sample_path)}") ui.lblSamplePath.setToolTip(current_sample_path) ui.bnPreviewSample.setEnabled(True) else: ui.lblSamplePath.setText("当前样本: 未设置样本") ui.bnPreviewSample.setEnabled(False) # 更新历史记录显示 def update_history_display(): global detection_history ui.cbHistory.clear() for i, result in enumerate(detection_history[-10:]): # 显示最近10条记录 timestamp = result['timestamp'].strftime("%H:%M:%S") status = "合格" if result['qualified'] else "不合格" ratio = f"{result['diff_ratio']:.4f}" ui.cbHistory.addItem(f"[{timestamp}] {status} - 差异: {ratio}") # 获取选取设备信息的索引,通过[]之间的字符去解析 def TxtWrapBy(start_str, end, all): start = all.find(start_str) if start >= 0: start += len(start_str) end = all.find(end, start) if end >= 0: return all[start:end].strip() # 将返回的错误码转换为十六进制显示 def ToHexStr(num): """将错误码转换为十六进制字符串""" # 处理非整数输入 if not isinstance(num, int): try: # 尝试转换为整数 num = int(num) except: # 无法转换时返回类型信息 return f"<非整数:{type(num)}>" chaDic = {10: 'a', 11: 'b', 12: 'c', 13: 'd', 14: 'e', 15: 'f'} hexStr = "" # 处理负数 if num < 0: num = num + 2 ** 32 # 转换为十六进制 while num >= 16: digit = num % 16 hexStr = chaDic.get(digit, str(digit)) + hexStr num //= 16 hexStr = chaDic.get(num, str(num)) + hexStr return "0x" + hexStr # ch:初始化SDK | en: initialize SDK MvCamera.MV_CC_Initialize() global deviceList deviceList = MV_CC_DEVICE_INFO_LIST() global cam cam = MvCamera() global nSelCamIndex nSelCamIndex = 0 global obj_cam_operation obj_cam_operation = 0 global isOpen isOpen = False global isGrabbing isGrabbing = False global isCalibMode # 是否是标定模式(获取原始图像) isCalibMode = True global frame_monitor_thread # 绑定下拉列表至设备信息索引 def xFunc(event): global nSelCamIndex nSelCamIndex = TxtWrapBy("[", "]", ui.ComboDevices.get()) # Decoding Characters def decoding_char(c_ubyte_value): c_char_p_value = ctypes.cast(c_ubyte_value, ctypes.c_char_p) try: decode_str = c_char_p_value.value.decode('gbk') # Chinese characters except UnicodeDecodeError: decode_str = str(c_char_p_value.value) return decode_str # ch:枚举相机 | en:enum devices def enum_devices(): global deviceList global obj_cam_operation deviceList = MV_CC_DEVICE_INFO_LIST() n_layer_type = (MV_GIGE_DEVICE | MV_USB_DEVICE | MV_GENTL_CAMERALINK_DEVICE | MV_GENTL_CXP_DEVICE | MV_GENTL_XOF_DEVICE) ret = MvCamera.MV_CC_EnumDevices(n_layer_type, deviceList) if ret != 0: strError = "Enum devices fail! ret = :" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) return ret if deviceList.nDeviceNum == 0: QMessageBox.warning(mainWindow, "Info", "Find no device", QMessageBox.Ok) return ret print("Find %d devices!" % deviceList.nDeviceNum) devList = [] for i in range(0, deviceList.nDeviceNum): mvcc_dev_info = cast(deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE or mvcc_dev_info.nTLayerType == MV_GENTL_GIGE_DEVICE: print("\ngige device: [%d]" % i) user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chUserDefinedName) model_name = decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName) print("device user define name: " + user_defined_name) print("device model name: " + model_name) nip1 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24) nip2 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16) nip3 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8) nip4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff) print("current ip: %d.%d.%d.%d " % (nip1, nip2, nip3, nip4)) devList.append( "[" + str(i) + "]GigE: " + user_defined_name + " " + model_name + "(" + str(nip1) + "." + str( nip2) + "." + str(nip3) + "." + str(nip4) + ")") elif mvcc_dev_info.nTLayerType == MV_USB_DEVICE: print("\nu3v device: [%d]" % i) user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stUsb3VInfo.chUserDefinedName) model_name = decoding_char(mvcc_dev_info.SpecialInfo.stUsb3VInfo.chModelName) print("device user define name: " + user_defined_name) print("device model name: " + model_name) strSerialNumber = "" for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chSerialNumber: if per == 0: break strSerialNumber = strSerialNumber + chr(per) print("user serial number: " + strSerialNumber) devList.append("[" + str(i) + "]USB: " + user_defined_name + " " + model_name + "(" + str(strSerialNumber) + ")") elif mvcc_dev_info.nTLayerType == MV_GENTL_CAMERALINK_DEVICE: print("\nCML device: [%d]" % i) user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stCMLInfo.chUserDefinedName) model_name = decoding_char(mvcc_dev_info.SpecialInfo.stCMLInfo.chModelName) print("device user define name: " + user_defined_name) print("device model name: " + model_name) strSerialNumber = "" for per in mvcc_dev_info.SpecialInfo.stCMLInfo.chSerialNumber: if per == 0: break strSerialNumber = strSerialNumber + chr(per) print("user serial number: " + strSerialNumber) devList.append("[" + str(i) + "]CML: " + user_defined_name + " " + model_name + "(" + str(strSerialNumber) + ")") elif mvcc_dev_info.nTLayerType == MV_GENTL_CXP_DEVICE: print("\nCXP device: [%极d]" % i) user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stCXPInfo.chUserDefinedName) model_name = decoding_char(mvcc_dev_info.SpecialInfo.stCXPInfo.chModel极Name) print("device user define name: " + user_defined_name) print("device model name: " + model_name) strSerialNumber = "" for per in mvcc_dev_info.SpecialInfo.stCXPInfo.chSerialNumber: if per == 0: break strSerialNumber = strSerialNumber + chr(per) print("user serial number: "+strSerialNumber) devList.append("[" + str(i) + "]CXP: " + user_defined_name + " " + model_name + "(" + str(strSerialNumber) + ")") elif mvcc_dev_info.nTLayerType == MV_GENTL_XOF_DEVICE: print("\nXoF device: [%d]" % i) user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stXoFInfo.chUserDefinedName) model_name = decoding_char(mvcc_dev_info.SpecialInfo.stXoFInfo.chModelName) print("device user define name: " + user_defined_name) print("device model name: " + model_name) strSerialNumber = "" for per in mvcc_dev_info.SpecialInfo.stXoFInfo.chSerialNumber: if per == 0: break strSerialNumber = strSerialNumber + chr(per) print("user serial number: " + strSerialNumber) devList.append("[" + str(i) + "]XoF: " + user_defined_name + " " + model_name + "(" + str(strSerialNumber) + ")") ui.ComboDevices.clear() ui.ComboDevices.addItems(devList) ui.ComboDevices.setCurrentIndex(0) # ch:打开相机 | en:open device def open_device(): global deviceList global nSelCamIndex global obj_cam_operation global isOpen global frame_monitor_thread if isOpen: QMessageBox.warning(mainWindow, "Error", 'Camera is Running!', QMessageBox.Ok) return MV_E_CALLORDER nSelCamIndex = ui.ComboDevices.currentIndex() if nSelCamIndex < 0: QMessageBox.warning(mainWindow, "Error", 'Please select a camera!', QMessageBox.Ok) return MV_E_CALLORDER obj_cam_operation = CameraOperation(cam, deviceList, nSelCamIndex) ret = obj_cam_operation.open_device() if 0 != ret: strError = "Open device failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) isOpen = False else: set_continue_mode() get_param() isOpen = True enable_controls() # 启动帧监控线程 frame_monitor_thread = FrameMonitorThread(obj_cam_operation) frame_monitor_thread.frame_status.connect(ui.statusBar.showMessage) frame_monitor_thread.start() # ch:开始取流 | en:Start grab image def start_grabbing(): global obj_cam_operation global isGrabbing ret = obj_cam_operation.start_grabbing(ui.widgetDisplay.winId()) if ret != 0: strError = "Start grabbing failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) else: isGrabbing = True enable_controls() # ch:停止取流 | en:Stop grab image def stop_grabbing(): global obj_cam_operation global isGrabbing ret = obj_cam_operation.Stop_grabbing() if ret != 0: strError = "Stop grabbing failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) else: isGrabbing = False enable_controls() # ch:关闭设备 | Close device def close_device(): global isOpen global isGrabbing global obj_cam_operation global frame_monitor_thread # 停止帧监控线程 if frame_monitor_thread and frame_monitor_thread.isRunning(): frame_monitor_thread.stop() frame_monitor_thread.wait(2000) if isOpen: obj_cam_operation.close_device() isOpen = False isGrabbing = False enable_controls() # ch:设置触发模式 | en:set trigger mode def set_continue_mode(): ret = obj_cam_operation.set_trigger_mode(False) if ret != 0: strError = "Set continue mode failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) else: ui.radioContinueMode.setChecked(True) ui.radioTriggerMode.setChecked(False) ui.bnSoftwareTrigger.setEnabled(False) # ch:设置软触发模式 | en:set software trigger mode def set_software_trigger_mode(): ret = obj_cam_operation.set_trigger_mode(True) if ret != 0: strError = "Set trigger mode failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) else: ui.radioContinueMode.setChecked(False) ui.radioTriggerMode.setChecked(True) ui.bnSoftwareTrigger.setEnabled(isGrabbing) # ch:设置触发命令 | en:set trigger software def trigger_once(): ret = obj_cam_operation.trigger_once() if ret != 0: strError = "TriggerSoftware failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) # 保存图像对话框 def save_image_dialog(): """ 打开保存图像对话框并保存当前帧 """ global isGrabbing, obj_cam_operation # 检查相机状态 if not isGrabbing: QMessageBox.warning(mainWindow, "相机未就绪", "请先开始取流并捕获图像!", QMessageBox.Ok) return # 检查是否有有效图像 if not obj_cam_operation.is_frame_available(): QMessageBox.warning(mainWindow, "无有效图像", "未捕获到有效图像,请检查相机状态!", QMessageBox.Ok) return # 读取上次使用的路径 settings = QSettings("ClothInspection", "CameraApp") last_dir = settings.value("last_save_dir", os.path.join(os.getcwd(), "captures")) # 创建默认文件名 timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") default_filename = f"capture_{timestamp}" # 弹出文件保存对话框 file_path, selected_filter = QFileDialog.getSaveFileName( mainWindow, "保存图像", os.path.join(last_dir, default_filename), # 初始路径 "BMP 图像 (*.bmp);;JPEG 图像 (*.jpg);;PNG 图像 (*.png);;TIFF 图像 (*.tiff);;所有文件 (*)", options=QFileDialog.DontUseNativeDialog ) # 用户取消操作 if not file_path: logging.info("用户取消了图像保存操作") return # 处理文件扩展名 file_extension = os.path.splitext(file_path)[1].lower() if not file_extension: # 根据选择的过滤器添加扩展名 if "BMP" in selected_filter: file_path += ".bmp" elif "JPEG" in selected_filter or "JPG" in selected_filter: file_path += ".jpg" elif "PNG" in selected_filter: file_path += ".png" elif "TIFF" in selected_filter: file_path += ".tiff" else: # 默认使用BMP格式 file_path += ".bmp" # 确定保存格式 format_mapping = { ".bmp": "bmp", ".jpg": "jpg", ".jpeg": "jpg", ".png": "png", ".tiff": "tiff", ".tif": "tiff" } file_extension = os.path.splitext(file_path)[1].lower() save_format = format_mapping.get(file_extension, "bmp") # 确保目录存在 directory = os.path.dirname(file_path) if directory and not os.path.exists(directory): try: os.makedirs(directory, exist_ok=True) except OSError as e: QMessageBox.critical(mainWindow, "目录错误", f"无法创建目录:\n{str(e)}", QMessageBox.Ok) return # 保存图像 try: ret = obj_cam_operation.save_image(file_path, save_format) if ret == MV_OK: QMessageBox.information(mainWindow, "保存成功", f"图像已保存至:\n{file_path}", QMessageBox.Ok) logging.info(f"图像保存成功: {file_path}") # 保存当前目录 settings.setValue("last_save_dir", os.path.dirname(file_path)) else: error_msg = f"保存失败! 错误代码: {hex(ret)}" QMessageBox.warning(mainWindow, "保存失败", error_msg, QMessageBox.Ok) logging.error(f"图像保存失败: {file_path}, 错误代码: {hex(ret)}") except Exception as e: QMessageBox.critical(mainWindow, "保存错误", f"保存图像时发生错误:\n{str(e)}", QMessageBox.Ok) logging.exception(f"保存图像时发生异常: {file_path}") def is_float(str): try: float(str) return True except ValueError: return False # ch: 获取参数 | en:get param def get_param(): try: # 调用方法获取参数 ret = obj_cam_operation.get_parameters() # 记录调用结果(调试用) logging.debug(f"get_param() 返回: {ret} (类型: {type(ret)})") # 处理错误码 if ret != MV_OK: strError = "获取参数失败,错误码: " + ToHexStr(ret) QMessageBox.warning(mainWindow, "错误", strError, QMessageBox.Ok) else: # 成功获取参数后更新UI ui.edtExposureTime.setText("{0:.2f}".format(obj_cam_operation.exposure_time)) ui.edtGain.setText("{0:.2f}".format(obj_cam_operation.gain)) ui.edtFrameRate.setText("{0:.2f}".format(obj_cam_operation.frame_rate)) # 记录成功信息 logging.info("成功获取相机参数") except Exception as e: # 处理所有异常 error_msg = f"获取参数时发生错误: {str(e)}" logging.error(error_msg) QMessageBox.critical(mainWindow, "严重错误", error_msg, QMessageBox.Ok) # ch: 设置参数 | en:set param def set_param(): frame_rate = ui.edtFrameRate.text() exposure = ui.edtExposureTime.text() gain = ui.edtGain.text() if not (is_float(frame_rate) and is_float(exposure) and is_float(gain)): strError = "设置参数失败: 参数必须是有效的浮点数" QMessageBox.warning(mainWindow, "错误", strError, QMessageBox.Ok) return MV_E_PARAMETER try: # 使用正确的参数顺序和关键字 ret = obj_cam_operation.set_param( frame_rate=float(frame_rate), exposure_time=float(exposure), gain=float(gain) ) if ret != MV_OK: strError = "设置参数失败,错误码: " + ToHexStr(ret) QMessageBox.warning(mainWindow, "错误", strError, QMessageBox.Ok) else: logging.info("参数设置成功") return MV_OK except Exception as e: error_msg = f"设置参数时发生错误: {str(e)}" logging.error(error_msg) QMessageBox.critical(mainWindow, "严重错误", error_msg, QMessageBox.Ok) return MV_E_STATE # ch: 设置控件状态 | en:set enable status def enable_controls(): global isGrabbing global isOpen # 先设置group的状态,再单独设置各控件状态 ui.groupGrab.setEnabled(isOpen) ui.groupParam.setEnabled(isOpen) ui.bnOpen.setEnabled(not isOpen) ui.bnClose.setEnabled(isOpen) ui.bnStart.setEnabled(isOpen and (not isGrabbing)) ui.bnStop.setEnabled(isOpen and isGrabbing) ui.bnSoftwareTrigger.setEnabled(isGrabbing and ui.radioTriggerMode.isChecked()) ui.bnSaveImage.setEnabled(isOpen and isGrabbing) # 添加检测按钮控制 ui.bnCheckPrint.setEnabled(isOpen and isGrabbing) ui.bnSaveSample.setEnabled(isOpen and isGrabbing) ui.bnPreviewSample.setEnabled(bool(current_sample_path)) if __name__ == "__main__": # ch:初始化SDK | en: initialize SDK MvCamera.MV_CC_Initialize() deviceList = MV_CC_DEVICE_INFO_LIST() cam = MvCamera() nSelCamIndex = 0 obj_cam_operation = 0 isOpen = False isGrabbing = False isCalibMode = True # 是否是标定模式(获取原始图像) frame_monitor_thread = None # 初始化UI app = QApplication(sys.argv) mainWindow = QMainWindow() ui = Ui_MainWindow() ui.setupUi(mainWindow) # 扩大主窗口尺寸 mainWindow.resize(1200, 800) # 宽度1200,高度800 # 创建工具栏 toolbar = mainWindow.addToolBar("检测工具") # 添加检测按钮 ui.bnCheckPrint = QPushButton("检测印花质量") toolbar.addWidget(ui.bnCheckPrint) # 添加保存样本按钮 ui.bnSaveSample = QPushButton("保存标准样本") toolbar.addWidget(ui.bnSaveSample) # 添加预览样本按钮 ui.bnPreviewSample = QPushButton("预览样本") toolbar.addWidget(ui.bnPreviewSample) # 添加历史记录下拉框 ui.cbHistory = QComboBox() ui.cbHistory.setMinimumWidth(300) toolbar.addWidget(QLabel("历史记录:")) toolbar.addWidget(ui.cbHistory) # 添加当前样本显示标签 ui.lblSamplePath = QLabel("当前样本: 未设置样本") status_bar = mainWindow.statusBar() status_bar.addPermanentWidget(ui.lblSamplePath) # 绑定按钮事件 ui.bnCheckPrint.clicked.connect(check_print) ui.bnSaveSample.clicked.connect(save_sample_image) ui.bnPreviewSample.clicked.connect(preview_sample) # 绑定其他按钮事件 ui.bnEnum.clicked.connect(enum_devices) ui.bnOpen.clicked.connect(open_device) ui.bnClose.clicked.connect(close_device) ui.bnStart.clicked.connect(start_grabbing) ui.bnStop.clicked.connect(stop_grabbing) ui.bnSoftwareTrigger.clicked.connect(trigger_once) ui.radioTriggerMode.clicked.connect(set_software_trigger_mode) ui.radioContinueMode.clicked.connect(set_continue_mode) ui.bnGetParam.clicked.connect(get_param) ui.bnSetParam.clicked.connect(set_param) # 修改保存图像按钮连接 ui.bnSaveImage.clicked.connect(save_image_dialog) # 显示主窗口 mainWindow.show() # 执行应用 app.exec_() # 关闭设备 close_device() # ch:反初始化SDK | en: finalize SDK MvCamera.MV_CC_Finalize() sys.exit() 将这个完整代码和你上面给出的改进方案结合并完整展示出来
07-08
把你的修改方案完整的展示出来 下面是目前完整代码: # -*- coding: utf-8 -*- import sys import os import cv2 import numpy as np from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton, QWidget, QVBoxLayout, QHBoxLayout, QMessageBox, QLabel, QFileDialog, QToolBar, QComboBox, QStatusBar, QGroupBox, QSlider, QDockWidget, QProgressDialog) from PyQt5.QtCore import QRect, Qt, QSettings, QThread, pyqtSignal sys.path.append("D:\\海康\\MVS\\Development\\Samples\\Python\\BasicDemo") from MvCameraControl_class import * from MvErrorDefine_const import * from CameraParams_header import * from PyUICBasicDemo import Ui_MainWindow import ctypes from datetime import datetime import logging import platform # 配置日志系统 logging.basicConfig( level=logging.DEBUG, # 设置为DEBUG级别获取更多信息 format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler("cloth_inspection_debug.log"), logging.StreamHandler() ] ) logging.info("布料印花检测系统启动") # 全局变量 current_sample_path = "" # 当前使用的样本路径 detection_history = [] # 检测历史记录 # ==================== 核心功能类 ==================== class CameraOperation: def __init__(self, cam, device_list, index): self.cam = cam self.device_list = device_list self.index = index self.g_bFrameAvailable = False # 帧数据可用标志 self.current_frame = None # 当前帧数据 self.exposure_time = 0.0 # 曝光时间 self.gain = 0.0 # 增益 self.frame_rate = 0.0 # 帧率 def __getattr__(self, name): """处理未定义属性的访问""" raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'") def is_frame_available(self): """检查是否有可用的帧数据""" return self.g_bFrameAvailable def get_current_frame(self): """获取当前帧数据""" return self.current_frame def open_device(self): """打开设备""" # 打开设备代码... # 在成功打开设备后设置初始状态 self.g_bFrameAvailable = False return MV_OK def start_grabbing(self, hwnd): """开始取流""" # 取流代码... # 在成功开始取流后设置标志 self.g_bFrameAvailable = True return MV_OK def Stop_grabbing(self): """停止取流""" # 停止取流代码... # 在停止取流后重置标志 self.g_bFrameAvailable = False return MV_OK def close_device(self): """关闭设备""" # 关闭设备代码... # 在关闭设备后重置标志 self.g_bFrameAvailable = False return MV_OK def get_one_frame(self): """获取一帧数据""" # 获取帧数据代码... # 在成功获取帧后更新当前帧 self.current_frame = frame_data self.g_bFrameAvailable = True return frame_data # 其他相机操作方法保持不变... # ==================== 线程类 ==================== # 帧监控线程(修复版) class FrameMonitorThread(QThread): frame_status = pyqtSignal(str) def __init__(self, cam_operation): super().__init__() self.cam_operation = cam_operation self.running = True def run(self): while self.running: if self.cam_operation: # 使用正确的方法检查帧状态 frame_available = self.cam_operation.is_frame_available() frame_text = "有帧" if frame_available else "无帧" self.frame_status.emit(f"帧状态: {frame_text}") QThread.msleep(500) def stop(self): self.running = False # ==================== 核心检测功能 ==================== # 布料印花检测函数(修复版) def check_print_quality(sample_image_path, test_image, threshold=0.05): """ 检测布料印花是否合格,直接使用内存中的测试图像 :param sample_image_path: 合格样本图像路径 :param test_image: 内存中的测试图像 (numpy数组) :param threshold: 差异阈值 :return: 是否合格,差异值,标记图像 """ # 读取样本图像 try: sample_img_data = np.fromfile(sample_image_path, dtype=np.uint8) sample_image = cv2.imdecode(sample_img_data, cv2.IMREAD_GRAYSCALE) if sample_image is None: logging.error(f"无法解码样本图像: {sample_image_path}") return None, None, None except Exception as e: logging.exception(f"样本图像读取异常: {str(e)}") return None, None, None # 确保测试图像是灰度图 if len(test_image.shape) == 3: # 如果是彩色图像 test_image = cv2.cvtColor(test_image, cv2.COLOR_BGR2GRAY) # 确保两个图像大小一致 try: test_image = cv2.resize(test_image, (sample_image.shape[1], sample_image.shape[0])) except Exception as e: logging.error(f"图像调整大小失败: {str(e)}") return None, None, None # 计算差异 diff = cv2.absdiff(sample_image, test_image) # 二值化差异 _, thresholded = cv2.threshold(diff, 30, 255, cv2.THRESH_BINARY) # 计算差异比例 diff_pixels = np.count_nonzero(thresholded) total_pixels = sample_image.size diff_ratio = diff_pixels / total_pixels # 判断是否合格 is_qualified = diff_ratio <= threshold # 创建标记图像(红色标记差异区域) marked_image = cv2.cvtColor(test_image, cv2.COLOR_GRAY2BGR) marked_image[thresholded == 255] = [0, 0, 255] # 红色标记 return is_qualified, diff_ratio, marked_image # ==================== UI更新函数 ==================== # 更新检测结果显示 def update_diff_display(diff_ratio, is_qualified): """ 更新差异度显示控件 """ # 更新当前差异度显示 ui.lblCurrentDiff.setText(f"当前差异度: {diff_ratio*100:.2f}%") # 根据合格状态设置颜色 if is_qualified: ui.lblDiffStatus.setText("状态: 合格") ui.lblDiffStatus.setStyleSheet("color: green; font-size: 12px;") else: ui.lblDiffStatus.setText("状态: 不合格") ui.lblDiffStatus.setStyleSheet("color: red; font-size: 12px;") # 更新差异度阈值显示 def update_diff_threshold(value): """ 当滑块值改变时更新阈值显示 """ ui.lblDiffValue.setText(f"{value}%") # 布料印花检测功能(修复版) def check_print(): global isGrabbing, obj_cam_operation, current_sample_path, detection_history logging.info("检测印花质量按钮按下") # 1. 检查相机状态 if not isGrabbing: logging.warning("相机未取流") QMessageBox.warning(mainWindow, "错误", "请先开始取流并捕获图像!", QMessageBox.Ok) return # 2. 检查相机操作对象 if not obj_cam_operation: logging.error("相机操作对象未初始化") QMessageBox.warning(mainWindow, "错误", "相机未正确初始化!", QMessageBox.Ok) return # 3. 检查样本路径 if not current_sample_path or not os.path.exists(current_sample_path): logging.warning(f"无效样本路径: {current_sample_path}") QMessageBox.warning(mainWindow, "错误", "请先设置有效的标准样本图像!", QMessageBox.Ok) return # 使用进度对话框防止UI阻塞 progress = QProgressDialog("正在检测...", "取消", 0, 100, mainWindow) progress.setWindowModality(Qt.WindowModal) progress.setValue(10) try: # 4. 获取当前帧 logging.info("尝试获取当前帧") test_image = obj_cam_operation.get_current_frame() progress.setValue(30) if test_image is None: logging.warning("获取当前帧失败") QMessageBox.warning(mainWindow, "错误", "无法获取当前帧图像!", QMessageBox.Ok) return # 5. 获取差异度阈值 diff_threshold = ui.sliderDiffThreshold.value() / 100.0 logging.info(f"使用差异度阈值: {diff_threshold}") progress.setValue(50) # 6. 执行检测 is_qualified, diff_ratio, marked_image = check_print_quality( current_sample_path, test_image, threshold=diff_threshold ) progress.setValue(70) # 检查返回结果是否有效 if is_qualified is None: logging.error("检测函数返回无效结果") QMessageBox.critical(mainWindow, "检测错误", "检测失败,请检查日志", QMessageBox.Ok) return logging.info(f"检测结果: 合格={is_qualified}, 差异={diff_ratio}") progress.setValue(90) # 7. 更新UI update_diff_display(diff_ratio, is_qualified) result_text = f"印花是否合格: {'合格' if is_qualified else '不合格'}\n差异占比: {diff_ratio*100:.2f}%\n阈值: {diff_threshold*100:.2f}%" QMessageBox.information(mainWindow, "检测结果", result_text, QMessageBox.Ok) if marked_image is not None: cv2.imshow("缺陷标记结果", marked_image) cv2.waitKey(0) cv2.destroyAllWindows() else: logging.warning("标记图像为空") # 8. 记录检测结果 detection_result = { 'timestamp': datetime.now(), 'qualified': is_qualified, 'diff_ratio': diff_ratio, 'threshold': diff_threshold } detection_history.append(detection_result) update_history_display() progress.setValue(100) except Exception as e: logging.exception("印花检测失败") QMessageBox.critical(mainWindow, "检测错误", f"检测过程中发生错误: {str(e)}", QMessageBox.Ok) finally: progress.close() # 保存标准样本函数 def save_sample_image(): global isGrabbing, obj_cam_operation, current_sample_path if not isGrabbing: QMessageBox.warning(mainWindow, "错误", "请先开始取流并捕获图像!", QMessageBox.Ok) return # 检查是否有有效图像 if not obj_cam_operation.is_frame_available(): QMessageBox.warning(mainWindow, "无有效图像", "未捕获到有效图像,请检查相机状态!", QMessageBox.Ok) return # 读取上次使用的路径 settings = QSettings("ClothInspection", "CameraApp") last_dir = settings.value("last_save_dir", os.path.join(os.getcwd(), "captures")) # 创建默认文件名 timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") default_filename = f"sample_{timestamp}" # 弹出文件保存对话框 file_path, selected_filter = QFileDialog.getSaveFileName( mainWindow, "保存标准样本图像", os.path.join(last_dir, default_filename), "BMP Files (*.bmp);;PNG Files (*.png);;JPEG Files (*.jpg);;所有文件 (*)", options=QFileDialog.DontUseNativeDialog ) if not file_path: logging.info("用户取消了图像保存操作") return # 用户取消保存 # 处理文件扩展名 file_extension = os.path.splitext(file_path)[1].lower() if not file_extension: # 根据选择的过滤器添加扩展名 if "BMP" in selected_filter: file_path += ".bmp" elif "PNG" in selected_filter: file_path += ".png" elif "JPEG" in selected_filter or "JPG" in selected_filter: file_path += ".jpg" else: # 默认使用BMP格式 file_path += ".bmp" file_extension = os.path.splitext(file_path)[1].lower() # 根据扩展名设置保存格式 format_mapping = { ".bmp": "bmp", ".png": "png", ".jpg": "jpg", ".jpeg": "jpg" } save_format = format_mapping.get(file_extension) if not save_format: QMessageBox.warning(mainWindow, "错误", "不支持的文件格式!", QMessageBox.Ok) return # 确保目录存在 directory = os.path.dirname(file_path) if directory and not os.path.exists(directory): try: os.makedirs(directory, exist_ok=True) logging.info(f"创建目录: {directory}") except OSError as e: error_msg = f"无法创建目录 {directory}: {str(e)}" QMessageBox.critical(mainWindow, "目录创建错误", error_msg, QMessageBox.Ok) return # 保存当前帧作为标准样本 try: ret = obj_cam_operation.save_image(file_path, save_format) if ret != MV_OK: strError = f"保存样本图像失败: {hex(ret)}" QMessageBox.warning(mainWindow, "错误", strError, QMessageBox.Ok) else: success_msg = f"标准样本已保存至:\n{file_path}" QMessageBox.information(mainWindow, "成功", success_msg, QMessageBox.Ok) # 更新当前样本路径 current_sample_path = file_path update_sample_display() # 保存当前目录 settings.setValue("last_save_dir", os.path.dirname(file_path)) except Exception as e: error_msg = f"保存图像时发生错误: {str(e)}" QMessageBox.critical(mainWindow, "异常错误", error_msg, QMessageBox.Ok) logging.exception("保存样本图像时发生异常") # 预览当前样本 def preview_sample(): global current_sample_path if not current_sample_path or not os.path.exists(current_sample_path): QMessageBox.warning(mainWindow, "错误", "请先设置有效的标准样本图像!", QMessageBox.Ok) return try: # 使用安全方法读取图像 img_data = np.fromfile(current_sample_path, dtype=np.uint8) sample_img = cv2.imdecode(img_data, cv2.IMREAD_COLOR) if sample_img is None: raise Exception("无法加载图像") cv2.imshow("标准样本预览", sample_img) cv2.waitKey(0) cv2.destroyAllWindows() except Exception as e: QMessageBox.warning(mainWindow, "错误", f"预览样本失败: {str(e)}", QMessageBox.Ok) # 更新样本路径显示 def update_sample_display(): global current_sample_path if current_sample_path: ui.lblSamplePath.setText(f"当前样本: {os.path.basename(current_sample_path)}") ui.lblSamplePath.setToolTip(current_sample_path) ui.bnPreviewSample.setEnabled(True) else: ui.lblSamplePath.setText("当前样本: 未设置样本") ui.bnPreviewSample.setEnabled(False) # 更新历史记录显示 def update_history_display(): global detection_history ui.cbHistory.clear() for i, result in enumerate(detection_history[-10:]): # 显示最近10条记录 timestamp = result['timestamp'].strftime("%H:%M:%S") status = "合格" if result['qualified'] else "不合格" ratio = f"{result['diff_ratio']*100:.2f}%" ui.cbHistory.addItem(f"[{timestamp}] {status} - 差异: {ratio}") # 获取选取设备信息的索引,通过[]之间的字符去解析 def TxtWrapBy(start_str, end, all): start = all.find(start_str) if start >= 0: start += len(start_str) end = all.find(end, start) if end >= 0: return all[start:end].strip() # 将返回的错误码转换为十六进制显示 def ToHexStr(num): """将错误码转换为十六进制字符串""" # 处理非整数输入 if not isinstance(num, int): try: # 尝试转换为整数 num = int(num) except: # 无法转换时返回类型信息 return f"<非整数:{type(num)}>" chaDic = {10: 'a', 11: 'b', 12: 'c', 13: 'd', 14: 'e', 15: 'f'} hexStr = "" # 处理负数 if num < 0: num = num + 2 ** 32 # 转换为十六进制 while num >= 16: digit = num % 16 hexStr = chaDic.get(digit, str(digit)) + hexStr num //= 16 hexStr = chaDic.get(num, str(num)) + hexStr return "0x" + hexStr # ch:初始化SDK | en: initialize SDK MvCamera.MV_CC_Initialize() global deviceList deviceList = MV_CC_DEVICE_INFO_LIST() global cam cam = MvCamera() global nSelCamIndex nSelCamIndex = 0 global obj_cam_operation obj_cam_operation = 0 global isOpen isOpen = False global isGrabbing isGrabbing = False global isCalibMode # 是否是标定模式(获取原始图像) isCalibMode = True global frame_monitor_thread # 绑定下拉列表至设备信息索引 def xFunc(event): global nSelCamIndex nSelCamIndex = TxtWrapBy("[", "]", ui.ComboDevices.get()) # Decoding Characters def decoding_char(c_ubyte_value): c_char_p_value = ctypes.cast(c_ubyte_value, ctypes.c_char_p) try: decode_str = c_char_p_value.value.decode('gbk') # Chinese characters except UnicodeDecodeError: decode_str = str(c_char_p_value.value) return decode_str # ch:枚举相机 | en:enum devices def enum_devices(): global deviceList global obj_cam_operation deviceList = MV_CC_DEVICE_INFO_LIST() n_layer_type = (MV_GIGE_DEVICE | MV_USB_DEVICE | MV_GENTL_CAMERALINK_DEVICE | MV_GENTL_CXP_DEVICE | MV_GENTL_XOF_DEVICE) ret = MvCamera.MV_CC_EnumDevices(n_layer_type, deviceList) if ret != 0: strError = "Enum devices fail! ret = :" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) return ret if deviceList.nDeviceNum == 0: QMessageBox.warning(mainWindow, "Info", "Find no device", QMessageBox.Ok) return ret print("Find %d devices!" % deviceList.nDeviceNum) devList = [] for i in range(0, deviceList.nDeviceNum): mvcc_dev_info = cast(deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE or mvcc_dev_info.nTLayerType == MV_GENTL_GIGE_DEVICE: print("\ngige device: [%d]" % i) user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chUserDefinedName) model_name = decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName) print("device user define name: " + user_defined_name) print("device model name: " + model_name) nip1 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24) nip2 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16) nip3 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8) nip4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff) print("current ip: %d.%d.%d.%d " % (nip1, nip2, nip3, nip4)) devList.append( "[" + str(i) + "]GigE: " + user_defined_name + " " + model_name + "(" + str(nip1) + "." + str( nip2) + "." + str(nip3) + "." + str(nip4) + ")") elif mvcc_dev_info.nTLayerType == MV_USB_DEVICE: print("\nu3v device: [%d]" % i) user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stUsb3VInfo.chUserDefinedName) model_name = decoding_char(mvcc_dev_info.SpecialInfo.stUsb3VInfo.chModelName) print("device user define name: " + user_defined_name) print("device model name: " + model_name) strSerialNumber = "" for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chSerialNumber: if per == 0: break strSerialNumber = strSerialNumber + chr(per) print("user serial number: " + strSerialNumber) devList.append("[" + str(i) + "]USB: " + user_defined_name + " " + model_name + "(" + str(strSerialNumber) + ")") elif mvcc_dev_info.nTLayerType == MV_GENTL_CAMERALINK_DEVICE: print("\nCML device: [%d]" % i) user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stCMLInfo.chUserDefinedName) model_name = decoding_char(mvcc_dev_info.SpecialInfo.stCMLInfo.chModelName) print("device user define name: " + user_defined_name) print("device model name: " + model_name) strSerialNumber = "" for per in mvcc_dev_info.SpecialInfo.stCMLInfo.chSerialNumber: if per == 0: break strSerialNumber = strSerialNumber + chr(per) print("user serial number: " + strSerialNumber) devList.append("[" + str(i) + "]CML: " + user_defined_name + " " + model_name + "(" + str(strSerialNumber) + ")") elif mvcc_dev_info.nTLayerType == MV_GENTL_CXP_DEVICE: print("\nCXP device: [%d]" % i) user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stCXPInfo.chUserDefinedName) model_name = decoding_char(mvcc_dev_info.SpecialInfo.stCXPInfo.chModelName) print("device user define name: " + user_defined_name) print("device model name: " + model_name) strSerialNumber = "" for per in mvcc_dev_info.SpecialInfo.stCXPInfo.chSerialNumber: if per == 0: break strSerialNumber = strSerialNumber + chr(per) print("user serial number: "+strSerialNumber) devList.append("[" + str(i) + "]CXP: " + user_defined_name + " " + model_name + "(" + str(strSerialNumber) + ")") elif mvcc_dev_info.nTLayerType == MV_GENTL_XOF_DEVICE: print("\nXoF device: [%d]" % i) user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stXoFInfo.chUserDefinedName) model_name = decoding_char(mvcc_dev_info.SpecialInfo.stXoFInfo.chModelName) print("device user define name: " + user_defined_name) print("device model name: " + model_name) strSerialNumber = "" for per in mvcc_dev_info.SpecialInfo.stXoFInfo.chSerialNumber: if per == 0: break strSerialNumber = strSerialNumber + chr(per) print("user serial number: " + strSerialNumber) devList.append("[" + str(i) + "]XoF: " + user_defined_name + " " + model_name + "(" + str(strSerialNumber) + ")") ui.ComboDevices.clear() ui.ComboDevices.addItems(devList) ui.ComboDevices.setCurrentIndex(0) # ch:打开相机 | en:open device def open_device(): global deviceList global nSelCamIndex global obj_cam_operation global isOpen global frame_monitor_thread if isOpen: QMessageBox.warning(mainWindow, "Error", 'Camera is Running!', QMessageBox.Ok) return MV_E_CALLORDER nSelCamIndex = ui.ComboDevices.currentIndex() if nSelCamIndex < 0: QMessageBox.warning(mainWindow, "Error", 'Please select a camera!', QMessageBox.Ok) return MV_E_CALLORDER obj_cam_operation = CameraOperation(cam, deviceList, nSelCamIndex) ret = obj_cam_operation.open_device() if 0 != ret: strError = "Open device failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) isOpen = False else: set_continue_mode() get_param() isOpen = True enable_controls() # 启动帧监控线程(修复版) frame_monitor_thread = FrameMonitorThread(obj_cam_operation) frame_monitor_thread.frame_status.connect(ui.statusBar.showMessage) frame_monitor_thread.start() # ch:开始取流 | en:Start grab image def start_grabbing(): global obj_cam_operation global isGrabbing ret = obj_cam_operation.start_grabbing(ui.widgetDisplay.winId()) if ret != 0: strError = "Start grabbing failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) else: isGrabbing = True enable_controls() # ch:停止取流 | en:Stop grab image def stop_grabbing(): global obj_cam_operation global isGrabbing ret = obj_cam_operation.Stop_grabbing() if ret != 0: strError = "Stop grabbing failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) else: isGrabbing = False enable_controls() # ch:关闭设备 | Close device def close_device(): global isOpen global isGrabbing global obj_cam_operation global frame_monitor_thread # 停止帧监控线程 if frame_monitor_thread and frame_monitor_thread.isRunning(): frame_monitor_thread.stop() frame_monitor_thread.wait(2000) if isOpen: obj_cam_operation.close_device() isOpen = False isGrabbing = False enable_controls() # ch:设置触发模式 | en:set trigger mode def set_continue_mode(): ret = obj_cam_operation.set_trigger_mode(False) if ret != 0: strError = "Set continue mode failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) else: ui.radioContinueMode.setChecked(True) ui.radioTriggerMode.setChecked(False) ui.bnSoftwareTrigger.setEnabled(False) # ch:设置软触发模式 | en:set software trigger mode def set_software_trigger_mode(): ret = obj_cam_operation.set_trigger_mode(True) if ret != 0: strError = "Set trigger mode failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) else: ui.radioContinueMode.setChecked(False) ui.radioTriggerMode.setChecked(True) ui.bnSoftwareTrigger.setEnabled(isGrabbing) # ch:设置触发命令 | en:set trigger software def trigger_once(): ret = obj_cam_operation.trigger_once() if ret != 0: strError = "TriggerSoftware failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) # 保存图像对话框 def save_image_dialog(): """ 打开保存图像对话框并保存当前帧 """ global isGrabbing, obj_cam_operation # 检查相机状态 if not isGrabbing: QMessageBox.warning(mainWindow, "相机未就绪", "请先开始取流并捕获图像!", QMessageBox.Ok) return # 检查是否有有效图像 if not obj_cam_operation.is_frame_available(): QMessageBox.warning(mainWindow, "无有效图像", "未捕获到有效图像,请检查相机状态!", QMessageBox.Ok) return # 读取上次使用的路径 settings = QSettings("ClothInspection", "CameraApp") last_dir = settings.value("last_save_dir", os.path.join(os.getcwd(), "captures")) # 创建默认文件名 timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") default_filename = f"capture_{timestamp}" # 弹出文件保存对话框 file_path, selected_filter = QFileDialog.getSaveFileName( mainWindow, "保存图像", os.path.join(last_dir, default_filename), # 初始路径 "BMP 图像 (*.bmp);;JPEG 图像 (*.jpg);;PNG 图像 (*.png);;TIFF 图像 (*.tiff);;所有文件 (*)", options=QFileDialog.DontUseNativeDialog ) # 用户取消操作 if not file_path: logging.info("用户取消了图像保存操作") return # 处理文件扩展名 file_extension = os.path.splitext(file_path)[1].lower() if not file_extension: # 根据选择的过滤器添加扩展名 if "BMP" in selected_filter: file_path += ".bmp" elif "JPEG" in selected_filter or "JPG" in selected_filter: file_path += ".jpg" elif "PNG" in selected_filter: file_path += ".png" elif "TIFF" in selected_filter: file_path += ".tiff" else: # 默认使用BMP格式 file_path += ".bmp" # 确定保存格式 format_mapping = { ".bmp": "bmp", ".jpg": "jpg", ".jpeg": "jpg", ".png": "png", ".tiff": "tiff", ".tif": "tiff" } file_extension = os.path.splitext(file_path)[1].lower() save_format = format_mapping.get(file_extension, "bmp") # 确保目录存在 directory = os.path.dirname(file_path) if directory and not os.path.exists(directory): try: os.makedirs(directory, exist_ok=True) except OSError as e: QMessageBox.critical(mainWindow, "目录错误", f"无法创建目录:\n{str(e)}", QMessageBox.Ok) return # 保存图像 try: ret = obj_cam_operation.save_image(file_path, save_format) if ret == MV_OK: QMessageBox.information(mainWindow, "保存成功", f"图像已保存至:\n{file_path}", QMessageBox.Ok) logging.info(f"图像保存成功: {file_path}") # 保存当前目录 settings.setValue("last_save_dir", os.path.dirname(file_path)) else: error_msg = f"保存失败! 错误代码: {hex(ret)}" QMessageBox.warning(mainWindow, "保存失败", error_msg, QMessageBox.Ok) logging.error(f"图像保存失败: {file_path}, 错误代码: {hex(ret)}") except Exception as e: QMessageBox.critical(mainWindow, "保存错误", f"保存图像时发生错误:\n{str(e)}", QMessageBox.Ok) logging.exception(f"保存图像时发生异常: {file_path}") def is_float(str): try: float(str) return True except ValueError: return False # ch: 获取参数 | en:get param def get_param(): try: # 调用方法获取参数 ret = obj_cam_operation.get_parameters() # 记录调用结果(调试用) logging.debug(f"get_param() 返回: {ret} (类型: {type(ret)})") # 处理错误码 if ret != MV_OK: strError = "获取参数失败,错误码: " + ToHexStr(ret) QMessageBox.warning(mainWindow, "错误", strError, QMessageBox.Ok) else: # 成功获取参数后更新UI ui.edtExposureTime.setText("{0:.2f}".format(obj_cam_operation.exposure_time)) ui.edtGain.setText("{0:.2f}".format(obj_cam_operation.gain)) ui.edtFrameRate.setText("{0:.2f}".format(obj_cam_operation.frame_rate)) # 记录成功信息 logging.info("成功获取相机参数") except Exception as e: # 处理所有异常 error_msg = f"获取参数时发生错误: {str(e)}" logging.error(error_msg) QMessageBox.critical(mainWindow, "严重错误", error_msg, QMessageBox.Ok) # ch: 设置参数 | en:set param def set_param(): frame_rate = ui.edtFrameRate.text() exposure = ui.edtExposureTime.text() gain = ui.edtGain.text() if not (is_float(frame_rate) and is_float(exposure) and is_float(gain)): strError = "设置参数失败: 参数必须是有效的浮点数" QMessageBox.warning(mainWindow, "错误", strError, QMessageBox.Ok) return MV_E_PARAMETER try: # 使用正确的参数顺序和关键字 ret = obj_cam_operation.set_param( frame_rate=float(frame_rate), exposure_time=float(exposure), gain=float(gain) ) if ret != MV_OK: strError = "设置参数失败,错误码: " + ToHexStr(ret) QMessageBox.warning(mainWindow, "错误", strError, QMessageBox.Ok) else: logging.info("参数设置成功") return MV_OK except Exception as e: error_msg = f"设置参数时发生错误: {str(e)}" logging.error(error_msg) QMessageBox.critical(mainWindow, "严重错误", error_msg, QMessageBox.Ok) return MV_E_STATE # ch: 设置控件状态 | en:set enable status def enable_controls(): global isGrabbing global isOpen # 先设置group的状态,再单独设置各控件状态 ui.groupGrab.setEnabled(isOpen) ui.groupParam.setEnabled(isOpen) ui.bnOpen.setEnabled(not isOpen) ui.bnClose.setEnabled(isOpen) ui.bnStart.setEnabled(isOpen and (not isGrabbing)) ui.bnStop.setEnabled(isOpen and isGrabbing) ui.bnSoftwareTrigger.setEnabled(isGrabbing and ui.radioTriggerMode.isChecked()) ui.bnSaveImage.setEnabled(isOpen and isGrabbing) # 添加检测按钮控制 ui.bnCheckPrint.setEnabled(isOpen and isGrabbing) ui.bnSaveSample.setEnabled(isOpen and isGrabbing) ui.bnPreviewSample.setEnabled(bool(current_sample_path)) if __name__ == "__main__": # ch:初始化SDK | en: initialize SDK MvCamera.MV_CC_Initialize() deviceList = MV_CC_DEVICE_INFO_LIST() cam = MvCamera() nSelCamIndex = 0 obj_cam_operation = 0 isOpen = False isGrabbing = False isCalibMode = True # 是否是标定模式(获取原始图像) frame_monitor_thread = None # 初始化UI app = QApplication(sys.argv) mainWindow = QMainWindow() ui = Ui_MainWindow() ui.setupUi(mainWindow) # 扩大主窗口尺寸 mainWindow.resize(1200, 800) # 宽度1200,高度800 # 创建工具栏 toolbar = mainWindow.addToolBar("检测工具") # 添加检测按钮 ui.bnCheckPrint = QPushButton("检测印花质量") toolbar.addWidget(ui.bnCheckPrint) # 添加保存样本按钮 ui.bnSaveSample = QPushButton("保存标准样本") toolbar.addWidget(ui.bnSaveSample) # 添加预览样本按钮 ui.bnPreviewSample = QPushButton("预览样本") toolbar.addWidget(ui.bnPreviewSample) # 添加历史记录下拉框 ui.cbHistory = QComboBox() ui.cbHistory.setMinimumWidth(300) toolbar.addWidget(QLabel("历史记录:")) toolbar.addWidget(ui.cbHistory) # 添加当前样本显示标签 ui.lblSamplePath = QLabel("当前样本: 未设置样本") status_bar = mainWindow.statusBar() status_bar.addPermanentWidget(ui.lblSamplePath) # === 新增差异度调整控件 === # 创建右侧面板容器 right_panel = QWidget() right_layout = QVBoxLayout(right_panel) right_layout.setContentsMargins(10, 10, 10, 10) # 创建差异度调整组 diff_group = QGroupBox("差异度调整") diff_layout = QVBoxLayout(diff_group) # 差异度阈值控制 ui.lblDiffThreshold = QLabel("差异度阈值 (0-100%):") ui.sliderDiffThreshold = QSlider(Qt.Horizontal) ui.sliderDiffThreshold.setRange(0, 100) # 0-100% ui.sliderDiffThreshold.setValue(5) # 默认5% ui.lblDiffValue = QLabel("5%") # 修复:使用正确的变量名 # 当前差异度显示 ui.lblCurrentDiff = QLabel("当前差异度: -") ui.lblCurrentDiff.setStyleSheet("font-size: 14px; font-weight: bold;") # 差异度状态指示器 ui.lblDiffStatus = QLabel("状态: 未检测") ui.lblDiffStatus.setStyleSheet("font-size: 12px;") # 布局控件 diff_layout.addWidget(ui.lblDiffThreshold) diff_layout.addWidget(ui.sliderDiffThreshold) diff_layout.addWidget(ui.lblDiffValue) # 修复:使用正确的变量名 diff_layout.addWidget(ui.lblCurrentDiff) diff_layout.addWidget(ui.lblDiffStatus) # 添加差异度组到右侧布局 right_layout.addWidget(diff_group) # 添加拉伸项使控件靠上 right_layout.addStretch(1) # 创建停靠窗口 dock = QDockWidget("检测控制面板", mainWindow) dock.setWidget(right_panel) dock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable) mainWindow.addDockWidget(Qt.RightDockWidgetArea, dock) # === 差异度调整功能实现 === # 更新差异度阈值显示 def update_diff_threshold(value): ui.lblDiffValue.setText(f"{value}%") # 连接滑块信号 ui.sliderDiffThreshold.valueChanged.connect(update_diff_threshold) # 更新检测结果显示 def update_diff_display(diff_ratio, is_qualified): # 更新当前差异度显示 ui.lblCurrentDiff.setText(f"当前差异度: {diff_ratio*100:.2f}%") # 根据合格状态设置颜色 if is_qualified: ui.lblDiffStatus.setText("状态: 合格") ui.lblDiffStatus.setStyleSheet("color: green; font-size: 12px;") else: ui.lblDiffStatus.setText("状态: 不合格") ui.lblDiffStatus.setStyleSheet("color: red; font-size: 12px;") # 绑定按钮事件 ui.bnCheckPrint.clicked.connect(check_print) ui.bnSaveSample.clicked.connect(save_sample_image) ui.bnPreviewSample.clicked.connect(preview_sample) # 绑定其他按钮事件 ui.bnEnum.clicked.connect(enum_devices) ui.bnOpen.clicked.connect(open_device) ui.bnClose.clicked.connect(close_device) ui.bnStart.clicked.connect(start_grabbing) ui.bnStop.clicked.connect(stop_grabbing) ui.bnSoftwareTrigger.clicked.connect(trigger_once) ui.radioTriggerMode.clicked.connect(set_software_trigger_mode) ui.radioContinueMode.clicked.connect(set_continue_mode) ui.bnGetParam.clicked.connect(get_param) ui.bnSetParam.clicked.connect(set_param) # 修改保存图像按钮连接 ui.bnSaveImage.clicked.connect(save_image_dialog) # 显示主窗口 mainWindow.show() # 执行应用 app.exec_() # 关闭设备 close_device() # ch:反初始化SDK | en: finalize SDK MvCamera.MV_CC_Finalize() sys.exit()
最新发布
07-11
# -*- coding: utf-8 -*- import sys import os import cv2 import numpy as np # 在导入部分添加缺失的模块 from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton, QWidget, QVBoxLayout, QHBoxLayout, QMessageBox, QLabel, QFileDialog, QToolBar, QComboBox, QStatusBar, QGroupBox, QSlider) # 添加 QGroupBox 和 QSlider from PyQt5.QtCore import QRect, Qt, QSettings, QThread, pyqtSignal from CamOperation_class import CameraOperation sys.path.append("D:\\海康\\MVS\\Development\\Samples\\Python\\BasicDemo") from MvCameraControl_class import * from MvErrorDefine_const import * from CameraParams_header import * from PyUICBasicDemo import Ui_MainWindow import ctypes from datetime import datetime import logging import platform # 配置日志系统 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler("cloth_inspection.log"), logging.StreamHandler() ] ) logging.info("布料印花检测系统启动") # 全局变量 current_sample_path = "" # 当前使用的样本路径 detection_history = [] # 检测历史记录 # 帧监控线程 class FrameMonitorThread(QThread): frame_status = pyqtSignal(str) def __init__(self, cam_operation): super().__init__() self.cam_operation = cam_operation self.running = True def run(self): while self.running: if self.cam_operation: status = self.cam_operation.get_frame_status() frame_text = "有帧" if status.get('current_frame', False) else "无帧" self.frame_status.emit(f"帧状态: {frame_text}") QThread.msleep(500) def stop(self): self.running = False # 布料印花检测函数(修复版) def check_print_quality(sample_image_path, test_image, threshold=0.05): """ 检测布料印花是否合格,直接使用内存中的测试图像 :param sample_image_path: 合格样本图像路径 :param test_image: 内存中的测试图像 (numpy数组) :param threshold: 差异阈值 :return: 是否合格,差异值,标记图像 """ # 读取样本图像 try: sample_img_data = np.fromfile(sample_image_path, dtype=np.uint8) sample_image = cv2.imdecode(sample_img_data, cv2.IMREAD_GRAYSCALE) if sample_image is None: logging.error(f"无法解码样本图像: {sample_image_path}") return None, None, None except Exception as e: logging.exception(f"样本图像读取异常: {str(e)}") return None, None, None # 确保测试图像是灰度图 if len(test_image.shape) == 3: # 如果是彩色图像 test_image = cv2.cvtColor(test_image, cv2.COLOR_BGR2GRAY) # 确保两个图像大小一致 try: test_image = cv2.resize(test_image, (sample_image.shape[1], sample_image.shape[0])) except Exception as e: logging.error(f"图像调整大小失败: {str(e)}") return None, None, None # 获取差异度阈值(从滑块获取) diff_threshold = ui.sliderDiffThreshold.value() / 100.0 # 转换为小数 # 执行印花检测(使用新的阈值) try: is_qualified, diff_ratio, marked_image = check_print_quality( current_sample_path, test_image, threshold=diff_threshold # 使用滑块设置的值 ) except Exception as e: QMessageBox.critical(mainWindow, "检测错误", f"检测过程中发生错误: {str(e)}", QMessageBox.Ok) logging.exception("印花检测失败") return # 更新差异度显示 update_diff_display(diff_ratio, is_qualified) # 显示结果 if marked_image is not None: # 显示结果 result_text = f"印花是否合格: {'合格' if is_qualified else '不合格'}\n差异占比: {diff_ratio:.4f}\n阈值: {diff_threshold:.4f}" QMessageBox.information(mainWindow, "检测结果", result_text, QMessageBox.Ok) # 显示标记图像 cv2.imshow("缺陷标记结果", marked_image) cv2.waitKey(0) cv2.destroyAllWindows() # 记录检测结果 detection_result = { 'timestamp': datetime.now(), 'qualified': is_qualified, 'diff_ratio': diff_ratio, 'threshold': diff_threshold } detection_history.append(detection_result) update_history_display() # 更新检测结果显示 def update_diff_display(diff_ratio, is_qualified): """ 更新差异度显示控件 """ # 更新当前差异度显示 ui.lblCurrentDiff.setText(f"当前差异度: {diff_ratio*100:.2f}%") # 根据合格状态设置颜色 if is_qualified: ui.lblDiffStatus.setText("状态: 合格") ui.lblDiffStatus.setStyleSheet("color: green; font-size: 12px;") else: ui.lblDiffStatus.setText("状态: 不合格") ui.lblDiffStatus.setStyleSheet("color: red; font-size: 12px;") # 更新差异度阈值显示 def update_diff_threshold(value): """ 当滑块值改变时更新阈值显示 """ ui.lblDiffValue.setText(f"{value}%") # 布料印花检测功能 def check_print(): global isGrabbing, obj_cam_operation, current_sample_path, detection_history if not isGrabbing: QMessageBox.warning(mainWindow, "错误", "请先开始取流并捕获图像!", QMessageBox.Ok) return # 检查样本路径是否有效 if not current_sample_path or not os.path.exists(current_sample_path): QMessageBox.warning(mainWindow, "错误", "请先设置有效的标准样本图像!", QMessageBox.Ok) return # 直接从相机获取当前帧 try: test_image = obj_cam_operation.get_current_frame() if test_image is None: QMessageBox.warning(mainWindow, "错误", "无法获取当前帧图像!", QMessageBox.Ok) return except Exception as e: QMessageBox.critical(mainWindow, "错误", f"获取图像帧失败: {str(e)}", QMessageBox.Ok) logging.exception("获取图像帧失败") return # 保存标准样本函数 def save_sample_image(): global isGrabbing, obj_cam_operation, current_sample_path if not isGrabbing: QMessageBox.warning(mainWindow, "错误", "请先开始取流并捕获图像!", QMessageBox.Ok) return # 检查是否有有效图像 if not obj_cam_operation.is_frame_available(): QMessageBox.warning(mainWindow, "无有效图像", "未捕获到有效图像,请检查相机状态!", QMessageBox.Ok) return # 读取上次使用的路径 settings = QSettings("ClothInspection", "CameraApp") last_dir = settings.value("last_save_dir", os.path.join(os.getcwd(), "captures")) # 创建默认文件名 timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") default_filename = f"sample_{timestamp}" # 弹出文件保存对话框 file_path, selected_filter = QFileDialog.getSaveFileName( mainWindow, "保存标准样本图像", os.path.join(last_dir, default_filename), "BMP Files (*.bmp);;PNG Files (*.png);;JPEG Files (*.jpg);;所有文件 (*)", options=QFileDialog.DontUseNativeDialog ) if not file_path: logging.info("用户取消了图像保存操作") return # 用户取消保存 # 处理文件扩展名 file_extension = os.path.splitext(file_path)[1].lower() if not file_extension: # 根据选择的过滤器添加扩展名 if "BMP" in selected_filter: file_path += ".bmp" elif "PNG" in selected_filter: file_path += ".png" elif "JPEG" in selected_filter or "JPG" in selected_filter: file_path += ".jpg" else: # 默认使用BMP格式 file_path += ".bmp" file_extension = os.path.splitext(file_path)[1].lower() # 根据扩展名设置保存格式 format_mapping = { ".bmp": "bmp", ".png": "png", ".jpg": "jpg", ".jpeg": "jpg" } save_format = format_mapping.get(file_extension) if not save_format: QMessageBox.warning(mainWindow, "错误", "不支持的文件格式!", QMessageBox.Ok) return # 确保目录存在 directory = os.path.dirname(file_path) if directory and not os.path.exists(directory): try: os.makedirs(directory, exist_ok=True) logging.info(f"创建目录: {directory}") except OSError as e: error_msg = f"无法创建目录 {directory}: {str(e)}" QMessageBox.critical(mainWindow, "目录创建错误", error_msg, QMessageBox.Ok) return # 保存当前帧作为标准样本 try: ret = obj_cam_operation.save_image(file_path, save_format) if ret != MV_OK: strError = f"保存样本图像失败: {hex(ret)}" QMessageBox.warning(mainWindow, "错误", strError, QMessageBox.Ok) else: success_msg = f"标准样本已保存至:\n{file_path}" QMessageBox.information(mainWindow, "成功", success_msg, QMessageBox.Ok) # 更新当前样本路径 current_sample_path = file_path update_sample_display() # 保存当前目录 settings.setValue("last_save_dir", os.path.dirname(file_path)) except Exception as e: error_msg = f"保存图像时发生错误: {str(e)}" QMessageBox.critical(mainWindow, "异常错误", error_msg, QMessageBox.Ok) logging.exception("保存样本图像时发生异常") # 预览当前样本 def preview_sample(): global current_sample_path if not current_sample_path or not os.path.exists(current_sample_path): QMessageBox.warning(mainWindow, "错误", "请先设置有效的标准样本图像!", QMessageBox.Ok) return try: # 使用安全方法读取图像 img_data = np.fromfile(current_sample_path, dtype=np.uint8) sample_img = cv2.imdecode(img_data, cv2.IMREAD_COLOR) if sample_img is None: raise Exception("无法加载图像") cv2.imshow("标准样本预览", sample_img) cv2.waitKey(0) cv2.destroyAllWindows() except Exception as e: QMessageBox.warning(mainWindow, "错误", f"预览样本失败: {str(e)}", QMessageBox.Ok) # 更新样本路径显示 def update_sample_display(): global current_sample_path if current_sample_path: ui.lblSamplePath.setText(f"当前样本: {os.path.basename(current_sample_path)}") ui.lblSamplePath.setToolTip(current_sample_path) ui.bnPreviewSample.setEnabled(True) else: ui.lblSamplePath.setText("当前样本: 未设置样本") ui.bnPreviewSample.setEnabled(False) # 更新历史记录显示 def update_history_display(): global detection_history ui.cbHistory.clear() for i, result in enumerate(detection_history[-10:]): # 显示最近10条记录 timestamp = result['timestamp'].strftime("%H:%M:%S") status = "合格" if result['qualified'] else "不合格" ratio = f"{result['diff_ratio']:.4f}" ui.cbHistory.addItem(f"[{timestamp}] {status} - 差异: {ratio}") # 获取选取设备信息的索引,通过[]之间的字符去解析 def TxtWrapBy(start_str, end, all): start = all.find(start_str) if start >= 0: start += len(start_str) end = all.find(end, start) if end >= 0: return all[start:end].strip() # 将返回的错误码转换为十六进制显示 def ToHexStr(num): """将错误码转换为十六进制字符串""" # 处理非整数输入 if not isinstance(num, int): try: # 尝试转换为整数 num = int(num) except: # 无法转换时返回类型信息 return f"<非整数:{type(num)}>" chaDic = {10: 'a', 11: 'b', 12: 'c', 13: 'd', 14: 'e', 15: 'f'} hexStr = "" # 处理负数 if num < 0: num = num + 2 ** 32 # 转换为十六进制 while num >= 16: digit = num % 16 hexStr = chaDic.get(digit, str(digit)) + hexStr num //= 16 hexStr = chaDic.get(num, str(num)) + hexStr return "0x" + hexStr # ch:初始化SDK | en: initialize SDK MvCamera.MV_CC_Initialize() global deviceList deviceList = MV_CC_DEVICE_INFO_LIST() global cam cam = MvCamera() global nSelCamIndex nSelCamIndex = 0 global obj_cam_operation obj_cam_operation = 0 global isOpen isOpen = False global isGrabbing isGrabbing = False global isCalibMode # 是否是标定模式(获取原始图像) isCalibMode = True global frame_monitor_thread # 绑定下拉列表至设备信息索引 def xFunc(event): global nSelCamIndex nSelCamIndex = TxtWrapBy("[", "]", ui.ComboDevices.get()) # Decoding Characters def decoding_char(c_ubyte_value): c_char_p_value = ctypes.cast(c_ubyte_value, ctypes.c_char_p) try: decode_str = c_char_p_value.value.decode('gbk') # Chinese characters except UnicodeDecodeError: decode_str = str(c_char_p_value.value) return decode_str # ch:枚举相机 | en:enum devices def enum_devices(): global deviceList global obj_cam_operation deviceList = MV_CC_DEVICE_INFO_LIST() n_layer_type = (MV_GIGE_DEVICE | MV_USB_DEVICE | MV_GENTL_CAMERALINK_DEVICE | MV_GENTL_CXP_DEVICE | MV_GENTL_XOF_DEVICE) ret = MvCamera.MV_CC_EnumDevices(n_layer_type, deviceList) if ret != 0: strError = "Enum devices fail! ret = :" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) return ret if deviceList.nDeviceNum == 0: QMessageBox.warning(mainWindow, "Info", "Find no device", QMessageBox.Ok) return ret print("Find %d devices!" % deviceList.nDeviceNum) devList = [] for i in range(0, deviceList.nDeviceNum): mvcc_dev_info = cast(deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE or mvcc_dev_info.nTLayerType == MV_GENTL_GIGE_DEVICE: print("\ngige device: [%d]" % i) user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chUserDefinedName) model_name = decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName) print("device user define name: " + user_defined_name) print("device model name: " + model_name) nip1 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24) nip2 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16) nip3 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8) nip4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff) print("current ip: %d.%d.%d.%d " % (nip1, nip2, nip3, nip4)) devList.append( "[" + str(i) + "]GigE: " + user_defined_name + " " + model_name + "(" + str(nip1) + "." + str( nip2) + "." + str(nip3) + "." + str(nip4) + ")") elif mvcc_dev_info.nTLayerType == MV_USB_DEVICE: print("\nu3v device: [%d]" % i) user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stUsb3VInfo.chUserDefinedName) model_name = decoding_char(mvcc_dev_info.SpecialInfo.stUsb3VInfo.chModelName) print("device user define name: " + user_defined_name) print("device model name: " + model_name) strSerialNumber = "" for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chSerialNumber: if per == 0: break strSerialNumber = strSerialNumber + chr(per) print("user serial number: " + strSerialNumber) devList.append("[" + str(i) + "]USB: " + user_defined_name + " " + model_name + "(" + str(strSerialNumber) + ")") elif mvcc_dev_info.nTLayerType == MV_GENTL_CAMERALINK_DEVICE: print("\nCML device: [%d]" % i) user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stCMLInfo.chUserDefinedName) model_name = decoding_char(mvcc_dev_info.SpecialInfo.stCMLInfo.chModelName) print("device user define name: " + user_defined_name) print("device model name: " + model_name) strSerialNumber = "" for per in mvcc_dev_info.SpecialInfo.stCMLInfo.chSerialNumber: if per == 0: break strSerialNumber = strSerialNumber + chr(per) print("user serial number: " + strSerialNumber) devList.append("[" + str(i) + "]CML: " + user_defined_name + " " + model_name + "(" + str(strSerialNumber) + ")") elif mvcc_dev_info.nTLayerType == MV_GENTL_CXP_DEVICE: print("\nCXP device: [%d]" % i) user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stCXPInfo.chUserDefinedName) model_name = decoding_char(mvcc_dev_info.SpecialInfo.stCXPInfo.chModel极Name) print("device user define name: " + user_defined_name) print("device model name: " + model_name) strSerialNumber = "" for per in mvcc_dev_info.SpecialInfo.stCXPInfo.chSerialNumber: if per == 0: break strSerialNumber = strSerialNumber + chr(per) print("user serial number: "+strSerialNumber) devList.append("[" + str(i) + "]CXP: " + user_defined_name + " " + model_name + "(" + str(strSerialNumber) + ")") elif mvcc_dev_info.nTLayerType == MV_GENTL_XOF_DEVICE: print("\nXoF device: [%d]" % i) user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stXoFInfo.chUserDefinedName) model_name = decoding_char(mvcc_dev_info.SpecialInfo.stXoFInfo.chModelName) print("device user define name: " + user_defined_name) print("device model name: " + model_name) strSerialNumber = "" for per in mvcc_dev_info.SpecialInfo.stXoFInfo.chSerialNumber: if per == 0: break strSerialNumber = strSerialNumber + chr(per) print("user serial number: " + strSerialNumber) devList.append("[" + str(i) + "]XoF: " + user_defined_name + " " + model_name + "(" + str(strSerialNumber) + ")") ui.ComboDevices.clear() ui.ComboDevices.addItems(devList) ui.ComboDevices.setCurrentIndex(0) # ch:打开相机 | en:open device def open_device(): global deviceList global nSelCamIndex global obj_cam_operation global isOpen global frame_monitor_thread if isOpen: QMessageBox.warning(mainWindow, "Error", 'Camera is Running!', QMessageBox.Ok) return MV_E_CALLORDER nSelCamIndex = ui.ComboDevices.currentIndex() if nSelCamIndex < 0: QMessageBox.warning(mainWindow, "Error", 'Please select a camera!', QMessageBox.Ok) return MV_E_CALLORDER obj_cam_operation = CameraOperation(cam, deviceList, nSelCamIndex) ret = obj_cam_operation.open_device() if 0 != ret: strError = "Open device failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) isOpen = False else: set_continue_mode() get_param() isOpen = True enable_controls() # 启动帧监控线程 frame_monitor_thread = FrameMonitorThread(obj_cam_operation) frame_monitor_thread.frame_status.connect(ui.statusBar.showMessage) frame_monitor_thread.start() # ch:开始取流 | en:Start grab image def start_grabbing(): global obj_cam_operation global isGrabbing ret = obj_cam_operation.start_grabbing(ui.widgetDisplay.winId()) if ret != 0: strError = "Start grabbing failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) else: isGrabbing = True enable_controls() # ch:停止取流 | en:Stop grab image def stop_grabbing(): global obj_cam_operation global isGrabbing ret = obj_cam_operation.Stop_grabbing() if ret != 0: strError = "Stop grabbing failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) else: isGrabbing = False enable_controls() # ch:关闭设备 | Close device def close_device(): global isOpen global isGrabbing global obj_cam_operation global frame_monitor_thread # 停止帧监控线程 if frame_monitor_thread and frame_monitor_thread.isRunning(): frame_monitor_thread.stop() frame_monitor_thread.wait(2000) if isOpen: obj_cam_operation.close_device() isOpen = False isGrabbing = False enable_controls() # ch:设置触发模式 | en:set trigger mode def set_continue_mode(): ret = obj_cam_operation.set_trigger_mode(False) if ret != 0: strError = "Set continue mode failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) else: ui.radioContinueMode.setChecked(True) ui.radioTriggerMode.setChecked(False) ui.bnSoftwareTrigger.setEnabled(False) # ch:设置软触发模式 | en:set software trigger mode def set_software_trigger_mode(): ret = obj_cam_operation.set_trigger_mode(True) if ret != 0: strError = "Set trigger mode failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) else: ui.radioContinueMode.setChecked(False) ui.radioTriggerMode.setChecked(True) ui.bnSoftwareTrigger.setEnabled(isGrabbing) # ch:设置触发命令 | en:set trigger software def trigger_once(): ret = obj_cam_operation.trigger_once() if ret != 0: strError = "TriggerSoftware failed ret:" + ToHexStr(ret) QMessageBox.warning(mainWindow, "Error", strError, QMessageBox.Ok) # 保存图像对话框 def save_image_dialog(): """ 打开保存图像对话框并保存当前帧 """ global isGrabbing, obj_cam_operation # 检查相机状态 if not isGrabbing: QMessageBox.warning(mainWindow, "相机未就绪", "请先开始取流并捕获图像!", QMessageBox.Ok) return # 检查是否有有效图像 if not obj_cam_operation.is_frame_available(): QMessageBox.warning(mainWindow, "无有效图像", "未捕获到有效图像,请检查相机状态!", QMessageBox.Ok) return # 读取上次使用的路径 settings = QSettings("ClothInspection", "CameraApp") last_dir = settings.value("last_save_dir", os.path.join(os.getcwd(), "captures")) # 创建默认文件名 timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") default_filename = f"capture_{timestamp}" # 弹出文件保存对话框 file_path, selected_filter = QFileDialog.getSaveFileName( mainWindow, "保存图像", os.path.join(last_dir, default_filename), # 初始路径 "BMP 图像 (*.bmp);;JPEG 图像 (*.jpg);;PNG 图像 (*.png);;TIFF 图像 (*.tiff);;所有文件 (*)", options=QFileDialog.DontUseNativeDialog ) # 用户取消操作 if not file_path: logging.info("用户取消了图像保存操作") return # 处理文件扩展名 file_extension = os.path.splitext(file_path)[1].lower() if not file_extension: # 根据选择的过滤器添加扩展名 if "BMP" in selected_filter: file_path += ".bmp" elif "JPEG" in selected_filter or "JPG" in selected_filter: file_path += ".jpg" elif "PNG" in selected_filter: file_path += ".png" elif "TIFF" in selected_filter: file_path += ".tiff" else: # 默认使用BMP格式 file_path += ".bmp" # 确定保存格式 format_mapping = { ".bmp": "bmp", ".jpg": "jpg", ".jpeg": "jpg", ".png": "png", ".tiff": "tiff", ".tif": "tiff" } file_extension = os.path.splitext(file_path)[1].lower() save_format = format_mapping.get(file_extension, "bmp") # 确保目录存在 directory = os.path.dirname(file_path) if directory and not os.path.exists(directory): try: os.makedirs(directory, exist_ok=True) except OSError as e: QMessageBox.critical(mainWindow, "目录错误", f"无法创建目录:\n{str(e)}", QMessageBox.Ok) return # 保存图像 try: ret = obj_cam_operation.save_image(file_path, save_format) if ret == MV_OK: QMessageBox.information(mainWindow, "保存成功", f"图像已保存至:\n{file_path}", QMessageBox.Ok) logging.info(f"图像保存成功: {file_path}") # 保存当前目录 settings.setValue("last_save_dir", os.path.dirname(file_path)) else: error_msg = f"保存失败! 错误代码: {hex(ret)}" QMessageBox.warning(mainWindow, "保存失败", error_msg, QMessageBox.Ok) logging.error(f"图像保存失败: {file_path}, 错误代码: {hex(ret)}") except Exception as e: QMessageBox.critical(mainWindow, "保存错误", f"保存图像时发生错误:\n{str(e)}", QMessageBox.Ok) logging.exception(f"保存图像时发生异常: {file_path}") def is_float(str): try: float(str) return True except ValueError: return False # ch: 获取参数 | en:get param def get_param(): try: # 调用方法获取参数 ret = obj_cam_operation.get_parameters() # 记录调用结果(调试用) logging.debug(f"get_param() 返回: {ret} (类型: {type(ret)})") # 处理错误码 if ret != MV_OK: strError = "获取参数失败,错误码: " + ToHexStr(ret) QMessageBox.warning(mainWindow, "错误", strError, QMessageBox.Ok) else: # 成功获取参数后更新UI ui.edtExposureTime.setText("{0:.2f}".format(obj_cam_operation.exposure_time)) ui.edtGain.setText("{0:.2f}".format(obj_cam_operation.gain)) ui.edtFrameRate.setText("{0:.2f}".format(obj_cam_operation.frame_rate)) # 记录成功信息 logging.info("成功获取相机参数") except Exception as e: # 处理所有异常 error_msg = f"获取参数时发生错误: {str(e)}" logging.error(error_msg) QMessageBox.critical(mainWindow, "严重错误", error_msg, QMessageBox.Ok) # ch: 设置参数 | en:set param def set_param(): frame_rate = ui.edtFrameRate.text() exposure = ui.edtExposureTime.text() gain = ui.edtGain.text() if not (is_float(frame_rate) and is_float(exposure) and is_float(gain)): strError = "设置参数失败: 参数必须是有效的浮点数" QMessageBox.warning(mainWindow, "错误", strError, QMessageBox.Ok) return MV_E_PARAMETER try: # 使用正确的参数顺序和关键字 ret = obj_cam_operation.set_param( frame_rate=float(frame_rate), exposure_time=float(exposure), gain=float(gain) ) if ret != MV_OK: strError = "设置参数失败,错误码: " + ToHexStr(ret) QMessageBox.warning(mainWindow, "错误", strError, QMessageBox.Ok) else: logging.info("参数设置成功") return MV_OK except Exception as e: error_msg = f"设置参数时发生错误: {str(e)}" logging.error(error_msg) QMessageBox.critical(mainWindow, "严重错误", error_msg, QMessageBox.Ok) return MV_E_STATE # ch: 设置控件状态 | en:set enable status def enable_controls(): global isGrabbing global isOpen # 先设置group的状态,再单独设置各控件状态 ui.groupGrab.setEnabled(isOpen) ui.groupParam.setEnabled(isOpen) ui.bnOpen.setEnabled(not isOpen) ui.bnClose.setEnabled(isOpen) ui.bnStart.setEnabled(isOpen and (not isGrabbing)) ui.bnStop.setEnabled(isOpen and isGrabbing) ui.bnSoftwareTrigger.setEnabled(isGrabbing and ui.radioTriggerMode.isChecked()) ui.bnSaveImage.setEnabled(isOpen and isGrabbing) # 添加检测按钮控制 ui.bnCheckPrint.setEnabled(isOpen and isGrabbing) ui.bnSaveSample.setEnabled(isOpen and isGrabbing) ui.bnPreviewSample.setEnabled(bool(current_sample_path)) # ... [前面的代码保持不变] ... if __name__ == "__main__": # ch:初始化SDK | en: initialize SDK MvCamera.MV_CC_Initialize() deviceList = MV_CC_DEVICE_INFO_LIST() cam = MvCamera() nSelCamIndex = 0 obj_cam_operation = 0 isOpen = False isGrabbing = False isCalibMode = True # 是否是标定模式(获取原始图像) frame_monitor_thread = None # 初始化UI app = QApplication(sys.argv) mainWindow = QMainWindow() ui = Ui_MainWindow() ui.setupUi(mainWindow) # 扩大主窗口尺寸 mainWindow.resize(1200, 800) # 宽度1200,高度800 # 创建工具栏 toolbar = mainWindow.addToolBar("检测工具") # 添加检测按钮 ui.bnCheckPrint = QPushButton("检测印花质量") toolbar.addWidget(ui.bnCheckPrint) # 添加保存样本按钮 ui.bnSaveSample = QPushButton("保存标准样本") toolbar.addWidget(ui.bnSaveSample) # 添加预览样本按钮 ui.bnPreviewSample = QPushButton("预览样本") toolbar.addWidget(ui.bnPreviewSample) # 添加历史记录下拉框 ui.cbHistory = QComboBox() ui.cbHistory.setMinimumWidth(300) toolbar.addWidget(QLabel("历史记录:")) toolbar.addWidget(ui.cbHistory) # 添加当前样本显示标签 ui.lblSamplePath = QLabel("当前样本: 未设置样本") status_bar = mainWindow.statusBar() status_bar.addPermanentWidget(ui.lblSamplePath) # === 新增差异度调整控件 === # 创建右侧面板容器 right_panel = QWidget() right_layout = QVBoxLayout(right_panel) right_layout.setContentsMargins(10, 10, 10, 10) # 创建差异度调整组 diff_group = QGroupBox("差异度调整") diff_layout = QVBoxLayout(diff_group) # 差异度阈值控制 ui.lblDiffThreshold = QLabel("差异度阈值 (0-100%):") ui.sliderDiffThreshold = QSlider(Qt.Horizontal) ui.sliderDiffThreshold.setRange(0, 100) # 0-100% ui.sliderDiffThreshold.setValue(5) # 默认5% ui.lblDiffValue = QLabel("5%") # 当前差异度显示 ui.lblCurrentDiff = QLabel("当前差异度: -") ui.lblCurrentDiff.setStyleSheet("font-size: 14px; font-weight: bold;") # 差异度状态指示器 ui.lblDiffStatus = QLabel("状态: 未检测") ui.lblDiffStatus.setStyleSheet("font-size: 12px;") # 布局控件 diff_layout.addWidget(ui.lblDiffThreshold) diff_layout.addWidget(ui.sliderDiffThreshold) diff_layout.addWidget(ui.lblDiffValue) diff_layout.addWidget(ui.lblCurrentDiff) diff_layout.addWidget(ui.lblDiffStatus) # 添加差异度组到右侧布局 right_layout.addWidget(diff_group) # 添加拉伸项使控件靠上 right_layout.addStretch(1) # 添加到主布局 main_layout = QHBoxLayout(mainWindow.centralWidget()) main_layout.addWidget(ui.widgetDisplay) # 保留原始显示区域 main_layout.addWidget(right_panel) # 添加右侧面板 # 设置右侧面板宽度 main_layout.setStretch(0, 3) # 显示区域占3份 main_layout.setStretch(1, 1) # 右侧面板占1份 # === 差异度调整功能实现 === # 更新差异度阈值显示 def update_diff_threshold(value): ui.lblDiffValue.setText(f"{value}%") # 连接滑块信号 ui.sliderDiffThreshold.valueChanged.connect(update_diff_threshold) # 更新检测结果显示 def update_diff_display(diff_ratio, is_qualified): # 更新当前差异度显示 ui.lblCurrentDiff.setText(f"当前差异度: {diff_ratio*100:.2f}%") # 根据合格状态设置颜色 if is_qualified: ui.lblDiffStatus.setText("状态: 合格") ui.lblDiffStatus.setStyleSheet("color: green; font-size: 12px;") else: ui.lblDiffStatus.setText("状态: 不合格") ui.lblDiffStatus.setStyleSheet("color: red; font-size: 12px;") # 绑定按钮事件 ui.bnCheckPrint.clicked.connect(check_print) ui.bnSaveSample.clicked.connect(save_sample_image) ui.bnPreviewSample.clicked.connect(preview_sample) # 绑定其他按钮事件 ui.bnEnum.clicked.connect(enum_devices) ui.bnOpen.clicked.connect(open_device) ui.bnClose.clicked.connect(close_device) ui.bnStart.clicked.connect(start_grabbing) ui.bnStop.clicked.connect(stop_grabbing) ui.bnSoftwareTrigger.clicked.connect(trigger_once) ui.radioTriggerMode.clicked.connect(set_software_trigger_mode) ui.radioContinueMode.clicked.connect(set_continue_mode) ui.bnGetParam.clicked.connect(get_param) ui.bnSetParam.clicked.connect(set_param) # 修改保存图像按钮连接 ui.bnSaveImage.clicked.connect(save_image_dialog) # 显示主窗口 mainWindow.show() # 执行应用 app.exec_() # 关闭设备 close_device() # ch:反初始化SDK | en: finalize SDK MvCamera.MV_CC_Finalize() sys.exit() 将你刚刚给出的解决方案结合进这个完整的代码,并完整将优化后的代码展示出来
07-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值