保姆级教程:用YOLOv11+PyQt5做个垃圾分类小助手(附完整代码和数据集)
从零构建YOLOv11垃圾分类应用:环境配置到界面开发的完整指南
当你第一次看到手机APP里自动识别垃圾类别的功能时,是否好奇过背后的技术原理?本文将带你亲手实现一个桌面端垃圾分类应用,从数据集准备到模型训练,再到图形界面开发,完整呈现AI落地的全流程。不同于简单的API调用,我们将深入技术细节,解决实际开发中可能遇到的各种"坑"。
1. 开发环境与工具链搭建
1.1 硬件准备与性能考量
在开始项目前,需要评估硬件配置对开发效率的影响。虽然YOLOv11可以在CPU上运行,但建议至少配备:
- GPU配置:NVIDIA GTX 1660及以上(6GB显存起步)
- 内存要求:16GB DDR4(处理大型数据集时建议32GB)
- 存储空间:至少50GB可用空间(用于存储数据集和模型)
提示:如果没有独立GPU,可以考虑使用Google Colab的免费GPU资源,但需要注意文件存储的时效性。
1.2 Python环境配置
我们使用conda创建隔离的Python环境,避免与其他项目产生依赖冲突:
conda create -n yolov11_trash python=3.8 -y
conda activate yolov11_trash
安装基础依赖包:
pip install torch==1.12.1+cu113 torchvision==0.13.1+cu113 --extra-index-url https://download.pytorch.org/whl/cu113
pip install ultralytics pyqt5 opencv-python
1.3 常见环境问题排查
在实际配置中,可能会遇到以下典型问题:
-
CUDA版本不匹配:
nvcc --version # 查看CUDA版本确保PyTorch版本与CUDA版本对应
-
OpenCV视频编解码问题:
sudo apt-get install ffmpeg # Linux系统 brew install ffmpeg # macOS系统 -
PyQt5兼容性问题: 如果出现GUI显示异常,可以尝试:
pip uninstall pyqt5 pip install pyqt5==5.15.4
2. 垃圾分类数据集处理
2.1 数据集获取与标注
我们使用公开的垃圾分类数据集,包含四种类别:
| 类别编号 | 类别名称 | 样本数量 | 典型示例 |
|---|---|---|---|
| 0 | 有害垃圾 | 320 | 电池、药品 |
| 1 | 厨余垃圾 | 450 | 果皮、剩饭 |
| 2 | 其他垃圾 | 380 | 纸巾、塑料袋 |
| 3 | 可回收垃圾 | 400 | 纸张、塑料瓶 |
数据集目录结构应组织为:
datasets/
└── trash_classification/
├── train/
│ ├── images/
│ └── labels/
├── val/
│ ├── images/
│ └── labels/
└── data.yaml
2.2 数据增强策略
在data.yaml中配置增强参数:
# 数据增强配置
augment:
hsv_h: 0.015 # 色调变化范围
hsv_s: 0.7 # 饱和度变化范围
hsv_v: 0.4 # 明度变化范围
degrees: 10 # 旋转角度范围
translate: 0.1 # 平移比例
scale: 0.5 # 缩放比例
shear: 0.0 # 剪切角度
perspective: 0.0001 # 透视变换
flipud: 0.0 # 上下翻转概率
fliplr: 0.5 # 左右翻转概率
2.3 类别不平衡处理
对于样本数量差异较大的类别,可以采用以下方法:
-
过采样少数类:
from torchsampler import ImbalancedDatasetSampler train_loader = DataLoader(dataset, sampler=ImbalancedDatasetSampler(dataset), batch_size=8) -
损失函数加权:
class_weights = torch.tensor([1.0, 0.8, 1.2, 0.9]) # 根据样本数调整 criterion = nn.CrossEntropyLoss(weight=class_weights)
3. YOLOv11模型训练与优化
3.1 模型架构选择
YOLOv11提供了多种预训练模型尺寸:
| 模型类型 | 参数量(M) | mAP@0.5 | 推理速度(FPS) | 适用场景 |
|---|---|---|---|---|
| YOLOv11n | 3.2 | 38.2 | 450 | 移动端 |
| YOLOv11s | 11.4 | 44.3 | 280 | 本教程选择 |
| YOLOv11m | 26.3 | 49.1 | 180 | 高性能GPU |
| YOLOv11l | 52.9 | 51.7 | 120 | 服务器 |
初始化模型:
from ultralytics import YOLO
model = YOLO('yolov11s.pt') # 加载预训练模型
model.info() # 查看模型结构
3.2 训练参数配置
创建train.py配置文件:
# 训练参数
args = {
'data': 'datasets/trash_classification/data.yaml',
'epochs': 100,
'batch': 16,
'imgsz': 640,
'device': '0', # 使用GPU 0
'workers': 4,
'optimizer': 'AdamW',
'lr0': 0.001, # 初始学习率
'lrf': 0.01, # 最终学习率 = lr0 * lrf
'weight_decay': 0.0005,
'warmup_epochs': 3,
'box': 7.5, # box损失权重
'cls': 0.5, # 分类损失权重
'hsv_h': 0.015, # 色调增强
'hsv_s': 0.7, # 饱和度增强
'hsv_v': 0.4, # 明度增强
'degrees': 10, # 旋转角度
'translate': 0.1, # 平移
'scale': 0.5, # 缩放
'fliplr': 0.5, # 水平翻转
'name': 'yolov11_trash' # 实验名称
}
启动训练:
python train.py --args args
3.3 训练监控与调优
使用TensorBoard监控训练过程:
tensorboard --logdir runs/detect
关键指标解读:
- mAP@0.5:IoU阈值为0.5时的平均精度
- precision/recall:精确率与召回率平衡
- box/cls loss:定位损失与分类损失
如果出现过拟合现象,可以尝试:
- 增加数据增强强度
- 添加Dropout层
- 提前停止训练(early stopping)
4. PyQt5界面开发实战
4.1 主界面设计
使用Qt Designer创建mainwindow.ui,核心组件包括:
- 图像显示区域:QLabel用于显示检测结果
- 控制面板:
- 文件选择按钮(QPushButton)
- 摄像头开关(QCheckBox)
- 置信度滑块(QSlider)
- 类别筛选(QComboBox)
- 状态栏:显示检测时间和FPS
转换UI文件为Python代码:
pyuic5 mainwindow.ui -o ui_mainwindow.py
4.2 视频流处理框架
创建视频处理线程类:
from PyQt5.QtCore import QThread, pyqtSignal
class VideoThread(QThread):
change_pixmap_signal = pyqtSignal(np.ndarray)
def __init__(self):
super().__init__()
self._run_flag = True
self.cap = cv2.VideoCapture(0) # 默认摄像头
def run(self):
while self._run_flag:
ret, frame = self.cap.read()
if ret:
self.change_pixmap_signal.emit(frame)
self.cap.release()
def stop(self):
self._run_flag = False
self.wait()
4.3 模型集成与实时检测
在主窗口中集成YOLOv11模型:
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# 加载训练好的模型
self.model = YOLO('runs/detect/yolov11_trash/weights/best.pt')
# 初始化视频线程
self.video_thread = VideoThread()
self.video_thread.change_pixmap_signal.connect(self.update_image)
def update_image(self, cv_img):
# 执行目标检测
results = self.model(cv_img)
annotated_frame = results[0].plot() # 绘制检测结果
# 转换图像格式用于显示
qt_img = self.convert_cv_qt(annotated_frame)
self.ui.label_image.setPixmap(qt_img)
def convert_cv_qt(self, cv_img):
"""将OpenCV图像转换为QPixmap"""
rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)
h, w, ch = rgb_image.shape
bytes_per_line = ch * w
convert_to_Qt_format = QImage(
rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)
return QPixmap.fromImage(convert_to_Qt_format)
4.4 性能优化技巧
-
异步处理:将检测任务放到单独线程,避免阻塞UI
class DetectionThread(QThread): detection_done = pyqtSignal(list) def __init__(self, frame): super().__init__() self.frame = frame def run(self): results = model(self.frame) self.detection_done.emit(results) -
帧采样:对于高帧率视频,可以每N帧处理一次
self.frame_count = 0 def update_image(self, cv_img): self.frame_count += 1 if self.frame_count % 3 == 0: # 每3帧处理一次 # 执行检测... else: # 直接显示原始帧 -
模型量化:减小模型大小,提升推理速度
model.export(format='onnx', dynamic=True, simplify=True) quantized_model = quantize_dynamic( model_fp32=model, qconfig_spec={torch.nn.Linear}, dtype=torch.qint8)
5. 应用打包与部署
5.1 使用PyInstaller打包
创建打包脚本build.spec:
# -*- mode: python -*-
from PyInstaller.utils.hooks import collect_data_files
block_cipher = None
a = Analysis(
['main.py'],
pathex=[],
binaries=[],
datas=collect_data_files('ultralytics') + [('runs/detect/yolov11_trash/weights/best.pt', '.')],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='TrashClassifier',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False, # 设置为True可查看控制台输出
icon='icon.ico',
)
执行打包命令:
pyinstaller build.spec --onefile --noconsole
5.2 跨平台兼容性处理
不同平台下的注意事项:
-
Windows:
- 可能需要安装Visual C++ Redistributable
- 摄像头访问需要管理员权限
-
macOS:
- 需要处理相机权限
codesign --force --deep -s - TrashClassifier.app -
Linux:
- 需要安装GTK和libSM
sudo apt-get install libsm6 libgtk-3-0 libxext6
5.3 性能基准测试
在不同硬件上的推理速度对比:
| 硬件配置 | 分辨率 | 平均FPS | 显存占用 |
|---|---|---|---|
| RTX 3090 | 640x640 | 120 | 2.3GB |
| GTX 1660 | 640x640 | 45 | 1.8GB |
| Core i7-10700 | 640x640 | 8 | - |
| Raspberry Pi 4 | 320x320 | 2 | - |
对于低性能设备,可以采取以下优化措施:
- 降低输入分辨率(如从640x640降至320x320)
- 使用更小的模型(如YOLOv11n)
- 启用TensorRT加速(仅限NVIDIA GPU)
6. 功能扩展与进阶方向
6.1 多模态输入支持
扩展应用支持多种输入源:
def load_input_source(self, source):
if source == 'camera':
self.cap = cv2.VideoCapture(0)
elif source.endswith(('.mp4', '.avi')):
self.cap = cv2.VideoCapture(source)
elif source.endswith(('.jpg', '.png')):
self.current_frame = cv2.imread(source)
elif os.path.isdir(source):
self.image_files = [f for f in os.listdir(source)
if f.endswith(('.jpg', '.png'))]
self.current_index = 0
6.2 分类结果后处理
添加垃圾处理建议功能:
def get_disposal_advice(self, class_name):
advice_map = {
'可回收垃圾': '请清洁后投入蓝色回收箱',
'有害垃圾': '请勿拆解,投入红色专用容器',
'厨余垃圾': '请去除包装后投入绿色湿垃圾桶',
'其他垃圾': '投入黑色干垃圾桶'
}
return advice_map.get(class_name, '请咨询当地垃圾分类标准')
6.3 模型更新机制
实现无需重新打包的模型热更新:
def check_model_update(self):
try:
response = requests.get('https://your-server.com/latest-model')
if response.status_code == 200:
remote_md5 = response.headers.get('Content-MD5')
local_md5 = self.calculate_md5('model.pt')
if remote_md5 != local_md5:
self.download_model_update()
except Exception as e:
print(f"模型更新检查失败: {e}")
6.4 数据收集与模型迭代
添加误分类反馈功能:
def save_feedback(self, image, predicted_class, correct_class):
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"feedback/{predicted_class}_as_{correct_class}_{timestamp}.jpg"
cv2.imwrite(filename, image)
# 记录到CSV文件
with open('feedback/log.csv', 'a') as f:
writer = csv.writer(f)
writer.writerow([timestamp, predicted_class, correct_class])
7. 实际应用中的问题排查
7.1 常见运行时错误
-
CUDA内存不足:
torch.cuda.empty_cache() # 释放缓存 model.half() # 使用半精度浮点数 -
视频流卡顿:
- 降低检测帧率
- 缩小输入分辨率
- 使用硬件加速解码
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) # 减少缓冲区 -
界面无响应:
- 确保所有耗时操作都在子线程中运行
- 使用QTimer替代循环
7.2 模型精度问题
如果发现特定类别识别率低:
-
检查训练数据:
from collections import Counter # 统计每个类别的样本数 label_counts = Counter() for label_file in glob.glob('labels/*.txt'): with open(label_file) as f: for line in f: class_id = int(line.split()[0]) label_counts[class_id] += 1 -
针对性数据增强:
- 对样本少的类别增加旋转、色彩变化
- 使用Copy-Paste增强(将目标粘贴到不同背景)
-
调整损失函数权重:
# 根据类别频率计算权重 class_weights = 1.0 / torch.tensor(list(label_counts.values())) class_weights = class_weights / class_weights.sum()
7.3 跨平台兼容性问题
-
路径处理:
from pathlib import Path config_path = Path(__file__).parent / 'config' / 'settings.ini' -
字体渲染差异:
font = cv2.FONT_HERSHEY_SIMPLEX if sys.platform == 'darwin': # macOS font_scale = 0.8 else: font_scale = 0.6 -
高DPI屏幕适配:
if hasattr(Qt, 'AA_EnableHighDpiScaling'): QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) if hasattr(Qt, 'AA_UseHighDpiPixmaps'): QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
8. 项目优化与性能提升
8.1 模型量化与加速
使用TensorRT加速推理:
model.export(format='engine', device='cuda') # 导出为TensorRT引擎
# 加载优化后的模型
trt_model = YOLO('model.engine')
量化模型减小体积:
model.export(format='onnx', dynamic=True, int8=True) # 8位整数量化
8.2 多线程处理架构
优化后的处理流水线:
class ProcessingPipeline:
def __init__(self):
self.input_queue = Queue(maxsize=3)
self.output_queue = Queue(maxsize=3)
# 视频采集线程
self.capture_thread = Thread(target=self.capture_frames)
# 检测线程池
self.detection_pool = [Thread(target=self.process_frames)
for _ in range(2)]
# 显示线程
self.display_thread = Thread(target=self.display_results)
def start(self):
self.capture_thread.start()
for t in self.detection_pool:
t.start()
self.display_thread.start()
8.3 内存优化技巧
-
图像缓存管理:
from functools import lru_cache @lru_cache(maxsize=100) def load_image(path): return cv2.imread(path) -
批量推理:
def process_batch(self, frames): # 将多帧堆叠为批量张量 batch = torch.stack([self.transform(f) for f in frames]) with torch.no_grad(): results = self.model(batch) return results -
显存监控:
def print_gpu_memory(): allocated = torch.cuda.memory_allocated() / 1024**2 reserved = torch.cuda.memory_reserved() / 1024**2 print(f"显存使用: {allocated:.2f}MB/{reserved:.2f}MB")
9. 用户界面美化与交互优化
9.1 主题与样式定制
使用QSS美化界面:
self.setStyleSheet("""
QMainWindow {
background-color: #f5f5f5;
}
QPushButton {
background-color: #4CAF50;
color: white;
border-radius: 4px;
padding: 6px;
}
QPushButton:hover {
background-color: #45a049;
}
QLabel#label_image {
border: 1px solid #ddd;
background-color: white;
}
""")
9.2 动画与过渡效果
添加平滑的过渡动画:
from PyQt5.QtCore import QPropertyAnimation
def fade_in(self, widget):
self.animation = QPropertyAnimation(widget, b"windowOpacity")
self.animation.setDuration(300)
self.animation.setStartValue(0)
self.animation.setEndValue(1)
self.animation.start()
9.3 快捷键与手势支持
添加快捷操作:
self.shortcut_next = QShortcut(QKeySequence("Right"), self)
self.shortcut_next.activated.connect(self.next_image)
self.shortcut_prev = QShortcut(QKeySequence("Left"), self)
self.shortcut_prev.activated.connect(self.prev_image)
支持手势缩放:
class ImageViewer(QLabel):
def __init__(self):
super().__init__()
self.zoom_factor = 1.0
def wheelEvent(self, event):
if event.angleDelta().y() > 0:
self.zoom(1.25)
else:
self.zoom(0.8)
def zoom(self, factor):
self.zoom_factor *= factor
self.setPixmap(self.original_pixmap.scaled(
self.original_pixmap.size() * self.zoom_factor))
10. 项目结构与代码组织
10.1 模块化设计
推荐的项目结构:
trash-classifier/
├── core/ # 核心功能模块
│ ├── detector.py # 检测逻辑
│ ├── utils.py # 工具函数
│ └── config.py # 配置管理
├── data/ # 数据相关
│ ├── datasets/ # 训练数据集
│ └── feedback/ # 用户反馈数据
├── models/ # 模型文件
│ ├── yolov11/ # 模型实现
│ └── weights/ # 训练好的权重
├── ui/ # 用户界面
│ ├── mainwindow.ui # Qt Designer文件
│ ├── resources/ # 图片等资源
│ └── styles/ # 样式表
├── train.py # 训练脚本
└── app.py # 主程序入口
10.2 配置管理
使用YAML管理配置:
# config.yaml
model:
path: models/weights/best.pt
confidence_threshold: 0.5
iou_threshold: 0.45
ui:
theme: dark
language: zh
recent_files:
- /path/to/image1.jpg
- /path/to/video.mp4
camera:
index: 0
resolution: 1280x720
加载配置:
import yaml
class Config:
def __init__(self):
with open('config.yaml') as f:
self.data = yaml.safe_load(f)
def save(self):
with open('config.yaml', 'w') as f:
yaml.safe_dump(self.data, f)
config = Config()
10.3 日志记录
配置详细日志系统:
import logging
from logging.handlers import RotatingFileHandler
def setup_logging():
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
# 文件日志(最大10MB,保留3个备份)
file_handler = RotatingFileHandler(
'app.log', maxBytes=10*1024*1024, backupCount=3)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
# 控制台日志
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
logger.addHandler(file_handler)
logger.addHandler(console_handler)
11. 测试与质量保证
11.1 单元测试
为关键功能编写测试用例:
import unittest
from core.detector import Detector
class TestDetector(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.detector = Detector('models/weights/best.pt')
def test_detection(self):
test_image = np.zeros((640, 640, 3), dtype=np.uint8)
results = self.detector.detect(test_image)
self.assertIsInstance(results, list)
def test_confidence_threshold(self):
self.detector.confidence_threshold = 0.8
self.assertEqual(self.detector.confidence_threshold, 0.8)
11.2 性能测试
评估关键操作耗时:
import timeit
def profile_detection():
setup = '''
from core.detector import Detector
import cv2
detector = Detector('model.pt')
image = cv2.imread('test.jpg')
'''
stmt = 'detector.detect(image)'
time = timeit.timeit(stmt, setup, number=100)
print(f"平均检测时间: {time/100*1000:.2f}ms")
11.3 用户界面自动化测试
使用PyQt5测试工具:
from PyQt5.QtTest import QTest
class UITests(unittest.TestCase):
def setUp(self):
self.app = QApplication([])
self.window = MainWindow()
def test_button_click(self):
button = self.window.ui.pushButton_start
QTest.mouseClick(button, Qt.LeftButton)
self.assertTrue(self.window.detector.running)
def tearDown(self):
self.window.close()
12. 持续集成与部署
12.1 GitHub Actions自动化
创建.github/workflows/build.yml:
name: Build and Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.8'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run tests
run: |
python -m unittest discover
12.2 自动构建安装包
使用PyInstaller构建后自动上传:
- name: Build executable
run: |
pip install pyinstaller
pyinstaller --onefile --windowed app.py
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: TrashClassifier
path: dist/
12.3 版本管理与更新日志
遵循语义化版本控制:
CHANGELOG.md
# Changelog
## [1.1.0] - 2023-08-15
### Added
- 支持摄像头设备选择
- 添加多语言支持
### Fixed
- 修复高DPI屏幕显示问题
- 修正模型内存泄漏问题
## [1.0.0] - 2023-07-01
### Initial Release
- 基本垃圾分类功能
- 支持图片/视频输入
13. 用户文档与帮助系统
13.1 内置帮助文档
创建可搜索的帮助系统:
class HelpDialog(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("帮助文档")
self.layout = QVBoxLayout()
self.search_box = QLineEdit()
self.search_box.setPlaceholderText("搜索帮助...")
self.search_box.textChanged.connect(self.update_display)
self.content_browser = QTextBrowser()
self.content_browser.setOpenExternalLinks(True)
self.layout.addWidget(self.search_box)
self.layout.addWidget(self.content_browser)
self.setLayout(self.layout)
self.load_content()
def load_content(self):
with open('help/contents.md') as f:
self.full_content = f.read()
self.content_browser.setMarkdown(self.full_content)
def update_display(self, text):
if not text:
self.content_browser.setMarkdown(self.full_content)
return
highlighted = self.full_content.replace(
text, f"<span style='background-color:yellow'>{text}</span>")
self.content_browser.setHtml(highlighted)
13.2 工具提示与状态信息
为UI元素添加详细说明:
# 为按钮添加工具提示
self.ui.pushButton_start.setToolTip(
"开始实时检测\n快捷键: Space")
self.ui.pushButton_start.setStatusTip(
"从当前选择的输入源开始检测")
# 为滑块添加数值显示
self.ui.slider_confidence.valueChanged.connect(
lambda v: self.ui.label_conf_value.setText(f"{v}%"))
13.3 视频教程与示例
嵌入多媒体帮助资源:
class VideoTutorial(QWidget):
def __init__(self):
super().__init__()
self.media_player = QMediaPlayer()
self.video_widget = QVideoWidget()
layout = QVBoxLayout()
layout.addWidget(self.video_widget)
self.setLayout(layout)
self.play_tutorial()
def play_tutorial(self):
file_path = QUrl.fromLocalFile("help/tutorial.mp4")
self.media_player.setMedia(QMediaContent(file_path))
self.media_player.setVideoOutput(self.video_widget)
self.media_player.play()
14. 安全与隐私考虑
14.1 数据安全处理
确保用户数据隐私:
import hashlib
def anonymize_image(image):
# 移除EXIF信息
image = Image.fromarray(image)
data = list(image.getdata())
image_without_exif = Image.new(image.mode, image.size)
image_without_exif.putdata(data)
# 返回匿名化的numpy数组
return np.array(image_without_exif)
def hash_filename(filename):
return hashlib.sha256(filename.encode()).hexdigest()[:16]
14.2 模型安全验证
检查模型完整性:
def verify_model_signature(model_path, expected_hash):
with open(model_path, 'rb') as f:
file_hash = hashlib.sha256(f.read()).hexdigest()
return file_hash == expected_hash
# 使用示例
if not verify_model_signature('model.pt', 'a1b2c3...'):
raise SecurityError("模型文件已被篡改")
14.3 权限管理
处理敏感操作权限:
import platform
def check_camera_permission():
if platform.system() == 'Darwin': # macOS
from Foundation import NSBundle
return NSBundle.mainBundle().infoDictionary().get(
'NSCameraUsageDescription') is not None
elif platform.system() == 'Linux':
return os.path.exists('/dev/video0')
else: # Windows
return True
15. 跨平台开发注意事项
15.1 平台特定代码处理
使用条件导入处理平台差异:
if sys.platform == 'win32':
from windows.camera import CameraController
elif sys.platform == 'darwin':
from mac.camera import CameraController
else:
from linux.camera import CameraController
15.2 打包差异处理
不同平台的打包配置:
# Windows特定配置
if sys.platform == 'win32':
datas += [('C:/Windows/Fonts/arial.ttf', 'fonts')]
# macOS特定配置
elif sys.platform == 'darwin':
binaries += [('/usr/lib/libomp.dylib', '.')]
# Linux特定配置
else:
datas += [('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', 'fonts')]
15.3 文件系统差异
跨平台路径处理:
from pathlib import Path
config_dir = Path.home() / '.config' / 'trash-classifier'
config_dir.mkdir(parents=True, exist_ok=True)
db_path = config_dir / 'feedback.db'
log_path = config_dir / 'app.log'
16. 错误处理与恢复
16.1 优雅降级机制
当GPU不可用时自动回退:
try:
device = 'cuda' if torch.cuda.is_available() else 'cpu'
model = model.to(device)
except RuntimeError as e:
print(f"GPU错误: {e}, 回退到CPU")
device = 'cpu'
model = model.to(device)
16.2 自动错误报告
收集错误信息帮助改进:
def send_error_report(error):
import platform, traceback
from datetime import datetime
report = {
'timestamp': datetime.now().isoformat(),
'os': platform.platform(),
'python_version': platform.python_version(),
'error_type': type(error).__name__,
'error_msg': str(error),
'traceback': traceback.format_exc(),
'app_version': __version__
}
try:
requests.post('https://your-domain.com/error-report',
json=report, timeout=3)
except:
pass # 避免错误报告本身导致崩溃
16.3 状态保存与恢复
实现崩溃恢复功能:
import atexit
class AutoSaver:
def __init__(self):
self.state_file = Path('autosave.state')
更多推荐


所有评论(0)