193 lines
6.8 KiB
Python
193 lines
6.8 KiB
Python
"""测量详细日志记录器 - 记录测量过程的所有详细信息"""
|
||
import sys
|
||
from pathlib import Path
|
||
from datetime import datetime
|
||
from typing import Optional
|
||
|
||
|
||
class MeasurementLogger:
|
||
"""测量过程详细日志记录器"""
|
||
|
||
def __init__(self, output_dir: str = "results"):
|
||
"""
|
||
初始化测量日志记录器
|
||
|
||
Args:
|
||
output_dir: 输出目录路径
|
||
"""
|
||
self.output_dir = Path(output_dir)
|
||
self.output_dir.mkdir(parents=True, exist_ok=True)
|
||
|
||
# 生成日志文件名
|
||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||
self.log_file = self.output_dir / f"measurement_detail_{timestamp}.log"
|
||
|
||
# 打开日志文件
|
||
self.file_handle = open(self.log_file, 'w', encoding='utf-8')
|
||
|
||
# 写入头部
|
||
self._write_header()
|
||
|
||
def _write_header(self):
|
||
"""写入日志头部"""
|
||
self.log("=" * 80)
|
||
self.log("点云测量系统 - 详细测量日志")
|
||
self.log("=" * 80)
|
||
self.log(f"开始时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||
self.log("=" * 80)
|
||
self.log("")
|
||
|
||
def log(self, message: str, also_print: bool = True):
|
||
"""
|
||
记录日志信息
|
||
|
||
Args:
|
||
message: 日志消息
|
||
also_print: 是否同时输出到控制台
|
||
"""
|
||
# 写入文件
|
||
self.file_handle.write(message + "\n")
|
||
self.file_handle.flush() # 立即刷新到文件
|
||
|
||
# 输出到控制台
|
||
if also_print:
|
||
print(message)
|
||
|
||
def log_task_start(self, task_index: int, total_tasks: int, task_name: str):
|
||
"""记录任务开始"""
|
||
self.log("")
|
||
self.log("=" * 80)
|
||
self.log(f"[{task_index}/{total_tasks}] 开始执行任务: {task_name}")
|
||
self.log("=" * 80)
|
||
|
||
def log_roi_extraction(self, roi_name: str, point_count: int, bbox_min: list, bbox_max: list):
|
||
"""
|
||
记录ROI提取信息
|
||
|
||
Args:
|
||
roi_name: ROI名称(如"源平面ROI"、"目标平面ROI")
|
||
point_count: 提取到的点数
|
||
bbox_min: ROI最小边界 [x, y, z]
|
||
bbox_max: ROI最大边界 [x, y, z]
|
||
"""
|
||
self.log(f" {roi_name}:")
|
||
self.log(f" 边界: X[{bbox_min[0]:.2f}, {bbox_max[0]:.2f}], "
|
||
f"Y[{bbox_min[1]:.2f}, {bbox_max[1]:.2f}], "
|
||
f"Z[{bbox_min[2]:.2f}, {bbox_max[2]:.2f}]")
|
||
self.log(f" 提取点数: {point_count:,} 个点")
|
||
|
||
def log_ransac_fitting(self, plane_name: str, total_points: int,
|
||
inlier_count: int, inlier_ratio: float,
|
||
plane_equation: Optional[tuple] = None):
|
||
"""
|
||
记录RANSAC平面拟合信息
|
||
|
||
Args:
|
||
plane_name: 平面名称(如"源平面"、"目标平面")
|
||
total_points: 参与拟合的总点数
|
||
inlier_count: 内点数量
|
||
inlier_ratio: 内点率(0-1之间的浮点数)
|
||
plane_equation: 平面方程 (a, b, c, d),满足 ax + by + cz + d = 0
|
||
"""
|
||
self.log(f" {plane_name} RANSAC拟合:")
|
||
self.log(f" 拟合点数: {total_points:,} 个点")
|
||
self.log(f" 内点数量: {inlier_count:,} 个点")
|
||
self.log(f" 内点率: {inlier_ratio:.2%}")
|
||
|
||
if plane_equation:
|
||
a, b, c, d = plane_equation
|
||
self.log(f" 平面方程: {a:.6f}x + {b:.6f}y + {c:.6f}z + {d:.6f} = 0")
|
||
# 计算法向量
|
||
import numpy as np
|
||
normal = np.array([a, b, c])
|
||
norm = np.linalg.norm(normal)
|
||
if norm > 0:
|
||
normal = normal / norm
|
||
self.log(f" 法向量: ({normal[0]:.6f}, {normal[1]:.6f}, {normal[2]:.6f})")
|
||
|
||
def log_sampling(self, sample_count: int, method: str = "均匀采样"):
|
||
"""
|
||
记录采样信息
|
||
|
||
Args:
|
||
sample_count: 采样点数
|
||
method: 采样方法
|
||
"""
|
||
self.log(f" 采样策略: {method}")
|
||
self.log(f" 采样点数: {sample_count} 个点")
|
||
|
||
def log_distance_result(self, mean_distance: float, std_distance: float,
|
||
min_distance: float, max_distance: float):
|
||
"""
|
||
记录距离测量结果
|
||
|
||
Args:
|
||
mean_distance: 平均距离
|
||
std_distance: 标准差
|
||
min_distance: 最小距离
|
||
max_distance: 最大距离
|
||
"""
|
||
self.log(f" 测量结果:")
|
||
self.log(f" 平均距离: {mean_distance:.4f} mm")
|
||
self.log(f" 标准差: {std_distance:.4f} mm")
|
||
self.log(f" 最小距离: {min_distance:.4f} mm")
|
||
self.log(f" 最大距离: {max_distance:.4f} mm")
|
||
self.log(f" 距离范围: [{min_distance:.4f}, {max_distance:.4f}] mm")
|
||
|
||
def log_task_success(self, execution_time: float):
|
||
"""记录任务成功"""
|
||
self.log(f" 任务状态: ✅ 成功")
|
||
self.log(f" 执行时间: {execution_time:.2f} 秒")
|
||
|
||
def log_task_failure(self, error_message: str, execution_time: float):
|
||
"""记录任务失败"""
|
||
self.log(f" 任务状态: ❌ 失败")
|
||
self.log(f" 错误信息: {error_message}")
|
||
self.log(f" 执行时间: {execution_time:.2f} 秒")
|
||
|
||
def log_circle_fitting(self, circle_name: str, center: tuple, radius: float,
|
||
point_count: int):
|
||
"""
|
||
记录圆拟合信息
|
||
|
||
Args:
|
||
circle_name: 圆的名称
|
||
center: 圆心坐标 (x, y, z)
|
||
radius: 半径
|
||
point_count: 拟合点数
|
||
"""
|
||
self.log(f" {circle_name}:")
|
||
self.log(f" 圆心: ({center[0]:.4f}, {center[1]:.4f}, {center[2]:.4f})")
|
||
self.log(f" 半径: {radius:.4f} mm")
|
||
self.log(f" 拟合点数: {point_count:,} 个点")
|
||
|
||
def log_kdtree_optimization(self, original_points: int, filtered_points: int,
|
||
percentage: float):
|
||
"""
|
||
记录KDTree优化信息
|
||
|
||
Args:
|
||
original_points: 原始点数
|
||
filtered_points: 筛选后点数
|
||
percentage: 保留百分比
|
||
"""
|
||
self.log(f" KDTree优化:")
|
||
self.log(f" 原始点数: {original_points:,} 个点")
|
||
self.log(f" 筛选后: {filtered_points:,} 个点 ({percentage:.2f}%)")
|
||
|
||
def close(self):
|
||
"""关闭日志文件"""
|
||
self.log("")
|
||
self.log("=" * 80)
|
||
self.log(f"结束时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||
self.log("=" * 80)
|
||
|
||
if self.file_handle:
|
||
self.file_handle.close()
|
||
print(f"\n详细测量日志已保存到: {self.log_file}")
|
||
|
||
def __del__(self):
|
||
"""析构函数,确保文件被关闭"""
|
||
if hasattr(self, 'file_handle') and self.file_handle:
|
||
self.file_handle.close()
|