Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
O
Oil_Level_Recognition_System
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Oil_Level_Recognition_System
Commits
026ceb91
Commit
026ceb91
authored
Nov 25, 2025
by
Yuhaibo
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
13
parent
582c107b
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
465 additions
and
377 deletions
+465
-377
.gitignore
.gitignore
+2
-0
IMPORT_PATH_FIX.md
handlers/modelpage/IMPORT_PATH_FIX.md
+0
-314
MODEL_TEST_MIGRATION_PLAN.md
handlers/modelpage/MODEL_TEST_MIGRATION_PLAN.md
+2
-1
model_test_handler.py
handlers/modelpage/model_test_handler.py
+295
-25
model_training_handler.py
handlers/modelpage/model_training_handler.py
+119
-1
modelset_page.py
widgets/modelpage/modelset_page.py
+47
-36
No files found.
.gitignore
View file @
026ceb91
...
...
@@ -10,6 +10,8 @@
!__main__.py
!__init__.py
!.gitignore
!*.md
!**/*.md
# 忽略Python缓存文件
__pycache__/
...
...
handlers/modelpage/IMPORT_PATH_FIX.md
deleted
100644 → 0
View file @
582c107b
# model_page_handler.py 导入路径修复说明
## 文件位置变更
### 变更前
```
widgets/modelpage/model_page_handler.py
```
### 变更后
```
handlers/modelpage/model_page_handler.py ✅
```
---
## 导入路径修复
### 问题分析
文件从
`widgets/modelpage/`
移动到
`handlers/modelpage/`
后,所有导入路径都需要相应调整:
#### 目录结构
```
项目根目录/
├── widgets/
│ └── modelpage/
│ ├── model_loader.py
│ ├── model_operations.py
│ ├── modelset_page.py
│ └── training_page.py
│
└── handlers/
└── modelpage/
├── model_page_handler.py ← 当前文件位置
└── model_training_handler.py
```
---
## 修复的导入路径
### 1. ModelLoader 和 ModelOperations
#### ❌ 修复前(错误)
```
python
from
.model_loader
import
ModelLoader
from
.model_operations
import
ModelOperations
```
**问题**
:
-
使用相对导入
`.`
,表示从当前包
`handlers.modelpage`
导入
-
但这两个文件在
`widgets.modelpage`
包中,不在当前包
#### ✅ 修复后(正确)
```
python
# 导入插件接口(从 widgets.modelpage)
try
:
# 优先使用相对导入
from
...widgets.modelpage.model_loader
import
ModelLoader
from
...widgets.modelpage.model_operations
import
ModelOperations
except
ImportError
:
try
:
# 备用:绝对导入
from
widgets.modelpage.model_loader
import
ModelLoader
from
widgets.modelpage.model_operations
import
ModelOperations
except
ImportError
:
# 如果都失败,创建占位类
class
ModelLoader
:
def
__init__
(
self
,
handler
=
None
):
pass
class
ModelOperations
:
def
__init__
(
self
,
handler
=
None
):
pass
```
**说明**
:
-
`...widgets.modelpage`
:向上3层(
`modelpage`
→
`handlers`
→ 项目根),然后进入
`widgets/modelpage`
-
添加备用的绝对导入和占位类,确保导入失败时不会崩溃
---
### 2. ModelSetPage 和 TrainingPage
#### ❌ 修复前(错误)
```
python
from
.modelset_page
import
ModelSetPage
from
.training_page
import
TrainingPage
```
**问题**
:同样使用了错误的相对导入
#### ✅ 修复后(正确)
```
python
# 导入页面组件(从 widgets.modelpage)
try
:
from
...widgets.modelpage.modelset_page
import
ModelSetPage
from
...widgets.modelpage.training_page
import
TrainingPage
except
ImportError
:
try
:
from
widgets.modelpage.modelset_page
import
ModelSetPage
from
widgets.modelpage.training_page
import
TrainingPage
except
ImportError
as
e
:
print
(
f
"[ERROR] 无法导入必要的页面组件: {e}"
)
return
QtWidgets
.
QWidget
()
```
**说明**
:
-
使用相对导入
`...widgets.modelpage`
从正确的包导入
-
提供备用的绝对导入
-
添加错误处理
---
### 3. ModelTrainingHandler
#### ❌ 修复前(错误)
```
python
from
...handlers.modelpage.model_training_handler
import
ModelTrainingHandler
```
**问题**
:
-
使用了错误的相对路径
`...handlers.modelpage`
-
应该使用
`.model_training_handler`
,因为在同一个包内
#### ✅ 修复后(正确)
```
python
# 导入训练处理器
try
:
# 优先使用相对导入(同一包内)
from
.model_training_handler
import
ModelTrainingHandler
except
ImportError
:
try
:
# 备用:绝对导入
from
handlers.modelpage.model_training_handler
import
ModelTrainingHandler
except
ImportError
:
ModelTrainingHandler
=
None
print
(
"[WARNING] 无法导入ModelTrainingHandler,训练功能将不可用"
)
```
**说明**
:
-
`.model_training_handler`
:从当前包
`handlers.modelpage`
导入
-
只需要一层相对导入,因为在同一个目录下
---
## 相对导入层级说明
### 相对导入语法
```
python
from
.
import
xxx
# 当前包
from
..
import
xxx
# 父包(上一层)
from
...
import
xxx
# 祖父包(上两层)
from
....
import
xxx
# 曾祖父包(上三层)
```
### 当前文件位置导入路径
当前文件:
`handlers/modelpage/model_page_handler.py`
| 目标模块 | 相对路径 | 层级解释 |
|---------|---------|---------|
|
`handlers.modelpage.model_training_handler`
|
`.model_training_handler`
| 同一包内 |
|
`handlers.model_load_handler`
|
`..model_load_handler`
| 父包(handlers) |
|
`widgets.modelpage.model_loader`
|
`...widgets.modelpage.model_loader`
| 祖父包(项目根) → widgets |
| 项目根目录模块 |
`...module_name`
| 祖父包(项目根) |
### 路径计算示例
从
`handlers/modelpage/model_page_handler.py`
导入
`widgets/modelpage/model_loader.py`
:
```
当前位置: handlers/modelpage/model_page_handler.py
目标位置: widgets/modelpage/model_loader.py
相对路径计算:
1. 从 model_page_handler.py 向上到 modelpage/ (.)
2. 从 modelpage/ 向上到 handlers/ (..)
3. 从 handlers/ 向上到 项目根/ (...)
4. 从 项目根/ 进入 widgets/modelpage/ (...widgets.modelpage)
5. 导入 model_loader.py (...widgets.modelpage.model_loader)
最终导入语句:
from ...widgets.modelpage.model_loader import ModelLoader
```
---
## 修复总结
### 修复的导入数量
-
✅
**ModelLoader**
- 从 widgets.modelpage 导入
-
✅
**ModelOperations**
- 从 widgets.modelpage 导入
-
✅
**ModelSetPage**
- 从 widgets.modelpage 导入
-
✅
**TrainingPage**
- 从 widgets.modelpage 导入
-
✅
**ModelTrainingHandler**
- 从 handlers.modelpage 导入(同一包)
### 修复原则
1.
**跨包导入**
(widgets ↔ handlers):
-
使用多层相对导入
`...widgets.xxx`
或
`...handlers.xxx`
-
提供绝对导入作为备用
2.
**同包导入**
(handlers.modelpage 内部):
-
使用单层相对导入
`.module_name`
-
提供绝对导入作为备用
3.
**错误处理**
:
-
所有导入都包裹在 try-except 中
-
提供多层备用方案
-
导入失败时不会导致程序崩溃
---
## 验证检查
### ✅ Linter 检查
```
bash
# 无 linter 错误
No linter errors found.
```
### 🔍 导入测试
```
python
# 测试导入是否成功
try
:
from
handlers.modelpage.model_page_handler
import
ModelPageHandler
print
(
"✅ 导入成功"
)
except
ImportError
as
e
:
print
(
f
"❌ 导入失败: {e}"
)
```
### 📋 检查清单
-
[
x
]
ModelLoader 导入路径正确
-
[
x
]
ModelOperations 导入路径正确
-
[
x
]
ModelSetPage 导入路径正确
-
[
x
]
TrainingPage 导入路径正确
-
[
x
]
ModelTrainingHandler 导入路径正确
-
[
x
]
无 linter 错误
-
[
x
]
提供多层备用导入方案
-
[
x
]
添加错误处理
---
## 相关文件
### 当前文件
-
`handlers/modelpage/model_page_handler.py`
- 已修复
### 依赖文件
-
`widgets/modelpage/model_loader.py`
- 插件接口
-
`widgets/modelpage/model_operations.py`
- 插件接口
-
`widgets/modelpage/modelset_page.py`
- 页面组件
-
`widgets/modelpage/training_page.py`
- 页面组件
-
`handlers/modelpage/model_training_handler.py`
- 训练处理器
---
## 注意事项
### ⚠️ 文件位置重要性
-
文件位置决定了导入路径
-
移动文件后必须更新所有导入语句
-
相对导入层级取决于文件在包结构中的位置
### 💡 最佳实践
1.
**优先使用相对导入**
:便于重构和移动包
2.
**提供备用绝对导入**
:增加兼容性
3.
**添加错误处理**
:防止导入失败导致崩溃
4.
**添加注释说明**
:帮助理解导入路径
### 🔄 如果再次移动文件
如果将来再次移动
`model_page_handler.py`
,需要:
1.
重新计算相对导入的层级
2.
更新所有
`from ...`
导入语句
3.
测试所有导入是否成功
4.
运行 linter 检查
---
## 测试建议
### 单元测试
```
python
def
test_imports
():
"""测试所有导入是否成功"""
try
:
from
handlers.modelpage.model_page_handler
import
ModelPageHandler
assert
ModelPageHandler
is
not
None
# 测试创建实例
handler
=
ModelPageHandler
()
assert
handler
is
not
None
print
(
"✅ 所有导入测试通过"
)
except
Exception
as
e
:
print
(
f
"❌ 导入测试失败: {e}"
)
raise
```
### 集成测试
1.
启动应用程序
2.
访问模型管理页面
3.
确认所有功能正常
4.
检查控制台无导入错误
---
**修复时间**
: 2025-11-06
**问题类型**
: 导入路径错误(文件位置变更)
**影响范围**
: model_page_handler.py
**修复状态**
: ✅ 已完成
**测试状态**
: ✅ Linter 检查通过
handlers/modelpage/MODEL_TEST_MIGRATION_PLAN.md
View file @
026ceb91
...
...
@@ -3,7 +3,8 @@
## 目标
将
`model_training_handler.py`
中所有模型测试相关的代码迁移到
`model_test_handler.py`
## 需要迁移的方法列表
## 需要迁移的方法列表14324141
### 1. 测试入口和控制方法
-
[
x
]
`_handleStartTest()`
- 开始/停止测试按钮处理 (第2005-2014行)
...
...
handlers/modelpage/model_test_handler.py
View file @
026ceb91
...
...
@@ -190,6 +190,10 @@ class ModelTestThread(QThread):
print
(
f
"[测试线程] 液位检测完成"
)
self
.
_detection_result
=
detection_result
# 保存图片测试结果
self
.
progress_updated
.
emit
(
90
,
"正在保存测试结果..."
)
print
(
f
"[测试线程] 保存图片测试结果..."
)
self
.
_saveImageTestResults
(
model_path
,
test_frame
,
detection_result
,
annotation_file
)
except
Exception
as
e
:
print
(
f
"[测试线程] 图片检测失败: {e}"
)
...
...
@@ -1342,6 +1346,11 @@ class ModelTestHandler:
print
(
f
"[视频检测] 处理帧数: {frame_index}"
)
print
(
f
"[视频检测] 检测统计: 总检测次数={detection_count}, 成功={success_count}, 失败={fail_count}"
)
# 保存测试结果到模型目录
if
not
self
.
_detection_stopped
:
print
(
f
"[视频检测] 保存测试结果..."
)
self
.
_saveVideoTestResults
(
model_path
,
video_path
,
output_video_path
,
frame_index
,
detection_count
,
success_count
,
fail_count
,
annotation_file
)
# 显示检测结果视频
if
not
self
.
_detection_stopped
:
...
...
@@ -1732,46 +1741,46 @@ class ModelTestHandler:
# 创建HTML内容,包含视频播放器和统计信息
html_content
=
f
"""
<div style="font-family:
Arial, sans-serif; padding: 15px; background: #fff; height: 100
%
; overflow-y: auto
;">
<div style="margin-bottom:
15px; padding: 12px; border: 1px solid #dee2e6; border-radius: 5px
;">
<h3 style="margin: 0 0 1
0px 0; color: #333
;">视频逐帧检测完成</h3>
<div style="font-family:
'Microsoft YaHei', 'SimHei', Arial, sans-serif; padding: 20px; background: #ffffff; height: 100
%
; overflow-y: auto; color: #333333
;">
<div style="margin-bottom:
20px; padding: 0
;">
<h3 style="margin: 0 0 1
5px 0; color: #333333; font-size: 18px; font-weight: 600
;">视频逐帧检测完成</h3>
</div>
<div style="margin-bottom:
15px; padding: 12px; border: 1px solid #dee2e6; border-radius: 5
px;">
<h4 style="margin
-top: 0; color: #333
;">检测结果视频</h4>
<video width="100
%
" height="auto" controls style="border:
1px solid #dee2e6; border-radius: 4px; max-height: 400px
;">
<div style="margin-bottom:
20
px;">
<h4 style="margin
: 0 0 10px 0; color: #333333; font-size: 16px; font-weight: 500
;">检测结果视频</h4>
<video width="100
%
" height="auto" controls style="border:
none; border-radius: 6px; max-height: 400px; background: #f8f9fa
;">
<source src="file:///{video_path_formatted}" type="video/mp4">
您的浏览器不支持视频播放
</video>
</div>
<div style="
padding: 12px; border: 1px solid #dee2e6; border-radius: 5
px;">
<h4 style="margin
-top: 0; color: #333
;">检测统计</h4>
<table style="width: 100
%
; border-collapse: collapse; font-size: 1
3px
;">
<tr style="border-bottom: 1px solid #
dee2e6
;">
<td style="padding:
8px; color: #666;"><strong>总帧数</strong>
</td>
<td style="padding:
8px; color: #333
;">{total_frames}</td>
<div style="
margin-bottom: 20
px;">
<h4 style="margin
: 0 0 15px 0; color: #333333; font-size: 16px; font-weight: 500
;">检测统计</h4>
<table style="width: 100
%
; border-collapse: collapse; font-size: 1
4px; font-family: 'Microsoft YaHei', 'SimHei', Arial, sans-serif
;">
<tr style="border-bottom: 1px solid #
e9ecef
;">
<td style="padding:
12px 8px; color: #333333; font-weight: 500;">总帧数
</td>
<td style="padding:
12px 8px; color: #333333; font-weight: 400
;">{total_frames}</td>
</tr>
<tr style="border-bottom: 1px solid #
dee2e6
;">
<td style="padding:
8px; color: #666;"><strong>检测次数</strong>
</td>
<td style="padding:
8px; color: #333
;">{detection_count}</td>
<tr style="border-bottom: 1px solid #
e9ecef
;">
<td style="padding:
12px 8px; color: #333333; font-weight: 500;">检测次数
</td>
<td style="padding:
12px 8px; color: #333333; font-weight: 400
;">{detection_count}</td>
</tr>
<tr style="border-bottom: 1px solid #
dee2e6
;">
<td style="padding:
8px; color: #666;"><strong>成功</strong>
</td>
<td style="padding:
8px; color: #28a745
;">✓ {success_count}</td>
<tr style="border-bottom: 1px solid #
e9ecef
;">
<td style="padding:
12px 8px; color: #333333; font-weight: 500;">成功
</td>
<td style="padding:
12px 8px; color: #28a745; font-weight: 400
;">✓ {success_count}</td>
</tr>
<tr style="border-bottom: 1px solid #
dee2e6
;">
<td style="padding:
8px; color: #666;"><strong>失败</strong>
</td>
<td style="padding:
8px; color: {'#dc3545' if fail_count > 0 else '#28a745'}
;">{'✗ ' if fail_count > 0 else '✓ '}{fail_count}</td>
<tr style="border-bottom: 1px solid #
e9ecef
;">
<td style="padding:
12px 8px; color: #333333; font-weight: 500;">失败
</td>
<td style="padding:
12px 8px; color: {'#dc3545' if fail_count > 0 else '#28a745'}; font-weight: 400
;">{'✗ ' if fail_count > 0 else '✓ '}{fail_count}</td>
</tr>
<tr>
<td style="padding:
8px; color: #666;"><strong>成功率</strong>
</td>
<td style="padding:
8px; color: #333
;">{success_rate:.1f}
%
</td>
<td style="padding:
12px 8px; color: #333333; font-weight: 500;">成功率
</td>
<td style="padding:
12px 8px; color: #333333; font-weight: 400
;">{success_rate:.1f}
%
</td>
</tr>
</table>
<div style="margin-top:
15px; padding: 10px; border-left: 4px solid #dee2e6
;">
<p style="margin: 0; font-size: 1
2px; color: #666
;">
<div style="margin-top:
20px; padding: 15px; background: #f8f9fa; border-radius: 6px
;">
<p style="margin: 0; font-size: 1
3px; color: #666666; line-height: 1.6; font-family: 'Microsoft YaHei', 'SimHei', Arial, sans-serif
;">
✓ 每3帧进行一次液位检测<br>
✓ 为每一帧绘制液位线(红色)<br>
✓ 使用历史数据填充未检测帧<br>
...
...
@@ -1810,3 +1819,264 @@ class ModelTestHandler:
# TODO: 完整实现需要从 model_training_handler.py 第1512-1548行复制
print
(
f
"[标注引擎] 创建标注引擎..."
)
return
None
def
_saveVideoTestResults
(
self
,
model_path
,
video_path
,
output_video_path
,
frame_count
,
detection_count
,
success_count
,
fail_count
,
annotation_file
):
"""保存视频测试结果到模型目录"""
import
os
import
shutil
from
datetime
import
datetime
try
:
# 获取模型所在目录
model_dir
=
os
.
path
.
dirname
(
model_path
)
model_filename
=
os
.
path
.
basename
(
model_path
)
model_name
=
os
.
path
.
splitext
(
model_filename
)[
0
]
# 创建test_results目录
test_results_dir
=
os
.
path
.
join
(
model_dir
,
'test_results'
)
os
.
makedirs
(
test_results_dir
,
exist_ok
=
True
)
# 生成时间戳
timestamp
=
datetime
.
now
()
.
strftime
(
"
%
Y
%
m
%
d_
%
H
%
M
%
S"
)
# 文件名前缀
file_prefix
=
f
"{model_name}_video_{timestamp}"
print
(
f
"[保存视频结果] 模型目录: {model_dir}"
)
print
(
f
"[保存视频结果] 结果目录: {test_results_dir}"
)
print
(
f
"[保存视频结果] 文件前缀: {file_prefix}"
)
# 1. 复制检测结果视频到test_results目录
result_video_filename
=
f
"{file_prefix}_result.mp4"
result_video_path
=
os
.
path
.
join
(
test_results_dir
,
result_video_filename
)
shutil
.
copy2
(
output_video_path
,
result_video_path
)
print
(
f
"[保存视频结果] 结果视频已保存: {result_video_path}"
)
# 2. 生成测试报告文本文件
report_filename
=
f
"{file_prefix}_report.txt"
report_path
=
os
.
path
.
join
(
test_results_dir
,
report_filename
)
video_name
=
os
.
path
.
basename
(
video_path
)
report_lines
=
[
"="
*
60
,
"视频液位检测测试报告"
,
"="
*
60
,
f
"测试时间: {datetime.now().strftime('
%
Y-
%
m-
%
d
%
H:
%
M:
%
S')}"
,
f
"模型文件: {model_filename}"
,
f
"模型路径: {model_path}"
,
f
"测试视频: {video_name}"
,
f
"视频路径: {video_path}"
,
""
,
"检测统计:"
,
f
" 总帧数: {frame_count}"
,
f
" 检测次数: {detection_count}"
,
f
" 成功检测: {success_count}"
,
f
" 失败检测: {fail_count}"
,
f
" 成功率: {(success_count/detection_count*100 if detection_count > 0 else 0):.1f}
%
"
,
""
,
"标注配置:"
,
f
" 标注文件: {annotation_file}"
,
""
,
"生成文件:"
,
f
" 结果视频: {result_video_filename}"
,
f
" 测试报告: {report_filename}"
,
""
,
"="
*
60
,
]
with
open
(
report_path
,
'w'
,
encoding
=
'utf-8'
)
as
f
:
f
.
write
(
'
\n
'
.
join
(
report_lines
))
print
(
f
"[保存视频结果] 测试报告已保存: {report_path}"
)
# 3. 生成JSON格式的详细结果
import
json
json_filename
=
f
"{file_prefix}_result.json"
json_path
=
os
.
path
.
join
(
test_results_dir
,
json_filename
)
result_data
=
{
"test_info"
:
{
"model_name"
:
model_name
,
"model_path"
:
model_path
,
"test_video"
:
video_name
,
"video_path"
:
video_path
,
"test_time"
:
datetime
.
now
()
.
isoformat
(),
"frame_count"
:
frame_count
,
"detection_count"
:
detection_count
,
"success_count"
:
success_count
,
"fail_count"
:
fail_count
,
"success_rate"
:
(
success_count
/
detection_count
*
100
if
detection_count
>
0
else
0
),
"annotation_file"
:
annotation_file
},
"files"
:
{
"result_video"
:
result_video_filename
,
"report"
:
report_filename
,
"json_result"
:
json_filename
}
}
with
open
(
json_path
,
'w'
,
encoding
=
'utf-8'
)
as
f
:
json
.
dump
(
result_data
,
f
,
ensure_ascii
=
False
,
indent
=
2
)
print
(
f
"[保存视频结果] JSON结果已保存: {json_path}"
)
print
(
f
"[保存视频结果] ✅ 所有测试结果已成功保存到: {test_results_dir}"
)
except
Exception
as
e
:
print
(
f
"[保存视频结果] ❌ 保存失败: {e}"
)
import
traceback
traceback
.
print_exc
()
def
_saveImageTestResults
(
self
,
model_path
,
test_frame
,
detection_result
,
annotation_file
):
"""保存图片测试结果到模型目录"""
import
os
import
cv2
from
datetime
import
datetime
try
:
# 获取模型所在目录
model_dir
=
os
.
path
.
dirname
(
model_path
)
model_filename
=
os
.
path
.
basename
(
model_path
)
model_name
=
os
.
path
.
splitext
(
model_filename
)[
0
]
# 创建test_results目录
test_results_dir
=
os
.
path
.
join
(
model_dir
,
'test_results'
)
os
.
makedirs
(
test_results_dir
,
exist_ok
=
True
)
# 生成时间戳
timestamp
=
datetime
.
now
()
.
strftime
(
"
%
Y
%
m
%
d_
%
H
%
M
%
S"
)
# 文件名前缀
file_prefix
=
f
"{model_name}_{timestamp}"
print
(
f
"[保存图片结果] 模型目录: {model_dir}"
)
print
(
f
"[保存图片结果] 结果目录: {test_results_dir}"
)
print
(
f
"[保存图片结果] 文件前缀: {file_prefix}"
)
# 1. 保存原始测试图像
original_filename
=
f
"{file_prefix}_original.png"
original_path
=
os
.
path
.
join
(
test_results_dir
,
original_filename
)
cv2
.
imwrite
(
original_path
,
test_frame
)
print
(
f
"[保存图片结果] 原始图像已保存: {original_path}"
)
# 2. 保存检测结果图像(带标注)
result_image
=
test_frame
.
copy
()
# 绘制检测结果
if
detection_result
and
'liquid_level_mm'
in
detection_result
:
liquid_level
=
detection_result
[
'liquid_level_mm'
]
# 使用PIL绘制中文文本
from
PIL
import
Image
,
ImageDraw
,
ImageFont
pil_result
=
Image
.
fromarray
(
cv2
.
cvtColor
(
result_image
,
cv2
.
COLOR_BGR2RGB
))
draw_result
=
ImageDraw
.
Draw
(
pil_result
)
# 加载中文字体
try
:
font_medium
=
ImageFont
.
truetype
(
"C:/Windows/Fonts/msyh.ttc"
,
18
)
font_small
=
ImageFont
.
truetype
(
"C:/Windows/Fonts/msyh.ttc"
,
14
)
except
:
font_medium
=
ImageFont
.
load_default
()
font_small
=
ImageFont
.
load_default
()
# 绘制液位信息
text
=
f
"液位: {liquid_level:.1f}mm"
text_bbox
=
draw_result
.
textbbox
((
0
,
0
),
text
,
font
=
font_medium
)
text_width
=
text_bbox
[
2
]
-
text_bbox
[
0
]
text_height
=
text_bbox
[
3
]
-
text_bbox
[
1
]
# 在图像顶部绘制文本背景
draw_result
.
rectangle
([
10
,
10
,
10
+
text_width
+
20
,
10
+
text_height
+
10
],
fill
=
(
0
,
0
,
0
,
180
))
draw_result
.
text
((
20
,
15
),
text
,
fill
=
(
255
,
255
,
255
),
font
=
font_medium
)
# 转换回OpenCV格式
result_image
=
cv2
.
cvtColor
(
np
.
array
(
pil_result
),
cv2
.
COLOR_RGB2BGR
)
result_filename
=
f
"{file_prefix}_result.png"
result_path
=
os
.
path
.
join
(
test_results_dir
,
result_filename
)
cv2
.
imwrite
(
result_path
,
result_image
)
print
(
f
"[保存图片结果] 检测结果图像已保存: {result_path}"
)
# 3. 生成测试报告文本文件
report_filename
=
f
"{file_prefix}_report.txt"
report_path
=
os
.
path
.
join
(
test_results_dir
,
report_filename
)
# 提取检测信息
liquid_level
=
detection_result
.
get
(
'liquid_level_mm'
,
0
)
if
detection_result
else
0
detection_success
=
detection_result
is
not
None
and
'liquid_level_mm'
in
detection_result
report_lines
=
[
"="
*
60
,
"图片液位检测测试报告"
,
"="
*
60
,
f
"测试时间: {datetime.now().strftime('
%
Y-
%
m-
%
d
%
H:
%
M:
%
S')}"
,
f
"模型文件: {model_filename}"
,
f
"模型路径: {model_path}"
,
f
"图像尺寸: {test_frame.shape[1]}x{test_frame.shape[0]}"
,
""
,
"检测结果:"
,
f
" 检测状态: {'成功' if detection_success else '失败'}"
,
f
" 液位高度: {liquid_level:.1f}mm"
if
detection_success
else
" 液位高度: 无法检测"
,
""
,
"标注配置:"
,
f
" 标注文件: {annotation_file}"
,
""
,
"生成文件:"
,
f
" 原始图像: {original_filename}"
,
f
" 检测结果: {result_filename}"
,
f
" 测试报告: {report_filename}"
,
""
,
"="
*
60
,
]
with
open
(
report_path
,
'w'
,
encoding
=
'utf-8'
)
as
f
:
f
.
write
(
'
\n
'
.
join
(
report_lines
))
print
(
f
"[保存图片结果] 测试报告已保存: {report_path}"
)
# 4. 生成JSON格式的详细结果
import
json
json_filename
=
f
"{file_prefix}_result.json"
json_path
=
os
.
path
.
join
(
test_results_dir
,
json_filename
)
result_data
=
{
"test_info"
:
{
"model_name"
:
model_name
,
"model_path"
:
model_path
,
"test_time"
:
datetime
.
now
()
.
isoformat
(),
"image_size"
:
{
"width"
:
int
(
test_frame
.
shape
[
1
]),
"height"
:
int
(
test_frame
.
shape
[
0
])
},
"annotation_file"
:
annotation_file
},
"detection_result"
:
{
"success"
:
detection_success
,
"liquid_level_mm"
:
float
(
liquid_level
)
if
detection_success
else
None
,
"raw_result"
:
detection_result
},
"files"
:
{
"original_image"
:
original_filename
,
"result_image"
:
result_filename
,
"report"
:
report_filename
,
"json_result"
:
json_filename
}
}
with
open
(
json_path
,
'w'
,
encoding
=
'utf-8'
)
as
f
:
json
.
dump
(
result_data
,
f
,
ensure_ascii
=
False
,
indent
=
2
)
print
(
f
"[保存图片结果] JSON结果已保存: {json_path}"
)
print
(
f
"[保存图片结果] ✅ 所有测试结果已成功保存到: {test_results_dir}"
)
except
Exception
as
e
:
print
(
f
"[保存图片结果] ❌ 保存失败: {e}"
)
import
traceback
traceback
.
print_exc
()
handlers/modelpage/model_training_handler.py
View file @
026ceb91
...
...
@@ -2514,7 +2514,7 @@ class ModelTrainingHandler(ModelTestHandler):
print
(
f
"🔧 [模型加载] ULTRALYTICS_OFFLINE = {os.environ.get('ULTRALYTICS_OFFLINE')}"
)
print
(
f
"🚀 [模型加载] 开始加载YOLO模型: {model_path}"
)
model
=
YOLO
(
model_path
)
(
model_path
)
print
(
f
"✅ [模型加载] YOLO模型加载成功"
)
# 读取标注配置
...
...
@@ -2669,3 +2669,121 @@ class ModelTrainingHandler(ModelTestHandler):
print
(
f
"[错误] 显示测试摘要失败: {e}"
)
import
traceback
traceback
.
print_exc
()
def
_refreshModelSetPage
(
self
):
"""刷新模型集管理页面"""
try
:
# 获取主窗口的模型集页面
if
hasattr
(
self
,
'main_window'
)
and
hasattr
(
self
.
main_window
,
'modelSetPage'
):
print
(
"[信息] 正在刷新模型集管理页面..."
)
self
.
main_window
.
modelSetPage
.
loadModelsFromConfig
()
print
(
"[信息] 模型集管理页面刷新完成"
)
else
:
print
(
"[警告] 无法访问模型集管理页面"
)
except
Exception
as
e
:
print
(
f
"[错误] 刷新模型集管理页面失败: {e}"
)
import
traceback
traceback
.
print_exc
()
def
_refreshModelTestPage
(
self
):
"""刷新模型测试页面的模型列表"""
try
:
# 获取主窗口的模型测试页面
if
hasattr
(
self
,
'main_window'
)
and
hasattr
(
self
.
main_window
,
'testModelPage'
):
print
(
"[信息] 正在刷新模型测试页面..."
)
# 如果有模型集页面,从模型集页面加载模型
if
hasattr
(
self
.
main_window
,
'modelSetPage'
):
self
.
main_window
.
testModelPage
.
loadModelsFromModelSetPage
(
self
.
main_window
.
modelSetPage
)
else
:
# 否则直接刷新模型测试页面
if
hasattr
(
self
.
main_window
.
testModelPage
,
'loadModelsFromConfig'
):
self
.
main_window
.
testModelPage
.
loadModelsFromConfig
()
print
(
"[信息] 模型测试页面刷新完成"
)
else
:
print
(
"[警告] 无法访问模型测试页面"
)
except
Exception
as
e
:
print
(
f
"[错误] 刷新模型测试页面失败: {e}"
)
import
traceback
traceback
.
print_exc
()
def
_addTrainedModelToModelSet
(
self
,
converted_files
,
weights_dir
):
"""将训练好的模型添加到模型集配置"""
try
:
if
not
converted_files
:
print
(
"[信息] 没有转换的模型文件,跳过添加到模型集"
)
return
print
(
f
"[信息] 正在将 {len(converted_files)} 个训练模型添加到模型集..."
)
# 获取项目根目录
project_root
=
get_project_root
()
# 为每个转换的模型文件创建模型信息
for
dat_file
in
converted_files
:
try
:
# 获取模型文件信息
model_path
=
Path
(
dat_file
)
model_name
=
f
"训练模型-{model_path.parent.name}-{model_path.stem}"
# 检查模型是否已存在
if
hasattr
(
self
,
'main_window'
)
and
hasattr
(
self
.
main_window
,
'modelSetPage'
):
existing_models
=
self
.
main_window
.
modelSetPage
.
getAllModels
()
if
any
(
model_name
in
model
for
model
in
existing_models
):
print
(
f
"[信息] 模型 {model_name} 已存在,跳过添加"
)
continue
# 创建模型参数
model_params
=
{
'name'
:
model_name
,
'type'
:
'YOLOv11'
,
'path'
:
str
(
dat_file
),
'config_path'
:
''
,
'description'
:
f
'训练于 {weights_dir}'
if
weights_dir
else
'自动训练生成的模型'
,
'size'
:
self
.
_getFileSize
(
str
(
dat_file
)),
'classes'
:
3
,
# liquid, foam, air
'input'
:
'640x640'
,
'confidence'
:
0.5
,
'iou'
:
0.45
,
'device'
:
'CUDA:0 (GPU)'
,
'batch_size'
:
16
,
'blur_training'
:
100
,
'epochs'
:
300
,
'workers'
:
8
,
'model_type'
:
'训练模型'
,
'source'
:
'training'
}
# 添加到模型集页面
if
hasattr
(
self
,
'main_window'
)
and
hasattr
(
self
.
main_window
,
'modelSetPage'
):
self
.
main_window
.
modelSetPage
.
_model_params
[
model_name
]
=
model_params
self
.
main_window
.
modelSetPage
.
addModelToList
(
model_name
)
print
(
f
"[信息] 已添加模型: {model_name}"
)
except
Exception
as
model_error
:
print
(
f
"[错误] 添加模型 {dat_file} 失败: {model_error}"
)
continue
print
(
"[信息] 训练模型添加到模型集完成"
)
except
Exception
as
e
:
print
(
f
"[错误] 添加训练模型到模型集失败: {e}"
)
import
traceback
traceback
.
print_exc
()
def
_getFileSize
(
self
,
file_path
):
"""获取文件大小(格式化字符串)"""
try
:
if
os
.
path
.
exists
(
file_path
):
size_bytes
=
os
.
path
.
getsize
(
file_path
)
if
size_bytes
<
1024
:
return
f
"{size_bytes} B"
elif
size_bytes
<
1024
*
1024
:
return
f
"{size_bytes / 1024:.1f} KB"
elif
size_bytes
<
1024
*
1024
*
1024
:
return
f
"{size_bytes / (1024 * 1024):.1f} MB"
else
:
return
f
"{size_bytes / (1024 * 1024 * 1024):.1f} GB"
else
:
return
"未知"
except
:
return
"未知"
widgets/modelpage/modelset_page.py
View file @
026ceb91
...
...
@@ -1237,45 +1237,56 @@ class ModelSetPage(QtWidgets.QWidget):
try
:
# 获取模型目录路径
current_dir
=
Path
(
__file__
)
.
parent
.
parent
.
parent
model_dir
=
current_dir
/
"database"
/
"model"
/
"detection_model"
if
not
model_dir
.
exists
():
pass
return
models
pass
# 遍历所有子目录,并按目录名排序(确保模型1最先)
sorted_subdirs
=
sorted
(
model_dir
.
iterdir
(),
key
=
lambda
x
:
x
.
name
if
x
.
is_dir
()
else
''
)
for
subdir
in
sorted_subdirs
:
if
subdir
.
is_dir
():
# 查找 .dat 文件(优先)
for
model_file
in
sorted
(
subdir
.
glob
(
"*.dat"
)):
models
.
append
({
'name'
:
f
"模型-{subdir.name}-{model_file.stem}"
,
'path'
:
str
(
model_file
),
'subdir'
:
subdir
.
name
,
'source'
:
'scan'
,
'format'
:
'dat'
})
pass
# 然后查找 .pt 文件
for
model_file
in
sorted
(
subdir
.
glob
(
"*.pt"
)):
models
.
append
({
'name'
:
f
"模型-{subdir.name}-{model_file.stem}"
,
'path'
:
str
(
model_file
),
'subdir'
:
subdir
.
name
,
'source'
:
'scan'
,
'format'
:
'pt'
})
pass
pass
# 扫描多个模型目录
model_dirs
=
[
(
current_dir
/
"database"
/
"model"
/
"detection_model"
,
"检测模型"
),
(
current_dir
/
"database"
/
"model"
/
"train_model"
,
"训练模型"
),
(
current_dir
/
"database"
/
"model"
/
"test_model"
,
"测试模型"
)
]
for
model_dir
,
dir_type
in
model_dirs
:
if
not
model_dir
.
exists
():
print
(
f
"[信息] {dir_type}目录不存在: {model_dir}"
)
continue
print
(
f
"[信息] 正在扫描{dir_type}目录: {model_dir}"
)
# 遍历所有子目录,并按目录名排序(确保模型1最先)
sorted_subdirs
=
sorted
(
model_dir
.
iterdir
(),
key
=
lambda
x
:
x
.
name
if
x
.
is_dir
()
else
''
)
for
subdir
in
sorted_subdirs
:
if
subdir
.
is_dir
():
# 查找 .dat 文件(优先)
for
model_file
in
sorted
(
subdir
.
glob
(
"*.dat"
)):
models
.
append
({
'name'
:
f
"{dir_type}-{subdir.name}-{model_file.stem}"
,
'path'
:
str
(
model_file
),
'subdir'
:
subdir
.
name
,
'source'
:
'scan'
,
'format'
:
'dat'
,
'model_type'
:
dir_type
})
print
(
f
"[信息] 找到{dir_type}: {model_file}"
)
# 然后查找 .pt 文件
for
model_file
in
sorted
(
subdir
.
glob
(
"*.pt"
)):
models
.
append
({
'name'
:
f
"{dir_type}-{subdir.name}-{model_file.stem}"
,
'path'
:
str
(
model_file
),
'subdir'
:
subdir
.
name
,
'source'
:
'scan'
,
'format'
:
'pt'
,
'model_type'
:
dir_type
})
print
(
f
"[信息] 找到{dir_type}: {model_file}"
)
print
(
f
"[信息] 模型扫描完成,共找到 {len(models)} 个模型"
)
except
Exception
as
e
:
pass
print
(
f
"[错误] 扫描模型目录失败: {e}"
)
import
traceback
traceback
.
print_exc
()
return
models
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment