python numpy.save 函数 SystemError: error return without exception set

解决numpy.save错误
部署运行你感兴趣的模型镜像
在使用python numpy.save 函数的时候出现了如下错误
  File..., line 181, in save_npy
    np.save(npy_path, data_dict)
  File "/usr/lib64/python2.7/site-packages/numpy/lib/npyio.py", line 509, in save
    pickle_kwargs=pickle_kwargs)
  File "/usr/lib64/python2.7/site-packages/numpy/lib/format.py", line 576, in write_array
    pickle.dump(array, fp, protocol=2, **pickle_kwargs)
SystemError: error return without exception set

感觉应该是numpy 的一个bug,可能是因为数组太大了的原因

您可能感兴趣的与本文相关的镜像

Python3.10

Python3.10

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import plotly.graph_objects as go import plotly.express as px from scipy.stats import gaussian_kde import matplotlib.font_manager as fm from matplotlib.colors import LinearSegmentedColormap # 设置中文字体支持 plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'Microsoft YaHei', 'WenQuanYi Micro Hei'] plt.rcParams['axes.unicode_minus'] = False # 设置随机种子,确保结果可复现 np.random.seed(42) # 根据92.02%的高准确率生成模拟数据 # 总样本数 n_samples = 1000 # 正确样本比例 (92.02%) correct_ratio = 0.9202 n_correct = int(n_samples * correct_ratio) n_incorrect = n_samples - n_correct # 生成预测不确定性数据 # 正确样本的不确定性较低,分布更集中 correct_uncertainty = np.random.normal(0.3, 0.15, n_correct) # 错误样本的不确定性较高,分布更分散 incorrect_uncertainty = np.random.normal(1.2, 0.4, n_incorrect) # 合并数据 uncertainty = np.concatenate([correct_uncertainty, incorrect_uncertainty]) correctness = np.concatenate([np.ones(n_correct), np.zeros(n_incorrect)]) # 添加峰高变异系数作为第三维度特征 cv_correct = np.random.normal(0.2, 0.1, n_correct) # 正确样本峰高变异系数较低 cv_incorrect = np.random.normal(0.6, 0.2, n_incorrect) # 错误样本峰高变异系数较高 cv = np.concatenate([cv_correct, cv_incorrect]) # 创建数据框 df = pd.DataFrame({ 'uncertainty': uncertainty, 'correctness': correctness, 'result': ['正确' if c == 1 else '错误' for c in correctness], 'cv': cv }) # 确保不确定性值为非负数 df['uncertainty'] = df['uncertainty'].clip(lower=0) # 计算整体准确率 overall_accuracy = df['correctness'].mean() print(f"模拟数据准确率: {overall_accuracy:.4f}") # 创建自定义颜色映射 def create_green_cmap(): colors = ["#f0f9e8", "#bae4bc", "#7bccc4", "#2b8cbe"] return LinearSegmentedColormap.from_list("green_cmap", colors) # 保存所有图像的函数 def save_all_figures(): # 方案1:核密度估计(KDE)+ 统计摘要图 plt.figure(figsize=(12, 8)) kde = gaussian_kde(df['uncertainty']) x_range = np.linspace(0, df['uncertainty'].max(), 200) y_kde = kde(x_range) # 计算统计指标 mean_uncert = df['uncertainty'].mean() median_uncert = df['uncertainty'].median() q25, q75 = np.percentile(df['uncertainty'], [25, 75]) std_uncert = df['uncertainty'].std() plt.plot(x_range, y_kde, 'b-', linewidth=2, label='KDE分布') plt.fill_between(x_range, y_kde, color='royalblue', alpha=0.2, label='分布区域') # 标注统计指标 plt.axvline(mean_uncert, color='r', linestyle='--', label=f'均值: {mean_uncert:.2f}') plt.axvline(median_uncert, color='g', linestyle=':', label=f'中位数: {median_uncert:.2f}') plt.axvline(q25, color='purple', linestyle='-.', label=f'25%分位数: {q25:.2f}') plt.axvline(q75, color='orange', linestyle='-.', label=f'75%分位数: {q75:.2f}') plt.title(f'预测不确定性分布 (准确率: {overall_accuracy * 100:.2f}%)', fontsize=16, pad=20) plt.xlabel('预测方差', fontsize=14) plt.ylabel('概率密度', fontsize=14) plt.legend(loc='upper right', fontsize=12) plt.grid(alpha=0.2, linestyle='--') # 添加统计信息框 stats_text = f'统计摘要:\n样本数: {n_samples}\n标准差: {std_uncert:.2f}\n最小值: {df["uncertainty"].min():.2f}\n最大值: {df["uncertainty"].max():.2f}' plt.text(0.95, 0.95, stats_text, transform=plt.gca().transAxes, fontsize=12, verticalalignment='top', horizontalalignment='right', bbox=dict(boxstyle='round', facecolor='white', alpha=0.8)) plt.tight_layout() plt.savefig('1_kde_distribution.png', dpi=300, bbox_inches='tight') plt.close() # 方案2:分组小提琴图 + 抖动散点图 plt.figure(figsize=(12, 8)) sns.set_style("whitegrid") # 创建自定义调色板 palette = {"正确": "#4caf50", "错误": "#f44336"} # 绘制小提琴图 sns.violinplot(x='result', y='uncertainty', data=df, palette=palette, inner='quartile', linewidth=2, saturation=0.8) # 绘制散点图(带透明度) sns.stripplot(x='result', y='uncertainty', data=df, palette=palette, alpha=0.4, size=4, jitter=0.2) # 添加中位数线 medians = df.groupby('result')['uncertainty'].median() for i, category in enumerate(medians.index): plt.hlines(medians[category], i - 0.3, i + 0.3, color='black', linestyles='dashed', linewidth=2) plt.title(f'预测不确定性与结果分类 (准确率: {overall_accuracy * 100:.2f}%)', fontsize=16, pad=15) plt.xlabel('预测结果', fontsize=14) plt.ylabel('预测方差', fontsize=14) plt.xticks(fontsize=12) plt.yticks(fontsize=12) # 添加准确率注释 for i, category in enumerate(['正确', '错误']): count = len(df[df['result'] == category]) percentage = count / len(df) * 100 plt.text(i, df['uncertainty'].max() + 0.1, f'{count}个样本 ({percentage:.1f}%)', ha='center', fontsize=12) plt.ylim(-0.1, df['uncertainty'].max() + 0.3) plt.tight_layout() plt.savefig('2_violin_scatter.png', dpi=300, bbox_inches='tight') plt.close() # 方案3:热力图(分箱统计正确率) bins = np.linspace(0, df['uncertainty'].max(), 11) df['bin'] = pd.cut(df['uncertainty'], bins=bins, include_lowest=True, labels=False) bin_stats = df.groupby(['bin', 'result']).size().unstack(fill_value=0) bin_stats['accuracy'] = bin_stats['正确'] / bin_stats.sum(axis=1) bin_stats['total_samples'] = bin_stats.sum(axis=1) # 创建热力图数据 heatmap_data = bin_stats['accuracy'].values.reshape(-1, 1) bin_labels = [f'{bins[i]:.2f}-{bins[i + 1]:.2f}' for i in range(len(bins) - 1)] # 使用自定义绿色渐变颜色映射 cmap = create_green_cmap() plt.figure(figsize=(12, 8)) plt.imshow(heatmap_data, cmap=cmap, aspect='auto', vmin=0, vmax=1) # 添加颜色条 cbar = plt.colorbar() cbar.set_label('正确率', fontsize=14) # 添加单元格注释 for i in range(len(bin_labels)): acc = heatmap_data[i, 0] samples = bin_stats['total_samples'].iloc[i] text_color = 'white' if acc < 0.6 else 'black' plt.text(0, i, f'{acc:.2%}\n({samples}样本)', ha='center', va='center', color=text_color, fontsize=11, fontweight='bold') # 设置坐标轴 plt.yticks(range(len(bin_labels)), bin_labels, fontsize=12) plt.xticks([]) plt.ylabel('方差区间', fontsize=14) plt.title(f'不同方差区间的预测正确率 (总体准确率: {overall_accuracy * 100:.2f}%)', fontsize=16, pad=20) # 添加网格线 plt.grid(False) for i in range(len(bin_labels) + 1): plt.axhline(i - 0.5, color='white', linewidth=1) plt.tight_layout() plt.savefig('3_heatmap.png', dpi=300, bbox_inches='tight') plt.close() # 方案4:动态箱线图 + 错误率趋势线 fig = go.Figure() # 添加箱线图 fig.add_trace(go.Box( y=df['uncertainty'], name='方差分布', boxpoints='outliers', marker=dict(color='#2196f3'), line=dict(color='#0d47a1'), fillcolor='rgba(33, 150, 243, 0.5)' )) # 错误率趋势线 df['error'] = 1 - df['correctness'] x_fit = np.linspace(0, df['uncertainty'].max(), 100) z = np.polyfit(df['uncertainty'], df['error'], 3) p = np.poly1d(z) y_fit = p(x_fit) fig.add_trace(go.Scatter( x=x_fit, y=y_fit, name='错误率趋势', mode='lines', line=dict(color='#e53935', width=3), yaxis='y2' )) fig.update_layout( title=dict( text=f'预测方差分布与错误率趋势 (准确率: {overall_accuracy * 100:.2f}%)', font=dict(size=20, family="Microsoft YaHei"), ), font=dict(family="Microsoft YaHei"), xaxis=dict( title='预测方差', title_font=dict(family="Microsoft YaHei", size=14), gridcolor='lightgray' ), yaxis=dict( title='方差值', title_font=dict(family="Microsoft YaHei", size=14, color='#2196f3'), tickfont=dict(color='#2196f3'), gridcolor='rgba(33, 150, 243, 0.1)' ), yaxis2=dict( title='错误率', title_font=dict(family="Microsoft YaHei", size=14, color='#e53935'), tickfont=dict(color='#e53935'), overlaying='y', side='right', range=[0, 1] ), template='plotly_white', width=1000, height=700, margin=dict(l=50, r=50, b=80, t=100), legend=dict( orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1, font=dict(family="Microsoft YaHei") ), hovermode="x unified" ) # 添加注释 fig.add_annotation( x=0.95, y=0.95, xref="paper", yref="paper", text=f"高方差区域错误率显著增加", showarrow=False, font=dict(size=14, color="#e53935", family="Microsoft YaHei"), bgcolor="rgba(255, 255, 255, 0.8)" ) fig.write_image('4_box_trend.png', scale=3) # 方案5:三维密度图 fig = go.Figure() # 添加正确样本 fig.add_trace(go.Scatter3d( x=df[df['result'] == '正确']['uncertainty'], y=df[df['result'] == '正确']['cv'], z=df[df['result'] == '正确']['correctness'], mode='markers', name='正确', marker=dict( size=5, color='#4caf50', opacity=0.7 ) )) # 添加错误样本 fig.add_trace(go.Scatter3d( x=df[df['result'] == '错误']['uncertainty'], y=df[df['result'] == '错误']['cv'], z=df[df['result'] == '错误']['correctness'], mode='markers', name='错误', marker=dict( size=7, color='#f44336', opacity=0.8, symbol='diamond' ) )) fig.update_layout( title=dict( text=f'三维预测不确定性分析 (准确率: {overall_accuracy * 100:.2f}%)', font=dict(size=20, family="Microsoft YaHei"), y=0.95 ), font=dict(family="Microsoft YaHei"), scene=dict( xaxis=dict( title='预测方差', title_font=dict(family="Microsoft YaHei", size=14) ), yaxis=dict( title='峰高变异系数', title_font=dict(family="Microsoft YaHei", size=14) ), zaxis=dict( title='预测正确(1)/错误(0)', title_font=dict(family="Microsoft YaHei", size=14) ), camera=dict( eye=dict(x=1.5, y=1.5, z=0.8) ) ), width=1000, height=800, margin=dict(l=0, r=0, b=0, t=50), legend=dict( yanchor="top", y=0.99, xanchor="left", x=0.01, font=dict(family="Microsoft YaHei") ) ) # 添加分类平面 fig.add_trace(go.Mesh3d( x=[0, 2, 2, 0], y=[0, 0, 1, 1], z=[0.5, 0.5, 0.5, 0.5], opacity=0.2, color='gray', name='分类平面' )) fig.write_image('5_3d_density.png', scale=3) print("所有图像已保存为PNG文件") # 生成并保存所有图像 save_all_figures() C:\python\py\.venv\Scripts\python.exe C:\python\py\3.py 模拟数据准确率: 0.9200 C:\python\py\3.py:113: FutureWarning: Passing `palette` without assigning `hue` is deprecated and will be removed in v0.14.0. Assign the `x` variable to `hue` and set `legend=False` for the same effect. C:\python\py\3.py:117: FutureWarning: Passing `palette` without assigning `hue` is deprecated and will be removed in v0.14.0. Assign the `x` variable to `hue` and set `legend=False` for the same effect. C:\python\py\3.py:140: UserWarning: Glyph 27491 (\N{CJK UNIFIED IDEOGRAPH-6B63}) missing from font(s) Arial. C:\python\py\3.py:140: UserWarning: Glyph 30830 (\N{CJK UNIFIED IDEOGRAPH-786E}) missing from font(s) Arial. C:\python\py\3.py:140: UserWarning: Glyph 38169 (\N{CJK UNIFIED IDEOGRAPH-9519}) missing from font(s) Arial. C:\python\py\3.py:140: UserWarning: Glyph 35823 (\N{CJK UNIFIED IDEOGRAPH-8BEF}) missing from font(s) Arial. C:\python\py\3.py:140: UserWarning: Glyph 39044 (\N{CJK UNIFIED IDEOGRAPH-9884}) missing from font(s) Arial. C:\python\py\3.py:140: UserWarning: Glyph 27979 (\N{CJK UNIFIED IDEOGRAPH-6D4B}) missing from font(s) Arial. C:\python\py\3.py:140: UserWarning: Glyph 32467 (\N{CJK UNIFIED IDEOGRAPH-7ED3}) missing from font(s) Arial. C:\python\py\3.py:140: UserWarning: Glyph 26524 (\N{CJK UNIFIED IDEOGRAPH-679C}) missing from font(s) Arial. C:\python\py\3.py:140: UserWarning: Glyph 26041 (\N{CJK UNIFIED IDEOGRAPH-65B9}) missing from font(s) Arial. C:\python\py\3.py:140: UserWarning: Glyph 24046 (\N{CJK UNIFIED IDEOGRAPH-5DEE}) missing from font(s) Arial. C:\python\py\3.py:140: UserWarning: Glyph 19981 (\N{CJK UNIFIED IDEOGRAPH-4E0D}) missing from font(s) Arial. C:\python\py\3.py:140: UserWarning: Glyph 23450 (\N{CJK UNIFIED IDEOGRAPH-5B9A}) missing from font(s) Arial. C:\python\py\3.py:140: UserWarning: Glyph 24615 (\N{CJK UNIFIED IDEOGRAPH-6027}) missing from font(s) Arial. C:\python\py\3.py:140: UserWarning: Glyph 19982 (\N{CJK UNIFIED IDEOGRAPH-4E0E}) missing from font(s) Arial. C:\python\py\3.py:140: UserWarning: Glyph 20998 (\N{CJK UNIFIED IDEOGRAPH-5206}) missing from font(s) Arial. C:\python\py\3.py:140: UserWarning: Glyph 31867 (\N{CJK UNIFIED IDEOGRAPH-7C7B}) missing from font(s) Arial. C:\python\py\3.py:140: UserWarning: Glyph 20934 (\N{CJK UNIFIED IDEOGRAPH-51C6}) missing from font(s) Arial. C:\python\py\3.py:140: UserWarning: Glyph 29575 (\N{CJK UNIFIED IDEOGRAPH-7387}) missing from font(s) Arial. C:\python\py\3.py:140: UserWarning: Glyph 20010 (\N{CJK UNIFIED IDEOGRAPH-4E2A}) missing from font(s) Arial. C:\python\py\3.py:140: UserWarning: Glyph 26679 (\N{CJK UNIFIED IDEOGRAPH-6837}) missing from font(s) Arial. C:\python\py\3.py:140: UserWarning: Glyph 26412 (\N{CJK UNIFIED IDEOGRAPH-672C}) missing from font(s) Arial. C:\python\py\3.py:141: UserWarning: Glyph 27491 (\N{CJK UNIFIED IDEOGRAPH-6B63}) missing from font(s) Arial. C:\python\py\3.py:141: UserWarning: Glyph 30830 (\N{CJK UNIFIED IDEOGRAPH-786E}) missing from font(s) Arial. C:\python\py\3.py:141: UserWarning: Glyph 38169 (\N{CJK UNIFIED IDEOGRAPH-9519}) missing from font(s) Arial. C:\python\py\3.py:141: UserWarning: Glyph 35823 (\N{CJK UNIFIED IDEOGRAPH-8BEF}) missing from font(s) Arial. C:\python\py\3.py:141: UserWarning: Glyph 39044 (\N{CJK UNIFIED IDEOGRAPH-9884}) missing from font(s) Arial. C:\python\py\3.py:141: UserWarning: Glyph 27979 (\N{CJK UNIFIED IDEOGRAPH-6D4B}) missing from font(s) Arial. C:\python\py\3.py:141: UserWarning: Glyph 32467 (\N{CJK UNIFIED IDEOGRAPH-7ED3}) missing from font(s) Arial. C:\python\py\3.py:141: UserWarning: Glyph 26524 (\N{CJK UNIFIED IDEOGRAPH-679C}) missing from font(s) Arial. C:\python\py\3.py:141: UserWarning: Glyph 26041 (\N{CJK UNIFIED IDEOGRAPH-65B9}) missing from font(s) Arial. C:\python\py\3.py:141: UserWarning: Glyph 24046 (\N{CJK UNIFIED IDEOGRAPH-5DEE}) missing from font(s) Arial. C:\python\py\3.py:141: UserWarning: Glyph 19981 (\N{CJK UNIFIED IDEOGRAPH-4E0D}) missing from font(s) Arial. C:\python\py\3.py:141: UserWarning: Glyph 23450 (\N{CJK UNIFIED IDEOGRAPH-5B9A}) missing from font(s) Arial. C:\python\py\3.py:141: UserWarning: Glyph 24615 (\N{CJK UNIFIED IDEOGRAPH-6027}) missing from font(s) Arial. C:\python\py\3.py:141: UserWarning: Glyph 19982 (\N{CJK UNIFIED IDEOGRAPH-4E0E}) missing from font(s) Arial. C:\python\py\3.py:141: UserWarning: Glyph 20998 (\N{CJK UNIFIED IDEOGRAPH-5206}) missing from font(s) Arial. C:\python\py\3.py:141: UserWarning: Glyph 31867 (\N{CJK UNIFIED IDEOGRAPH-7C7B}) missing from font(s) Arial. C:\python\py\3.py:141: UserWarning: Glyph 20934 (\N{CJK UNIFIED IDEOGRAPH-51C6}) missing from font(s) Arial. C:\python\py\3.py:141: UserWarning: Glyph 29575 (\N{CJK UNIFIED IDEOGRAPH-7387}) missing from font(s) Arial. C:\python\py\3.py:141: UserWarning: Glyph 20010 (\N{CJK UNIFIED IDEOGRAPH-4E2A}) missing from font(s) Arial. C:\python\py\3.py:141: UserWarning: Glyph 26679 (\N{CJK UNIFIED IDEOGRAPH-6837}) missing from font(s) Arial. C:\python\py\3.py:141: UserWarning: Glyph 26412 (\N{CJK UNIFIED IDEOGRAPH-672C}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 27491 (\N{CJK UNIFIED IDEOGRAPH-6B63}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 30830 (\N{CJK UNIFIED IDEOGRAPH-786E}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 38169 (\N{CJK UNIFIED IDEOGRAPH-9519}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 35823 (\N{CJK UNIFIED IDEOGRAPH-8BEF}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 39044 (\N{CJK UNIFIED IDEOGRAPH-9884}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 27979 (\N{CJK UNIFIED IDEOGRAPH-6D4B}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 32467 (\N{CJK UNIFIED IDEOGRAPH-7ED3}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 26524 (\N{CJK UNIFIED IDEOGRAPH-679C}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 26041 (\N{CJK UNIFIED IDEOGRAPH-65B9}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 24046 (\N{CJK UNIFIED IDEOGRAPH-5DEE}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 19981 (\N{CJK UNIFIED IDEOGRAPH-4E0D}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 23450 (\N{CJK UNIFIED IDEOGRAPH-5B9A}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 24615 (\N{CJK UNIFIED IDEOGRAPH-6027}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 19982 (\N{CJK UNIFIED IDEOGRAPH-4E0E}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 20998 (\N{CJK UNIFIED IDEOGRAPH-5206}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 31867 (\N{CJK UNIFIED IDEOGRAPH-7C7B}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 20934 (\N{CJK UNIFIED IDEOGRAPH-51C6}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 29575 (\N{CJK UNIFIED IDEOGRAPH-7387}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 20010 (\N{CJK UNIFIED IDEOGRAPH-4E2A}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 26679 (\N{CJK UNIFIED IDEOGRAPH-6837}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 26412 (\N{CJK UNIFIED IDEOGRAPH-672C}) missing from font(s) Arial. C:\python\py\3.py:185: UserWarning: Glyph 26041 (\N{CJK UNIFIED IDEOGRAPH-65B9}) missing from font(s) Arial. C:\python\py\3.py:185: UserWarning: Glyph 24046 (\N{CJK UNIFIED IDEOGRAPH-5DEE}) missing from font(s) Arial. C:\python\py\3.py:185: UserWarning: Glyph 21306 (\N{CJK UNIFIED IDEOGRAPH-533A}) missing from font(s) Arial. C:\python\py\3.py:185: UserWarning: Glyph 38388 (\N{CJK UNIFIED IDEOGRAPH-95F4}) missing from font(s) Arial. C:\python\py\3.py:185: UserWarning: Glyph 19981 (\N{CJK UNIFIED IDEOGRAPH-4E0D}) missing from font(s) Arial. C:\python\py\3.py:185: UserWarning: Glyph 21516 (\N{CJK UNIFIED IDEOGRAPH-540C}) missing from font(s) Arial. C:\python\py\3.py:185: UserWarning: Glyph 30340 (\N{CJK UNIFIED IDEOGRAPH-7684}) missing from font(s) Arial. C:\python\py\3.py:185: UserWarning: Glyph 39044 (\N{CJK UNIFIED IDEOGRAPH-9884}) missing from font(s) Arial. C:\python\py\3.py:185: UserWarning: Glyph 27979 (\N{CJK UNIFIED IDEOGRAPH-6D4B}) missing from font(s) Arial. C:\python\py\3.py:185: UserWarning: Glyph 27491 (\N{CJK UNIFIED IDEOGRAPH-6B63}) missing from font(s) Arial. C:\python\py\3.py:185: UserWarning: Glyph 30830 (\N{CJK UNIFIED IDEOGRAPH-786E}) missing from font(s) Arial. C:\python\py\3.py:185: UserWarning: Glyph 29575 (\N{CJK UNIFIED IDEOGRAPH-7387}) missing from font(s) Arial. C:\python\py\3.py:185: UserWarning: Glyph 24635 (\N{CJK UNIFIED IDEOGRAPH-603B}) missing from font(s) Arial. C:\python\py\3.py:185: UserWarning: Glyph 20307 (\N{CJK UNIFIED IDEOGRAPH-4F53}) missing from font(s) Arial. C:\python\py\3.py:185: UserWarning: Glyph 20934 (\N{CJK UNIFIED IDEOGRAPH-51C6}) missing from font(s) Arial. C:\python\py\3.py:185: UserWarning: Glyph 26679 (\N{CJK UNIFIED IDEOGRAPH-6837}) missing from font(s) Arial. C:\python\py\3.py:185: UserWarning: Glyph 26412 (\N{CJK UNIFIED IDEOGRAPH-672C}) missing from font(s) Arial. C:\python\py\3.py:186: UserWarning: Glyph 26041 (\N{CJK UNIFIED IDEOGRAPH-65B9}) missing from font(s) Arial. C:\python\py\3.py:186: UserWarning: Glyph 24046 (\N{CJK UNIFIED IDEOGRAPH-5DEE}) missing from font(s) Arial. C:\python\py\3.py:186: UserWarning: Glyph 21306 (\N{CJK UNIFIED IDEOGRAPH-533A}) missing from font(s) Arial. C:\python\py\3.py:186: UserWarning: Glyph 38388 (\N{CJK UNIFIED IDEOGRAPH-95F4}) missing from font(s) Arial. C:\python\py\3.py:186: UserWarning: Glyph 19981 (\N{CJK UNIFIED IDEOGRAPH-4E0D}) missing from font(s) Arial. C:\python\py\3.py:186: UserWarning: Glyph 21516 (\N{CJK UNIFIED IDEOGRAPH-540C}) missing from font(s) Arial. C:\python\py\3.py:186: UserWarning: Glyph 30340 (\N{CJK UNIFIED IDEOGRAPH-7684}) missing from font(s) Arial. C:\python\py\3.py:186: UserWarning: Glyph 39044 (\N{CJK UNIFIED IDEOGRAPH-9884}) missing from font(s) Arial. C:\python\py\3.py:186: UserWarning: Glyph 27979 (\N{CJK UNIFIED IDEOGRAPH-6D4B}) missing from font(s) Arial. C:\python\py\3.py:186: UserWarning: Glyph 27491 (\N{CJK UNIFIED IDEOGRAPH-6B63}) missing from font(s) Arial. C:\python\py\3.py:186: UserWarning: Glyph 30830 (\N{CJK UNIFIED IDEOGRAPH-786E}) missing from font(s) Arial. C:\python\py\3.py:186: UserWarning: Glyph 29575 (\N{CJK UNIFIED IDEOGRAPH-7387}) missing from font(s) Arial. C:\python\py\3.py:186: UserWarning: Glyph 24635 (\N{CJK UNIFIED IDEOGRAPH-603B}) missing from font(s) Arial. C:\python\py\3.py:186: UserWarning: Glyph 20307 (\N{CJK UNIFIED IDEOGRAPH-4F53}) missing from font(s) Arial. C:\python\py\3.py:186: UserWarning: Glyph 20934 (\N{CJK UNIFIED IDEOGRAPH-51C6}) missing from font(s) Arial. C:\python\py\3.py:186: UserWarning: Glyph 26679 (\N{CJK UNIFIED IDEOGRAPH-6837}) missing from font(s) Arial. C:\python\py\3.py:186: UserWarning: Glyph 26412 (\N{CJK UNIFIED IDEOGRAPH-672C}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 26041 (\N{CJK UNIFIED IDEOGRAPH-65B9}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 24046 (\N{CJK UNIFIED IDEOGRAPH-5DEE}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 21306 (\N{CJK UNIFIED IDEOGRAPH-533A}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 38388 (\N{CJK UNIFIED IDEOGRAPH-95F4}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 19981 (\N{CJK UNIFIED IDEOGRAPH-4E0D}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 21516 (\N{CJK UNIFIED IDEOGRAPH-540C}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 30340 (\N{CJK UNIFIED IDEOGRAPH-7684}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 39044 (\N{CJK UNIFIED IDEOGRAPH-9884}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 27979 (\N{CJK UNIFIED IDEOGRAPH-6D4B}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 27491 (\N{CJK UNIFIED IDEOGRAPH-6B63}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 30830 (\N{CJK UNIFIED IDEOGRAPH-786E}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 29575 (\N{CJK UNIFIED IDEOGRAPH-7387}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 24635 (\N{CJK UNIFIED IDEOGRAPH-603B}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 20307 (\N{CJK UNIFIED IDEOGRAPH-4F53}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 20934 (\N{CJK UNIFIED IDEOGRAPH-51C6}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 26679 (\N{CJK UNIFIED IDEOGRAPH-6837}) missing from font(s) Arial. C:\python\PyCharm 2024.3.5\plugins\python-ce\helpers\pycharm_matplotlib_backend\backend_interagg.py:124: UserWarning: Glyph 26412 (\N{CJK UNIFIED IDEOGRAPH-672C}) missing from font(s) Arial. choreographer.browsers.chromium.ChromeNotFoundError The above exception was the direct cause of the following exception: Traceback (most recent call last): File "C:\python\py\.venv\Lib\site-packages\plotly\io\_kaleido.py", line 380, in to_image img_bytes = kaleido.calc_fig_sync( fig_dict, ...<7 lines>... kopts=kopts, ) File "C:\python\py\.venv\Lib\site-packages\kaleido\__init__.py", line 145, in calc_fig_sync return _async_thread_run(calc_fig, args=args, kwargs=kwargs) File "C:\python\py\.venv\Lib\site-packages\kaleido\__init__.py", line 138, in _async_thread_run raise res File "C:\python\py\.venv\Lib\site-packages\kaleido\__init__.py", line 129, in run q.put(asyncio.run(func(*args, **kwargs))) ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\Ran\AppData\Local\Programs\Python\Python313\Lib\asyncio\runners.py", line 195, in run return runner.run(main) ~~~~~~~~~~^^^^^^ File "C:\Users\Ran\AppData\Local\Programs\Python\Python313\Lib\asyncio\runners.py", line 118, in run return self._loop.run_until_complete(task) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "C:\Users\Ran\AppData\Local\Programs\Python\Python313\Lib\asyncio\base_events.py", line 725, in run_until_complete return future.result() ~~~~~~~~~~~~~^^ File "C:\python\py\.venv\Lib\site-packages\kaleido\__init__.py", line 54, in calc_fig async with Kaleido(**kopts) as k: ~~~~~~~^^^^^^^^^ File "C:\python\py\.venv\Lib\site-packages\kaleido\kaleido.py", line 128, in __init__ raise ChromeNotFoundError( ...<4 lines>... ) from ChromeNotFoundError choreographer.browsers.chromium.ChromeNotFoundError: Kaleido v1 and later requires Chrome to be installed. To install Chrome, use the CLI command `kaleido_get_chrome`, or from Python, use either `kaleido.get_chrome()` or `kaleido.get_chrome_sync()`. During handling of the above exception, another exception occurred: Traceback (most recent call last): File "C:\python\py\3.py", line 356, in <module> save_all_figures() ~~~~~~~~~~~~~~~~^^ File "C:\python\py\3.py", line 270, in save_all_figures fig.write_image('4_box_trend.png', scale=3) ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\python\py\.venv\Lib\site-packages\plotly\basedatatypes.py", line 3895, in write_image return pio.write_image(self, *args, **kwargs) ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^ File "C:\python\py\.venv\Lib\site-packages\plotly\io\_kaleido.py", line 510, in write_image img_data = to_image( fig, ...<5 lines>... engine=engine, ) File "C:\python\py\.venv\Lib\site-packages\plotly\io\_kaleido.py", line 392, in to_image raise RuntimeError(PLOTLY_GET_CHROME_ERROR_MSG) RuntimeError: Kaleido requires Google Chrome to be installed. Either download and install Chrome yourself following Google's instructions for your operating system, or install it from your terminal by running: $ plotly_get_chrome 进程已结束,退出代码为 1
07-31
# -*- coding: utf-8 -*- import threading import time import sys import inspect import ctypes import os import cv2 import numpy as np import logging from MvCameraControl_class import * from datetime import datetime from ctypes import * from enum import Enum # 配置日志系统 def setup_logging(log_level=logging.INFO): """配置全局日志系统""" logging.basicConfig( level=log_level, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler("camera_operation.log"), logging.StreamHandler() ] ) logging.info("日志系统初始化完成") # 强制关闭线程 def async_raise(tid, exctype): """安全终止线程""" tid = ctypes.c_long(tid) if not inspect.isclass(exctype): exctype = type(exctype) res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype)) if res == 0: raise ValueError("invalid thread id") elif res != 1: ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None) raise SystemError("PyThreadState_SetAsyncExc failed") def stop_thread(thread): """停止指定线程""" async_raise(thread.ident, SystemExit) # 像素格式常量定义(兼容不同SDK版本)tr try: from CameraParams_header import * # 如果导入成功,使用SDK中的常量定义 PIXEL_FORMATS = { "MONO8": PixelType_Gvsp_Mono8, "MONO10": PixelType_Gvsp_Mono10, "MONO12": PixelType_Gvsp_Mono12, "BAYER_BG8": PixelType_Gvsp_BayerBG8, "BAYER_GB8": PixelType_Gvsp_BayerGB8, "BAYER_GR8": PixelType_Gvsp_BayerGR8, "BAYER_RG8": PixelType_Gvsp_BayerRG8, "RGB8": PixelType_Gvsp_RGB8_Packed, "YUV422": PixelType_Gvsp_YUV422_Packed, "YUV422_YUYV": PixelType_Gvsp_YUV422_YUYV_Packed } except ImportError: # 如果导入失败,使用默认值(兼容旧版本) logging.warning("CameraParams_header 导入失败,使用默认像素格式常量") PIXEL_FORMATS = { "MONO8": 0x01080001, "MONO10": 0x01100003, "MONO12": 0x01100005, "BAYER_BG8": 0x0108000B, "BAYER_GB8": 0x0108000A, "BAYER_GR8": 0x01080008, "BAYER_RG8": 0x01080009, "RGB8": 0x02180014, "YUV422": 0x02100032, "YUV422_YUYV": 0x0210001F } # 像素格式枚举 class PixelFormat(Enum): MONO8 = PIXEL_FORMATS["MONO8"] MONO10 = PIXEL_FORMATS["MONO10"] MONO12 = PIXEL_FORMATS["MONO12"] BAYER_BG8 = PIXEL_FORMATS["BAYER_BG8"] BAYER_GB8 = PIXEL_FORMATS["BAYER_GB8"] BAYER_GR8 = PIXEL_FORMATS["BAYER_GR8"] BAYER_RG8 = PIXEL_FORMATS["BAYER_RG8"] RGB8 = PIXEL_FORMATS["RGB8"] YUV422 = PIXEL_FORMATS["YUV422"] YUV422_YUYV = PIXEL_FORMATS["YUV422_YUYV"] def is_mono_data(enGvspPixelType): """判断是否为单色图像格式""" mono_formats = [ PIXEL_FORMATS["MONO8"], PIXEL_FORMATS["MONO10"], PIXEL_FORMATS["MONO12"], 0x010C0004, # Mono10 Packed 0x010C0006, # Mono12 Packed 0x01080002, # Mono8 Signed 0x0110000C # Mono16 ] return enGvspPixelType in mono_formats def is_color_data(enGvspPixelType): """判断是否为彩色图像格式""" color_formats = [ # Bayer格式 PIXEL_FORMATS["BAYER_BG8"], PIXEL_FORMATS["BAYER_GB8"], PIXEL_FORMATS["BAYER_GR8"], PIXEL_FORMATS["BAYER_RG8"], 0x01100011, # BayerBG10 0x01100010, # BayerGB10 0x0110000E, # BayerGR10 0x0110000F, # BayerRG10 0x01100017, # BayerBG12 0x01100016, # BayerGB12 0x01100014, # BayerGR12 0x01100015, # BayerRG12 # YUV格式 PIXEL_FORMATS["YUV422"], PIXEL_FORMATS["YUV422_YUYV"], # RGB格式 PIXEL_FORMATS["RGB8"], 0x0220001E, # RGB10_Packed 0x02300020, # RGB12_Packed 0x02400021, # RGB16_Packed 0x02180032 # BGR8_Packed ] return enGvspPixelType in color_formats def get_pixel_format_name(pixel_value): """根据像素值获取可读的名称""" for name, value in PIXEL_FORMATS.items(): if value == pixel_value: return name return f"未知格式: 0x{pixel_value:08X}" # 相机操作类 class CameraOperation: # 状态码定义 MV_OK = 0 MV_E_CALLORDER = -2147483647 MV_E_PARAMETER = -2147483646 MV_E_NO_DATA = -2147483645 MV_E_SAVE_IMAGE = -2147483644 MV_E_STATE = -2147483643 def __init__(self, obj_cam, st_device_list, n_connect_num=0): """ 初始化相机操作对象 :param obj_cam: 相机对象 :param st_device_list: 设备列表 :param n_connect_num: 连接编号 """ self.obj_cam = obj_cam self.st_device_list = st_device_list self.n_connect_num = n_connect_num # 状态标志 self.b_open_device = False self.b_start_grabbing = False self.b_thread_running = False self.b_exit = False # 线程相关 self.h_thread_handle = None # 图像数据 self.st_frame_info = None self.buf_save_image = None self.n_save_image_size = 0 self.current_frame = None # 参数 self.frame_rate = 0.0 self.exposure_time = 0.0 self.gain = 0.0 # 线程安全锁 self.buf_lock = threading.Lock() # 图像缓冲区锁 self.frame_lock = threading.Lock() # 当前帧锁 self.param_lock = threading.Lock() # 参数锁 logging.info("相机操作对象初始化完成") def is_ready(self): """检查设备是否就绪""" return self.b_open_device and not self.b_exit def open_device(self): """打开相机设备""" if self.b_open_device: logging.warning("设备已打开,无需重复操作") return self.MV_OK if self.n_connect_num < 0: logging.error("无效的连接编号") return self.MV_E_CALLORDER try: # 选择设备并创建句柄 nConnectionNum = int(self.n_connect_num) stDeviceList = cast(self.st_device_list.pDeviceInfo[int(nConnectionNum)], POINTER(MV_CC_DEVICE_INFO)).contents self.obj_cam = MvCamera() # 创建相机句柄 ret = self.obj_cam.MV_CC_CreateHandle(stDeviceList) if ret != self.MV_OK: self.obj_cam.MV_CC_DestroyHandle() logging.error(f"创建句柄失败: {hex(ret)}") return ret # 打开设备 ret = self.obj_cam.MV_CC_OpenDevice() if ret != self.MV_OK: logging.error(f"打开设备失败: {hex(ret)}") return ret # 探测网络最佳包大小(仅对GigE相机有效) if stDeviceList.nTLayerType in [MV_GIGE_DEVICE, MV_GENTL_GIGE_DEVICE]: nPacketSize = self.obj_cam.MV_CC_GetOptimalPacketSize() if int(nPacketSize) > 0: ret = self.obj_cam.MV_CC_SetIntValue("GevSCPSPacketSize", nPacketSize) if ret != self.MV_OK: logging.warning(f"设置包大小失败: {hex(ret)}") else: logging.warning(f"获取最佳包大小失败: {hex(nPacketSize)}") # 获取采集帧率使能状态 stBool = c_bool(False) ret = self.obj_cam.MV_CC_GetBoolValue("AcquisitionFrameRateEnable", stBool) if ret != self.MV_OK: logging.warning(f"获取帧率使能状态失败: {hex(ret)}") # 设置默认触发模式为关闭 ret = self.set_continue_mode() if ret != self.MV_OK: logging.warning(f"设置连续模式失败: {hex(ret)}") self.b_open_device = True self.b_exit = False logging.info("设备打开成功") return self.MV_OK except Exception as e: logging.exception(f"打开设备异常: {str(e)}") return self.MV_E_STATE def close_device(self): """关闭相机设备""" if not self.b_open_device: logging.warning("设备未打开,无需关闭") return self.MV_OK try: # 停止采集(如果正在采集) if self.b_start_grabbing: self.stop_grabbing() # 关闭设备 ret = self.obj_cam.MV_CC_CloseDevice() if ret != self.MV_OK: logging.error(f"关闭设备失败: {hex(ret)}") # 销毁句柄 ret = self.obj_cam.MV_CC_DestroyHandle() if ret != self.MV_OK: logging.error(f"销毁句柄失败: {hex(ret)}") self.b_open_device = False self.b_exit = True logging.info("设备关闭成功") return self.MV_OK except Exception as e: logging.exception(f"关闭设备异常: {str(e)}") return self.MV_E_STATE def start_grabbing(self, winHandle=None): """开始图像采集""" if not self.b_open_device: logging.error("设备未打开,无法开始采集") return self.MV_E_CALLORDER if self.b_start_grabbing: logging.warning("采集已在进行中") return self.MV_OK try: # 开始采集 ret = self.obj_cam.MV_CC_StartGrabbing() if ret != self.MV_OK: logging.error(f"开始采集失败: {hex(ret)}") return ret self.b_start_grabbing = True self.b_exit = False # 启动采集线程 self.h_thread_handle = threading.Thread( target=self.work_thread, args=(winHandle,), daemon=True ) self.h_thread_handle.start() self.b_thread_running = True logging.info("图像采集已开始") return self.MV_OK except Exception as e: logging.exception(f"开始采集异常: {str(e)}") return self.MV_E_STATE def stop_grabbing(self): """停止图像采集""" if not self.b_open_device: logging.error("设备未打开,无法停止采集") return self.MV_E_CALLORDER if not self.b_start_grabbing: logging.warning("采集未在进行中") return self.MV_OK try: # 设置退出标志 self.b_exit = True # 等待线程结束 if self.b_thread_running and self.h_thread_handle.is_alive(): self.h_thread_handle.join(timeout=2.0) if self.h_thread_handle.is_alive(): logging.warning("采集线程未正常退出,尝试强制终止") stop_thread(self.h_thread_handle) # 停止采集 ret = self.obj_cam.MV_CC_StopGrabbing() if ret != self.MV_OK: logging.error(f"停止采集失败: {hex(ret)}") return ret self.b_start_grabbing = False self.b_thread_running = False logging.info("图像采集已停止") return self.MV_OK except Exception as e: logging.exception(f"停止采集异常: {str(e)}") return self.MV_E_STATE def set_trigger_mode(self, enable=True, source=MV_TRIGGER_SOURCE_SOFTWARE): """ 设置触发模式 :param enable: 是否启用触发模式 :param source: 触发源 (默认软件触发) :return: 操作结果 """ if not self.b_open_device: logging.error("设备未打开,无法设置触发模式") return self.MV_E_CALLORDER try: mode = MV_TRIGGER_MODE_ON if enable else MV_TRIGGER_MODE_OFF ret = self.obj_cam.MV_CC_SetEnumValue("TriggerMode", mode) if ret != self.MV_OK: logging.error(f"设置触发模式失败: {hex(ret)}") return ret if enable: ret = self.obj_cam.MV_CC_SetEnumValue("TriggerSource", source) if ret != self.MV_OK: logging.error(f"设置触发源失败: {hex(ret)}") return ret logging.info(f"触发模式设置成功: {'启用' if enable else '禁用'}") return self.MV_OK except Exception as e: logging.exception(f"设置触发模式异常: {str(e)}") return self.MV_E_STATE def set_continue_mode(self): """设置连续采集模式""" return self.set_trigger_mode(enable=False) def trigger_once(self): """执行一次软触发""" if not self.b_open_device: logging.error("设备未打开,无法触发") return self.MV_E_CALLORDER try: ret = self.obj_cam.MV_CC_SetCommandValue("TriggerSoftware") if ret != self.MV_OK: logging.error(f"软触发失败: {hex(ret)}") return ret logging.info("软触发成功") return self.MV_OK except Exception as e: logging.exception(f"软触发异常: {str(e)}") return self.MV_E_STATE def get_parameters(self): """获取相机参数""" if not self.b_open_device: logging.error("设备未打开,无法获取参数") return self.MV_E_CALLORDER try: # 使用锁保护参数访问 with self.param_lock: # 帧率 stFrameRate = MVCC_FLOATVALUE() memset(byref(stFrameRate), 0, sizeof(MVCC_FLOATVALUE)) ret = self.obj_cam.MV_CC_GetFloatValue("AcquisitionFrameRate", stFrameRate) if ret == self.MV_OK: self.frame_rate = stFrameRate.fCurValue # 曝光时间 stExposure = MVCC_FLOATVALUE() memset(byref(stExposure), 0, sizeof(MVCC_FLOATVALUE)) ret = self.obj_cam.MV_CC_GetFloatValue("ExposureTime", stExposure) if ret == self.MV_OK: self.exposure_time = stExposure.fCurValue # 增益 stGain = MVCC_FLOATVALUE() memset(byref(stGain), 0, sizeof(MVCC_FLOATVALUE)) ret = self.obj_cam.MV_CC_GetFloatValue("Gain", stGain) if ret == self.MV_OK: self.gain = stGain.fCurValue logging.info(f"参数获取成功: 帧率={self.frame_rate}, 曝光={self.exposure_time}, 增益={self.gain}") return self.MV_OK except Exception as e: logging.exception(f"获取参数异常: {str(e)}") return self.MV_E_STATE def set_parameters(self, frame_rate=None, exposure_time=None, gain=None): """ 设置相机参数 :param frame_rate: 帧率 (None表示不修改) :param exposure_time: 曝光时间 (None表示不修改) :param gain: 增益 (None表示不修改) :return: 操作结果 """ if not self.b_open_device: logging.error("设备未打开,无法设置参数") return self.MV_E_CALLORDER try: # 使用锁保护参数修改 with self.param_lock: # 禁用自动曝光 ret = self.obj_cam.MV_CC_SetEnumValue("ExposureAuto", 0) if ret != self.MV_OK: logging.warning(f"禁用自动曝光失败: {hex(ret)}") # 设置帧率 if frame_rate is not None: ret = self.obj_cam.MV_CC_SetFloatValue("AcquisitionFrameRate", float(frame_rate)) if ret != self.MV_OK: logging.error(f"设置帧率失败: {hex(ret)}") return ret self.frame_rate = float(frame_rate) # 设置曝光时间 if exposure_time is not None: ret = self.obj_cam.MV_CC_SetFloatValue("ExposureTime", float(exposure_time)) if ret != self.MV_OK: logging.error(f"设置曝光时间失败: {hex(ret)}") return ret self.exposure_time = float(exposure_time) # 设置增益 if gain is not None: ret = self.obj_cam.MV_CC_SetFloatValue("Gain", float(gain)) if ret != self.MV_OK: logging.error(f"设置增益失败: {hex(ret)}") return ret self.gain = float(gain) logging.info(f"参数设置成功: 帧率={self.frame_rate}, 曝光={self.exposure_time}, 增益={self.gain}") return self.MV_OK except Exception as e: logging.exception(f"设置参数异常: {str(e)}") return self.MV_E_STATE def work_thread(self, winHandle=None): """图像采集工作线程""" stOutFrame = MV_FRAME_OUT() memset(byref(stOutFrame), 0, sizeof(stOutFrame)) logging.info("采集线程启动") while not self.b_exit: try: # 获取图像缓冲区 ret = self.obj_cam.MV_CC_GetImageBuffer(stOutFrame, 1000) if ret != self.MV_OK: if ret != MV_E_NO_DATA: # 忽略无数据错误 logging.warning(f"获取图像缓冲区失败: {hex(ret)}") time.sleep(0.01) continue # 更新帧信息 self.st_frame_info = stOutFrame.stFrameInfo # 记录像素格式信息 pixel_format = get_pixel_format_name(self.st_frame_info.enPixelType) logging.debug(f"获取到图像: {self.st_frame_info.nWidth}x{self.st_frame_info.nHeight}, " f"格式: {pixel_format}, 大小: {self.st_frame_info.nFrameLen}字节") # 分配/更新图像缓冲区 frame_size = stOutFrame.stFrameInfo.nFrameLen with self.buf_lock: if self.buf_save_image is None or self.n_save_image_size < frame_size: self.buf_save_image = (c_ubyte * frame_size)() self.n_save_image_size = frame_size # 复制图像数据 cdll.msvcrt.memcpy(byref(self.buf_save_image), stOutFrame.pBufAddr, frame_size) # 更新当前帧 self.update_current_frame() # 显示图像(如果指定了窗口句柄) if winHandle is not None: stDisplayParam = MV_DISPLAY_FRAME_INFO() memset(byref(stDisplayParam), 0, sizeof(stDisplayParam)) stDisplayParam.hWnd = int(winHandle) stDisplayParam.nWidth = self.st_frame_info.nWidth stDisplayParam.nHeight = self.st_frame_info.nHeight stDisplayParam.enPixelType = self.st_frame_info.enPixelType stDisplayParam.pData = self.buf_save_image stDisplayParam.nDataLen = frame_size self.obj_cam.MV_CC_DisplayOneFrame(stDisplayParam) # 释放图像缓冲区 self.obj_cam.MV_CC_FreeImageBuffer(stOutFrame) except Exception as e: logging.exception(f"采集线程异常: {str(e)}") time.sleep(0.1) # 清理资源 with self.buf_lock: if self.buf_save_image is not None: del self.buf_save_image self.buf_save_image = None self.n_save_image_size = 0 logging.info("采集线程退出") def update_current_frame(self): """将原始图像数据转换为OpenCV格式""" if not self.st_frame_info or not self.buf_save_image: return try: # 获取图像信息 width = self.st_frame_info.nWidth height = self.st_frame_info.nHeight pixel_type = self.st_frame_info.enPixelType # 复制缓冲区数据 with self.buf_lock: buffer_copy = bytearray(self.buf_save_image) # 转换为numpy数组 np_buffer = np.frombuffer(buffer_copy, dtype=np.uint8) # 根据像素类型处理图像 frame = None if is_mono_data(pixel_type): # 单色图像 frame = np_buffer.reshape(height, width) elif pixel_type == PIXEL_FORMATS["BAYER_BG8"]: # Bayer BG格式 frame = cv2.cvtColor(np_buffer.reshape(height, width), cv2.COLOR_BayerBG2RGB) elif pixel_type == PIXEL_FORMATS["BAYER_GB8"]: # Bayer GB格式 frame = cv2.cvtColor(np_buffer.reshape(height, width), cv2.COLOR_BayerGB2RGB) elif pixel_type == PIXEL_FORMATS["BAYER_GR8"]: # Bayer GR格式 frame = cv2.cvtColor(np_buffer.reshape(height, width), cv2.COLOR_BayerGR2RGB) elif pixel_type == PIXEL_FORMATS["BAYER_RG8"]: # Bayer RG格式 frame = cv2.cvtColor(np_buffer.reshape(height, width), cv2.COLOR_BayerRG2RGB) elif pixel_type == PIXEL_FORMATS["RGB8"]: # RGB格式 frame = np_buffer.reshape(height, width, 3) elif pixel_type in [PIXEL_FORMATS["YUV422"], PIXEL_FORMATS["YUV422_YUYV"]]: # YUV格式 frame = cv2.cvtColor(np_buffer.reshape(height, width, 2), cv2.COLOR_YUV2RGB_YUYV) else: # 尝试自动处理其他格式 if pixel_type in [PIXEL_FORMATS["MONO10"], PIXEL_FORMATS["MONO12"]]: # 10位或12位单色图像需要特殊处理 frame = self.process_high_bit_depth(np_buffer, width, height, pixel_type) else: logging.warning(f"不支持的像素格式: {get_pixel_format_name(pixel_type)}") return # 更新当前帧 with self.frame_lock: self.current_frame = frame except Exception as e: logging.exception(f"更新当前帧异常: {str(e)}") def process_high_bit_depth(self, buffer, width, height, pixel_type): """处理高位深度图像格式""" try: # 10位或12位图像处理 if pixel_type == PIXEL_FORMATS["MONO10"]: # 将10位数据转换为16位 data_16bit = np.frombuffer(buffer, dtype=np.uint16) # 10位数据存储方式:每个像素占用2字节,但只有10位有效 data_16bit = (data_16bit >> 6) # 右移6位使10位数据对齐到低10位 frame = data_16bit.reshape(height, width).astype(np.uint16) elif pixel_type == PIXEL_FORMATS["MONO12"]: # 将12位数据转换为16位 data_16bit = np.frombuffer(buffer, dtype=np.uint16) # 12位数据存储方式:每个像素占用2字节,但只有12位有效 data_16bit = (data_16bit >> 4) # 右移4位使12位数据对齐到低12位 frame = data_16bit.reshape(height, width).astype(np.uint16) else: logging.warning(f"不支持的高位深度格式: {get_pixel_format_name(pixel_type)}") return None # 归一化到8位用于显示(如果需要) frame_8bit = cv2.convertScaleAbs(frame, alpha=(255.0/4095.0)) return frame_8bit except Exception as e: logging.exception(f"处理高位深度图像异常: {str(e)}") return None def get_current_frame(self): """获取当前帧的副本""" if not self.current_frame: return None with self.frame_lock: return self.current_frame.copy() def save_image(self, file_path, save_format="bmp", quality=95): """ 保存当前帧到文件 :param file_path: 文件路径 :param save_format: 图像格式 (bmp, png, jpg/jpeg, tiff) :param quality: 压缩质量 (0-100),仅对JPEG有效 :return: 操作结果 """ if not self.b_open_device or not self.b_start_grabbing: logging.error("设备未就绪,无法保存图像") return self.MV_E_CALLORDER frame = self.get_current_frame() if frame is None: logging.error("无可用图像数据") return self.MV_E_NO_DATA try: # 确保目录存在 os.makedirs(os.path.dirname(file_path), exist_ok=True) # 根据格式保存图像 save_format = save_format.lower() if save_format == "bmp": cv2.imwrite(file_path, frame) elif save_format in ["jpg", "jpeg"]: cv2.imwrite(file_path, frame, [cv2.IMWRITE_JPEG_QUALITY, quality]) elif save_format == "png": cv2.imwrite(file_path, frame, [cv2.IMWRITE_PNG_COMPRESSION, 9]) elif save_format == "tiff": cv2.imwrite(file_path, frame, [cv2.IMWRITE_TIFF_COMPRESSION, 1]) else: logging.error(f"不支持的图像格式: {save_format}") return self.MV_E_PARAMETER logging.info(f"图像已保存: {file_path}") return self.MV_OK except Exception as e: logging.exception(f"保存图像异常: {str(e)}") return self.MV_E_SAVE_IMAGE # 兼容旧方法的保存接口 def save_jpg(self, file_path=None, quality=95): """保存为JPEG格式""" if file_path is None: timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") file_path = f"capture_{timestamp}.jpg" return self.save_image(file_path, "jpg", quality) def save_bmp(self, file_path=None): """保存为BMP格式""" if file_path is None: timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") file_path = f"capture_{timestamp}.bmp" return self.save_image(file_path, "bmp") # 使用示例 if __name__ == "__main__": # 初始化日志 setup_logging(logging.DEBUG) try: # 枚举设备 deviceList = MV_CC_DEVICE_INFO_LIST() memset(byref(deviceList), 0, sizeof(deviceList)) ret = MvCamera.MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, deviceList) if ret != MV_OK or deviceList.nDeviceNum == 0: logging.error("未找到相机设备") sys.exit(1) # 创建相机操作对象 cam = MvCamera() cam_op = CameraOperation(cam, deviceList, 0) # 打开设备 ret = cam_op.open_device() if ret != CameraOperation.MV_OK: logging.error("打开设备失败") sys.exit(1) # 设置参数 cam_op.set_parameters(frame_rate=30, exposure_time=10000, gain=1.0) # 开始采集(无窗口显示) ret = cam_op.start_grabbing() if ret != CameraOperation.MV_OK: logging.error("开始采集失败") cam_op.close_device() sys.exit(1) # 等待几秒 time.sleep(3) # 获取并保存图像 frame = cam_op.get_current_frame() if frame is not None: cv2.imshow("Preview", frame) cv2.waitKey(1000) cam_op.save_image("test_image.jpg", "jpg", 90) # 停止采集 cam_op.stop_grabbing() # 关闭设备 cam_op.close_device() logging.info("程序执行完成") except Exception as e: logging.exception(f"主程序异常: {str(e)}") 这是CamOperation_class.py的全部代码你检查一下
07-08
你可以帮我修改PyCharm上的火灾检测可视化系统吗,要求使界面更加美观高级,增加缩放窗口,运行更加丝滑,具体代码如下import re from ui.parameter_frame import ParameterFrame from ui.parameterdisplay import ParameterDisplay from ui.operation_frame import OperationFrame import sys from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget, QLabel from PyQt5.QtWidgets import QMessageBox, QDialog, QVBoxLayout, QTextEdit, QScrollArea, QPushButton import argparse import os import sys from pathlib import Path import threading import cv2 from ultralytics import YOLO import os # 调用display_results更新图像 from ui.result_frame import ResultFrame from PyQt5.QtWidgets import QFrame, QLabel, QTextEdit from PyQt5.QtWidgets import QPushButton, QApplication from PyQt5.QtGui import QFont from PyQt5.QtGui import QIcon from PyQt5.QtCore import QSize, Qt # 导入 QSize class BackupFrame(QFrame): def __init__(self, parent=None, run_detection=None): super().__init__(parent) self.setStyleSheet("background-color: white; border: 2px solid white;") self.setFixedSize(440, 140) self.setGeometry(20, 440 + 20 + 320, 440, 140) # 你可以在这里添加该框架的其他功能代码 self.set_title() # 添加标题 self.run_detection = run_detection # 保存传入的检测函数 self.is_detecting = True # 创建按钮 self.button1 = QPushButton("运行代码", self) self.button2 = QPushButton("退出系统", self) self.button3 = QPushButton("停止摄像", self) # 新增停止检测按钮 # 设置按钮的位置和大小 self.button1.setGeometry(15, 80, 125, 50) self.button2.setGeometry(300, 80, 125, 50) self.button3.setGeometry(17+140, 80, 125, 50) # 停止按钮位置 # 调整按钮文字大小 font = QFont() font.setPointSize(14) # 设置文字大小为14 self.button1.setFont(font) self.button2.setFont(font) self.button3.setFont(font) # 设置按钮图标 # self.set_button_icon(self.button1, "ui/image/icons_run.png") # 替换为实际路径 # self.set_button_icon(self.button2, "ui/image/icons_Esc.png") # 替换为实际路径 # self.set_button_icon(self.button3, "ui/image/icons_Esc.png") # 替换为停止图标 # 设置按钮的样式(包括凸起和下陷效果) self.set_button_style(self.button1, background_color="#90EE90") self.set_button_style(self.button2, background_color="#FFB6C1") self.set_button_style(self.button3, background_color="#FFFFE0") # 连接按钮的点击事件 self.button1.clicked.connect(self.run_code) self.button2.clicked.connect(self.exit_system) self.button3.clicked.connect(self.stop_detection) # 停止按钮的点击事件 def set_button_icon(self, button, image_path): # 设置按钮的图标 icon = QIcon(image_path) button.setIcon(icon) button.setIconSize(QSize(40, 40)) # 设置图标的大小 def set_button_style(self, button, background_color="#f0f0f0", border_color="#ccc", pressed_color="#ddd"): # 设置按钮的样式 button.setStyleSheet(f""" QPushButton {{ background-color: {background_color}; border: 2px solid {border_color}; border-radius: 10px; padding: 10px 20px; font-size: 20px; font-weight: bold; }} QPushButton:pressed {{ background-color: {pressed_color}; border: 2px solid {border_color}; padding: 10px 18px; /* 点击时按钮稍微变小 */ }} """) def set_title(self): title = QLabel("系统运行与退出", self) # 设置标题文字 title.setFont(QFont("仿宋", 18)) # 设置字体为仿宋,大小为16 title.setAlignment(Qt.AlignCenter) # 让文字居中 title.setGeometry(0, 15, self.width(), 50) # 设置位置,0为X轴,10为顶部间距,宽度为窗口宽度,高度为30 title.setStyleSheet("color: black; font-weight: bold;") # 设置字体颜色为黑色 def stop_detection(self): print("停止检测...") if self.is_detecting: self.is_detecting = False # 设置为 False,停止检测 def run_code(self): print("运行run代码") # 触发YOLO检测 if self.run_detection: self.run_detection() # 调用主窗口中的run_detection方法 def exit_system(self): # 退出系统 print("正在退出系统...") QApplication.quit() # 退出应用程序 class MainWindow(QWidget): # update_image_signal = pyqtSignal(np.ndarray) # 定义信号,传递图像数据 def __init__(self): super().__init__() # 设置窗口标题 self.setWindowTitle("YOLOv8目标检测系统") "设置界面颜色" # self.setStyleSheet("background-color: #FACAC2;") # 设置背景颜色 self.setStyleSheet(""" QWidget { background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #40BCEA, stop: 0.85 #40BCEA, stop: 0.85 #FF99FF, stop: 1 #FF99FF); } """) # 设置窗口大小 # self.setGeometry(100, 100, 800, 600) # 调用 adjustSize() 方法使窗口大小根据内容自动调整 # 界面大小和布局设置 "设置界面大小的参数" screen = QApplication.primaryScreen() # 获取主显示器的屏幕对象。 screen_rect = screen.availableGeometry() # 返回屏幕的可用区域的矩形坐标,包括屏幕的宽度和高度。 width, height = 1500, 950 # 窗口的宽度为 1000,高度为 600 x = (screen_rect.width() - width) // 2 # 计算窗口水平方向的位置,使其居中。 y = (screen_rect.height() - height) // 2 # 计算窗口垂直方向的位置,使其居中。 self.setGeometry(x, y, width, height) # 设置窗口的大小为 1000x600,并将其位置设置为 x, y。 self.setFixedSize(width, height) # 会将窗口的大小固定为 1000x600,这样用户就无法通过拖动窗口边界来改变窗口大小。 # 创建一个垂直布局 self.layout = QVBoxLayout() self.layout.setSpacing(15) # 设置控件之间的间距为15像素 # 创建一个QLabel来显示文字 "在界面中间该界面文字信息代码" self.title_label = QLabel("陈世豪基于YOLOv8火灾检测系统", self) self.title_label.setAlignment(Qt.AlignCenter) # 使文本居中显示 self.layout.addWidget(self.title_label) # 将文字标签添加到布局 self.layout.addStretch(1) # 在布局底部添加一个弹性空间,使文字标签保持在顶部 # 设置文字大小和字体 font = QFont() # 创建一个字体对象 font.setFamily("FangSong") # 设置字体为 Arial(你可以根据需要换成其他字体) font.setPointSize(35) # 设置字体大小为 36 font.setBold(True) # 设置加粗 self.title_label.setFont(font) # 将字体应用到 QLabel 上 self.layout.addWidget(self.title_label) # 将文字标签添加到布局 # 将布局设置为窗口的布局 self.setLayout(self.layout) # 创建并添加框架+ self.result_frame = ResultFrame(self) self.operation_frame = OperationFrame(self) self.parameter_frame = ParameterFrame(self) self.backup_frame = BackupFrame(self) self.backup_frame = BackupFrame(self, self.run_detection) # 将 run_detection 传递给 BackupFrame self.parameter_display = ParameterDisplay(self) self.is_running = True # 将信号连接到 ResultFrame 的 display_image_path_results 方法 self.operation_frame.display_image.connect(self.result_frame.display_image_path_results) def run_detection(self): new_results = [] def run(data_path, model_path='best.pt', conf_thres=0.5, iou_thres=0.45, imgsz=640, save_txt=False, save_conf=False, save_crop=False): """ 执行 YOLO 模型推理 """ # 加载训练好的模型 model = YOLO(model_path) # 使用指定路径的模型 # 创建exp文件夹,并处理文件夹命名 base_dir = 'runs' if not os.path.exists(base_dir): os.makedirs(base_dir) # 查找现有的exp文件夹 exp_dir = os.path.join(base_dir, 'exp') if not os.path.exists(exp_dir): os.makedirs(exp_dir) else: # 如果exp文件夹已存在,生成类似exp1, exp2的文件夹 i = 1 while os.path.exists(os.path.join(base_dir, f'exp{i}')): i += 1 exp_dir = os.path.join(base_dir, f'exp{i}') # 这里 f'exp{i}' 会被自动转换为字符串 os.makedirs(exp_dir) if os.path.isdir(data_path): # 文件夹路径 image_files = [os.path.join(data_path, f) for f in os.listdir(data_path) if f.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff', '.webp', '.heif', '.raw', '.ico', '.jfif'))] video_files = [os.path.join(data_path, f) for f in os.listdir(data_path) if f.lower().endswith(('.mp4', '.avi', '.mov', '.mkv', '.flv', '.webm', '.wmv', '.mpeg', '.mpg', '.3gp'))] # 如果需要保存txt文件,确保创建labels文件夹 if save_txt: labels_dir = os.path.join(exp_dir, 'labels') # labels文件夹路径 if not os.path.exists(labels_dir): os.makedirs(labels_dir) # 创建labels文件夹 # 如果需要保存裁剪图像,确保创建crops文件夹 if save_crop: crops_dir = os.path.join(exp_dir, 'crops') # crops文件夹路径 if not os.path.exists(crops_dir): os.makedirs(crops_dir) # 创建crops文件夹 # 处理图片文件 for image_file in image_files: # 获取原始图像 original_image = cv2.imread(image_file) results = model.predict(image_file, conf=conf_thres, iou=iou_thres, imgsz=imgsz)[0] im0 = results.plot() # 获取带检测框的 numpy.ndarray 图像 self.result_frame.display_results(im0) # 显示推理结果 # 保存检测结果图像 file_name = os.path.basename(image_file) # 获取文件名 save_path = os.path.join(exp_dir, file_name) # 组合保存路径 # 使用 OpenCV 保存图像 cv2.imwrite(save_path, im0) # 这里 im0 是带有检测框的图像 new_results.append({"路径": save_path}) # 获取检测框的位置、置信度和类别 location_list = results.boxes.xyxy.tolist() # [[x1, y1, x2, y2], ...] conf_list = results.boxes.conf.tolist() # [confidence1, confidence2, ...] cls_list = results.boxes.cls.tolist() # [class1, class2, ...] name = results.names # 如果需要保存txt文件 if save_txt: # 获取对应的txt文件路径 txt_file_path = os.path.join(labels_dir, os.path.splitext(file_name)[0] + '.txt') # 保存检测结果到txt文件 with open(txt_file_path, 'w') as f: for location, conf, cls in zip(location_list, conf_list, cls_list): # location = [x1, y1, x2, y2] # 根据save_conf来决定是否保存置信度 if save_conf: f.write(f"{int(cls)} {' '.join(map(str, location))} {conf:.4f}\n") else: f.write(f"{int(cls)} {' '.join(map(str, location))}\n") if save_crop: for i, (location, cls) in enumerate(zip(location_list, cls_list)): # 获取类别名称,可以从模型中获取类名,假设模型有 classes 属性 class_name = name[int(cls)] # 获取带检测框的 numpy.ndarray 图像 # 根据类别创建对应的文件夹 class_dir = os.path.join(crops_dir, class_name) if not os.path.exists(class_dir): os.makedirs(class_dir) # 获取检测框的坐标 x1, y1, x2, y2 = map(int, location) # 裁剪原始图像 cropped_image = original_image[y1:y2, x1:x2] # [y1:y2, x1:x2]裁剪 # 保存裁剪后的图像 crop_file_name = f"{os.path.splitext(file_name)[0]}_{class_name}_{i}.jpg" crop_save_path = os.path.join(class_dir, crop_file_name) # 使用 OpenCV 保存裁剪的图像 cv2.imwrite(crop_save_path, cropped_image) # 处理视频文件 def process_video(video_file): cap = cv2.VideoCapture(video_file) if not cap.isOpened(): print(f"无法打开视频文件 {video_file}") return # 获取视频的帧率和大小 fps = cap.get(cv2.CAP_PROP_FPS) # 视频的帧率 frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) # 视频的宽度 frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 视频的高度 # 获取视频文件名并组合保存路径 file_name = os.path.basename(video_file) # 获取原视频文件名 file_name_without_extension = os.path.splitext(file_name)[0] # 去除文件扩展名 output_video_path = os.path.join(exp_dir, f"{file_name_without_extension}.mp4") # 保存检测结果的视频路径 # 使用 VideoWriter 来保存处理后的视频 fourcc = cv2.VideoWriter_fourcc(*'mp4v') # 视频编码(使用 mp4 编码) out = cv2.VideoWriter(output_video_path, fourcc, fps, (frame_width, frame_height)) # 输出视频对象 def display_video(): """视频推理并显示结果""" frame_id = 0 # 帧计数器 while cap.isOpened() and self.is_running: ret, frame = cap.read() # 逐帧读取视频 if not ret: break # 推理单帧 result = model.predict(frame, conf=conf_thres, iou=iou_thres, imgsz=imgsz)[0] im0 = result.plot() # 获取带检测框的 numpy.ndarray 图像 # 获取检测框的位置、置信度和类别 location_list = result.boxes.xyxy.tolist() # [[x1, y1, x2, y2], ...] conf_list = result.boxes.conf.tolist() # [confidence1, confidence2, ...] cls_list = result.boxes.cls.tolist() # [class1, class2, ...] name = result.names # 如果需要保存txt文件 if save_txt: # 获取对应的txt文件路径,使用帧编号来命名 frame_txt_path = os.path.join(labels_dir, f"{file_name_without_extension}_{frame_id}.txt") # 保存检测结果到txt文件 with open(frame_txt_path, 'w') as f: for location, conf, cls in zip(location_list, conf_list, cls_list): # location = [x1, y1, x2, y2] # 根据save_conf来决定是否保存置信度 if save_conf: f.write(f"{int(cls)} {' '.join(map(str, location))} {conf:.4f}\n") else: f.write(f"{int(cls)} {' '.join(map(str, location))}\n") if save_crop: for i, (location, cls) in enumerate(zip(location_list, cls_list)): # 获取类别名称,可以从模型中获取类名,假设模型有 classes 属性 class_name = name[int(cls)] # 获取带检测框的 numpy.ndarray 图像 # 根据类别创建对应的文件夹 class_dir = os.path.join(crops_dir, class_name) if not os.path.exists(class_dir): os.makedirs(class_dir) # 获取检测框的坐标 x1, y1, x2, y2 = map(int, location) # 裁剪原始图像 cropped_image = frame[y1:y2, x1:x2] # [y1:y2, x1:x2]裁剪 # 保存裁剪后的图像 crop_file_name = f"{os.path.splitext(file_name)[0]}_{class_name}_{i}.jpg" crop_save_path = os.path.join(class_dir, crop_file_name) # 使用 OpenCV 保存裁剪的图像 cv2.imwrite(crop_save_path, cropped_image) # 将推理结果写入视频 out.write(im0) # 显示推理结果 self.result_frame.display_results(im0) cv2.waitKey(2) # 等待 1ms,刷新显示 frame_id += 1 # 增加帧计数 # 释放资源 cap.release() out.release() cv2.destroyAllWindows() print(f"保存的视频文件:{output_video_path}") new_results.append({"路径": output_video_path}) # 启动视频推理线程 inference_thread = threading.Thread(target=display_video) inference_thread.start() inference_thread.join() # 等待该线程完成再继续 # 处理多个视频文件 for video_file in video_files: process_video(video_file) elif data_path.lower().endswith(('.mp4', '.avi', '.mov', '.mkv', '.flv', '.webm', '.wmv', '.mpeg', '.mpg', '.3gp')): # 如果需要保存txt文件,确保创建labels文件夹 if save_txt: labels_dir = os.path.join(exp_dir, 'labels') # labels文件夹路径 if not os.path.exists(labels_dir): os.makedirs(labels_dir) # 创建labels文件夹 # 如果需要保存裁剪图像,确保创建crops文件夹 if save_crop: crops_dir = os.path.join(exp_dir, 'crops') # crops文件夹路径 if not os.path.exists(crops_dir): os.makedirs(crops_dir) # 创建crops文件夹 cap = cv2.VideoCapture(data_path) if not cap.isOpened(): print(f"无法打开视频文件 {data_path}") return # 获取视频的帧率和大小 fps = cap.get(cv2.CAP_PROP_FPS) # 视频的帧率 frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) # 视频的宽度 frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 视频的高度 # 获取视频文件名并组合保存路径 file_name = os.path.basename(data_path) # 获取原视频文件名 file_name_without_extension = os.path.splitext(file_name)[0] # 去除文件扩展名 output_video_path = os.path.join(exp_dir, f"{file_name_without_extension}.mp4") # 保存检测结果的视频路径 # 使用 VideoWriter 来保存处理后的视频 fourcc = cv2.VideoWriter_fourcc(*'mp4v') # 视频编码(使用 mp4 编码) out = cv2.VideoWriter(output_video_path, fourcc, fps, (frame_width, frame_height)) # 输出视频对象 def display_video(): """视频推理并显示结果""" frame_id = 0 # 帧计数器 """视频推理并显示结果""" while cap.isOpened() and self.is_running: ret, frame = cap.read() # 逐帧读取视频 if not ret: break # 推理单帧 result = model.predict(frame, conf=conf_thres, iou=iou_thres, imgsz=imgsz)[0] im0 = result.plot() # 获取带检测框的 numpy.ndarray 图像 # 获取检测框的位置、置信度和类别 location_list = result.boxes.xyxy.tolist() # [[x1, y1, x2, y2], ...] conf_list = result.boxes.conf.tolist() # [confidence1, confidence2, ...] cls_list = result.boxes.cls.tolist() # [class1, class2, ...] name = result.names # 显示推理结果 self.result_frame.display_results(im0) # 显示推理结果 # 如果需要保存txt文件 if save_txt: # 获取对应的txt文件路径,使用帧编号来命名 frame_txt_path = os.path.join(labels_dir, f"{file_name_without_extension}_{frame_id}.txt") # 保存检测结果到txt文件 with open(frame_txt_path, 'w') as f: for location, conf, cls in zip(location_list, conf_list, cls_list): # location = [x1, y1, x2, y2] # 根据save_conf来决定是否保存置信度 if save_conf: f.write(f"{int(cls)} {' '.join(map(str, location))} {conf:.4f}\n") else: f.write(f"{int(cls)} {' '.join(map(str, location))}\n") if save_crop: for i, (location, cls) in enumerate(zip(location_list, cls_list)): # 获取类别名称,可以从模型中获取类名,假设模型有 classes 属性 class_name = name[int(cls)] # 获取带检测框的 numpy.ndarray 图像 # 根据类别创建对应的文件夹 class_dir = os.path.join(crops_dir, class_name) if not os.path.exists(class_dir): os.makedirs(class_dir) # 获取检测框的坐标 x1, y1, x2, y2 = map(int, location) # 裁剪原始图像 cropped_image = frame[y1:y2, x1:x2] # [y1:y2, x1:x2]裁剪 # 保存裁剪后的图像 crop_file_name = f"{os.path.splitext(file_name)[0]}_{class_name}_{i}.jpg" crop_save_path = os.path.join(class_dir, crop_file_name) # 使用 OpenCV 保存裁剪的图像 cv2.imwrite(crop_save_path, cropped_image) # 将带检测框的图像写入到输出视频 out.write(im0) cv2.waitKey(2) # 等待 1ms,刷新显示 frame_id += 1 # 增加帧计数 cap.release() # 释放视频捕获对象 out.release() # 释放 VideoWriter 对象 cv2.destroyAllWindows() # 关闭所有 OpenCV 窗口 new_results.append({"路径": output_video_path}) # 启动一个新线程进行视频推理 inference_thread = threading.Thread(target=display_video) inference_thread.start() elif data_path.isdigit(): # 摄像头 ID(数字形式) # 如果需要保存txt文件,确保创建labels文件夹 if save_txt: labels_dir = os.path.join(exp_dir, 'labels') # labels文件夹路径 if not os.path.exists(labels_dir): os.makedirs(labels_dir) # 创建labels文件夹 # 如果需要保存裁剪图像,确保创建crops文件夹 if save_crop: crops_dir = os.path.join(exp_dir, 'crops') # crops文件夹路径 if not os.path.exists(crops_dir): os.makedirs(crops_dir) # 创建crops文件夹 cap = cv2.VideoCapture(int(data_path)) # 通过摄像头 ID 打开摄像头 if not cap.isOpened(): print(f"无法打开摄像头 {data_path}") return # 获取视频的帧率和大小 fps = cap.get(cv2.CAP_PROP_FPS) # 视频的帧率 frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) # 视频的宽度 frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 视频的高度 # 获取视频文件名并组合保存路径 file_name = os.path.basename(data_path) # 获取原视频文件名 file_name_without_extension = os.path.splitext(file_name)[0] # 去除文件扩展名 output_video_path = os.path.join(exp_dir, f"{file_name_without_extension}.mp4") # 保存检测结果的视频路径 # 使用 VideoWriter 来保存处理后的视频 fourcc = cv2.VideoWriter_fourcc(*'mp4v') # 视频编码(使用 mp4 编码) out = cv2.VideoWriter(output_video_path, fourcc, fps, (frame_width, frame_height)) # 输出视频对象 def display_video(): """视频推理并显示结果""" frame_id = 0 # 帧计数器 """视频推理并显示结果""" while cap.isOpened() and self.is_running: ret, frame = cap.read() # 逐帧读取视频 if not ret: break # 推理单帧 result = model.predict(frame, conf=conf_thres, iou=iou_thres, imgsz=imgsz)[0] im0 = result.plot() # 获取带检测框的 numpy.ndarray 图像 if not self.backup_frame.is_detecting: print("检测停止") self.backup_frame.is_detecting = True return # 获取检测框的位置、置信度和类别 location_list = result.boxes.xyxy.tolist() # [[x1, y1, x2, y2], ...] conf_list = result.boxes.conf.tolist() # [confidence1, confidence2, ...] cls_list = result.boxes.cls.tolist() # [class1, class2, ...] name = result.names # 显示推理结果 self.result_frame.display_results(im0) # 显示推理结果 # 如果需要保存txt文件 if save_txt: # 获取对应的txt文件路径,使用帧编号来命名 frame_txt_path = os.path.join(labels_dir, f"{file_name_without_extension}_{frame_id}.txt") # 保存检测结果到txt文件 with open(frame_txt_path, 'w') as f: for location, conf, cls in zip(location_list, conf_list, cls_list): # location = [x1, y1, x2, y2] # 根据save_conf来决定是否保存置信度 if save_conf: f.write(f"{int(cls)} {' '.join(map(str, location))} {conf:.4f}\n") else: f.write(f"{int(cls)} {' '.join(map(str, location))}\n") if save_crop: for i, (location, cls) in enumerate(zip(location_list, cls_list)): # 获取类别名称,可以从模型中获取类名,假设模型有 classes 属性 class_name = name[int(cls)] # 获取带检测框的 numpy.ndarray 图像 # 根据类别创建对应的文件夹 class_dir = os.path.join(crops_dir, class_name) if not os.path.exists(class_dir): os.makedirs(class_dir) # 获取检测框的坐标 x1, y1, x2, y2 = map(int, location) # 裁剪原始图像 cropped_image = frame[y1:y2, x1:x2] # [y1:y2, x1:x2]裁剪 # 保存裁剪后的图像 crop_file_name = f"{os.path.splitext(file_name)[0]}_{class_name}_{i}.jpg" crop_save_path = os.path.join(class_dir, crop_file_name) # 使用 OpenCV 保存裁剪的图像 cv2.imwrite(crop_save_path, cropped_image) # 将带检测框的图像写入到输出视频 out.write(im0) cv2.waitKey(2) # 等待 1ms,刷新显示 frame_id += 1 # 增加帧计数 cap.release() # 释放视频捕获对象 out.release() # 释放 VideoWriter 对象 cv2.destroyAllWindows() # 关闭所有 OpenCV 窗口 new_results.append({"路径": output_video_path}) # 启动一个新线程进行视频推理 inference_thread = threading.Thread(target=display_video) inference_thread.start() else: # 单张图片路径 # 判断文件格式是否为图片 if data_path.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff', '.webp', '.jfif')): try: # 获取原始图像 original_image = cv2.imread(data_path) results = model.predict(data_path, conf=conf_thres, iou=iou_thres, imgsz=imgsz) # 如果需要保存txt文件,确保创建labels文件夹 if save_txt: labels_dir = os.path.join(exp_dir, 'labels') # labels文件夹路径 if not os.path.exists(labels_dir): os.makedirs(labels_dir) # 创建labels文件夹 # 如果需要保存裁剪图像,确保创建crops文件夹 if save_crop: crops_dir = os.path.join(exp_dir, 'crops') # crops文件夹路径 if not os.path.exists(crops_dir): os.makedirs(crops_dir) # 创建crops文件夹 for result in results: im0 = result.plot() # 获取带检测框的 numpy.ndarray 图像 self.result_frame.display_results(im0) # 显示推理结果 location_list = result.boxes.xyxy.tolist() conf_list = result.boxes.conf.tolist() cls_list = result.boxes.cls.tolist() name = result.names # 保存检测结果图像 file_name = os.path.basename(data_path) # 获取文件名 save_path = os.path.join(exp_dir, file_name) # 组合保存路径 # 使用 OpenCV 保存图像 cv2.imwrite(save_path, im0) # 这里 im0 是带有检测框的图像 new_results.append({"路径": save_path}) # 如果需要保存txt文件 if save_txt: # 获取对应的txt文件路径 txt_file_path = os.path.join(labels_dir, os.path.splitext(file_name)[0] + '.txt') with open(txt_file_path, 'w') as f: # 遍历每一个检测到的目标 for location, conf, cls in zip(location_list, conf_list, cls_list): # location = [x1, y1, x2, y2] # 保存为 类别 置信度 x1 y1 x2 y2 格式,如果save_conf为True则包括置信度 if save_conf: f.write(f"{int(cls)} {' '.join(map(str, location))} {conf:.4f}\n") else: f.write(f"{int(cls)} {' '.join(map(str, location))}\n") # 如果需要裁剪目标 if save_crop: for i, (location, cls) in enumerate(zip(location_list, cls_list)): # 获取类别名称,可以从模型中获取类名,假设模型有 classes 属性 class_name = name[int(cls)] # 获取带检测框的 numpy.ndarray 图像 # 根据类别创建对应的文件夹 class_dir = os.path.join(crops_dir, class_name) if not os.path.exists(class_dir): os.makedirs(class_dir) # 获取检测框的坐标 x1, y1, x2, y2 = map(int, location) # 裁剪原始图像 cropped_image = original_image[y1:y2, x1:x2] # [y1:y2, x1:x2]裁剪 # 保存裁剪后的图像 crop_file_name = f"{os.path.splitext(file_name)[0]}_{class_name}_{i}.jpg" crop_save_path = os.path.join(class_dir, crop_file_name) # 使用 OpenCV 保存裁剪的图像 cv2.imwrite(crop_save_path, cropped_image) except Exception as e: print(f"Error processing the image: {e}") else: print(f"Provided path {data_path} is not a valid image file.") # 将多个检测结果传递给 `new_detection_result` 方法 self.operation_frame.new_detection_result(new_results) def contains_chinese(path): # 判断路径中是否包含中文字符 return bool(re.search(r'[\u4e00-\u9fff]', path)) # 获取模型输入参数 if self.parameter_frame.path_label4.text() == "开启": # 如果摄像头已开启 data_path = "0" # 这里假设"0"代表摄像头 elif self.parameter_frame.path_label1.text(): # 如果选择了图片 data_path = self.parameter_frame.path_label1.text() if contains_chinese(data_path): msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setWindowTitle("输入路径有中文") msg.setText(f"图片路径有中文,请重新选择路径!\n当前路径:{data_path}") msg.setStandardButtons(QMessageBox.Ok) msg.exec_() return elif self.parameter_frame.path_label2.text(): # 如果选择了文件夹 data_path = self.parameter_frame.path_label2.text() if contains_chinese(data_path): msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setWindowTitle("输入路径有中文") msg.setText(f"文件夹路径有中文,请重新选择路径!\n当前路径:{data_path}") msg.setStandardButtons(QMessageBox.Ok) msg.exec_() return # 用一个列表记录包含中文字符的文件 files_with_chinese = [] # 遍历文件夹中的所有文件,检查文件名是否包含中文 for filename in os.listdir(data_path): file_path = os.path.join(data_path, filename) # 只检查图片文件(根据文件扩展名) if os.path.isfile(file_path) and filename.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff', '.webp', '.jfif')): if contains_chinese(filename): # 将包含中文的文件路径加入列表 files_with_chinese.append((filename, file_path)) # 如果有中文文件,显示滚动的提示框 if files_with_chinese: # 构建提示信息文本 msg_text = "文件夹中包含中文的图片文件,请重新命名文件!\n\n" for filename, file_path in files_with_chinese: # msg_text += f"当前文件:{filename}\n文件路径:{file_path}\n\n" msg_text += f"{filename}\n" # 自定义对话框 dialog = QDialog() dialog.setWindowTitle("图片文件名包含中文") layout = QVBoxLayout() # 创建 QTextEdit 来显示信息,并设置为只读 text_edit = QTextEdit() text_edit.setText(msg_text) text_edit.setReadOnly(True) # 创建滚动区域,将 QTextEdit 放入其中 scroll_area = QScrollArea() scroll_area.setWidget(text_edit) scroll_area.setWidgetResizable(True) # 创建确认按钮 btn_ok = QPushButton("确认") btn_ok.clicked.connect(dialog.accept) # 点击按钮关闭对话框 # 添加控件到布局 layout.addWidget(scroll_area) layout.addWidget(btn_ok) dialog.setLayout(layout) # 设置对话框的大小 dialog.resize(600, 400) # 调整对话框大小(宽600,高400) dialog.setFixedSize(dialog.size()) # 设置固定大小(防止调整) # 显示对话框 dialog.exec_() return elif self.parameter_frame.path_label3.text(): # 如果选择了视频 data_path = self.parameter_frame.path_label3.text() if contains_chinese(data_path): msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setWindowTitle("输入路径有中文") msg.setText(f"视频路径有中文,请重新选择路径!\n当前路径:{data_path}") msg.setStandardButtons(QMessageBox.Ok) msg.exec_() return else: # 如果没有选择任何输入,弹出提示框 msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setWindowTitle("输入数据提示") msg.setText("请输入模型的输入数据!\n数据路径不可以有中文") msg.setStandardButtons(QMessageBox.Ok) msg.exec_() # 显示弹窗 # 退出或其他操作 return # 或者你可以选择抛出异常或其他逻辑 # # # 获取权重路径 new_weights = self.parameter_display.path_label1.text() if self.parameter_display.path_label1.text() == "": # 如果没有选择任何输入,弹出提示框 msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setWindowTitle("输入权重") msg.setText("请输入模型的权重!数据路径不可以有中文") msg.setStandardButtons(QMessageBox.Ok) msg.exec_() # 显示弹窗 # 退出或其他操作 return # 或者你可以选择抛出异常或其他逻辑 if contains_chinese(new_weights): msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setWindowTitle("输入路径有中文") msg.setText(f"权重路径有中文,请重新选择路径!\n当前路径:{new_weights}") msg.setStandardButtons(QMessageBox.Ok) msg.exec_() return new_weights = Path(new_weights) model_path = new_weights # 获取置信度阈值 new_confidence = self.parameter_display.path_label2.text() conf_thres = float(new_confidence) # 获取IOU阈值 new_iou_thres = self.parameter_display.path_label3.text() iou_thres = float(new_iou_thres) # 获取图片大小参数 new_imgsz = self.parameter_display.path_label4.text() imgsz = [int(new_imgsz), int(new_imgsz)] # # 是否保存txt结果 if self.parameter_display.path_label5.text() == "是": # 保存txt结果 save_txt = True else: save_txt = False # # 是否保存conf结果 if self.parameter_display.path_label6.text() == "是": # 保存conf结果 save_conf = True else: save_conf = False # 是否保存crop结果 if self.parameter_display.path_label7.text() == "是": # 保存crop结果 save_crop = True else: save_crop = False run(data_path=data_path, model_path=model_path, conf_thres=conf_thres, iou_thres=iou_thres, imgsz=imgsz, save_txt=save_txt, save_conf=save_conf, save_crop=save_crop) if __name__ == '__main__': app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())
10-26
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值