feat: initial commit - massive-cloud point cloud processing system

This commit is contained in:
2026-05-27 09:51:42 +00:00
commit 16811100d1
391 changed files with 78338 additions and 0 deletions

View File

@@ -0,0 +1,245 @@
"""Excel结果导出器"""
from pathlib import Path
from typing import List, Dict, Any
from datetime import datetime
from openpyxl import Workbook
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
from openpyxl.utils import get_column_letter
from measurements.base import MeasurementResult
class ExcelExporter:
"""将测量结果导出为Excel文件"""
def __init__(self, output_dir: str):
"""
初始化导出器
Args:
output_dir: 输出目录路径
"""
self.output_dir = Path(output_dir)
self.output_dir.mkdir(parents=True, exist_ok=True)
def export(self, results: List[MeasurementResult], filename: str = None, task_configs: List[Dict[str, Any]] = None) -> str:
"""
导出测量结果到Excel
Args:
results: 测量结果列表
filename: 输出文件名(不含扩展名),默认使用时间戳
task_configs: 任务配置列表(用于提取描述和参数)
Returns:
str: 导出的Excel文件路径
"""
if filename is None:
filename = f"measurement_results_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
output_path = self.output_dir / f"{filename}.xlsx"
# 创建任务名称到配置的映射
task_config_map = {}
if task_configs:
for config in task_configs:
task_config_map[config.get('name')] = config
# 创建工作簿
wb = Workbook()
ws = wb.active
ws.title = "测量结果"
# 写入标题
self._write_header(ws)
# 写入数据
row = 2
for result in results:
task_config = task_config_map.get(result.task_name, {})
row = self._write_result(ws, result, row, task_config)
# 调整列宽
self._adjust_column_widths(ws)
# 保存文件
wb.save(output_path)
return str(output_path)
def _write_header(self, ws):
"""写入表头"""
headers = [
"任务名称",
"任务类型",
"任务描述",
"点位参数",
"状态",
"平均距离 (mm)",
"标准差 (mm)",
"最小距离 (mm)",
"最大距离 (mm)",
"采样点数",
"源ROI点数",
"目标ROI点数",
"源平面内点率",
"目标平面内点率",
"错误信息"
]
# 样式
header_font = Font(bold=True, color="FFFFFF")
header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
header_alignment = Alignment(horizontal="center", vertical="center")
border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
for col, header in enumerate(headers, 1):
cell = ws.cell(row=1, column=col)
cell.value = header
cell.font = header_font
cell.fill = header_fill
cell.alignment = header_alignment
cell.border = border
def _format_task_params(self, task_config: Dict[str, Any]) -> str:
"""
格式化任务参数为字符串
Args:
task_config: 任务配置字典
Returns:
str: 格式化后的参数字符串
"""
task_type = task_config.get('type', '')
params_parts = []
if task_type == 'plane_to_plane':
# 平面到平面:显示源ROI和目标ROI
source_roi = task_config.get('source_roi', {})
target_roi = task_config.get('target_roi', {})
if source_roi:
params_parts.append(f"源ROI: min{source_roi.get('bbox_min', [])} max{source_roi.get('bbox_max', [])}")
if target_roi:
params_parts.append(f"目标ROI: min{target_roi.get('bbox_min', [])} max{target_roi.get('bbox_max', [])}")
elif task_type == 'point_to_plane':
mode = task_config.get('mode', '')
if mode == 'exact_points':
# 精确点模式:显示目标点坐标
target_points = task_config.get('target_points', [])
if target_points:
params_parts.append(f"目标点({len(target_points)}个): {target_points}")
plane = task_config.get('plane', {})
if plane:
params_parts.append(f"平面: {plane.get('a', 0)}x+{plane.get('b', 0)}y+{plane.get('c', 0)}z+{plane.get('d', 0)}=0")
elif mode == 'bbox_filtered':
# 包围盒过滤模式:显示包围盒
bboxes = task_config.get('bboxes', [])
if bboxes:
bbox_strs = [f"{bbox.get('name', '')}: min{bbox.get('min', [])} max{bbox.get('max', [])}" for bbox in bboxes]
params_parts.append(f"包围盒: {'; '.join(bbox_strs)}")
filter_info = task_config.get('filter', {})
if filter_info:
params_parts.append(f"过滤: {filter_info.get('type', '')} axis={filter_info.get('axis', '')} top_n={filter_info.get('top_n', '')}")
elif task_type == 'point_to_point':
# 点到点:显示点对
point_pairs = task_config.get('point_pairs', [])
if point_pairs:
pair_strs = [f"{pair.get('name', '')}: {pair.get('point1', [])} <-> {pair.get('point2', [])}" for pair in point_pairs]
params_parts.append(f"点对({len(point_pairs)}对): {'; '.join(pair_strs)}")
return '\n'.join(params_parts) if params_parts else "无"
def _write_result(self, ws, result: MeasurementResult, row: int, task_config: Dict[str, Any]) -> int:
"""
写入单个测量结果
Args:
ws: 工作表
result: 测量结果
row: 起始行号
task_config: 任务配置
Returns:
int: 下一行号
"""
border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
# 基本信息
ws.cell(row=row, column=1).value = result.task_name
ws.cell(row=row, column=2).value = result.task_type
ws.cell(row=row, column=3).value = task_config.get('description', '')
ws.cell(row=row, column=4).value = self._format_task_params(task_config)
ws.cell(row=row, column=5).value = "成功" if result.success else "失败"
# 状态颜色
status_cell = ws.cell(row=row, column=5)
if result.success:
status_cell.fill = PatternFill(start_color="C6EFCE", end_color="C6EFCE", fill_type="solid")
status_cell.font = Font(color="006100")
else:
status_cell.fill = PatternFill(start_color="FFC7CE", end_color="FFC7CE", fill_type="solid")
status_cell.font = Font(color="9C0006")
# 测量数据
if result.success and result.data:
data = result.data
ws.cell(row=row, column=6).value = round(data.get('mean_distance', 0), 2)
ws.cell(row=row, column=7).value = round(data.get('std_distance', 0), 2)
ws.cell(row=row, column=8).value = round(data.get('min_distance', 0), 2)
ws.cell(row=row, column=9).value = round(data.get('max_distance', 0), 2)
ws.cell(row=row, column=10).value = data.get('sample_count', 0)
ws.cell(row=row, column=11).value = data.get('source_roi_points', 0)
ws.cell(row=row, column=12).value = data.get('target_roi_points', 0)
ws.cell(row=row, column=13).value = f"{data.get('source_inlier_ratio', 0) * 100:.1f}%"
ws.cell(row=row, column=14).value = f"{data.get('target_inlier_ratio', 0) * 100:.1f}%"
else:
# 失败时显示错误信息
ws.cell(row=row, column=15).value = result.error_message
# 应用边框和对齐
for col in range(1, 16):
cell = ws.cell(row=row, column=col)
cell.border = border
# 点位参数列使用左对齐,其他居中
if col == 4:
cell.alignment = Alignment(horizontal="left", vertical="center", wrap_text=True)
else:
cell.alignment = Alignment(horizontal="center", vertical="center")
return row + 1
def _adjust_column_widths(self, ws):
"""自动调整列宽"""
column_widths = {
1: 25, # 任务名称
2: 18, # 任务类型
3: 30, # 任务描述
4: 60, # 点位参数
5: 10, # 状态
6: 15, # 平均距离
7: 15, # 标准差
8: 15, # 最小距离
9: 15, # 最大距离
10: 12, # 采样点数
11: 12, # 源ROI点数
12: 12, # 目标ROI点数
13: 15, # 源平面内点率
14: 15, # 目标平面内点率
15: 40 # 错误信息
}
for col, width in column_widths.items():
ws.column_dimensions[get_column_letter(col)].width = width