I'm coloring a mesh using vertex colors:
mesh = cur_obj.data
vcol_layer = mesh.vertex_colors.new('Col')
for ind in indices:
vcol_layer.data[ind].colors = colors[ind]
I don't have values for all the vertices. Can I get values for these vertices by smoothing?
****** Update **********
Let me be more clear. Here is the code I'm using to color objects:
mesh = cur_obj.data
scn = bpy.context.scene
valid_verts = np.where(np.abs(verts_values) >= threshold)[0]
verts_colors = calc_colors(verts_values)
scn.objects.active = cur_obj
cur_obj.select = True
vcol_layer = mesh.vertex_colors.new('Col')
for vert in valid_verts:
x = lookup[vert]
for loop_ind in x[x > -1]:
d = vcol_layer.data[loop_ind]
d.color = verts_colors[vert]
Where the lookup table was created using the following function:
def calc_faces_verts(verts, faces, out_file):
_faces = faces.ravel()
faces_arg_sort = np.argsort(_faces)
faces_sort = np.sort(_faces)
faces_count = Counter(faces_sort)
max_len = max([v for v in faces_count.values()])
lookup = np.ones((verts.shape[0], max_len)) * -1
diff = np.diff(faces_sort)
n = 0
for ind, (k, v) in enumerate(zip(faces_sort, faces_arg_sort)):
lookup[k, n] = v
n = 0 if ind < len(diff) and diff[ind] > 0 else n + 1
np.save(out_file, lookup.astype(np.int))
if len(_faces) != int(np.max(lookup)) + 1:
raise Exception('Wrong values in lookup table! ' +
'faces ravel: {}, max looup val: {}'.format(len(_faces), int(np.max(lookup))))
This is working great when I have a value per vertice. The problem starts when this isn't the case. For example, I have an object with >100k vertices, where only ~4k have values. Currently, I'm smoothing those values on the surface outside Blender and saves the result to a numpy array (verts_values). The question is whether I can do the smoothing inside Blender (which hopefully will be also faster), without the necessity of a pre-processing step.
**** Update ****
You can find here some example files:
- vertices.npy - An array of vertices indices that I have values for
- data.npy - values for vertices.npy
- lh.pial.ply - The object I want to color
- lh.pial.npz - Same as 3, but in here the vertices and faces are stored in a npz file (faster and easier to read)
To calculate the colors, I just pick a colormap (jet for example), and do something like this:
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.cm as cmx
def array_to_color(x, color_map):
x_min, x_max = np.min(x), np.max(x)
cm = plt.get_cmap(color_map)
cNorm = matplotlib.colors.Normalize(vmin=x_min, vmax=x_max)
scalar_map = cmx.ScalarMappable(norm=cNorm, cmap=cm)
return scalar_map.to_rgba(x)
**** Update ****
I've used both suggested solutions, and added another option to set a threshold for the values. Here is the code:
import bpy
import numpy as np
from collections import defaultdict
def color_vertices_rgb(obj, vert_colors, fixed_vertices, vert_links, verts_to_loop, loops_num=25):
"""Merges the colors assigned to the FIXED vertices across the rest of the mesh"""
obj.update_from_editmode()
mesh = obj.data
scn = bpy.context.scene
# check if our mesh already has Vertex Colors, and if not add some... (first we need to make sure it's the active object)
scn.objects.active = obj
obj.select = True
mesh.vertex_colors.new()
print("Captured.")
for ind in range(loops_num):
new_colors = {}
for vert in obj.data.vertices:
vertidx = vert.index
if vertidx not in fixed_vertices:
new_colors[vertidx] = average_color(vert_colors, vert_links, vertidx)
# Copy new colors back to vert_colors
for vert in obj.data.vertices:
vertidx = vert.index
if vertidx not in fixed_vertices:
vert_colors[vertidx] = new_colors[vertidx]
print("Storing colors...")
for vert in obj.data.vertices:
vertidx = vert.index
set_vertex_color(obj, vertidx, vert_colors[vertidx], verts_to_loop)
def set_vertex_color(obj, vertid, color, verts_to_loop):
for loop_index in verts_to_loop[vertid]:
obj.data.vertex_colors[0].data[loop_index].color = color
def make_verts_to_loop_lookup(obj):
verts_to_loop_verts = defaultdict(list)
for li, l in enumerate(obj.data.loops):
verts_to_loop_verts[l.vertex_index].append(li)
return verts_to_loop_verts
def average_color(vert_colors, vert_links, vertid):
r = 0.0
g = 0.0
b = 0.0
num_verts = 0
for linkedvertid in vert_links[vertid]:
col = vert_colors[linkedvertid]
r = r + col[0]
g = g + col[1]
b = b + col[2]
num_verts += 1
return (r / num_verts, g / num_verts, b / num_verts)
def get_linked_verts(obj, vertid):
verts = []
for edge in obj.data.edges:
if edge.vertices[0] == vertid:
verts.append(edge.vertices[1])
elif edge.vertices[1] == vertid:
verts.append(edge.vertices[0])
return verts
def create_linked_verts_lookup(obj):
verts_lookup = defaultdict(list)
for ind, edge in enumerate(obj.data.edges):
verts_lookup[edge.vertices[0]].append(edge.vertices[1])
verts_lookup[edge.vertices[1]].append(edge.vertices[0])
return verts_lookup
def get_vertices_colors(obj, fixed_vertices_set, fixed_vertices_data, default_color=(1, 1, 1)):
fixed_vertices_colors = calc_colors(fixed_vertices_data)
vert_colors = {}
fixed_verts_ind = 0
for vert in obj.data.vertices:
vert_ind = vert.index
if vert_ind in fixed_vertices_set:
vert_colors[vert_ind] = fixed_vertices_colors[fixed_verts_ind]
fixed_verts_ind += 1
else:
vert_colors[vert_ind] = default_color
return vert_colors
def calc_colors(data):
# todo: Calc the colors according to the data
colors = None
return colors
def get_fixed_vertices(fixed_vertices_data, threshold=0):
fixed_vertices = np.load('vertices.npy')
fixed_vertices_set = set()
for ind, fix_vert in enumerate(fixed_vertices):
if abs(fixed_vertices_data[ind]) > threshold:
fixed_vertices_set.add(fix_vert)
return fixed_vertices_set
threshold = 1
default_color = (1, 1, 1)
loops_num = 250
object_name = 'my_object'
obj = bpy.data.objects[object_name]
fixed_vertices_data = np.load('data.npy')
fixed_vertices_set = get_fixed_vertices(fixed_vertices_data, threshold)
linked_verts_lookup = create_linked_verts_lookup(obj)
verts_to_loop_verts = make_verts_to_loop_lookup(obj)
vertices_colors = get_vertices_colors(obj, fixed_vertices_set, fixed_vertices_data, default_color)
color_vertices_rgb(obj, vertices_colors, fixed_vertices_set, linked_verts_lookup, verts_to_loop_verts, loops_num)
The calc_colors function implementation is out of this scope, so I left it empty. Outside Blender I've created colormap numpy array (cm: 256x3) from matplotlib, and I'm using this function to calculate the colors:
def calc_colors(vert_values, data, cm):
data_min, data_max = np.min(data), np.max(data)
colors_ratio = 256 / (data_max - data_min)
colors_indices = ((np.array(vert_values) - data_min) * colors_ratio).astype(int)
verts_colors = cm[colors_indices]
return verts_colors
Thanks for your help! I wish I could split the bounty...