# -*- coding: utf-8 -*-
import sys
import os
import cv2
import numpy as np
import json
import time
import logging
import platform
from datetime import datetime
from scipy import ndimage
from scipy.spatial import distance
# PyQt5 导入
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QPushButton, QWidget,
QVBoxLayout, QHBoxLayout, QMessageBox, QLabel,
QFileDialog, QToolBar, QComboBox, QStatusBar,
QGroupBox, QSlider, QDockWidget, QProgressDialog,
QCheckBox # 添加 QCheckBox
)
from PyQt5.QtCore import QRect, Qt, QSettings, QThread, pyqtSignal
from PyQt5.QtGui import QImage, QPixmap # 可能需要这些用于图像显示
# 海康 SDK 导入
sys.path.append("D:\\海康\\MVS\\Development\\Samples\\Python\\BasicDemo")
from MvCameraControl_class import *
from MvErrorDefine_const import *
from CameraParams_header import *
# 自定义模块导入
from CamOperation_class import CameraOperation
from PyUICBasicDemo import Ui_MainWindow # 如果使用 UI 文件生成
# 图像处理库导入
try:
from skimage.feature import local_binary_pattern
from skimage import exposure
except ImportError:
# 提供替代方案或错误处理
pass
# 配置日志系统
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 = [] # 检测历史记录
sample_features = {} # 存储样本特征数据
config_file = "detection_config.json" # 配置文件路径
# 加载配置文件
def load_config():
global config_file
if os.path.exists(config_file):
try:
with open(config_file, 'r') as f:
return json.load(f)
except Exception as e:
logging.error(f"加载配置文件失败: {str(e)}")
return {}
return {}
# 保存配置文件
def save_config(config):
global config_file
try:
with open(config_file, 'w') as f:
json.dump(config, f, indent=4)
logging.info("配置文件已保存")
except Exception as e:
logging.error(f"保存配置文件失败: {str(e)}")
# 初始化配置
default_config = {
"threshold": 0.05,
"min_defect_area": 50,
"max_defect_area": 5000,
"texture_analysis": True,
"color_sensitivity": 0.8,
"edge_detection": True,
"adaptive_threshold": True,
"multi_scale_levels": 3,
"feature_extraction": "histogram", # histogram, lbp, sift, orb
"defect_classification": True
}
app_config = {**default_config, **load_config()}
# 帧监控线程
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 extract_features(image):
"""
提取图像的多尺度特征
:param image: 输入图像 (灰度或彩色)
:return: 特征字典
"""
features = {}
# 确保图像是灰度图
if len(image.shape) == 3:
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
else:
gray = image
# 1. 直方图特征
hist = cv2.calcHist([gray], [0], None, [256], [0, 256])
hist = cv2.normalize(hist, hist).flatten()
features['histogram'] = hist.tolist()
# 2. LBP纹理特征 (局部二值模式)
radius = 3
n_points = 8 * radius
lbp = local_binary_pattern(gray, n_points, radius, method='uniform')
lbp_hist, _ = np.histogram(lbp.ravel(), bins=np.arange(0, n_points + 3), range=(0, n_points + 2))
lbp_hist = lbp_hist.astype("float")
lbp_hist /= (lbp_hist.sum() + 1e-7) # 归一化
features['lbp'] = lbp_hist.tolist()
# 3. 颜色特征 (如果是彩色图像)
if len(image.shape) == 3:
# 在HSV空间中计算颜色直方图
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
h_hist = cv2.calcHist([hsv], [0], None, [180], [0, 180])
s_hist = cv2.calcHist([hsv], [1], None, [256], [0, 256])
v_hist = cv2.calcHist([hsv], [2], None, [256], [0, 256])
h_hist = cv2.normalize(h_hist, h_hist).flatten()
s_hist = cv2.normalize(s_hist, s_hist).flatten()
v_hist = cv2.normalize(v_hist, v_hist).flatten()
features['h_hist'] = h_hist.tolist()
features['s_hist'] = s_hist.tolist()
features['v_hist'] = v_hist.tolist()
# 4. 边缘特征
edges = cv2.Canny(gray, 100, 200)
edge_density = np.sum(edges > 0) / (edges.shape[0] * edges.shape[1])
features['edge_density'] = edge_density
# 5. 多尺度分析
scales = [1.0, 0.5, 0.25]
scale_features = []
for scale in scales:
scaled = cv2.resize(gray, None, fx=scale, fy=scale)
# 计算小波变换 (近似)
coeffs = cv2.dct(np.float32(scaled)/255.0)
coeffs_flat = coeffs[:10, :10].flatten()
scale_features.extend(coeffs_flat.tolist())
features['multi_scale'] = scale_features
return features
def compute_feature_distance(features1, features2):
"""
计算两个特征集之间的距离
:param features1: 特征集1
:param features2: 特征集2
:return: 距离分数 (0-1, 0表示完全相同)
"""
# 直方图距离 (卡方距离)
hist_dist = distance.chisquare(features1['histogram'], features2['histogram'])[0] / 1000
# LBP距离 (巴氏距离)
lbp_dist = distance.bhattacharyya(features1['lbp'], features2['lbp'])
# 颜色距离 (如果存在)
color_dist = 0
if 'h_hist' in features1 and 'h_hist' in features2:
h_dist = distance.chisquare(features1['h_hist'], features2['h_hist'])[0] / 1000
s_dist = distance.chisquare(features1['s_hist'], features2['s_hist'])[0] / 1000
v_dist = distance.chisquare(features1['v_hist'], features2['v_hist'])[0] / 1000
color_dist = (h_dist + s_dist + v_dist) / 3
# 边缘密度差异
edge_dist = abs(features1['edge_density'] - features2['edge_density'])
# 多尺度特征差异 (欧氏距离)
scale_dist = distance.euclidean(features1['multi_scale'], features2['multi_scale']) / 100
# 加权组合距离
weights = {
'hist': 0.3,
'lbp': 0.3,
'color': 0.2,
'edge': 0.1,
'scale': 0.1
}
total_dist = (
weights['hist'] * hist_dist +
weights['lbp'] * lbp_dist +
weights['color'] * color_dist +
weights['edge'] * edge_dist +
weights['scale'] * scale_dist
)
# 归一化到0-1范围
total_dist = min(1.0, max(0.0, total_dist))
return total_dist
def adaptive_threshold_diff(sample_gray, test_gray):
"""
自适应阈值差异检测
:param sample_gray: 样本灰度图像
:param test_gray: 测试灰度图像
:return: 二值差异图像
"""
# 计算绝对差异
diff = cv2.absdiff(sample_gray, test_gray)
# 自适应阈值
block_size = max(11, min(sample_gray.shape[0] // 20, 51))
adaptive_thresh = cv2.adaptiveThreshold(
diff, 255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY,
block_size,
5
)
# 形态学操作增强缺陷区域
kernel = np.ones((3, 3), np.uint8)
enhanced = cv2.morphologyEx(adaptive_thresh, cv2.MORPH_CLOSE, kernel)
enhanced = cv2.morphologyEx(enhanced, cv2.MORPH_OPEN, kernel)
return enhanced
def detect_defects(sample_gray, test_gray, diff_map):
"""
检测并分类缺陷
:param sample_gray: 样本灰度图像
:param test_gray: 测试灰度图像
:param diff_map: 差异图
:return: 缺陷列表, 标记图像
"""
# 查找连通区域
num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(diff_map, connectivity=8)
defects = []
marked_image = cv2.cvtColor(test_gray, cv2.COLOR_GRAY2BGR)
# 最小和最大缺陷面积
min_area = app_config.get("min_defect_area", 50)
max_area = app_config.get("max_defect_area", 5000)
for i in range(1, num_labels): # 跳过背景
area = stats[i, cv2.CC_STAT_AREA]
# 过滤太小或太大的区域
if area < min_area or area > max_area:
continue
# 获取缺陷区域
x, y, w, h = stats[i, cv2.CC_STAT_LEFT], stats[i, cv2.CC_STAT_TOP], stats[i, cv2.CC_STAT_WIDTH], stats[i, cv2.CC_STAT_HEIGHT]
defect_roi = diff_map[y:y+h, x:x+w]
# 计算缺陷特征
sample_roi = sample_gray[y:y+h, x:x+w]
test_roi = test_gray[y:y+h, x:x+w]
# 缺陷类型分类
defect_type = classify_defect(sample_roi, test_roi, defect_roi)
# 保存缺陷信息
defects.append({
"type": defect_type,
"area": area,
"location": (x, y, w, h),
"centroid": (int(centroids[i][0]), int(centroids[i][1]))
})
# 在标记图像上绘制
color = (0, 0, 255) # 默认红色
if defect_type == "color":
color = (0, 165, 255) # 橙色
elif defect_type == "texture":
color = (0, 255, 255) # 黄色
elif defect_type == "missing":
color = (255, 0, 0) # 蓝色
elif defect_type == "stain":
color = (0, 255, 0) # 绿色
cv2.rectangle(marked_image, (x, y), (x+w, y+h), color, 2)
cv2.putText(marked_image, f"{defect_type}", (x, y-5),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1)
return defects, marked_image
def classify_defect(sample_roi, test_roi, defect_mask):
"""
分类缺陷类型
:param sample_roi: 样本区域
:param test_roi: 测试区域
:param defect_mask: 缺陷掩码
:return: 缺陷类型 (color, texture, missing, stain)
"""
# 计算颜色差异
color_diff = np.mean(np.abs(sample_roi.astype(np.float32) - test_roi.astype(np.float32)))
# 计算纹理差异 (使用LBP)
radius = 2
n_points = 8 * radius
lbp_sample = local_binary_pattern(sample_roi, n_points, radius, method='uniform')
lbp_test = local_binary_pattern(test_roi, n_points, radius, method='uniform')
hist_sample, _ = np.histogram(lbp_sample.ravel(), bins=np.arange(0, n_points + 3), range=(0, n_points + 2))
hist_test, _ = np.histogram(lbp_test.ravel(), bins=np.arange(0, n_points + 3), range=(0, n_points + 2))
hist_sample = hist_sample.astype("float")
hist_test = hist_test.astype("float")
hist_sample /= (hist_sample.sum() + 1e-7)
hist_test /= (hist_test.sum() + 1e-7)
texture_diff = distance.chisquare(hist_sample, hist_test)[0]
# 计算边缘密度差异
edges_sample = cv2.Canny(sample_roi, 50, 150)
edges_test = cv2.Canny(test_roi, 50, 150)
edge_density_sample = np.sum(edges_sample > 0) / edges_sample.size
edge_density_test = np.sum(edges_test > 0) / edges_test.size
edge_diff = abs(edge_density_sample - edge_density_test)
# 根据特征分类
if color_diff > 30 and texture_diff < 10:
return "color" # 颜色缺陷
elif texture_diff > 15 and color_diff < 20:
return "texture" # 纹理缺陷
elif edge_diff > 0.1 and np.mean(test_roi[defect_mask > 0]) < np.mean(sample_roi[defect_mask > 0]) - 20:
return "missing" # 缺失图案
else:
return "stain" # 污渍
def check_print_quality(sample_image_path, test_image, threshold=None):
"""
增强版布料印花检测算法
:param sample_image_path: 合格样本图像路径
:param test_image: 内存中的测试图像 (numpy数组)
:param threshold: 差异阈值
:return: 是否合格, 差异值, 标记图像, 缺陷列表
"""
global sample_features, app_config
# 使用配置中的阈值
if threshold is None:
threshold = app_config.get("threshold", 0.05)
# 读取样本图像
try:
sample_img_data = np.fromfile(sample_image_path, dtype=np.uint8)
sample_image = cv2.imdecode(sample_img_data, cv2.IMREAD_COLOR)
if sample_image is None:
logging.error(f"无法解码样本图像: {sample_image_path}")
return None, None, None, None
except Exception as e:
logging.exception(f"样本图像读取异常: {str(e)}")
return None, None, None, None
# 确保测试图像是彩色
if len(test_image.shape) == 2: # 如果是灰度图像
test_image = cv2.cvtColor(test_image, cv2.COLOR_GRAY2BGR)
# 确保两个图像大小一致
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, None
# 特征提取和比较
if sample_image_path not in sample_features:
logging.info(f"提取样本特征: {sample_image_path}")
sample_features[sample_image_path] = extract_features(sample_image)
test_features = extract_features(test_image)
feature_diff = compute_feature_distance(sample_features[sample_image_path], test_features)
# 转换为灰度图像进行像素级比较
sample_gray = cv2.cvtColor(sample_image, cv2.COLOR_BGR2GRAY)
test_gray = cv2.cvtColor(test_image, cv2.COLOR_BGR2GRAY)
# 自适应阈值差异检测
diff_map = adaptive_threshold_diff(sample_gray, test_gray)
# 计算差异比例
diff_pixels = np.count_nonzero(diff_map)
total_pixels = sample_gray.size
pixel_diff_ratio = diff_pixels / total_pixels
# 综合差异分数
combined_diff = 0.7 * feature_diff + 0.3 * pixel_diff_ratio
# 检测和分类缺陷
defects, marked_image = detect_defects(sample_gray, test_gray, diff_map)
# 判断是否合格
is_qualified = combined_diff <= threshold and len(defects) == 0
return is_qualified, combined_diff, marked_image, defects
# ======================== UI更新函数 ========================
def update_diff_display(diff_ratio, is_qualified, defects):
"""
更新差异度显示控件
"""
# 更新当前差异度显示
ui.lblCurrentDiff.setText(f"当前差异度: {diff_ratio*100:.2f}%")
# 显示缺陷数量
defect_count = len(defects)
ui.lblDefectCount.setText(f"缺陷数量: {defect_count}")
# 根据合格状态设置颜色
if is_qualified:
ui.lblDiffStatus.setText("状态: 合格")
ui.lblDiffStatus.setStyleSheet("color: green; font-size: 12px;")
ui.lblDefectCount.setStyleSheet("color: green;")
else:
ui.lblDiffStatus.setText("状态: 不合格")
ui.lblDiffStatus.setStyleSheet("color: red; font-size: 12px;")
ui.lblDefectCount.setStyleSheet("color: red;")
def update_diff_threshold(value):
"""
当滑块值改变时更新阈值显示
"""
global app_config
app_config["threshold"] = value / 100.0
ui.lblDiffValue.setText(f"{value}%")
save_config(app_config)
def update_min_defect_area(value):
"""
更新最小缺陷面积阈值
"""
global app_config
app_config["min_defect_area"] = value
ui.lblMinAreaValue.setText(f"{value} px²")
save_config(app_config)
def update_max_defect_area(value):
"""
更新最大缺陷面积阈值
"""
global app_config
app_config["max_defect_area"] = value
ui.lblMaxAreaValue.setText(f"{value} px²")
save_config(app_config)
def toggle_texture_analysis(state):
"""
切换纹理分析功能
"""
global app_config
app_config["texture_analysis"] = (state == Qt.Checked)
save_config(app_config)
def toggle_edge_detection(state):
"""
切换边缘检测功能
"""
global app_config
app_config["edge_detection"] = (state == Qt.Checked)
save_config(app_config)
def toggle_adaptive_threshold(state):
"""
切换自适应阈值功能
"""
global app_config
app_config["adaptive_threshold"] = (state == Qt.Checked)
save_config(app_config)
def update_color_sensitivity(value):
"""
更新颜色敏感度
"""
global app_config
app_config["color_sensitivity"] = value / 100.0
ui.lblColorSensitivityValue.setText(f"{value}%")
save_config(app_config)
# 布料印花检测功能(增强版)
def check_print():
global isGrabbing, obj_cam_operation, current_sample_path, detection_history, app_config
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 = app_config.get("threshold", 0.05)
logging.info(f"使用差异度阈值: {diff_threshold}")
progress.setValue(50)
# 6. 执行检测
is_qualified, diff_ratio, marked_image, defects = 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}, 缺陷数={len(defects)}")
progress.setValue(90)
# 7. 更新UI
update_diff_display(diff_ratio, is_qualified, defects)
# 显示详细结果
result_text = f"印花是否合格: {'合格' if is_qualified else '不合格'}\n"
result_text += f"综合差异度: {diff_ratio*100:.2f}%\n"
result_text += f"缺陷数量: {len(defects)}\n"
result_text += f"阈值: {diff_threshold*100:.2f}%"
if not is_qualified and defects:
result_text += "\n\n缺陷详情:"
for i, defect in enumerate(defects[:3]): # 最多显示3个主要缺陷
result_text += f"\n{i+1}. {defect['type']} (大小: {defect['area']}像素)"
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,
'defects': defects,
'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, sample_features
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()
# 提取并保存样本特征
sample_img = cv2.imread(file_path)
if sample_img is not None:
sample_features[file_path] = extract_features(sample_img)
logging.info(f"样本特征已提取并保存: {file_path}")
# 保存当前目录
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("无法加载图像")
# 显示特征信息
if current_sample_path in sample_features:
features = sample_features[current_sample_path]
edge_density = features.get('edge_density', 0)
feature_info = f"边缘密度: {edge_density:.4f}\n特征点: {len(features.get('multi_scale', []))}"
cv2.putText(sample_img, feature_info, (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
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}%"
defects = len(result.get('defects', []))
ui.cbHistory.addItem(f"[{timestamp}] {status} - 差异: {ratio} - 缺陷: {defects}")
# ... [其余原有函数保持不变,如设备枚举、相机操作等] ...
if __name__ == "__main__":
# ... [原有初始化代码] ...
# 初始化UI
app = QApplication(sys.argv)
mainWindow = QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(mainWindow)
# 扩大主窗口尺寸
mainWindow.resize(1400, 900) # 更宽的界面
# 创建工具栏
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(350)
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(int(app_config.get("threshold", 0.05) * 100))
ui.lblDiffValue = QLabel(f"{ui.sliderDiffThreshold.value()}%")
# 当前差异度显示
ui.lblCurrentDiff = QLabel("当前差异度: -")
ui.lblCurrentDiff.setStyleSheet("font-size: 14px; font-weight: bold;")
# 缺陷数量显示
ui.lblDefectCount = QLabel("缺陷数量: -")
ui.lblDefectCount.setStyleSheet("font-size: 14px;")
# 差异度状态指示器
ui.lblDiffStatus = QLabel("状态: 未检测")
ui.lblDiffStatus.setStyleSheet("font-size: 12px;")
# 添加分隔线
diff_layout.addWidget(QLabel(" "))
diff_layout.addWidget(QLabel("缺陷检测参数"))
# 最小缺陷面积
ui.lblMinArea = QLabel("最小缺陷面积:")
ui.sliderMinArea = QSlider(Qt.Horizontal)
ui.sliderMinArea.setRange(10, 500)
ui.sliderMinArea.setValue(app_config.get("min_defect_area", 50))
ui.lblMinAreaValue = QLabel(f"{ui.sliderMinArea.value()} px²")
# 最大缺陷面积
ui.lblMaxArea = QLabel("最大缺陷面积:")
ui.sliderMaxArea = QSlider(Qt.Horizontal)
ui.sliderMaxArea.setRange(1000, 10000)
ui.sliderMaxArea.setValue(app_config.get("max_defect_area", 5000))
ui.lblMaxAreaValue = QLabel(f"{ui.sliderMaxArea.value()} px²")
# 颜色敏感度
ui.lblColorSensitivity = QLabel("颜色敏感度:")
ui.sliderColorSensitivity = QSlider(Qt.Horizontal)
ui.sliderColorSensitivity.setRange(0, 100)
ui.sliderColorSensitivity.setValue(int(app_config.get("color_sensitivity", 0.8) * 100))
ui.lblColorSensitivityValue = QLabel(f"{ui.sliderColorSensitivity.value()}%")
# 添加分隔线
diff_layout.addWidget(QLabel(" "))
diff_layout.addWidget(QLabel("高级分析选项"))
# 纹理分析开关
ui.chkTextureAnalysis = QCheckBox("启用纹理分析")
ui.chkTextureAnalysis.setChecked(app_config.get("texture_analysis", True))
# 边缘检测开关
ui.chkEdgeDetection = QCheckBox("启用边缘检测")
ui.chkEdgeDetection.setChecked(app_config.get("edge_detection", True))
# 自适应阈值开关
ui.chkAdaptiveThreshold = QCheckBox("使用自适应阈值")
ui.chkAdaptiveThreshold.setChecked(app_config.get("adaptive_threshold", True))
# 布局控件
diff_layout.addWidget(ui.lblDiffThreshold)
diff_layout.addWidget(ui.sliderDiffThreshold)
diff_layout.addWidget(ui.lblDiffValue)
diff_layout.addWidget(ui.lblCurrentDiff)
diff_layout.addWidget(ui.lblDefectCount)
diff_layout.addWidget(ui.lblDiffStatus)
diff_layout.addWidget(ui.lblMinArea)
diff_layout.addWidget(ui.sliderMinArea)
diff_layout.addWidget(ui.lblMinAreaValue)
diff_layout.addWidget(ui.lblMaxArea)
diff_layout.addWidget(ui.sliderMaxArea)
diff_layout.addWidget(ui.lblMaxAreaValue)
diff_layout.addWidget(ui.lblColorSensitivity)
diff_layout.addWidget(ui.sliderColorSensitivity)
diff_layout.addWidget(ui.lblColorSensitivityValue)
diff_layout.addWidget(ui.chkTextureAnalysis)
diff_layout.addWidget(ui.chkEdgeDetection)
diff_layout.addWidget(ui.chkAdaptiveThreshold)
# 添加拉伸项使控件靠上
diff_layout.addStretch(1)
# 创建停靠窗口
dock = QDockWidget("检测参数面板", mainWindow)
dock.setWidget(right_panel)
dock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable)
mainWindow.addDockWidget(Qt.RightDockWidgetArea, dock)
# === 连接信号 ===
# 差异度阈值滑块
ui.sliderDiffThreshold.valueChanged.connect(update_diff_threshold)
# 最小缺陷面积滑块
ui.sliderMinArea.valueChanged.connect(update_min_defect_area)
# 最大缺陷面积滑块
ui.sliderMaxArea.valueChanged.connect(update_max_defect_area)
# 颜色敏感度滑块
ui.sliderColorSensitivity.valueChanged.connect(update_color_sensitivity)
# 分析选项复选框
ui.chkTextureAnalysis.stateChanged.connect(toggle_texture_analysis)
ui.chkEdgeDetection.stateChanged.connect(toggle_edge_detection)
ui.chkAdaptiveThreshold.stateChanged.connect(toggle_adaptive_threshold)
# 绑定按钮事件
ui.bnCheckPrint.clicked.connect(check_print)
ui.bnSaveSample.clicked.connect(save_sample_image)
ui.bnPreviewSample.clicked.connect(preview_sample)
# ... [其余原有绑定代码] ...
# 显示主窗口
mainWindow.show()
# 执行应用
app.exec_()
# 关闭设备
close_device()
# ch:反初始化SDK | en: finalize SDK
MvCamera.MV_CC_Finalize()
sys.exit()
这个程序运行后出现了查找不到打不开相机同时原有的差异度检查还有新增的功能都看不到了
最新发布