From ca4bb5c34b26d55d28973c9e7cd6047374cff6e3 Mon Sep 17 00:00:00 2001 From: Xiang Silian Date: Wed, 15 Oct 2025 17:09:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86=E9=A1=B6=E7=82=B9?= =?UTF-8?q?=E5=8C=B9=E9=85=8D=E8=84=9A=E6=9C=AC=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python/face_picture_map.py | 161 +++++++++++++++++++++++++++++++++++++ python/map.h | 58 +++++++++++++ 2 files changed, 219 insertions(+) create mode 100644 python/face_picture_map.py create mode 100644 python/map.h diff --git a/python/face_picture_map.py b/python/face_picture_map.py new file mode 100644 index 0000000..d55b84c --- /dev/null +++ b/python/face_picture_map.py @@ -0,0 +1,161 @@ +import numpy as np + +def read_obj_vertices(obj_file_path): + """读取OBJ文件中的顶点数据""" + vertices = [] + with open(obj_file_path, 'r') as f: + for line in f: + line = line.strip() + if line.startswith('v ') and not line.startswith('vt ') and not line.startswith('vn '): + parts = line.split() + if len(parts) >= 4: + # 提取顶点坐标,转换为浮点数 + vertex = [float(parts[1]), float(parts[2]), float(parts[3])] + vertices.append(vertex) + return np.array(vertices) + +def find_vertex_mapping(original_vertices, modified_vertices, tolerance=1e-4): + """ + 找到修改后顶点在原始顶点中的索引映射 + 使用严格的数值匹配,匹配失败或重复匹配都会报错 + """ + mapping = [] + used_indices = set() + + for i, mod_vertex in enumerate(modified_vertices): + matches = [] + + # 查找所有在容差范围内的匹配顶点 + for j, orig_vertex in enumerate(original_vertices): + if np.allclose(mod_vertex, orig_vertex, atol=tolerance): + matches.append(j) + + # 检查匹配结果 + if len(matches) == 0: + # 没有找到匹配 + print(f"错误: 无法为修改后顶点 {i} ({mod_vertex}) 找到匹配的原始顶点") + print(f" 最近的原始顶点:") + # 找到最近的几个顶点供参考 + distances = [] + for j, orig_vertex in enumerate(original_vertices): + dist = np.linalg.norm(mod_vertex - orig_vertex) + distances.append((dist, j, orig_vertex)) + distances.sort() + for dist, j, vertex in distances[:3]: # 显示最近的3个 + print(f" 索引 {j}: {vertex}, 距离: {dist:.6f}") + raise ValueError(f"顶点 {i} 匹配失败") + + elif len(matches) > 1: + # 找到多个匹配 + print(f"错误: 为修改后顶点 {i} ({mod_vertex}) 找到多个匹配的原始顶点:") + for match_idx in matches: + print(f" 原始顶点索引 {match_idx}: {original_vertices[match_idx]}") + raise ValueError(f"顶点 {i} 匹配到多个原始顶点") + + else: + # 找到一个匹配 + match_idx = matches[0] + if match_idx in used_indices: + print(f"错误: 原始顶点 {match_idx} 已经被多个修改后顶点匹配") + print(f" 当前修改后顶点 {i}: {mod_vertex}") + # 找出哪个修改后顶点已经匹配了这个原始顶点 + for k, mapped_idx in enumerate(mapping): + if mapped_idx == match_idx: + print(f" 已经被修改后顶点 {k}: {modified_vertices[k]} 匹配") + raise ValueError(f"原始顶点 {match_idx} 被重复匹配") + + mapping.append(match_idx) + used_indices.add(match_idx) + + return mapping + +def validate_mapping_completeness(mapping, original_vertices, modified_vertices): + """验证映射的完整性""" + print("\n验证映射完整性...") + + # 检查是否有未匹配的原始顶点 + all_original_indices = set(range(len(original_vertices))) + used_original_indices = set(mapping) + unused_original_indices = all_original_indices - used_original_indices + + if unused_original_indices: + print(f"警告: 有 {len(unused_original_indices)} 个原始顶点未被使用:") + for idx in list(unused_original_indices)[:5]: # 只显示前5个 + print(f" 原始顶点 {idx}: {original_vertices[idx]}") + if len(unused_original_indices) > 5: + print(f" ... 还有 {len(unused_original_indices) - 5} 个") + + # 检查顶点数量是否一致 + if len(modified_vertices) != len(original_vertices): + print(f"警告: 顶点数量不一致 - 原始: {len(original_vertices)}, 修改后: {len(modified_vertices)}") + + return len(unused_original_indices) == 0 + +def generate_header_file(mapping, output_file="map.h"): + """生成C++头文件""" + with open(output_file, 'w') as f: + f.write("#ifndef VERTEX_MAP_H\n") + f.write("#define VERTEX_MAP_H\n\n") + f.write("// 顶点索引映射表\n") + f.write("// 用于将face_picture_3dmax.obj的顶点索引映射到face_picture.obj的顶点索引\n") + f.write("// 生成说明: modified_vertex_index -> original_vertex_index\n") + f.write("// 注意: 这个映射是严格的一对一匹配,匹配容差为1e-4\n") + f.write(f"const int indexMap[{len(mapping)}] = {{\n") + + # 每行输出10个元素 + for i in range(0, len(mapping), 10): + line_indices = mapping[i:i+10] + line = " " + ", ".join(f"{idx:3d}" for idx in line_indices) + if i + 10 < len(mapping): + line += "," + f.write(line + "\n") + + f.write("};\n\n") + f.write("#endif // VERTEX_MAP_H\n") + +def main(): + # 文件路径 + original_obj = "../assets/face_picture.obj" + modified_obj = "../assets/face_picture_3dmax.obj" + output_header = "map.h" + + # 匹配容差 - 根据你的数据精度调整这个值 + tolerance = 1e-4 + + print("正在读取OBJ文件...") + + try: + # 读取顶点数据 + original_vertices = read_obj_vertices(original_obj) + modified_vertices = read_obj_vertices(modified_obj) + + print(f"原始文件顶点数: {len(original_vertices)}") + print(f"修改文件顶点数: {len(modified_vertices)}") + print(f"匹配容差: {tolerance}") + + # 找到顶点映射 + print("正在严格匹配顶点...") + mapping = find_vertex_mapping(original_vertices, modified_vertices, tolerance) + + # 验证完整性 + is_complete = validate_mapping_completeness(mapping, original_vertices, modified_vertices) + + if is_complete: + print("✓ 所有原始顶点都被正确使用") + else: + print("⚠ 部分原始顶点未被使用") + + print(f"✓ 成功匹配所有 {len(mapping)} 个顶点") + + # 生成头文件 + print(f"生成头文件: {output_header}") + generate_header_file(mapping, output_header) + + print("\n完成!映射文件已生成。") + + except Exception as e: + print(f"\n❌ 错误: {e}") + print("映射生成失败,请检查数据或调整匹配容差") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/python/map.h b/python/map.h new file mode 100644 index 0000000..e319754 --- /dev/null +++ b/python/map.h @@ -0,0 +1,58 @@ +#ifndef VERTEX_MAP_H +#define VERTEX_MAP_H + +// ӳ +// ڽface_picture_3dmax.objĶӳ䵽face_picture.objĶ +// ˵: modified_vertex_index -> original_vertex_index +// ע: ӳϸһһƥ䣬ƥݲΪ1e-4 +const int indexMap[468] = { + 127, 34, 139, 11, 0, 37, 232, 231, 120, 72, + 39, 128, 121, 47, 104, 69, 67, 175, 171, 148, + 118, 50, 101, 73, 40, 9, 151, 108, 48, 115, + 131, 194, 204, 211, 74, 185, 80, 42, 183, 92, + 186, 230, 229, 202, 212, 214, 83, 18, 17, 76, + 61, 146, 160, 29, 30, 56, 157, 173, 106, 135, + 192, 203, 165, 98, 21, 71, 68, 51, 45, 4, + 144, 24, 23, 77, 91, 205, 187, 201, 200, 182, + 90, 181, 85, 84, 206, 36, 140, 193, 189, 244, + 159, 158, 28, 247, 246, 161, 236, 3, 196, 54, + 168, 8, 117, 228, 31, 55, 97, 99, 126, 100, + 166, 79, 218, 155, 154, 26, 209, 49, 136, 150, + 217, 223, 52, 53, 134, 170, 43, 119, 226, 130, + 63, 238, 20, 242, 46, 70, 156, 78, 62, 96, + 143, 227, 123, 111, 44, 125, 19, 216, 153, 22, + 167, 208, 142, 57, 60, 35, 113, 27, 210, 225, + 137, 116, 41, 38, 129, 64, 240, 102, 207, 184, + 169, 149, 176, 105, 66, 122, 6, 147, 65, 107, + 89, 180, 93, 15, 86, 14, 87, 145, 88, 179, + 95, 138, 172, 215, 58, 219, 81, 195, 199, 82, + 163, 110, 234, 109, 235, 191, 222, 141, 221, 197, + 25, 7, 33, 220, 237, 245, 162, 188, 174, 2, + 241, 164, 12, 13, 198, 133, 112, 243, 239, 190, + 32, 178, 132, 177, 1, 213, 59, 94, 75, 224, + 233, 114, 124, 356, 389, 368, 302, 267, 452, 350, + 349, 303, 269, 357, 343, 277, 453, 333, 332, 297, + 152, 377, 347, 348, 330, 304, 270, 336, 337, 278, + 279, 360, 418, 262, 431, 408, 409, 310, 415, 407, + 410, 450, 422, 430, 434, 313, 314, 306, 307, 375, + 387, 388, 260, 286, 414, 398, 335, 406, 364, 367, + 416, 423, 358, 327, 251, 284, 298, 281, 5, 373, + 374, 253, 320, 321, 425, 427, 411, 421, 405, 404, + 315, 16, 426, 266, 400, 369, 322, 391, 417, 465, + 464, 386, 257, 258, 466, 456, 399, 419, 285, 346, + 340, 261, 413, 441, 460, 328, 355, 371, 329, 392, + 439, 438, 382, 341, 256, 429, 420, 394, 379, 437, + 443, 444, 283, 275, 440, 363, 338, 273, 451, 446, + 342, 467, 293, 334, 282, 458, 461, 462, 276, 353, + 383, 308, 324, 325, 300, 372, 345, 447, 352, 274, + 248, 436, 381, 252, 393, 428, 287, 250, 384, 265, + 259, 424, 292, 366, 271, 294, 455, 272, 432, 395, + 299, 351, 280, 319, 295, 296, 403, 323, 454, 316, + 380, 318, 402, 365, 435, 397, 344, 311, 291, 396, + 268, 445, 254, 339, 449, 264, 10, 442, 370, 263, + 255, 359, 412, 301, 378, 326, 457, 362, 459, 463, + 354, 401, 361, 309, 376, 433, 289, 305, 448, 290, + 288, 249, 103, 385, 331, 317, 312, 390 +}; + +#endif // VERTEX_MAP_H