File size: 7,336 Bytes
736dcf7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# Modified from https://github.com/PeizhuoLi/neural-blend-shapes/blob/main/blender_scripts/vertex_color.py
import bpy
import numpy as np
import random
import os
import matplotlib.pyplot as plt

def generate_color_table(num_colors, base_color_path):
    base_colors = np.load(base_color_path)
    idx = list(range(base_colors.shape[0]))
    random.seed(5)
    random.shuffle(idx)
    pt = 0
    res = []
    for i in range(num_colors):
        res.append(base_colors[idx[pt]])
        pt += 1
        pt %= base_colors.shape[0]
    return np.array(res)

def weight2color(weight, colors):
    res = np.matmul(weight, colors)
    res = np.clip(res, 0, 1)
    return res

def export_obj_with_vertex_colors(obj_object, vertex_colors, filepath):
    mesh = obj_object.data

    with open(filepath, 'w') as f:
        f.write("# Exported OBJ with vertex colors\n")
        for v, color in zip(mesh.vertices, vertex_colors):
            f.write("v {} {} {} {} {} {}\n".format(
                v.co.x, v.co.y, v.co.z,
                color[0], color[1], color[2]
            ))
        
        f.write("g {}\n".format(obj_object.name))
        for poly in mesh.polygons:
            vertices = [v + 1 for v in poly.vertices]
            face = "f " + " ".join([str(v) for v in vertices]) + "\n"
            f.write(face)

def compute_error_map(weight_method, weight_gt):
    error = np.abs(weight_method - weight_gt)  # Shape: (n_vertex, n_color)
    error_map = np.max(error, axis=1)  # Taking the maximum error across all colors for each vertex
    # error_map = np.mean(error, axis=1) 
    # Normalize error_map to [0,1]
    error_map = np.clip(error_map, 0, 1)
    return error_map

def error_map_to_color(error_map, colormap='Reds'):
    cmap = plt.get_cmap(colormap)
    colors = cmap(error_map)[:, :3]  # Discard alpha channel
    return colors

def generate_color_bar(filepath, colormap='Reds'):
    fig, ax = plt.subplots(figsize=(8, 1.5))
    fig.subplots_adjust(bottom=0.5)

    cmap = plt.get_cmap(colormap)
    norm = plt.Normalize(vmin=0, vmax=1)
    cb1 = plt.colorbar(plt.cm.ScalarMappable(norm=norm, cmap=cmap),
                      cax=ax, orientation='horizontal')
    cb1.ax.tick_params(labelsize=26) 
    plt.savefig(filepath, bbox_inches='tight')
    plt.close()

def main():
    base_color_path = "utils/tableau_color.npy"

    ### xlv2_test
    obj_path = "xlv2_test_mesh" # save from npz files, check skeleton/data_utils/convert_npz_to_mesh_rig.py
    weight_path1 = "gt_onxlv2_npy"
    weight_path2 = "ours_onxlv2_npy"
    # weight_path3 = "rignet_onxlv2_npy"
    # weight_path4 = "gvb_onxlv2_npy"
    save_path = "obj_w_skincolor_errormap_onxlv2_test"
    
    color_bar_path = "error_color_bar.png" # Path to save the color bar image

    if not os.path.exists(save_path):
        os.makedirs(save_path)
        
    generate_color_bar(color_bar_path, colormap='Reds')

    bpy.ops.object.select_all(action='SELECT')
    bpy.ops.object.delete(use_global=False)

    for obj_file in os.listdir(obj_path):
        obj_file_path = os.path.join(obj_path, obj_file)
        wt_gt_file = os.path.join(weight_path1, obj_file.replace(".obj", "_skin.npy"))
        wt_ours_file = os.path.join(weight_path2, obj_file.replace(".obj", "_skin.npy"))
        # wt_rignet_file = os.path.join(weight_path3, obj_file.replace(".obj", "_skin.npy"))
        # wt_gvb_file = os.path.join(weight_path4, obj_file.replace(".obj", "_skin.npy"))
        if not os.path.exists(wt_gt_file) or not os.path.exists(wt_ours_file): # or not os.path.exists(wt_rignet_file) or not os.path.exists(wt_gvb_file):
            continue

        # 导入OBJ文件
        bpy.ops.wm.obj_import(filepath=obj_file_path)
        imported_objects = bpy.context.selected_objects
        if not imported_objects:
            print("Failed to import OBJ file:", obj_file_path)
            continue
        obj = imported_objects[0]

        weight_gt = np.load(wt_gt_file)
        weight_ours = np.load(wt_ours_file)
        # weight_rignet = np.load(wt_rignet_file)
        # weight_gvb = np.load(wt_gvb_file)
        if weight_gt.shape != weight_ours.shape: # or weight_gt.shape != weight_rignet.shape or weight_gt.shape != weight_gvb.shape:
            print("Weight shape mismatch among files")
            bpy.ops.object.select_all(action='SELECT')
            bpy.ops.object.delete(use_global=False)
            continue

        if len(weight_gt.shape) != 2:
            print("Weight file should be 2D array")
            bpy.ops.object.select_all(action='SELECT')
            bpy.ops.object.delete(use_global=False)
            continue

        n_vertices = len(obj.data.vertices)
        if weight_gt.shape[0] != n_vertices:
            print(f"Vertex count mismatch: OBJ has {n_vertices}, but weight file has {weight_gt.shape[0]}")
            bpy.ops.object.select_all(action='SELECT')
            bpy.ops.object.delete(use_global=False)
            continue

        # generate colors
        n_color = weight_gt.shape[1]
        colors = generate_color_table(n_color, base_color_path)
        vertex_colors_gt = weight2color(weight_gt, colors)
        vertex_colors_ours = weight2color(weight_ours, colors)
        # vertex_colors_rignet = weight2color(weight_rignet, colors)
        # vertex_colors_gvb = weight2color(weight_gvb, colors)

        # Save obj with vertex colors
        output_path_gt = os.path.join(save_path, obj_file.replace(".obj", "_gt.obj"))
        output_path_ours = os.path.join(save_path, obj_file.replace(".obj", "_ours.obj"))
        # output_path_rignet = os.path.join(save_path, obj_file.replace(".obj", "_rignet.obj"))
        # output_path_gvb = os.path.join(save_path, obj_file.replace(".obj", "_gvb.obj"))
        export_obj_with_vertex_colors(obj, vertex_colors_gt, output_path_gt)
        export_obj_with_vertex_colors(obj, vertex_colors_ours, output_path_ours)
        # export_obj_with_vertex_colors(obj, vertex_colors_rignet, output_path_rignet)
        # export_obj_with_vertex_colors(obj, vertex_colors_gvb, output_path_gvb)

        # Save object with error color map
        # 1. Ours vs GT
        error_ours = compute_error_map(weight_ours, weight_gt)
        colors_error_ours = error_map_to_color(error_ours, colormap='Reds')
        output_path_error_ours = os.path.join(save_path, obj_file.replace(".obj", "_error_ours.obj"))
        export_obj_with_vertex_colors(obj, colors_error_ours, output_path_error_ours)

        # 2. Rignet vs GT
        # error_rignet = compute_error_map(weight_rignet, weight_gt)
        # colors_error_rignet = error_map_to_color(error_rignet, colormap='Reds')
        # output_path_error_rignet = os.path.join(save_path, obj_file.replace(".obj", "_error_rignet.obj"))
        # export_obj_with_vertex_colors(obj, colors_error_rignet, output_path_error_rignet)

        # 3. GVB vs GT
        # error_gvb = compute_error_map(weight_gvb, weight_gt)
        # colors_error_gvb = error_map_to_color(error_gvb, colormap='Reds')
        # output_path_error_gvb = os.path.join(save_path, obj_file.replace(".obj", "_error_gvb.obj"))
        # export_obj_with_vertex_colors(obj, colors_error_gvb, output_path_error_gvb)

        bpy.ops.object.select_all(action='SELECT')
        bpy.ops.object.delete(use_global=False)

    print("Done")

if __name__ == "__main__":
    main()