Spaces:
Paused
Paused
Add code/cube3d/training/ldr_coordinate_analyzer.py
Browse files
code/cube3d/training/ldr_coordinate_analyzer.py
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import re
|
| 3 |
+
import numpy as np
|
| 4 |
+
import matplotlib.pyplot as plt
|
| 5 |
+
from matplotlib.ticker import MaxNLocator
|
| 6 |
+
import pandas as pd
|
| 7 |
+
from glob import glob
|
| 8 |
+
|
| 9 |
+
# 设置中文字体
|
| 10 |
+
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
|
| 11 |
+
plt.rcParams["axes.unicode_minus"] = False # 正确显示负号
|
| 12 |
+
|
| 13 |
+
def parse_ldr_file(file_path):
|
| 14 |
+
"""解析LDR文件,提取零件的XYZ坐标(修复:用空格分割,兼容不同格式)"""
|
| 15 |
+
coordinates = []
|
| 16 |
+
|
| 17 |
+
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
|
| 18 |
+
for line_num, line in enumerate(f, 1):
|
| 19 |
+
line = line.strip()
|
| 20 |
+
# 只处理以1开头的零件行,且排除空行
|
| 21 |
+
if line.startswith('1') and len(line.split()) >= 5:
|
| 22 |
+
try:
|
| 23 |
+
# 核心修复:用split()按任意空格分割,取第3、4、5个元素(索引2、3、4)
|
| 24 |
+
parts = line.split()
|
| 25 |
+
x = float(parts[2]) # 原正则的group(3)对应split后的索引2
|
| 26 |
+
y = float(parts[3]) # 原正则的group(4)对应split后的索引3
|
| 27 |
+
z = float(parts[4]) # 原正则的group(5)对应split后的索引4
|
| 28 |
+
coordinates.append((x, y, z))
|
| 29 |
+
except (ValueError, IndexError) as e:
|
| 30 |
+
# 详细打印错误行,方便排查格式问题
|
| 31 |
+
print(f"警告: 文件 {os.path.basename(file_path)} 第 {line_num} 行解析失败 - {e}")
|
| 32 |
+
print(f" 错误行内容: {line}")
|
| 33 |
+
|
| 34 |
+
return np.array(coordinates) if coordinates else None
|
| 35 |
+
|
| 36 |
+
def translate_coordinates(coords):
|
| 37 |
+
"""将坐标平移到原点附近,平移量为max_x/2, max_y/2, max_z/2"""
|
| 38 |
+
if coords is None or len(coords) == 0:
|
| 39 |
+
return None, (0, 0, 0)
|
| 40 |
+
|
| 41 |
+
# 计算各维度最大值(确保坐标≥0的前提下)
|
| 42 |
+
max_x = np.max(coords[:, 0])
|
| 43 |
+
max_y = np.max(coords[:, 1])
|
| 44 |
+
max_z = np.max(coords[:, 2])
|
| 45 |
+
|
| 46 |
+
# 计算平移量(向原点方向移动一半最大值)
|
| 47 |
+
tx = max_x / 2
|
| 48 |
+
ty = max_y / 2
|
| 49 |
+
tz = max_z / 2
|
| 50 |
+
|
| 51 |
+
# 执行平移
|
| 52 |
+
translated = coords - np.array([tx, ty, tz])
|
| 53 |
+
|
| 54 |
+
return translated, (tx, ty, tz)
|
| 55 |
+
|
| 56 |
+
def analyze_ldr_folder(folder_path):
|
| 57 |
+
"""分析文件夹中所有LDR文件的坐标分布"""
|
| 58 |
+
all_coords = []
|
| 59 |
+
# 递归获取所有LDR文件(包括子文件夹)
|
| 60 |
+
ldr_files = glob(os.path.join(folder_path, '**', '*.ldr'), recursive=True)
|
| 61 |
+
|
| 62 |
+
if not ldr_files:
|
| 63 |
+
print(f"错误:在 {folder_path} 中未找到任何LDR文件")
|
| 64 |
+
return None
|
| 65 |
+
|
| 66 |
+
print(f"找到 {len(ldr_files)} 个LDR文件,开始分析...")
|
| 67 |
+
|
| 68 |
+
for file_idx, file_path in enumerate(ldr_files, 1):
|
| 69 |
+
# 解析单个文件的坐标
|
| 70 |
+
coords = parse_ldr_file(file_path)
|
| 71 |
+
if coords is None or len(coords) == 0:
|
| 72 |
+
print(f"文件 {file_idx}/{len(ldr_files)}: {os.path.basename(file_path)} 中未找到有效坐标")
|
| 73 |
+
continue
|
| 74 |
+
|
| 75 |
+
# 平移坐标到原点附近
|
| 76 |
+
translated_coords, translation = translate_coordinates(coords)
|
| 77 |
+
|
| 78 |
+
# 收集所有平移后的坐标
|
| 79 |
+
all_coords.append(translated_coords)
|
| 80 |
+
|
| 81 |
+
# 每处理10个文件或最后一个文件时,打印进度
|
| 82 |
+
if file_idx % 10 == 0 or file_idx == len(ldr_files):
|
| 83 |
+
total_parts = sum(len(c) for c in all_coords)
|
| 84 |
+
print(f"已处理 {file_idx}/{len(ldr_files)} 个文件,累计零件数: {total_parts}")
|
| 85 |
+
|
| 86 |
+
if not all_coords:
|
| 87 |
+
print("未找到任何有效坐标数据,终止分析")
|
| 88 |
+
return None
|
| 89 |
+
|
| 90 |
+
# 合并所有文件的坐标(按行堆叠)
|
| 91 |
+
all_coords = np.vstack(all_coords)
|
| 92 |
+
print(f"\n分析完成!共处理 {len(all_coords)} 个零件坐标")
|
| 93 |
+
|
| 94 |
+
return all_coords
|
| 95 |
+
|
| 96 |
+
def plot_coordinate_distributions(coords, output_dir):
|
| 97 |
+
"""绘制XYZ三个维度的坐标分布直方图"""
|
| 98 |
+
if coords is None or len(coords) == 0:
|
| 99 |
+
print("没有可绘制的坐标数据")
|
| 100 |
+
return
|
| 101 |
+
|
| 102 |
+
# 创建输出目录(不存在则自动创建)
|
| 103 |
+
os.makedirs(output_dir, exist_ok=True)
|
| 104 |
+
|
| 105 |
+
# 提取XYZ三个维度的坐标
|
| 106 |
+
x_coords = coords[:, 0]
|
| 107 |
+
y_coords = coords[:, 1]
|
| 108 |
+
z_coords = coords[:, 2]
|
| 109 |
+
|
| 110 |
+
# 计算各维度的统计信息(最小值、最大值、平均值、标准差)
|
| 111 |
+
stats = {
|
| 112 |
+
'X轴': {
|
| 113 |
+
'最小值': np.min(x_coords),
|
| 114 |
+
'最大值': np.max(x_coords),
|
| 115 |
+
'平均值': np.mean(x_coords),
|
| 116 |
+
'标准差': np.std(x_coords)
|
| 117 |
+
},
|
| 118 |
+
'Y轴': {
|
| 119 |
+
'最小值': np.min(y_coords),
|
| 120 |
+
'最大值': np.max(y_coords),
|
| 121 |
+
'平均值': np.mean(y_coords),
|
| 122 |
+
'标准差': np.std(y_coords)
|
| 123 |
+
},
|
| 124 |
+
'Z轴': {
|
| 125 |
+
'最小值': np.min(z_coords),
|
| 126 |
+
'最大值': np.max(z_coords),
|
| 127 |
+
'平均值': np.mean(z_coords),
|
| 128 |
+
'标准差': np.std(z_coords)
|
| 129 |
+
}
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
# 打印统计信息(控制台直观查看)
|
| 133 |
+
print("\n=== 坐标分布统计信息 ===")
|
| 134 |
+
for axis, info in stats.items():
|
| 135 |
+
print(f"{axis}:")
|
| 136 |
+
for key, val in info.items():
|
| 137 |
+
print(f" {key}: {val:.2f}")
|
| 138 |
+
|
| 139 |
+
# 保存统计信息到CSV文件(便于后续分析)
|
| 140 |
+
stats_df = pd.DataFrame(stats).T
|
| 141 |
+
stats_csv_path = os.path.join(output_dir, 'coordinate_stats.csv')
|
| 142 |
+
stats_df.to_csv(stats_csv_path, encoding='utf-8-sig')
|
| 143 |
+
print(f"\n统计信息已保存到: {stats_csv_path}")
|
| 144 |
+
|
| 145 |
+
# 绘制XYZ三个维度的分布直方图(1行3列布局)
|
| 146 |
+
fig, axes = plt.subplots(1, 3, figsize=(18, 6))
|
| 147 |
+
bins = 50 # 直方图分箱数(可根据数据量调整,50适合中等数据量)
|
| 148 |
+
|
| 149 |
+
# X轴坐标分布(红色)
|
| 150 |
+
axes[0].hist(x_coords, bins=bins, alpha=0.7, color='#FF6B6B')
|
| 151 |
+
axes[0].set_title(f'X轴坐标分布\n(均值: {stats["X轴"]["平均值"]:.2f}, 标准差: {stats["X轴"]["标准差"]:.2f})', pad=15)
|
| 152 |
+
axes[0].set_xlabel('坐标值', fontsize=12)
|
| 153 |
+
axes[0].set_ylabel('频数', fontsize=12)
|
| 154 |
+
axes[0].grid(True, alpha=0.3)
|
| 155 |
+
axes[0].xaxis.set_major_locator(MaxNLocator(6)) # 限制X轴刻度数量,避免拥挤
|
| 156 |
+
|
| 157 |
+
# Y轴坐标分布(绿色)
|
| 158 |
+
axes[1].hist(y_coords, bins=bins, alpha=0.7, color='#4ECDC4')
|
| 159 |
+
axes[1].set_title(f'Y轴坐标分布\n(均值: {stats["Y轴"]["平均值"]:.2f}, 标准差: {stats["Y轴"]["标准差"]:.2f})', pad=15)
|
| 160 |
+
axes[1].set_xlabel('坐标值', fontsize=12)
|
| 161 |
+
axes[1].set_ylabel('频数', fontsize=12)
|
| 162 |
+
axes[1].grid(True, alpha=0.3)
|
| 163 |
+
axes[1].xaxis.set_major_locator(MaxNLocator(6))
|
| 164 |
+
|
| 165 |
+
# Z轴坐标分布(蓝色)
|
| 166 |
+
axes[2].hist(z_coords, bins=bins, alpha=0.7, color='#45B7D1')
|
| 167 |
+
axes[2].set_title(f'Z轴坐标分布\n(均值: {stats["Z轴"]["平均值"]:.2f}, 标准差: {stats["Z轴"]["标准差"]:.2f})', pad=15)
|
| 168 |
+
axes[2].set_xlabel('坐标值', fontsize=12)
|
| 169 |
+
axes[2].set_ylabel('频数', fontsize=12)
|
| 170 |
+
axes[2].grid(True, alpha=0.3)
|
| 171 |
+
axes[2].xaxis.set_major_locator(MaxNLocator(6))
|
| 172 |
+
|
| 173 |
+
# 调整子图间距,避免标题/标签重叠
|
| 174 |
+
plt.tight_layout()
|
| 175 |
+
|
| 176 |
+
# 保存图片(高分辨率300dpi,适合后续使用)
|
| 177 |
+
plot_path = os.path.join(output_dir, 'coordinate_distributions.png')
|
| 178 |
+
plt.savefig(plot_path, dpi=300, bbox_inches='tight')
|
| 179 |
+
print(f"坐标分布图已保存到: {plot_path}")
|
| 180 |
+
|
| 181 |
+
# 显示图片(如果运行环境支持GUI)
|
| 182 |
+
plt.show()
|
| 183 |
+
|
| 184 |
+
if __name__ == "__main__":
|
| 185 |
+
# 配置路径(请根据实际情况修改)
|
| 186 |
+
LDR_FOLDER = "/public/home/wangshuo/gap/assembly/data/car_1k/subset_self/ldr_l30_rotrans_expand_wom" # LDR文件根目录
|
| 187 |
+
OUTPUT_DIR = "./ldr_coordinate_analysis" # 结果输出目录(统计CSV+分布图)
|
| 188 |
+
|
| 189 |
+
# 1. 分析所有LDR文件的坐标
|
| 190 |
+
all_translated_coords = analyze_ldr_folder(LDR_FOLDER)
|
| 191 |
+
|
| 192 |
+
# 2. 绘制并保存坐标分布图
|
| 193 |
+
if all_translated_coords is not None:
|
| 194 |
+
plot_coordinate_distributions(all_translated_coords, OUTPUT_DIR)
|