当前位置: 首页 > news >正文

偷懒工具9-工业视觉图像自动筛选工具:基于PyQt5整合清晰度与拖影识别逻辑

一、 痛点背景

在工业视觉检测、AGV路径监控或半导体外观检查等场景中,我们经常会采集到成千上万张图片。然而,受限于光源环境或相机高速运动,产生的失焦模糊(Out of Focus)运动拖影(Motion Blur)会导致后续的 AI 模型(如 YOLO 或 OCR)识别率大幅下降。

为了提高数据标注质量,我开发了一款基于PyQt5的轻量化桌面工具,能够一键自动化筛选剔除“废图”。

二、 核心算法逻辑

本工具集成了两种主流的图像质量评价算法,支持手动开启或关闭:

  1. 清晰度筛选(Laplacian 算子)

    • 原理:利用拉普拉斯算子提取图像的高频成分,计算其方差。

    • 适用场景:过滤掉因焦距没对准而产生的整体模糊图片。

  2. 拖影筛选(Sobel 梯度比值法)

    • 原理:分别计算 X 方向和 Y 方向的梯度能量。如果某个方向的能量远超另一方向(Ratio 值大),则判定为明显的运动拖影。

    • 适用场景:过滤相机或被测物高速移动产生的拖影。

三、 工具功能亮点
  • 可视化界面:基于 PyQt5 设计,支持 Dark Tech 工业暗色系风格。

  • 多线程处理:图像算法在后台线程运行,处理万级像素图片界面不卡死。

  • 灵活配置:支持自定义输入文件夹、导出文件夹,并可实时调整过滤阈值。

  • 实时日志:详细记录每一张图片的 OK/NG 状态及其具体的物理指标(Score/Energy/Ratio)。

四、 核心代码实现展示
import sys import os import cv2 import shutil import numpy as np from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QFileDialog, QLabel, QCheckBox, QLineEdit, QTextEdit, QProgressBar, QFrame, QGroupBox) from PyQt5.QtCore import Qt, QThread, pyqtSignal class FilterThread(QThread): progress_update = pyqtSignal(int) log_update = pyqtSignal(str) finished = pyqtSignal(int, int) def __init__(self, input_dir, output_dir, use_clarity, use_motion, clarity_th, energy_th, ratio_th): super().__init__() self.input_dir = input_dir self.output_dir = output_dir self.use_clarity = use_clarity self.use_motion = use_motion self.clarity_th = clarity_th self.energy_th = energy_th self.ratio_th = ratio_th def run(self): files = [f for f in os.listdir(self.input_dir) if f.lower().endswith(('.jpg', '.png', '.jpeg', '.bmp', '.tif'))] total = len(files) success_count = 0 if not os.path.exists(self.output_dir): os.makedirs(self.output_dir) for i, filename in enumerate(files): file_path = os.path.join(self.input_dir, filename) img = cv2.imread(file_path) if img is None: continue passed = True log_detail = "" # 功能 1: 清晰度 (Laplacian) if self.use_clarity: gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) score = cv2.Laplacian(gray, cv2.CV_64F).var() if score < self.clarity_th: passed = False log_detail = f" [模糊: {score:.1f}]" # 功能 2: 拖影 (Sobel Ratio) if self.use_motion and passed: gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3) sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3) mean_x = np.mean(np.abs(sobelx)) mean_y = np.mean(np.abs(sobely)) total_energy = mean_x + mean_y ratio = max(mean_x, mean_y) / (min(mean_x, mean_y) + 0.001) if total_energy < self.energy_th or ratio > self.ratio_th: passed = False log_detail = f" [拖影/低能 E:{total_energy:.1f} R:{ratio:.2f}]" if passed: shutil.copy(file_path, os.path.join(self.output_dir, filename)) success_count += 1 self.log_update.emit(f"OK: {filename}") else: self.log_update.emit(f"NG: {filename}{log_detail}") self.progress_update.emit(int((i + 1) / total * 100)) self.finished.emit(total, success_count) class ImageFilterApp(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): self.setWindowTitle("图像筛选工具 - 路径导出版") self.resize(800, 650) # 保持你喜欢的 Dark Tech 风格 self.setStyleSheet(""" QWidget { background-color: #1A1A1A; color: #DCDCDC; font-family: 'Consolas', '微软雅黑'; } QPushButton { background-color: #333333; border: 1px solid #555; padding: 5px; min-height: 25px; } QPushButton:hover { background-color: #444444; border-color: #00FF41; } QLineEdit { background-color: #252525; border: 1px solid #444; padding: 3px; color: #00FF41; } QGroupBox { border: 1px solid #444; margin-top: 10px; padding-top: 10px; font-weight: bold; } """) layout = QVBoxLayout() # --- 1. 路径选择区域 (醒目化处理) --- path_box = QGroupBox("路径配置") path_layout = QVBoxLayout(path_box) # 输入路径行 input_row = QHBoxLayout() self.btn_in = QPushButton(" 📁 选择源文件夹 ") self.edit_in = QLineEdit() self.edit_in.setPlaceholderText("请选择包含待处理图片的文件夹...") input_row.addWidget(self.btn_in) input_row.addWidget(self.edit_in) path_layout.addLayout(input_row) # 输出路径行 output_row = QHBoxLayout() self.btn_out = QPushButton(" 📂 设置导出路径 ") self.edit_out = QLineEdit() self.edit_out.setPlaceholderText("请选择筛选结果存放的文件夹...") output_row.addWidget(self.btn_out) output_row.addWidget(self.edit_out) path_layout.addLayout(output_row) layout.addWidget(path_box) # --- 2. 算法开关区域 --- algo_box = QGroupBox("筛选功能开关与阈值") algo_layout = QVBoxLayout(algo_box) # 清晰度开关 clarity_row = QHBoxLayout() self.cb_clarity = QCheckBox("启用清晰度过滤 (Laplacian)") self.cb_clarity.setChecked(True) self.val_clarity = QLineEdit("80.0") self.val_clarity.setFixedWidth(80) clarity_row.addWidget(self.cb_clarity) clarity_row.addStretch() clarity_row.addWidget(QLabel("阈值:")) clarity_row.addWidget(self.val_clarity) algo_layout.addLayout(clarity_row) # 拖影开关 motion_row = QHBoxLayout() self.cb_motion = QCheckBox("启用拖影过滤 (Sobel Ratio)") self.cb_motion.setChecked(True) self.val_energy = QLineEdit("200.0") self.val_ratio = QLineEdit("2.5") self.val_energy.setFixedWidth(60) self.val_ratio.setFixedWidth(60) motion_row.addWidget(self.cb_motion) motion_row.addStretch() motion_row.addWidget(QLabel("能量阈值:")) motion_row.addWidget(self.val_energy) motion_row.addWidget(QLabel("拖影比例:")) motion_row.addWidget(self.val_ratio) algo_layout.addLayout(motion_row) layout.addWidget(algo_box) # --- 3. 运行状态与日志 --- self.log_area = QTextEdit() self.log_area.setReadOnly(True) self.log_area.setStyleSheet("background-color: #000; border: 1px solid #00FF41; color: #00FF41;") layout.addWidget(self.log_area) self.pbar = QProgressBar() self.pbar.setFixedHeight(15) layout.addWidget(self.pbar) self.btn_run = QPushButton(" ⚡ 执行自动化筛选 ") self.btn_run.setFixedHeight(45) self.btn_run.setStyleSheet("background-color: #005A9E; font-weight: bold; font-size: 14px; border: none;") layout.addWidget(self.btn_run) # 信号绑定 self.btn_in.clicked.connect(lambda: self.select_folder(self.edit_in)) self.btn_out.clicked.connect(lambda: self.select_folder(self.edit_out)) self.btn_run.clicked.connect(self.run_process) self.setLayout(layout) def select_folder(self, edit_widget): folder = QFileDialog.getExistingDirectory(self, "选择目录") if folder: edit_widget.setText(folder) def run_process(self): in_p = self.edit_in.text() out_p = self.edit_out.text() if not in_p or not out_p: self.log_area.append("[ERROR] 请指定源路径和导出路径!") return try: params = (float(self.val_clarity.text()), float(self.val_energy.text()), float(self.val_ratio.text())) except: self.log_area.append("[ERROR] 阈值格式不正确!") return self.btn_run.setEnabled(False) self.log_area.clear() self.log_area.append(">> 正在初始化工业视觉筛选序列...") self.thread = FilterThread(in_p, out_p, self.cb_clarity.isChecked(), self.cb_motion.isChecked(), *params) self.thread.progress_update.connect(self.pbar.setValue) self.thread.log_update.connect(lambda msg: self.log_area.append(msg)) self.thread.finished.connect(self.on_finished) self.thread.start() def on_finished(self, total, saved): self.btn_run.setEnabled(True) self.log_area.append(f"\n[FINISH] 处理完毕。") self.log_area.append(f"总计扫描: {total} | 成功导出: {saved}") if __name__ == "__main__": app = QApplication(sys.argv) window = ImageFilterApp() window.show() sys.exit(app.exec_())
http://www.jsqmd.com/news/823710/

相关文章:

  • Android平台HWASan报告深度解析与实战调试
  • WzComparerR2:解锁冒险岛游戏数据的全能工具箱
  • 嵌入式时序AI开发实战:eIQ Time Series Studio数据标签核心技巧与避坑指南
  • 软件工程冲刺
  • 人手一个数据库,Kimi背后这套AI基建到底有多能扛?
  • 3步完成HTML网页到Figma设计稿的终极转换指南
  • 手把手教 OpenClaw 无缝接入阿里百炼大模型
  • Linux重定向与管道:掌握数据流控制,提升命令行效率
  • MCP服务器集中化管理:CentralWize架构解析与部署实践
  • 远方好物 VS 良久团购:私域两大顶流模型深度对比,看懂再入局
  • 3分钟破解Windows热键冲突:Hotkey Detective精准检测工具全面指南
  • AI + 工作流驱动的跨平台 UniApp 低代码平台
  • 5个实用技巧解决AKShare金融数据接口的HTTP API调用问题
  • iOS 16.1的5GHz WiFi Bug实锤了?从技术角度聊聊无线频段兼容性那些坑
  • 2026年盘点10款免费降AI率工具合集【亲测推荐,建议收藏】 - 降AI实验室
  • 基于Docker的代码沙盒执行器:安全运行AI生成代码的架构与实践
  • 3分钟完成3D建模!Wonder3D:用AI将单张图片变成立体模型的神奇工具
  • 2026年降AI率工具实测:5个真实有效降AI工具推荐【附免费降AI方法】 - 降AI实验室
  • STC8A8K64D4上跑RTOS:手把手教你移植Small RTOS51 1.12(附源码和避坑点)
  • [开源] 病案翻拍质量自动检测器:面向病案无纸化归档的合规质检工具,支持CLI批量扫描与Web API集成
  • 深度解析GroundingDINO:SwinT与SwinB配置实战对比与部署指南
  • 深圳家族信托服务商排行:合规与专业维度实测 - 奔跑123
  • LunaTranslator完整指南:5步掌握视觉小说实时翻译技巧
  • 从YARN资源调度角度,根治Hive执行报错return code 2(以CDH 6.3集群为例)
  • 2026长三角数学建模B题 参考文章+代码分享
  • 零基础也能上岸?丽水四大成人高考学历提升机构特色对比,哪个是最优选呢? - 浙江教育测评
  • Midjourney提示词风格迁移秘技(Stable Diffusion用户转战必读的5步对齐法)
  • 深圳海外公司注册服务商排行:合规与专业维度解析 - 奔跑123
  • 2026 网页开发效能蓝皮书:业内评价顶级的开发辅助软件深度评测
  • 明辨是非5:当课本结论遭遇少年质疑——我们该如何讲述“谁创造了历史”?